tutil.c - 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
       ---
       tutil.c (8372B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <draw.h>
            4 #include <thread.h>
            5 #include <cursor.h>
            6 #include <mouse.h>
            7 #include <keyboard.h>
            8 #include <frame.h>
            9 #include <fcall.h>
           10 #include <plumb.h>
           11 #include <libsec.h>
           12 #include "dat.h"
           13 #include "fns.h"
           14 
           15 static        Point                prevmouse;
           16 static        Window        *mousew;
           17 
           18 Range
           19 range(int q0, int q1)
           20 {
           21         Range r;
           22 
           23         r.q0 = q0;
           24         r.q1 = q1;
           25         return r;
           26 }
           27 
           28 Runestr
           29 runestr(Rune *r, uint n)
           30 {
           31         Runestr rs;
           32 
           33         rs.r = r;
           34         rs.nr = n;
           35         return rs;
           36 }
           37 
           38 void
           39 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
           40 {
           41         uchar *q;
           42         Rune *s;
           43         int j, w;
           44 
           45         /*
           46          * Always guaranteed that n bytes may be interpreted
           47          * without worrying about partial runes.  This may mean
           48          * reading up to UTFmax-1 more bytes than n; the caller
           49          * knows this.  If n is a firm limit, the caller should
           50          * set p[n] = 0.
           51          */
           52         q = (uchar*)p;
           53         s = r;
           54         for(j=0; j<n; j+=w){
           55                 if(*q < Runeself){
           56                         w = 1;
           57                         *s = *q++;
           58                 }else{
           59                         w = chartorune(s, (char*)q);
           60                         q += w;
           61                 }
           62                 if(*s)
           63                         s++;
           64                 else if(nulls)
           65                         *nulls = TRUE;
           66         }
           67         *nb = (char*)q-p;
           68         *nr = s-r;
           69 }
           70 
           71 void
           72 error(char *s)
           73 {
           74         fprint(2, "acme: %s: %r\n", s);
           75         threadexitsall(nil);
           76 }
           77 
           78 Window*
           79 errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
           80 {
           81         Window *w;
           82         Rune *r;
           83         int i, n;
           84         static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
           85 
           86         r = runemalloc(ndir+8);
           87         if((n = ndir) != 0){
           88                 runemove(r, dir, ndir);
           89                 r[n++] = L'/';
           90         }
           91         runemove(r+n, Lpluserrors, 7);
           92         n += 7;
           93         w = lookfile(r, n);
           94         if(w == nil){
           95                 if(row.ncol == 0)
           96                         if(rowadd(&row, nil, -1) == nil)
           97                                 error("can't create column to make error window");
           98                 w = coladd(row.col[row.ncol-1], nil, nil, -1);
           99                 w->filemenu = FALSE;
          100                 winsetname(w, r, n);
          101                 xfidlog(w, "new");
          102         }
          103         free(r);
          104         for(i=nincl; --i>=0; ){
          105                 n = runestrlen(incl[i]);
          106                 r = runemalloc(n);
          107                 runemove(r, incl[i], n);
          108                 winaddincl(w, r, n);
          109         }
          110         w->autoindent = globalautoindent;
          111         return w;
          112 }
          113 
          114 /* make new window, if necessary; return with it locked */
          115 Window*
          116 errorwin(Mntdir *md, int owner)
          117 {
          118         Window *w;
          119 
          120         for(;;){
          121                 if(md == nil)
          122                         w = errorwin1(nil, 0, nil, 0);
          123                 else
          124                         w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
          125                 winlock(w, owner);
          126                 if(w->col != nil)
          127                         break;
          128                 /* window was deleted too fast */
          129                 winunlock(w);
          130         }
          131         return w;
          132 }
          133 
          134 /*
          135  * Incoming window should be locked.
          136  * It will be unlocked and returned window
          137  * will be locked in its place.
          138  */
          139 Window*
          140 errorwinforwin(Window *w)
          141 {
          142         int i, n, nincl, owner;
          143         Rune **incl;
          144         Runestr dir;
          145         Text *t;
          146 
          147         t = &w->body;
          148         dir = dirname(t, nil, 0);
          149         if(dir.nr==1 && dir.r[0]=='.'){        /* sigh */
          150                 free(dir.r);
          151                 dir.r = nil;
          152                 dir.nr = 0;
          153         }
          154         incl = nil;
          155         nincl = w->nincl;
          156         if(nincl > 0){
          157                 incl = emalloc(nincl*sizeof(Rune*));
          158                 for(i=0; i<nincl; i++){
          159                         n = runestrlen(w->incl[i]);
          160                         incl[i] = runemalloc(n+1);
          161                         runemove(incl[i], w->incl[i], n);
          162                 }
          163         }
          164         owner = w->owner;
          165         winunlock(w);
          166         for(;;){
          167                 w = errorwin1(dir.r, dir.nr, incl, nincl);
          168                 winlock(w, owner);
          169                 if(w->col != nil)
          170                         break;
          171                 /* window deleted too fast */
          172                 winunlock(w);
          173         }
          174         return w;
          175 }
          176 
          177 typedef struct Warning Warning;
          178 
          179 struct Warning{
          180         Mntdir *md;
          181         Buffer buf;
          182         Warning *next;
          183 };
          184 
          185 static Warning *warnings;
          186 
          187 static
          188 void
          189 addwarningtext(Mntdir *md, Rune *r, int nr)
          190 {
          191         Warning *warn;
          192 
          193         for(warn = warnings; warn; warn=warn->next){
          194                 if(warn->md == md){
          195                         bufinsert(&warn->buf, warn->buf.nc, r, nr);
          196                         return;
          197                 }
          198         }
          199         warn = emalloc(sizeof(Warning));
          200         warn->next = warnings;
          201         warn->md = md;
          202         if(md)
          203                 fsysincid(md);
          204         warnings = warn;
          205         bufinsert(&warn->buf, 0, r, nr);
          206         nbsendp(cwarn, 0);
          207 }
          208 
          209 /* called while row is locked */
          210 void
          211 flushwarnings(void)
          212 {
          213         Warning *warn, *next;
          214         Window *w;
          215         Text *t;
          216         int owner, nr, q0, n;
          217         Rune *r;
          218 
          219         for(warn=warnings; warn; warn=next) {
          220                 w = errorwin(warn->md, 'E');
          221                 t = &w->body;
          222                 owner = w->owner;
          223                 if(owner == 0)
          224                         w->owner = 'E';
          225                 wincommit(w, t);
          226                 /*
          227                  * Most commands don't generate much output. For instance,
          228                  * Edit ,>cat goes through /dev/cons and is already in blocks
          229                  * because of the i/o system, but a few can.  Edit ,p will
          230                  * put the entire result into a single hunk.  So it's worth doing
          231                  * this in blocks (and putting the text in a buffer in the first
          232                  * place), to avoid a big memory footprint.
          233                  */
          234                 r = fbufalloc();
          235                 q0 = t->file->b.nc;
          236                 for(n = 0; n < warn->buf.nc; n += nr){
          237                         nr = warn->buf.nc - n;
          238                         if(nr > RBUFSIZE)
          239                                 nr = RBUFSIZE;
          240                         bufread(&warn->buf, n, r, nr);
          241                         textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
          242                 }
          243                 textshow(t, q0, t->file->b.nc, 1);
          244                 free(r);
          245                 winsettag(t->w);
          246                 textscrdraw(t);
          247                 w->owner = owner;
          248                 w->dirty = FALSE;
          249                 winunlock(w);
          250                 bufclose(&warn->buf);
          251                 next = warn->next;
          252                 if(warn->md)
          253                         fsysdelid(warn->md);
          254                 free(warn);
          255         }
          256         warnings = nil;
          257 }
          258 
          259 void
          260 warning(Mntdir *md, char *s, ...)
          261 {
          262         Rune *r;
          263         va_list arg;
          264 
          265         va_start(arg, s);
          266         r = runevsmprint(s, arg);
          267         va_end(arg);
          268         if(r == nil)
          269                 error("runevsmprint failed");
          270         addwarningtext(md, r, runestrlen(r));
          271         free(r);
          272 }
          273 
          274 int
          275 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
          276 {
          277         if(n1 != n2)
          278                 return FALSE;
          279         if(n1 == 0)
          280                 return TRUE;
          281         return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
          282 }
          283 
          284 uint
          285 min(uint a, uint b)
          286 {
          287         if(a < b)
          288                 return a;
          289         return b;
          290 }
          291 
          292 uint
          293 max(uint a, uint b)
          294 {
          295         if(a > b)
          296                 return a;
          297         return b;
          298 }
          299 
          300 char*
          301 runetobyte(Rune *r, int n)
          302 {
          303         char *s;
          304 
          305         if(r == nil)
          306                 return nil;
          307         s = emalloc(n*UTFmax+1);
          308         setmalloctag(s, getcallerpc(&r));
          309         snprint(s, n*UTFmax+1, "%.*S", n, r);
          310         return s;
          311 }
          312 
          313 Rune*
          314 bytetorune(char *s, int *ip)
          315 {
          316         Rune *r;
          317         int nb, nr;
          318 
          319         nb = strlen(s);
          320         r = runemalloc(nb+1);
          321         cvttorunes(s, nb, r, &nb, &nr, nil);
          322         r[nr] = '\0';
          323         *ip = nr;
          324         return r;
          325 }
          326 
          327 int
          328 isalnum(Rune c)
          329 {
          330         /*
          331          * Hard to get absolutely right.  Use what we know about ASCII
          332          * and assume anything above the Latin control characters is
          333          * potentially an alphanumeric.
          334          */
          335         if(c <= ' ')
          336                 return FALSE;
          337         if(0x7F<=c && c<=0xA0)
          338                 return FALSE;
          339         if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
          340                 return FALSE;
          341         return TRUE;
          342 }
          343 
          344 int
          345 rgetc(void *v, uint n)
          346 {
          347         return ((Rune*)v)[n];
          348 }
          349 
          350 int
          351 tgetc(void *a, uint n)
          352 {
          353         Text *t;
          354 
          355         t = a;
          356         if(n >= t->file->b.nc)
          357                 return 0;
          358         return textreadc(t, n);
          359 }
          360 
          361 Rune*
          362 skipbl(Rune *r, int n, int *np)
          363 {
          364         while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
          365                 --n;
          366                 r++;
          367         }
          368         *np = n;
          369         return r;
          370 }
          371 
          372 Rune*
          373 findbl(Rune *r, int n, int *np)
          374 {
          375         while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
          376                 --n;
          377                 r++;
          378         }
          379         *np = n;
          380         return r;
          381 }
          382 
          383 void
          384 savemouse(Window *w)
          385 {
          386         prevmouse = mouse->xy;
          387         mousew = w;
          388 }
          389 
          390 int
          391 restoremouse(Window *w)
          392 {
          393         int did;
          394 
          395         did = 0;
          396         if(mousew!=nil && mousew==w) {
          397                 moveto(mousectl, prevmouse);
          398                 did = 1;
          399         }
          400         mousew = nil;
          401         return did;
          402 }
          403 
          404 void
          405 clearmouse()
          406 {
          407         mousew = nil;
          408 }
          409 
          410 char*
          411 estrdup(char *s)
          412 {
          413         char *t;
          414 
          415         t = strdup(s);
          416         if(t == nil)
          417                 error("strdup failed");
          418         setmalloctag(t, getcallerpc(&s));
          419         return t;
          420 }
          421 
          422 void*
          423 emalloc(uint n)
          424 {
          425         void *p;
          426 
          427         p = malloc(n);
          428         if(p == nil)
          429                 error("malloc failed");
          430         setmalloctag(p, getcallerpc(&n));
          431         memset(p, 0, n);
          432         return p;
          433 }
          434 
          435 void*
          436 erealloc(void *p, uint n)
          437 {
          438         p = realloc(p, n);
          439         if(p == nil)
          440                 error("realloc failed");
          441         setmalloctag(p, getcallerpc(&n));
          442         return p;
          443 }
          444 
          445 /*
          446  * Heuristic city.
          447  */
          448 Window*
          449 makenewwindow(Text *t)
          450 {
          451         Column *c;
          452         Window *w, *bigw, *emptyw;
          453         Text *emptyb;
          454         int i, y, el;
          455 
          456         if(activecol)
          457                 c = activecol;
          458         else if(seltext && seltext->col)
          459                 c = seltext->col;
          460         else if(t && t->col)
          461                 c = t->col;
          462         else{
          463                 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
          464                         error("can't make column");
          465                 c = row.col[row.ncol-1];
          466         }
          467         activecol = c;
          468         if(t==nil || t->w==nil || c->nw==0)
          469                 return coladd(c, nil, nil, -1);
          470 
          471         /* find biggest window and biggest blank spot */
          472         emptyw = c->w[0];
          473         bigw = emptyw;
          474         for(i=1; i<c->nw; i++){
          475                 w = c->w[i];
          476                 /* use >= to choose one near bottom of screen */
          477                 if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
          478                         bigw = w;
          479                 if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
          480                         emptyw = w;
          481         }
          482         emptyb = &emptyw->body;
          483         el = emptyb->fr.maxlines-emptyb->fr.nlines;
          484         /* if empty space is big, use it */
          485         if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
          486                 y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
          487         else{
          488                 /* if this window is in column and isn't much smaller, split it */
          489                 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
          490                         bigw = t->w;
          491                 y = (bigw->r.min.y + bigw->r.max.y)/2;
          492         }
          493         w = coladd(c, nil, nil, y);
          494         if(w->body.fr.maxlines < 2)
          495                 colgrow(w->col, w, 1);
          496         return w;
          497 }