├── .gitignore ├── abaco ├── passwd.6.font ├── guide ├── plumbing ├── NOTES ├── charsets.awk ├── mkfile ├── abaco.fonts ├── chan ├── tcs.txt ├── fonts.h ├── time.c ├── fns.h ├── tcs.h ├── urls.c ├── rows.c ├── tabs.c ├── scrl.c ├── wind.c ├── dat.h ├── main.c ├── exec.c ├── cols.c ├── page.c └── text.c ├── mkfile ├── abaco.sh ├── webfs ├── mkfile ├── main.c ├── buf.c ├── util.c ├── io.c ├── fns.h ├── webget.c ├── dat.h ├── plumb.c ├── client.c ├── http.c └── fs.c ├── README └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | o.* 3 | -------------------------------------------------------------------------------- /abaco/passwd.6.font: -------------------------------------------------------------------------------- 1 | 11 9 2 | 0x0000 0x0000 lsr.10 3 | -------------------------------------------------------------------------------- /abaco/guide: -------------------------------------------------------------------------------- 1 | stacks stk 2 | mk install && mk clean 3 | slay abaco | rc 4 | mk dist 5 | http://www.w3.org/TR/DOM-Level-3-Core/ -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | <$PLAN9/src/mkhdr 2 | 3 | MKSHELL=rc 4 | 5 | DIRS=abaco webfs 6 | 7 | none:V: all 8 | 9 | all clean nuke install installall:V: 10 | for (i in $DIRS) @{ 11 | cd $i 12 | mk $target 13 | } 14 | -------------------------------------------------------------------------------- /abaco.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -f $HOME/.webcookies ]; then 3 | touch $HOME/.webcookies 4 | fi 5 | if [ ! -e `namespace`/web ]; then 6 | webfs -s web 7 | fi 8 | 9 | # export PLAN9=/usr/share/abaco 10 | abaco.bin $* 11 | -------------------------------------------------------------------------------- /abaco/plumbing: -------------------------------------------------------------------------------- 1 | # to update: cp /usr/$user/lib/plumbing /mnt/plumb/rules 2 | 3 | editor = acme 4 | 5 | include basic 6 | 7 | type is text 8 | data matches 'https?://[a-zA-Z0-9_@\-]+([.:][a-zA-Z0-9_@\-]+)*/?[a-zA-Z0-9_?,%#~&/\-+=]+([:.][a-zA-Z0-9_?,''%#~&/\-+=;]+)*' 9 | plumb to web 10 | plumb client window -dx 900 -dy 800 abaco 11 | -------------------------------------------------------------------------------- /abaco/NOTES: -------------------------------------------------------------------------------- 1 | Bugs: 2 | * fix text selection and double clicking; 3 | 4 | Not Bugs: 5 | * complaints like "gif: decode failed: ReadGIF: can't recognize format ��" 6 | are caused by sites that return a jpeg and send Content-Type: image/gif. 7 | 8 | Not implented: 9 | * frame's borders and scroll are ignored 10 | * Image: maps 11 | * table atributes 12 | * css 13 | * Js 14 | 15 | -------------------------------------------------------------------------------- /webfs/mkfile: -------------------------------------------------------------------------------- 1 | <$PLAN9/src/mkhdr 2 | BIN=$PLAN9/bin 3 | 4 | TARG=webfs 5 | 6 | SCHEMEOFILES=\ 7 | file.$O\ 8 | ftp.$O\ 9 | http.$O\ 10 | 11 | OFILES=\ 12 | buf.$O\ 13 | client.$O\ 14 | cookies.$O\ 15 | fs.$O\ 16 | http.$O\ 17 | io.$O\ 18 | main.$O\ 19 | plumb.$O\ 20 | url.$O\ 21 | util.$O\ 22 | # $SCHEMEOFILES 23 | 24 | HFILES=\ 25 | dat.h\ 26 | fns.h\ 27 | 28 | UPDATE=\ 29 | mkfile\ 30 | $HFILES\ 31 | ${OFILES:%.$O=%.c}\ 32 | ${TARG:%=/386/bin/%}\ 33 | 34 | <$PLAN9/src/mkone 35 | 36 | -------------------------------------------------------------------------------- /abaco/charsets.awk: -------------------------------------------------------------------------------- 1 | #!/bin/awk -f 2 | # makes a table of character sets from http://www.iana.org/assignments/character-sets 3 | # and tcs.txt 4 | 5 | BEGIN{ 6 | if(ARGC != 3){ 7 | print "Usage: " ARGV[0] " charsets.txt tcs.txt" 8 | exit 1 9 | } 10 | while(getline charsets.txt 36 | 37 | tcs.h: charsets.awk charsets.txt tcs.txt 38 | charsets.awk charsets.txt tcs.txt > tcs.h 39 | 40 | syms:V: 41 | 8c -a $CFLAGS main.c > syms 42 | 8c -aa ????.c >> syms 43 | 44 | tgz:V: 45 | cd .. 46 | tar c abaco | gzip -9 > $home/src/tar/abaco_^`{date -n}^.tgz 47 | 48 | dist: 49 | 9fs sources 50 | mk clean 51 | cd .. 52 | tar c $TARG | gzip -9 > /n/sources/contrib/fgb/abaco-test.tgz 53 | -------------------------------------------------------------------------------- /abaco/abaco.fonts: -------------------------------------------------------------------------------- 1 | /opt/plan9/font/freefont/sans/sans.12.font 2 | /opt/plan9/font/freefont/sans/sans.13.font 3 | /opt/plan9/font/freefont/sans/sans.15.font 4 | /opt/plan9/font/freefont/sans/sans.18.font 5 | /opt/plan9/font/freefont/sans/sans.20.font 6 | /opt/plan9/font/freefont/sansi/sansi.12.font 7 | /opt/plan9/font/freefont/sansi/sansi.13.font 8 | /opt/plan9/font/freefont/sansi/sansi.15.font 9 | /opt/plan9/font/freefont/sansi/sansi.18.font 10 | /opt/plan9/font/freefont/sansi/sansi.20.font 11 | /opt/plan9/font/freefont/sansbd/sansbd.12.font 12 | /opt/plan9/font/freefont/sansbd/sansbd.13.font 13 | /opt/plan9/font/freefont/sansbd/sansbd.15.font 14 | /opt/plan9/font/freefont/sansbd/sansbd.18.font 15 | /opt/plan9/font/freefont/sansbd/sansbd.20.font 16 | /opt/plan9/font/freefont/mono/mono.12.font 17 | /opt/plan9/font/freefont/mono/mono.13.font 18 | /opt/plan9/font/freefont/mono/mono.15.font 19 | /opt/plan9/font/freefont/mono/mono.18.font 20 | /opt/plan9/font/freefont/mono/mono.20.font 21 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Abaco: lame web browser for Plan 9, eternally a work in progress. 2 | Plan9port version. 3 | 4 | Build: 5 | If you have plan9port installed in a non-standard location, 6 | change sources accordingly. An easy way to do it is 7 | sed -i 's|/usr/local/plan9|/where/is/plan9port|g' abaco/*.[ch] 8 | 9 | you can also build with -DSTANDALONE, which will use freefont 10 | in /usr/share/abaco (intended for p9p-less binary packages) 11 | 12 | mk 13 | cp abaco/passwd.6.font /usr/local/plan9/font/lucsans/ 14 | 15 | check ./plumbing 16 | Run: 17 | If you want a nicer antialiased font, put freefont into your 18 | conventional plan9port font directory and 19 | cp abaco/abaco.fonts $HOME/.abaco.fonts 20 | You may modify this file to your liking without rebuilding abaco. 21 | 22 | See abaco.sh for what to do before running abaco itself. 23 | 24 | Enjoy! 25 | 26 | Federico G. Benavento - benavento@gmail.com 27 | 28 | plan9port maintainer: Oleg Finkelshteyn - olegfink@gmail.com 29 | -------------------------------------------------------------------------------- /abaco/chan: -------------------------------------------------------------------------------- 1 | main.c:136: rowinit(&row, screen->clipr); 2 | main.c:297: rowresize(&row, screen->clipr); 3 | main.c:357: tagcols[HIGH] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen); 4 | main.c:358: tagcols[BORD] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen); 5 | main.c:370: button = eallocimage(display, r, screen->chan, 0, DNofill); 6 | main.c:376: colbutton = eallocimage(display, r, screen->chan, 0, 0x00994CFF); 7 | main.c:378: but2col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0xAA0000FF); 8 | main.c:379: but3col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0x444488FF); 9 | page.c:418: tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1); 10 | scrl.c:61: vscrtmp = eallocimage(display, Rect(0, 0, 32, screen->r.max.y), screen->chan, 0, DNofill); 11 | scrl.c:62: hscrtmp = eallocimage(display, Rect(0, 0, screen->r.max.x, 32), screen->chan, 0, DNofill); 12 | util.c:516: c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF); 13 | -------------------------------------------------------------------------------- /abaco/tcs.txt: -------------------------------------------------------------------------------- 1 | iso_8859-1 8859-1 2 | iso_8859-2 8859-2 3 | iso_8859-3 8859-3 4 | iso_8859-4 8859-4 5 | iso_8859-5 8859-5 6 | iso_8859-6 8859-6 7 | iso_8859-7 8859-7 8 | iso_8859-8 8859-8 9 | iso_8859-9 8859-9 10 | iso_8859-10 8859-10 11 | iso_8859-15 8859-15 12 | ascii ascii 13 | atari 14 | av 15 | big5 big5 16 | ibm437 ibm437 17 | ibm720 18 | ibm737 19 | ibm735 ibm775 20 | ibm850 ibm850 21 | ibm852 ibm852 22 | ibm855 ibm855 23 | ibm857 ibm857 24 | ibm858 25 | ibm862 ibm862 26 | ibm866 ibm866 27 | ibm874 28 | windows-1250 windows-1250 29 | windows-1251 windows-1251 30 | windows-1252 windows-1252 31 | windows-1253 windows-1253 32 | windows-1254 windows-1254 33 | windows-1255 windows-1255 34 | windows-1256 windows-1256 35 | windows-1257 windows-1257 36 | windows-1258 windows-1258 37 | ebcdic 38 | korean euc-k 39 | euc-kr euc-k 40 | gb2312 gb 41 | gb_2312-80 gb 42 | iso-2022-jp jis-kanji 43 | koi8-r koi8 44 | macintosh macrom 45 | ibm865 msdos2 46 | shift_jis ms-kanji 47 | next 48 | ov 49 | se sf1 50 | se2 sf2 51 | tis-620 tis 52 | ucode 53 | euc-jp ujis 54 | utf16 unicode 55 | iso-10646-utf-1 utf1 56 | viscii-1 viet1 57 | viscii-2 viet2 58 | viscii viscii 59 | -------------------------------------------------------------------------------- /webfs/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include <9p.h> 9 | #include "dat.h" 10 | #include "fns.h" 11 | 12 | char *cookiefile; 13 | char *service = "web"; 14 | 15 | Ctl globalctl = 16 | { 17 | 1, /* accept cookies */ 18 | 1, /* send cookies */ 19 | 10, /* redirect limit */ 20 | "webfs/2.0 (plan 9)" /* user agent */ 21 | }; 22 | 23 | void 24 | usage(void) 25 | { 26 | fprint(2, "usage: webfs [-c cookies] [-s service]\n"); 27 | threadexitsall("usage"); 28 | } 29 | 30 | int 31 | threadmaybackground(void) 32 | { 33 | return 1; 34 | } 35 | 36 | //#include 37 | void 38 | threadmain(int argc, char **argv) 39 | { 40 | rfork(RFNOTEG); 41 | ARGBEGIN{ 42 | case 'd': 43 | //mainmem->flags |= POOL_PARANOIA|POOL_ANTAGONISM; 44 | break; 45 | case 'D': 46 | chatty9p++; 47 | break; 48 | case 'c': 49 | cookiefile = EARGF(usage()); 50 | break; 51 | case 's': 52 | service = EARGF(usage()); 53 | break; 54 | default: 55 | usage(); 56 | }ARGEND 57 | 58 | quotefmtinstall(); 59 | if(argc != 0) 60 | usage(); 61 | 62 | plumbinit(); 63 | globalctl.useragent = estrdup(globalctl.useragent); 64 | initcookies(cookiefile); 65 | initurl(); 66 | initfs(); 67 | threadpostmountsrv(&fs, service, nil, MREPL); 68 | threadexits(nil); 69 | } 70 | -------------------------------------------------------------------------------- /webfs/buf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include <9p.h> 9 | #include "dat.h" 10 | #include "fns.h" 11 | 12 | void 13 | initibuf(Ibuf *b, Ioproc *io, int fd) 14 | { 15 | b->fd = fd; 16 | b->io = io; 17 | b->rp = b->wp = b->buf; 18 | } 19 | 20 | int 21 | readibuf(Ibuf *b, char *buf, int len) 22 | { 23 | int n; 24 | 25 | n = b->wp - b->rp; 26 | if(n > 0){ 27 | if(n > len) 28 | n = len; 29 | memmove(buf, b->rp, n); 30 | b->rp += n; 31 | return n; 32 | } 33 | return ioreadn(b->io, b->fd, buf, len); 34 | } 35 | 36 | void 37 | unreadline(Ibuf *b, char *line) 38 | { 39 | int i, n; 40 | 41 | i = strlen(line); 42 | n = b->wp - b->rp; 43 | memmove(&b->buf[i+1], b->rp, n); 44 | memmove(b->buf, line, i); 45 | b->buf[i] = '\n'; 46 | b->rp = b->buf; 47 | b->wp = b->rp+i+1+n; 48 | } 49 | 50 | int 51 | readline(Ibuf *b, char *buf, int len) 52 | { 53 | int n; 54 | char *p; 55 | 56 | len--; 57 | 58 | for(p = buf;;){ 59 | if(b->rp >= b->wp){ 60 | n = ioread(b->io, b->fd, b->wp, sizeof(b->buf)/2); 61 | if(n < 0) 62 | return -1; 63 | if(n == 0) 64 | break; 65 | b->wp += n; 66 | } 67 | n = *b->rp++; 68 | if(len > 0){ 69 | *p++ = n; 70 | len--; 71 | } 72 | if(n == '\n') 73 | break; 74 | } 75 | 76 | /* drop trailing white */ 77 | for(;;){ 78 | if(p <= buf) 79 | break; 80 | n = *(p-1); 81 | if(n != ' ' && n != '\t' && n != '\r' && n != '\n') 82 | break; 83 | p--; 84 | } 85 | 86 | *p = 0; 87 | return p-buf; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /webfs/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include <9p.h> 8 | #include 9 | #include "dat.h" 10 | #include "fns.h" 11 | 12 | void* 13 | erealloc(void *a, uint n) 14 | { 15 | a = realloc(a, n); 16 | if(a == nil) 17 | sysfatal("realloc %d: out of memory", n); 18 | setrealloctag(a, getcallerpc(&a)); 19 | return a; 20 | } 21 | 22 | void* 23 | emalloc(uint n) 24 | { 25 | void *a; 26 | 27 | a = mallocz(n, 1); 28 | if(a == nil) 29 | sysfatal("malloc %d: out of memory", n); 30 | setmalloctag(a, getcallerpc(&n)); 31 | return a; 32 | } 33 | 34 | char* 35 | estrdup(char *s) 36 | { 37 | s = strdup(s); 38 | if(s == nil) 39 | sysfatal("strdup: out of memory"); 40 | setmalloctag(s, getcallerpc(&s)); 41 | return s; 42 | } 43 | 44 | char* 45 | estredup(char *s, char *e) 46 | { 47 | char *t; 48 | 49 | t = emalloc(e-s+1); 50 | memmove(t, s, e-s); 51 | t[e-s] = '\0'; 52 | setmalloctag(t, getcallerpc(&s)); 53 | return t; 54 | } 55 | 56 | char* 57 | estrmanydup(char *s, ...) 58 | { 59 | char *p, *t; 60 | int len; 61 | va_list arg; 62 | 63 | len = strlen(s); 64 | va_start(arg, s); 65 | while((p = va_arg(arg, char*)) != nil) 66 | len += strlen(p); 67 | len++; 68 | 69 | t = emalloc(len); 70 | strcpy(t, s); 71 | va_start(arg, s); 72 | while((p = va_arg(arg, char*)) != nil) 73 | strcat(t, p); 74 | return t; 75 | } 76 | 77 | char* 78 | strlower(char *s) 79 | { 80 | char *t; 81 | 82 | for(t=s; *t; t++) 83 | if('A' <= *t && *t <= 'Z') 84 | *t += 'a'-'A'; 85 | return s; 86 | } 87 | -------------------------------------------------------------------------------- /webfs/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include <9p.h> 9 | #include 10 | #include 11 | #include "dat.h" 12 | #include "fns.h" 13 | 14 | static long 15 | _iovfprint(va_list *arg) 16 | { 17 | int fd; 18 | char *fmt; 19 | 20 | fd = va_arg(*arg, int); 21 | fmt = va_arg(*arg, char*); 22 | return vfprint(fd, fmt, va_arg(*arg, va_list)); 23 | } 24 | 25 | int 26 | iovfprint(Ioproc *io, int fd, char *fmt, va_list arg) 27 | { 28 | return iocall(io, _iovfprint, fd, fmt, arg); 29 | } 30 | 31 | int 32 | ioprint(Ioproc *io, int fd, char *fmt, ...) 33 | { 34 | int n; 35 | va_list arg; 36 | 37 | va_start(arg, fmt); 38 | n = iovfprint(io, fd, fmt, arg); 39 | va_end(arg); 40 | return n; 41 | } 42 | 43 | static long 44 | _iotlsdial(va_list *arg) 45 | { 46 | char *addr, *local, *dir; 47 | int *cfdp, fd, tfd, usetls; 48 | TLSconn conn; 49 | 50 | addr = va_arg(*arg, char*); 51 | local = va_arg(*arg, char*); 52 | dir = va_arg(*arg, char*); 53 | cfdp = va_arg(*arg, int*); 54 | usetls = va_arg(*arg, int); 55 | 56 | fd = dial(addr, local, dir, cfdp); 57 | if(fd < 0) 58 | return -1; 59 | if(!usetls) 60 | return fd; 61 | 62 | memset(&conn, 0, sizeof conn); 63 | tfd = tlsClient(fd, &conn); 64 | if(tfd < 0){ 65 | print("tls %r\n"); 66 | close(fd); 67 | return -1; 68 | } 69 | /* BUG: check cert here? */ 70 | if(conn.cert) 71 | free(conn.cert); 72 | close(fd); 73 | return tfd; 74 | } 75 | 76 | int 77 | iotlsdial(Ioproc *io, char *addr, char *local, char *dir, int *cfdp, int usetls) 78 | { 79 | return iocall(io, _iotlsdial, addr, local, dir, cfdp, usetls); 80 | } 81 | -------------------------------------------------------------------------------- /webfs/fns.h: -------------------------------------------------------------------------------- 1 | /* buf.c */ 2 | void initibuf(Ibuf*, Ioproc*, int); 3 | int readibuf(Ibuf*, char*, int); 4 | void unreadline(Ibuf*, char*); 5 | int readline(Ibuf*, char*, int); 6 | 7 | /* client.c */ 8 | int newclient(int); 9 | void closeclient(Client*); 10 | void clonectl(Ctl*); 11 | int ctlwrite(Req*, Ctl*, char*, char*); 12 | int clientctlwrite(Req*, Client*, char*, char*); 13 | int globalctlwrite(Req*, char*, char*); 14 | void ctlread(Req*, Client*); 15 | void globalctlread(Req*); 16 | void plumburl(char*, char*); 17 | 18 | /* cookies.c */ 19 | void cookieread(Req*); 20 | void cookiewrite(Req*); 21 | void cookieopen(Req*); 22 | void cookieclunk(Fid*); 23 | void initcookies(char*); 24 | void closecookies(void); 25 | void httpsetcookie(char*, char*, char*); 26 | char* httpcookies(char*, char*, int); 27 | 28 | /* fs.c */ 29 | void initfs(void); 30 | 31 | /* http.c */ 32 | int httpopen(Client*, Url*); 33 | int httpread(Client*, Req*); 34 | void httpclose(Client*); 35 | 36 | /* io.c */ 37 | int iotlsdial(Ioproc*, char*, char*, char*, int*, int); 38 | int ioprint(Ioproc*, int, char*, ...); 39 | #pragma varargck argpos ioprint 3 40 | 41 | /* plumb.c */ 42 | void plumbinit(void); 43 | void plumbstart(void); 44 | void replumb(Client*); 45 | 46 | /* url.c */ 47 | Url* parseurl(char*, Url*); 48 | void freeurl(Url*); 49 | void rewriteurl(Url*); 50 | int seturlquery(Url*, char*); 51 | Url* copyurl(Url*); 52 | char* escapeurl(char*, int(*)(int)); 53 | char* unescapeurl(char*); 54 | void initurl(void); 55 | 56 | /* util.c */ 57 | char* estrdup(char*); 58 | char* estrmanydup(char*, ...); 59 | char* estredup(char*, char*); 60 | void* emalloc(uint); 61 | void* erealloc(void*, uint); 62 | char* strlower(char*); 63 | -------------------------------------------------------------------------------- /webfs/webget.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample client. 3 | */ 4 | #include 5 | #include 6 | 7 | void 8 | xfer(int from, int to) 9 | { 10 | char buf[12*1024]; 11 | int n; 12 | 13 | while((n = read(from, buf, sizeof buf)) > 0) 14 | if(write(to, buf, n) < 0) 15 | sysfatal("write failed: %r"); 16 | if(n < 0) 17 | sysfatal("read failed: %r"); 18 | } 19 | 20 | void 21 | usage(void) 22 | { 23 | fprint(2, "usage: webget [-b baseurl] [-m mtpt] [-p postbody] url\n"); 24 | exits("usage"); 25 | } 26 | 27 | void 28 | main(int argc, char **argv) 29 | { 30 | int conn, ctlfd, fd, i, n; 31 | char buf[128], *base, *mtpt, *post, *url; 32 | 33 | mtpt = "/mnt/web"; 34 | post = nil; 35 | base = nil; 36 | ARGBEGIN{ 37 | default: 38 | usage(); 39 | case 'b': 40 | base = EARGF(usage()); 41 | break; 42 | case 'm': 43 | mtpt = EARGF(usage()); 44 | break; 45 | case 'p': 46 | post = EARGF(usage()); 47 | break; 48 | }ARGEND; 49 | 50 | if (argc != 1) 51 | usage(); 52 | 53 | url = argv[0]; 54 | 55 | snprint(buf, sizeof buf, "%s/clone", mtpt); 56 | if((ctlfd = open(buf, ORDWR)) < 0) 57 | sysfatal("couldn't open %s: %r", buf); 58 | if((n = read(ctlfd, buf, sizeof buf-1)) < 0) 59 | sysfatal("reading clone: %r"); 60 | if(n == 0) 61 | sysfatal("short read on clone"); 62 | buf[n] = '\0'; 63 | conn = atoi(buf); 64 | 65 | if(base) 66 | if(fprint(ctlfd, "baseurl %s", base) < 0) 67 | sysfatal("baseurl ctl write: %r"); 68 | 69 | if(fprint(ctlfd, "url %s", url) <= 0) 70 | sysfatal("get ctl write: %r"); 71 | 72 | if(post){ 73 | snprint(buf, sizeof buf, "%s/%d/postbody", mtpt, conn); 74 | if((fd = open(buf, OWRITE)) < 0) 75 | sysfatal("open %s: %r", buf); 76 | if(write(fd, post, strlen(post)) < 0) 77 | sysfatal("post write failed: %r"); 78 | close(fd); 79 | } 80 | 81 | snprint(buf, sizeof buf, "%s/%d/body", mtpt, conn); 82 | if((fd = open(buf, OREAD)) < 0) 83 | sysfatal("open %s: %r", buf); 84 | 85 | xfer(fd, 1); 86 | exits(nil); 87 | } 88 | -------------------------------------------------------------------------------- /webfs/dat.h: -------------------------------------------------------------------------------- 1 | typedef struct Client Client; 2 | typedef struct Ctl Ctl; 3 | typedef struct Ibuf Ibuf; 4 | typedef struct Url Url; 5 | 6 | /* simple buffered i/o for network connections; shared by http, ftp */ 7 | struct Ibuf 8 | { 9 | int fd; 10 | Ioproc *io; 11 | char buf[4096]; 12 | char *rp, *wp; 13 | }; 14 | 15 | struct Ctl 16 | { 17 | int acceptcookies; 18 | int sendcookies; 19 | int redirectlimit; 20 | char *useragent; 21 | }; 22 | 23 | struct Client 24 | { 25 | Url *url; 26 | Url *baseurl; 27 | Ctl ctl; 28 | Channel *creq; /* chan(Req*) */ 29 | int num; 30 | int plumbed; 31 | char *contenttype; 32 | char *postbody; 33 | char *redirect; 34 | char *authenticate; 35 | char *ext; 36 | int npostbody; 37 | int havepostbody; 38 | int iobusy; 39 | int bodyopened; 40 | Ioproc *io; 41 | int ref; 42 | void *aux; 43 | }; 44 | 45 | /* 46 | * If ischeme is USunknown, then the given URL is a relative 47 | * URL which references the "current document" in the context of the base. 48 | * If this is the case, only the "fragment" and "url" members will have 49 | * meaning, and the given URL structure may not be used as a base URL itself. 50 | */ 51 | enum 52 | { 53 | USunknown, 54 | UShttp, 55 | UShttps, 56 | USftp, 57 | USfile, 58 | UScurrent, 59 | }; 60 | 61 | struct Url 62 | { 63 | int ischeme; 64 | char* url; 65 | char* scheme; 66 | int (*open)(Client*, Url*); 67 | int (*read)(Client*, Req*); 68 | void (*close)(Client*); 69 | char* schemedata; 70 | char* authority; 71 | char* user; 72 | char* passwd; 73 | char* host; 74 | char* port; 75 | char* path; 76 | char* query; 77 | char* fragment; 78 | union { 79 | struct { 80 | char *page_spec; 81 | } http; 82 | struct { 83 | char *path_spec; 84 | char *type; 85 | } ftp; 86 | }; 87 | }; 88 | 89 | enum 90 | { 91 | STACK = 16384, 92 | }; 93 | 94 | extern Client** client; 95 | extern int cookiedebug; 96 | extern Srv fs; 97 | extern int fsdebug; 98 | extern Ctl globalctl; 99 | extern int nclient; 100 | extern int urldebug; 101 | extern int httpdebug; 102 | extern char* status[]; 103 | 104 | -------------------------------------------------------------------------------- /abaco/fonts.h: -------------------------------------------------------------------------------- 1 | #ifndef STANDALONE 2 | "/usr/local/plan9/font/lucsans/unicode.6.font", 3 | "/usr/local/plan9/font/lucsans/unicode.7.font", 4 | "/usr/local/plan9/font/lucsans/unicode.8.font", 5 | "/usr/local/plan9/font/lucsans/unicode.10.font", 6 | "/usr/local/plan9/font/lucsans/unicode.13.font", 7 | "/usr/local/plan9/font/lucsans/italicunicode.6.font", 8 | "/usr/local/plan9/font/lucsans/italicunicode.7.font", 9 | "/usr/local/plan9/font/lucsans/italicunicode.8.font", 10 | "/usr/local/plan9/font/lucsans/italicunicode.10.font", 11 | "/usr/local/plan9/font/lucsans/italicunicode.13.font", 12 | "/usr/local/plan9/font/lucsans/boldunicode.6.font", 13 | "/usr/local/plan9/font/lucsans/boldunicode.7.font", 14 | "/usr/local/plan9/font/lucsans/boldunicode.8.font", 15 | "/usr/local/plan9/font/lucsans/boldunicode.10.font", 16 | "/usr/local/plan9/font/lucsans/boldunicode.13.font", 17 | "/usr/local/plan9/font/fixed/unicode.6x12.font", 18 | "/usr/local/plan9/font/fixed/unicode.8x13.font", 19 | "/usr/local/plan9/font/fixed/unicode.9x15.font", 20 | "/usr/local/plan9/font/fixed/unicode.9x18.font", 21 | "/usr/local/plan9/font/fixed/unicode.10x20.font", 22 | #else 23 | "/usr/local/plan9/font/freefont/sans/sans.12.font", 24 | "/usr/local/plan9/font/freefont/sans/sans.13.font", 25 | "/usr/local/plan9/font/freefont/sans/sans.15.font", 26 | "/usr/local/plan9/font/freefont/sans/sans.18.font", 27 | "/usr/local/plan9/font/freefont/sans/sans.20.font", 28 | "/usr/local/plan9/font/freefont/sansi/sansi.12.font", 29 | "/usr/local/plan9/font/freefont/sansi/sansi.13.font", 30 | "/usr/local/plan9/font/freefont/sansi/sansi.15.font", 31 | "/usr/local/plan9/font/freefont/sansi/sansi.18.font", 32 | "/usr/local/plan9/font/freefont/sansi/sansi.20.font", 33 | "/usr/local/plan9/font/freefont/sansbd/sansbd.12.font", 34 | "/usr/local/plan9/font/freefont/sansbd/sansbd.13.font", 35 | "/usr/local/plan9/font/freefont/sansbd/sansbd.15.font", 36 | "/usr/local/plan9/font/freefont/sansbd/sansbd.18.font", 37 | "/usr/local/plan9/font/freefont/sansbd/sansbd.20.font", 38 | "/usr/local/plan9/font/freefont/mono/mono.12.font", 39 | "/usr/local/plan9/font/freefont/mono/mono.13.font", 40 | "/usr/local/plan9/font/freefont/mono/mono.15.font", 41 | "/usr/local/plan9/font/freefont/mono/mono.18.font", 42 | "/usr/local/plan9/font/freefont/mono/mono.20.font", 43 | #endif 44 | -------------------------------------------------------------------------------- /abaco/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | static Channel* ctimer; /* chan(Timer*)[100] */ 18 | static Timer *timer; 19 | 20 | static 21 | uint 22 | msec(void) 23 | { 24 | return nsec()/1000000; 25 | } 26 | 27 | void 28 | timerstop(Timer *t) 29 | { 30 | t->next = timer; 31 | timer = t; 32 | } 33 | 34 | void 35 | timercancel(Timer *t) 36 | { 37 | t->cancel = TRUE; 38 | } 39 | 40 | static 41 | void 42 | timerproc(void *notused) 43 | { 44 | int i, nt, na, dt, del; 45 | Timer **t, *x; 46 | uint old, new; 47 | 48 | threadsetname("timerproc"); 49 | t = nil; 50 | na = 0; 51 | nt = 0; 52 | old = msec(); 53 | for(;;){ 54 | sleep(1); /* will sleep minimum incr */ 55 | new = msec(); 56 | dt = new-old; 57 | old = new; 58 | if(dt < 0) /* timer wrapped; go around, losing a tick */ 59 | continue; 60 | for(i=0; idt -= dt; 63 | del = FALSE; 64 | if(x->cancel){ 65 | timerstop(x); 66 | del = TRUE; 67 | }else if(x->dt <= 0){ 68 | /* 69 | * avoid possible deadlock if client is 70 | * now sending on ctimer 71 | */ 72 | if(nbsendul(x->c, 0) > 0) 73 | del = TRUE; 74 | } 75 | if(del){ 76 | memmove(&t[i], &t[i+1], (nt-i-1)*sizeof t[0]); 77 | --nt; 78 | --i; 79 | } 80 | } 81 | if(nt == 0){ 82 | x = recvp(ctimer); 83 | gotit: 84 | if(nt == na){ 85 | na += 10; 86 | t = erealloc(t, na*sizeof(Timer*)); 87 | } 88 | t[nt++] = x; 89 | old = msec(); 90 | } 91 | if(nbrecv(ctimer, &x) > 0) 92 | goto gotit; 93 | } 94 | } 95 | 96 | void 97 | timerinit(void) 98 | { 99 | ctimer = chancreate(sizeof(Timer*), 100); 100 | proccreate(timerproc, nil, STACK); 101 | } 102 | 103 | Timer* 104 | timerstart(int dt) 105 | { 106 | Timer *t; 107 | 108 | t = timer; 109 | if(t) 110 | timer = timer->next; 111 | else{ 112 | t = emalloc(sizeof(Timer)); 113 | t->c = chancreate(sizeof(int), 0); 114 | } 115 | t->next = nil; 116 | t->dt = dt; 117 | t->cancel = FALSE; 118 | sendp(ctimer, t); 119 | return t; 120 | } 121 | -------------------------------------------------------------------------------- /abaco/fns.h: -------------------------------------------------------------------------------- 1 | #define runemalloc(a) emalloc((a)*sizeof(Rune)) 2 | #define runerealloc(a, b) erealloc(a, (b)*sizeof(Rune)) 3 | #define runemove(a, b, c) memmove(a, b, (c)*sizeof(Rune)) 4 | 5 | #define hasbrk(x) ((x)&IFbrk || (x)&IFbrksp) 6 | #define istrue(x) ((x) ? "true" : "false") 7 | 8 | void plumblook(Plumbmsg*m); 9 | int plumbrunestr(Runestr *, char *); 10 | //void putsnarf(Runestr *); 11 | //void getsnarf(Runestr *); 12 | 13 | void tablesize(Table *, int); 14 | void drawtable(Box *, Page *, Image *); 15 | void laytable(Itable *, Rectangle); 16 | void settables(Page *); 17 | void laysnarf(Page *, Lay *, Runestr *); 18 | Timer* timerstart(int); 19 | void timerstop(Timer*); 20 | void timercancel(Timer*); 21 | void timerinit(void); 22 | 23 | void cut(Text *, Text *, int, int, Rune *, int); 24 | void get(Text *, Text *, int, int, Rune *, int); 25 | void paste(Text *, Text *, int, int, Rune *, int); 26 | void execute(Text *, uint, uint, Text *); 27 | void look3(Text *, uint, uint); 28 | int search(Text *, Rune *, uint); 29 | 30 | void scrsleep(uint); 31 | void scrlresize(void); 32 | void tmpresize(void); 33 | 34 | void initfontpaths(void); 35 | void cvttorunes(char*, int, Rune*, int*, int*, int*); 36 | void error(char *); 37 | void closerunestr(Runestr *); 38 | void copyrunestr(Runestr *, Runestr *); 39 | int runestreq(Runestr, Runestr); 40 | int validurl(Rune *); 41 | int runeeq(Rune *, uint, Rune *, uint); 42 | int min(int, int); 43 | int max(int, int); 44 | //int isalnum(Rune); 45 | Rune* skipbl(Rune *, int, int *); 46 | Rune* findbl(Rune *r, int, int *); 47 | Rune* erunestrdup(Rune *); 48 | Rune* ucvt(Rune *s); 49 | int dimwidth(Dimen , int); 50 | void frdims(Dimen *, int, int, int **); 51 | Image *getbg(Page *); 52 | Rune *getbase(Page *); 53 | Image* eallocimage(Display *, Rectangle, ulong, int, int); 54 | Image* getcolor(int); 55 | void freecolors(void); 56 | Font* getfont(int); 57 | void freefonts(void); 58 | void colarray(Image **, Image *, Image *, Image *, int); 59 | void rect3d(Image *, Rectangle, int, Image **, Point); 60 | void ellipse3d(Image *, Point, int, int, Image **, Point); 61 | void reverseimages(Iimage **); 62 | void setstatus(Window *, char *, ...); 63 | int istextfield(Item *); 64 | int forceitem(Item *); 65 | int xtofchar(Rune *, Font *, long); 66 | int istextsel(Page *, Rectangle, int *, int *, Rune *, Font *); 67 | char* convert(Runestr, char *, long *); 68 | void execproc(void *); 69 | void getimage(Cimage *, Rune *); 70 | Point getpt(Page *p, Point); 71 | Rune *urlcombine(Rune *, Rune *); 72 | void fixtext(Page *); 73 | void addrefresh(Page *, char *, ...); 74 | void flushrefresh(void); 75 | void savemouse(Window *); 76 | void restoremouse(Window *); 77 | void clearmouse(void); 78 | void bytetorunestr(char *, Runestr *); 79 | Window* makenewwindow(Page *); 80 | 81 | Line* linewhich(Lay *, Point); 82 | Box* pttobox(Line *, Point); 83 | Box* boxwhich(Lay *, Point); 84 | 85 | -------------------------------------------------------------------------------- /webfs/plumb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include <9p.h> 8 | 9 | #include "dat.h" 10 | #include "fns.h" 11 | 12 | static int plumbsendfd; 13 | static int plumbwebfd; 14 | static Channel *plumbchan; 15 | 16 | static void plumbwebproc(void*); 17 | static void plumbwebthread(void*); 18 | static void plumbsendproc(void*); 19 | 20 | void 21 | plumbinit(void) 22 | { 23 | plumbsendfd = plumbopen("send", OWRITE|OCEXEC); 24 | plumbwebfd = plumbopen("web", OREAD|OCEXEC); 25 | } 26 | 27 | void 28 | plumbstart(void) 29 | { 30 | plumbchan = chancreate(sizeof(Plumbmsg*), 0); 31 | proccreate(plumbwebproc, nil, STACK); 32 | threadcreate(plumbwebthread, nil, STACK); 33 | } 34 | 35 | static void 36 | plumbwebthread(void *notused) 37 | { 38 | char *base; 39 | Plumbmsg *m; 40 | 41 | for(;;){ 42 | m = recvp(plumbchan); 43 | if(m == nil) 44 | threadexits(nil); 45 | base = plumblookup(m->attr, "baseurl"); 46 | if(base == nil) 47 | base = m->wdir; 48 | plumburl(m->data, base); 49 | plumbfree(m); 50 | } 51 | } 52 | 53 | static void 54 | plumbwebproc(void *notused) 55 | { 56 | Plumbmsg *m; 57 | 58 | for(;;){ 59 | m = plumbrecv(plumbwebfd); 60 | sendp(plumbchan, m); 61 | if(m == nil) 62 | threadexits(nil); 63 | } 64 | } 65 | 66 | static void 67 | addattr(Plumbmsg *m, char *name, char *value) 68 | { 69 | Plumbattr *a; 70 | 71 | a = malloc(sizeof(Plumbattr)); 72 | a->name = name; 73 | a->value = value; 74 | a->next = m->attr; 75 | m->attr = a; 76 | } 77 | 78 | static void 79 | freeattrs(Plumbmsg *m) 80 | { 81 | Plumbattr *a, *next; 82 | 83 | a = m->attr; 84 | while(a != nil) { 85 | next = a->next; 86 | free(a); 87 | a = next; 88 | } 89 | } 90 | 91 | static struct 92 | { 93 | char *ctype; 94 | char *ext; 95 | } 96 | ctypes[] = 97 | { 98 | { "application/msword", "doc" }, 99 | { "application/pdf", "pdf" }, 100 | { "application/postscript", "ps" }, 101 | { "application/rtf", "rtf" }, 102 | { "image/gif", "gif" }, 103 | { "image/jpeg", "jpg" }, 104 | { "image/png", "png" }, 105 | { "image/ppm", "ppm" }, 106 | { "image/tiff", "tiff" }, 107 | { "text/html", "html" }, 108 | { "text/plain", "txt" }, 109 | { "text/xml", "xml" }, 110 | }; 111 | 112 | void 113 | replumb(Client *c) 114 | { 115 | int i; 116 | Plumbmsg *m; 117 | char name[128], *ctype, *ext, *p; 118 | 119 | if(!c->plumbed) 120 | return; 121 | m = emalloc(sizeof(Plumbmsg)); 122 | m->src = "webfs"; 123 | m->dst = nil; 124 | m->wdir = "/"; 125 | m->type = "text"; 126 | m->attr = nil; 127 | addattr(m, "url", c->url->url); 128 | ctype = c->contenttype; 129 | ext = nil; 130 | if(ctype != nil) { 131 | addattr(m, "content-type", ctype); 132 | for(i = 0; i < nelem(ctypes); i++) { 133 | if(strcmp(ctype, ctypes[i].ctype) == 0) { 134 | ext = ctypes[i].ext; 135 | break; 136 | } 137 | } 138 | } 139 | if(ext == nil) { 140 | p = strrchr(c->url->url, '/'); 141 | if(p != nil) 142 | p = strrchr(p+1, '.'); 143 | if(p != nil && strlen(p) <= 5) 144 | ext = p+1; 145 | else 146 | ext = "txt"; /* punt */ 147 | } 148 | c->ext = ext; 149 | if(0)fprint(2, "content type %s -> extension .%s\n", ctype, ext); 150 | m->ndata = snprint(name, sizeof name, "/mnt/web/%d/body.%s", c->num, ext); 151 | m->data = estrdup(name); 152 | proccreate(plumbsendproc, m, STACK); /* separate proc to avoid a deadlock */ 153 | } 154 | 155 | static void 156 | plumbsendproc(void *x) 157 | { 158 | Plumbmsg *m; 159 | 160 | m = x; 161 | plumbsend(plumbsendfd, m); 162 | freeattrs(m); 163 | free(m->data); 164 | free(m); 165 | } 166 | -------------------------------------------------------------------------------- /abaco/tcs.h: -------------------------------------------------------------------------------- 1 | "iso_8859-1:1987", "8859-1", 2 | "iso-ir-100", "8859-1", 3 | "iso_8859-1", "8859-1", 4 | "iso-8859-1", "8859-1", 5 | "latin1", "8859-1", 6 | "l1", "8859-1", 7 | "ibm819", "8859-1", 8 | "cp819", "8859-1", 9 | "csisolatin1", "8859-1", 10 | "iso_8859-2:1987", "8859-2", 11 | "iso-ir-101", "8859-2", 12 | "iso_8859-2", "8859-2", 13 | "iso-8859-2", "8859-2", 14 | "latin2", "8859-2", 15 | "l2", "8859-2", 16 | "csisolatin2", "8859-2", 17 | "iso_8859-3:1988", "8859-3", 18 | "iso-ir-109", "8859-3", 19 | "iso_8859-3", "8859-3", 20 | "iso-8859-3", "8859-3", 21 | "latin3", "8859-3", 22 | "l3", "8859-3", 23 | "csisolatin3", "8859-3", 24 | "iso_8859-4:1988", "8859-4", 25 | "iso-ir-110", "8859-4", 26 | "iso_8859-4", "8859-4", 27 | "iso-8859-4", "8859-4", 28 | "latin4", "8859-4", 29 | "l4", "8859-4", 30 | "csisolatin4", "8859-4", 31 | "iso_8859-5:1988", "8859-5", 32 | "iso-ir-144", "8859-5", 33 | "iso_8859-5", "8859-5", 34 | "iso-8859-5", "8859-5", 35 | "cyrillic", "8859-5", 36 | "csisolatincyrillic", "8859-5", 37 | "iso_8859-6:1987", "8859-6", 38 | "iso-ir-127", "8859-6", 39 | "iso_8859-6", "8859-6", 40 | "iso-8859-6", "8859-6", 41 | "ecma-114", "8859-6", 42 | "asmo-708", "8859-6", 43 | "arabic", "8859-6", 44 | "csisolatinarabic", "8859-6", 45 | "iso_8859-7:1987", "8859-7", 46 | "iso-ir-126", "8859-7", 47 | "iso_8859-7", "8859-7", 48 | "iso-8859-7", "8859-7", 49 | "elot_928", "8859-7", 50 | "ecma-118", "8859-7", 51 | "greek", "8859-7", 52 | "greek8", "8859-7", 53 | "csisolatingreek", "8859-7", 54 | "iso_8859-8:1988", "8859-8", 55 | "iso-ir-138", "8859-8", 56 | "iso_8859-8", "8859-8", 57 | "iso-8859-8", "8859-8", 58 | "hebrew", "8859-8", 59 | "csisolatinhebrew", "8859-8", 60 | "iso_8859-9:1989", "8859-9", 61 | "iso-ir-148", "8859-9", 62 | "iso_8859-9", "8859-9", 63 | "iso-8859-9", "8859-9", 64 | "latin5", "8859-9", 65 | "l5", "8859-9", 66 | "csisolatin5", "8859-9", 67 | "iso-8859-15", "8859-15", 68 | "iso_8859-15", "8859-15", 69 | "latin-9", "8859-15", 70 | "ansi_x3.4-1968", "ascii", 71 | "iso-ir-6", "ascii", 72 | "ansi_x3.4-1986", "ascii", 73 | "iso_646.irv:1991", "ascii", 74 | "ascii", "ascii", 75 | "iso646-us", "ascii", 76 | "us-ascii", "ascii", 77 | "us", "ascii", 78 | "ibm367", "ascii", 79 | "cp367", "ascii", 80 | "csascii", "ascii", 81 | "big5", "big5", 82 | "csbig5", "big5", 83 | "ibm437", "ibm437", 84 | "cp437", "ibm437", 85 | "437", "ibm437", 86 | "cspc8codepage437", "ibm437", 87 | "ibm850", "ibm850", 88 | "cp850", "ibm850", 89 | "850", "ibm850", 90 | "cspc850multilingual", "ibm850", 91 | "ibm852", "ibm852", 92 | "cp852", "ibm852", 93 | "852", "ibm852", 94 | "cspcp852", "ibm852", 95 | "ibm855", "ibm855", 96 | "cp855", "ibm855", 97 | "855", "ibm855", 98 | "csibm855", "ibm855", 99 | "ibm857", "ibm857", 100 | "cp857", "ibm857", 101 | "857", "ibm857", 102 | "csibm857", "ibm857", 103 | "ibm862", "ibm862", 104 | "cp862", "ibm862", 105 | "862", "ibm862", 106 | "cspc862latinhebrew", "ibm862", 107 | "ibm866", "ibm866", 108 | "cp866", "ibm866", 109 | "866", "ibm866", 110 | "csibm866", "ibm866", 111 | "windows-1250", "windows-1250", 112 | "windows-1251", "windows-1251", 113 | "windows-1252", "windows-1252", 114 | "windows-1253", "windows-1253", 115 | "windows-1254", "windows-1254", 116 | "windows-1255", "windows-1255", 117 | "windows-1256", "windows-1256", 118 | "windows-1257", "windows-1257", 119 | "windows-1258", "windows-1258", 120 | "ks_c_5601-1987", "euc-k", 121 | "iso-ir-149", "euc-k", 122 | "ks_c_5601-1989", "euc-k", 123 | "ksc_5601", "euc-k", 124 | "korean", "euc-k", 125 | "csksc56011987", "euc-k", 126 | "euc-kr", "euc-k", 127 | "cseuckr", "euc-k", 128 | "gb2312", "gb", 129 | "csgb2312", "gb", 130 | "gb_2312-80", "gb", 131 | "iso-ir-58", "gb", 132 | "chinese", "gb", 133 | "csiso58gb231280", "gb", 134 | "iso-2022-jp", "jis-kanji", 135 | "csiso2022jp", "jis-kanji", 136 | "koi8-r", "koi8", 137 | "cskoi8r", "koi8", 138 | "macintosh", "macrom", 139 | "mac", "macrom", 140 | "csmacintosh", "macrom", 141 | "ibm865", "msdos2", 142 | "cp865", "msdos2", 143 | "865", "msdos2", 144 | "csibm865", "msdos2", 145 | "shift_jis", "ms-kanji", 146 | "ms_kanji", "ms-kanji", 147 | "csshiftjis", "ms-kanji", 148 | "sen_850200_b", "sf1", 149 | "iso-ir-10", "sf1", 150 | "fi", "sf1", 151 | "iso646-fi", "sf1", 152 | "iso646-se", "sf1", 153 | "se", "sf1", 154 | "csiso10swedish", "sf1", 155 | "sen_850200_c", "sf2", 156 | "iso-ir-11", "sf2", 157 | "iso646-se2", "sf2", 158 | "se2", "sf2", 159 | "csiso11swedishfornames", "sf2", 160 | "tis-620", "tis", 161 | "extended_unix_code_packed_format_for_japanese", "ujis", 162 | "cseucpkdfmtjapanese", "ujis", 163 | "euc-jp", "ujis", 164 | "iso-10646-utf-1", "utf1", 165 | "csiso10646utf1", "utf1", 166 | "viscii", "viscii", 167 | "csviscii", "viscii", 168 | -------------------------------------------------------------------------------- /abaco/urls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | Url * 18 | urlalloc(Runestr *src, Runestr *post, int m) 19 | { 20 | Url *u; 21 | 22 | u = emalloc(sizeof(Url)); 23 | copyrunestr(&u->src, src); 24 | if(m==HPost) 25 | copyrunestr(&u->post, post); 26 | u->method = m; 27 | incref(&u->ref); 28 | return u; 29 | } 30 | 31 | void 32 | urlfree(Url *u) 33 | { 34 | if(u && decref(&u->ref)==0){ 35 | closerunestr(&u->src); 36 | closerunestr(&u->act); 37 | closerunestr(&u->post); 38 | closerunestr(&u->ctype); 39 | free(u); 40 | } 41 | } 42 | 43 | Url * 44 | urldup(Url *a) 45 | { 46 | Url *b; 47 | 48 | b = emalloc(sizeof(Url)); 49 | b->method = a->method; 50 | copyrunestr(&b->src, &a->src); 51 | copyrunestr(&b->act, &a->act); 52 | copyrunestr(&b->post, &a->post); 53 | copyrunestr(&b->ctype, &a->ctype); 54 | return b; 55 | } 56 | 57 | static 58 | Runestr 59 | getattr(int conn, char *s) 60 | { 61 | char buf[BUFSIZE]; 62 | CFid *fid; 63 | int n; 64 | 65 | snprint(buf, sizeof(buf), "%d/%s", conn, s); 66 | fid = fsopen(webfs, buf, OREAD); 67 | if(fid == nil) 68 | error("can't open attr file"); 69 | 70 | n = fsread(fid, buf, sizeof(buf)-1); 71 | if(n < 0) 72 | error("can't read"); 73 | 74 | fsclose(fid); 75 | buf[n] = '\0'; 76 | return (Runestr){runesmprint("%s", buf), n}; 77 | } 78 | 79 | int 80 | urlopen(Url *u) 81 | { 82 | char buf[BUFSIZE]; 83 | CFid *cfid, *fid; 84 | int fd, conn, n; 85 | 86 | cfid = fsopen(webfs, "clone", ORDWR); 87 | if(cfid == nil) 88 | error("can't open clone file"); 89 | 90 | n = fsread(cfid, buf, sizeof(buf)-1); 91 | if(n <= 0) 92 | error("reading clone"); 93 | 94 | buf[n] = '\0'; 95 | conn = atoi(buf); 96 | 97 | snprint(buf, sizeof(buf), "url %S", u->src.r); 98 | if(fswrite(cfid, buf, strlen(buf)) < 0){ 99 | // fprint(2, "write: %s: %r\n", buf); 100 | Err: 101 | fsclose(cfid); 102 | return -1; 103 | } 104 | if(u->method==HPost && u->post.r != nil){ 105 | snprint(buf, sizeof(buf), "%d/postbody", conn); 106 | fid = fsopen(webfs, buf, OWRITE); 107 | if(fid == nil){ 108 | // fprint(2, "urlopen: bad query: %s: %r\n", buf); 109 | goto Err; 110 | } 111 | snprint(buf, sizeof(buf), "%S", u->post.r); 112 | if(fswrite(fid, buf, strlen(buf)) < 0) 113 | // fprint(2, "urlopen: bad query: %s: %r\n", buf); 114 | 115 | fsclose(fid); 116 | } 117 | snprint(buf, sizeof(buf), "%d/body", conn); 118 | fd = fsopenfd(webfs, buf, OREAD); 119 | if(fd < 0){ 120 | // fprint(2, "open: %S: %r\n", u->src.r); 121 | goto Err; 122 | } 123 | u->ctype = getattr(conn, "contenttype"); 124 | u->act = getattr(conn, "parsed/url"); 125 | if(u->act.nr == 0) 126 | copyrunestr(&u->act, &u->src); 127 | 128 | fsclose(cfid); 129 | return fd; 130 | } 131 | 132 | 133 | void 134 | urlcanon(Rune *name){ 135 | Rune *s, *t; 136 | Rune **comp, **p, **q; 137 | Rune dot[] = {'.',0}; 138 | Rune up[] = {'.','.',0}; 139 | Rune e[] = {0}; 140 | int rooted; 141 | 142 | name = runestrchr(name, L'/')+2; 143 | rooted=name[0]==L'/'; 144 | /* 145 | * Break the name into a list of components 146 | */ 147 | comp=emalloc(runestrlen(name)*sizeof(char *)); 148 | p=comp; 149 | *p++=name; 150 | for(s=name;;s++){ 151 | if(*s==L'/'){ 152 | *p++=s+1; 153 | *s='\0'; 154 | } 155 | else if(*s=='\0') 156 | break; 157 | } 158 | *p=0; 159 | /* 160 | * go through the component list, deleting components that are empty (except 161 | * the last component) or ., and any .. and its non-.. predecessor. 162 | */ 163 | p=q=comp; 164 | while(*p){ 165 | if(runestrcmp(*p, e)==0 && p[1]!=0 166 | || runestrcmp(*p, dot)==0) 167 | p++; 168 | else if(runestrcmp(*p, up)==0 && q!=comp && runestrcmp(q[-1], up)!=0){ 169 | --q; 170 | p++; 171 | } 172 | else 173 | *q++=*p++; 174 | } 175 | *q=0; 176 | /* 177 | * rebuild the path name 178 | */ 179 | s=name; 180 | if(rooted) *s++='/'; 181 | for(p=comp;*p;p++){ 182 | t=*p; 183 | while(*t) *s++=*t++; 184 | if(p[1]!=0) *s++='/'; 185 | } 186 | *s='\0'; 187 | free(comp); 188 | } 189 | 190 | /* this is a HACK */ 191 | Rune * 192 | urlcombine(Rune *b, Rune *u) 193 | { 194 | Rune *p, *q, *sep, *s; 195 | Rune endrune[] = { L'?', L'#' }; 196 | int i, restore; 197 | if(u == nil) 198 | error("urlcombine: u == nil"); 199 | 200 | if(validurl(u)) 201 | return erunestrdup(u); 202 | 203 | if(b==nil || !validurl(b)) 204 | error("urlcombine: b==nil || !validurl(b)"); 205 | 206 | if(runestrncmp(u, (Rune[]){'/','/',0}, 2) == 0){ 207 | q = runestrchr(b, L':'); 208 | return runesmprint("%.*S:%S", (int)(q-b), b, u); 209 | } 210 | p = runestrstr(b, (Rune[]){':','/','/',0})+3; 211 | sep = (Rune[]){0}; 212 | q = nil; 213 | if(*u ==L'/') 214 | q = runestrchr(p, L'/'); 215 | else if(*u==L'#' || *u==L'?'){ 216 | for(i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | void 18 | rowinit(Row *row, Rectangle r) 19 | { 20 | Rectangle r1; 21 | Text *t; 22 | 23 | draw(screen, r, display->white, nil, ZP); 24 | row->r = r; 25 | row->col = nil; 26 | row->ncol = 0; 27 | r1 = r; 28 | r1.max.y = r1.min.y + font->height; 29 | t = &row->tag; 30 | textinit(t, screen, r1, font, tagcols); 31 | t->what = Rowtag; 32 | t->row = row; 33 | t->w = nil; 34 | t->col = nil; 35 | r1.min.y = r1.max.y; 36 | r1.max.y += Border; 37 | draw(screen, r1, display->black, nil, ZP); 38 | textinsert(t, 0, (Rune[]){'N','e','w','c','o','l',' ','G','o','o','g','l','e',' ','E','x','i','t',' ',0} , 19); 39 | textsetselect(t, t->rs.nr, t->rs.nr); 40 | } 41 | 42 | Column* 43 | rowadd(Row *row, Column *c, int x) 44 | { 45 | Rectangle r, r1; 46 | Column *d; 47 | int i; 48 | 49 | d = nil; 50 | r = row->r; 51 | r.min.y = row->tag.r.max.y+Border; 52 | if(xncol>0){ /*steal 40% of last column by default */ 53 | d = row->col[row->ncol-1]; 54 | x = d->r.min.x + 3*Dx(d->r)/5; 55 | } 56 | /* look for column we'll land on */ 57 | for(i=0; incol; i++){ 58 | d = row->col[i]; 59 | if(x < d->r.max.x) 60 | break; 61 | } 62 | if(row->ncol > 0){ 63 | if(i < row->ncol) 64 | i++; /* new column will go after d */ 65 | r = d->r; 66 | if(Dx(r) < 100) 67 | return nil; 68 | draw(screen, r, display->white, nil, ZP); 69 | r1 = r; 70 | r1.max.x = min(x, r.max.x-50); 71 | if(Dx(r1) < 50) 72 | r1.max.x = r1.min.x+50; 73 | colresize(d, r1); 74 | r1.min.x = r1.max.x; 75 | r1.max.x = r1.min.x+Border; 76 | draw(screen, r1, display->black, nil, ZP); 77 | r.min.x = r1.max.x; 78 | } 79 | if(c == nil){ 80 | c = emalloc(sizeof(Column)); 81 | colinit(c, r); 82 | }else 83 | colresize(c, r); 84 | c->row = row; 85 | c->tag.row = row; 86 | row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*)); 87 | memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*)); 88 | row->col[i] = c; 89 | row->ncol++; 90 | clearmouse(); 91 | return c; 92 | } 93 | 94 | void 95 | rowresize(Row *row, Rectangle r) 96 | { 97 | int i, dx, odx; 98 | Rectangle r1, r2; 99 | Column *c; 100 | 101 | dx = Dx(r); 102 | odx = Dx(row->r); 103 | row->r = r; 104 | r1 = r; 105 | r1.max.y = r1.min.y + font->height; 106 | textresize(&row->tag, screen, r1); 107 | r1.min.y = r1.max.y; 108 | r1.max.y += Border; 109 | draw(screen, r1, display->black, nil, ZP); 110 | r.min.y = r1.max.y; 111 | r1 = r; 112 | r1.max.x = r1.min.x; 113 | for(i=0; incol; i++){ 114 | c = row->col[i]; 115 | r1.min.x = r1.max.x; 116 | if(i == row->ncol-1) 117 | r1.max.x = r.max.x; 118 | else 119 | r1.max.x = r1.min.x+Dx(c->r)*dx/odx; 120 | if(i > 0){ 121 | r2 = r1; 122 | r2.max.x = r2.min.x+Border; 123 | draw(screen, r2, display->black, nil, ZP); 124 | r1.min.x = r2.max.x; 125 | } 126 | colresize(c, r1); 127 | } 128 | } 129 | 130 | void 131 | rowdragcol(Row *row, Column *c, int notused) 132 | { 133 | Rectangle r; 134 | int i, b, x; 135 | Point p, op; 136 | Column *d; 137 | 138 | clearmouse(); 139 | setcursor(mousectl, &boxcursor); 140 | b = mouse->buttons; 141 | op = mouse->xy; 142 | while(mouse->buttons == b) 143 | readmouse(mousectl); 144 | setcursor(mousectl, nil); 145 | if(mouse->buttons){ 146 | while(mouse->buttons) 147 | readmouse(mousectl); 148 | return; 149 | } 150 | 151 | for(i=0; incol; i++) 152 | if(row->col[i] == c) 153 | goto Found; 154 | error("can't find column"); 155 | 156 | Found: 157 | if(i == 0) 158 | return; 159 | p = mouse->xy; 160 | if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5)) 161 | return; 162 | if((i>0 && p.xcol[i-1]->r.min.x) || (incol-1 && p.x>c->r.max.x)){ 163 | /* shuffle */ 164 | x = c->r.min.x; 165 | rowclose(row, c, FALSE); 166 | if(rowadd(row, c, p.x) == nil) /* whoops! */ 167 | if(rowadd(row, c, x) == nil) /* WHOOPS! */ 168 | if(rowadd(row, c, -1)==nil){ /* shit! */ 169 | rowclose(row, c, TRUE); 170 | return; 171 | } 172 | colmousebut(c); 173 | return; 174 | } 175 | d = row->col[i-1]; 176 | if(p.x < d->r.min.x+80+Scrollsize) 177 | p.x = d->r.min.x+80+Scrollsize; 178 | if(p.x > c->r.max.x-80-Scrollsize) 179 | p.x = c->r.max.x-80-Scrollsize; 180 | r = d->r; 181 | r.max.x = c->r.max.x; 182 | draw(screen, r, display->white, nil, ZP); 183 | r.max.x = p.x; 184 | colresize(d, r); 185 | r = c->r; 186 | r.min.x = p.x; 187 | r.max.x = r.min.x; 188 | r.max.x += Border; 189 | draw(screen, r, display->black, nil, ZP); 190 | r.min.x = r.max.x; 191 | r.max.x = c->r.max.x; 192 | colresize(c, r); 193 | colmousebut(c); 194 | } 195 | 196 | void 197 | rowclose(Row *row, Column *c, int dofree) 198 | { 199 | Rectangle r; 200 | int i; 201 | 202 | for(i=0; incol; i++) 203 | if(row->col[i] == c) 204 | goto Found; 205 | error("can't find column"); 206 | Found: 207 | r = c->r; 208 | if(dofree) 209 | colcloseall(c); 210 | memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*)); 211 | row->ncol--; 212 | row->col = realloc(row->col, row->ncol*sizeof(Column*)); 213 | if(row->ncol == 0){ 214 | draw(screen, r, display->white, nil, ZP); 215 | return; 216 | } 217 | if(i == row->ncol){ /* extend last column right */ 218 | c = row->col[i-1]; 219 | r.min.x = c->r.min.x; 220 | r.max.x = row->r.max.x; 221 | }else{ /* extend next window left */ 222 | c = row->col[i]; 223 | r.max.x = c->r.max.x; 224 | } 225 | draw(screen, r, display->white, nil, ZP); 226 | colresize(c, r); 227 | } 228 | 229 | Column* 230 | rowwhichcol(Row *row, Point p) 231 | { 232 | int i; 233 | Column *c; 234 | 235 | for(i=0; incol; i++){ 236 | c = row->col[i]; 237 | if(ptinrect(p, c->r)) 238 | return c; 239 | } 240 | return nil; 241 | } 242 | 243 | Text* 244 | rowwhich(Row *row, Point p, Rune r, int key) 245 | { 246 | Column *c; 247 | 248 | if(ptinrect(p, row->tag.all)) 249 | return &row->tag; 250 | c = rowwhichcol(row, p); 251 | if(c) 252 | return colwhich(c, p, r, key); 253 | return nil; 254 | } 255 | 256 | -------------------------------------------------------------------------------- /abaco/tabs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | void 18 | drawtable(Box *b, Page *p, Image *im) 19 | { 20 | Rectangle r, cr; 21 | Tablecell *c; 22 | Table *t; 23 | 24 | t = ((Itable *)b->i)->table; 25 | r = rectsubpt(b->r, p->pos); 26 | draw(im, r, getcolor(t->background.color), nil, ZP); 27 | if(t->border) 28 | border(im, r, t->border, display->black, ZP); 29 | for(c=t->cells; c!=nil; c=c->next){ 30 | cr = rectsubpt(c->lay->r, p->pos); 31 | if(c->background.color != t->background.color) 32 | draw(im, cr, getcolor(c->background.color), nil, ZP); 33 | if(t->border) 34 | border(im, cr, t->border, display->black, ZP); 35 | laydraw(p, im, c->lay); 36 | } 37 | } 38 | 39 | enum 40 | { 41 | Tablemax = 2000, 42 | Ttoplevel = 1<<0, 43 | 44 | }; 45 | 46 | static 47 | void 48 | settable(Table *t) 49 | { 50 | Tablecell *c; 51 | Lay *lay; 52 | 53 | for(c=t->cells; c!=nil; c=c->next){ 54 | lay = layitems(c->content, Rect(0,0,0,0), FALSE); 55 | c->minw = Dx(lay->r); 56 | layfree(lay); 57 | if(dimenkind(c->wspec) == Dnone){ 58 | lay = layitems(c->content, Rect(0,0,Tablemax,0), FALSE); 59 | c->maxw = Dx(lay->r); 60 | layfree(lay); 61 | } 62 | } 63 | } 64 | 65 | void 66 | settables(Page *p) 67 | { 68 | Table *t; 69 | Item *i; 70 | 71 | if(p->doc==nil) 72 | return; 73 | for(i=p->items; i!=nil; i=i->next) 74 | if(i->tag == Itabletag) 75 | ((Itable *)i)->table->flags |= Ttoplevel; 76 | 77 | for(t=p->doc->tables; t!=nil; t=t->next) 78 | settable(t); 79 | } 80 | 81 | static 82 | int 83 | cellwidth(Table *t, Tablecell *c, int sep) 84 | { 85 | int w, i, n; 86 | 87 | n = c->colspan; 88 | if(n == 1) 89 | return t->cols[c->col].width; 90 | if(n == 0) 91 | n = t->ncol - c->col; 92 | 93 | w = t->cellspacing*(n-1) + n*sep; 94 | for(i=c->col; icol+n; i++) 95 | w += t->cols[i].width; 96 | 97 | return w; 98 | } 99 | 100 | static 101 | int 102 | cellheight(Table *t, Tablecell *c, int sep) 103 | { 104 | int h, i, n; 105 | 106 | n = c->rowspan; 107 | if(n == 1) 108 | return t->rows[c->row].height; 109 | if(n == 0) 110 | n = t->nrow - c->row; 111 | 112 | h = t->cellspacing*(n-1) + n*sep; 113 | for(i=c->row; irow+n; i++) 114 | h += t->rows[i].height; 115 | 116 | return h; 117 | } 118 | 119 | static 120 | int 121 | getwidth(int *w, int n) 122 | { 123 | int i, tot; 124 | 125 | tot = 0; 126 | for(i=0; icells; c!=nil; c=c->next){ 141 | if(c->colspan == 1) 142 | continue; 143 | 144 | n = c->colspan; 145 | if(n == 0) 146 | n = t->ncol - c->col; 147 | 148 | w = domax ? c->maxw : c->minw; 149 | w -= t->cellspacing*(n-1) + n*sep; 150 | 151 | aw = 0; 152 | for(i=c->col; icol+n; i++) 153 | aw += width[i]; 154 | 155 | rem = w-aw; 156 | if(rem <= 0) 157 | continue; 158 | 159 | for(i=c->col; icol+n; i++){ 160 | if(aw > 0){ 161 | d = width[i]*100/aw; 162 | d = d*rem/100; 163 | }else 164 | d = rem/n; 165 | width[i] += d; 166 | } 167 | } 168 | } 169 | 170 | static 171 | int 172 | tablewidth(Table *t, int tw, int sep) 173 | { 174 | Tablecell *c; 175 | int i, w, tmin, tmax, d; 176 | int *maxw, *minw; 177 | int totw; 178 | 179 | maxw = emalloc(sizeof(int)*t->ncol); 180 | minw = emalloc(sizeof(int)*t->ncol); 181 | for(c=t->cells; c!=nil; c=c->next){ 182 | if(dimenkind(c->wspec) != Dnone){ 183 | d = c->minw; 184 | c->minw = c->maxw = max(dimwidth(c->wspec, tw), c->minw); 185 | c->minw = d; 186 | } 187 | if(c->colspan != 1) 188 | continue; 189 | maxw[c->col] = max(maxw[c->col], c->maxw); 190 | minw[c->col] = max(minw[c->col], c->minw); 191 | } 192 | totw = 0; 193 | fixcols(t, maxw, sep, TRUE); 194 | tmax = getwidth(maxw, t->ncol); 195 | if(tmax <= tw){ 196 | d = 0; 197 | if(tw>tmax && dimenkind(t->width)!=Dnone && t->availw!=Tablemax) 198 | d = (tw-tmax)/t->ncol; 199 | for(i=0; incol; i++){ 200 | t->cols[i].width = maxw[i] + d; 201 | totw += t->cols[i].width; 202 | } 203 | }else{ 204 | fixcols(t, minw, sep, FALSE); 205 | tmin = getwidth(minw, t->ncol); 206 | w = tw - tmin; 207 | d = tmax - tmin; 208 | for(i=0; incol; i++){ 209 | if(w<=0 || d<=0) 210 | t->cols[i].width = minw[i]; 211 | else 212 | t->cols[i].width = minw[i] + (maxw[i] - minw[i])*w/d; 213 | totw += t->cols[i].width; 214 | } 215 | } 216 | free(minw); 217 | free(maxw); 218 | 219 | return totw; 220 | } 221 | 222 | static 223 | void 224 | fixrows(Table *t, int sep) 225 | { 226 | Tablecell *c; 227 | Lay *lay; 228 | int h, ah, i, d, n, rem; 229 | 230 | for(c=t->cells; c!=nil; c=c->next){ 231 | if(c->rowspan == 1) 232 | continue; 233 | n = c->rowspan; 234 | if(n == 0 || c->row+n>t->nrow) 235 | n = t->nrow - c->row; 236 | 237 | lay = layitems(c->content, Rect(0,0,cellwidth(t, c, sep),0), FALSE); 238 | h = max(Dy(lay->r), c->hspec); 239 | layfree(lay); 240 | h -= t->cellspacing*(n-1) + n*sep; 241 | ah = 0; 242 | for(i=c->row; irow+n; i++) 243 | ah += t->rows[i].height; 244 | 245 | rem = h-ah; 246 | if(rem <= 0) 247 | continue; 248 | 249 | for(i=c->row; irow+n; i++){ 250 | if(ah > 0){ 251 | d = t->rows[i].height*100/ah; 252 | d = d*rem/100; 253 | }else 254 | d = rem/n; 255 | 256 | t->rows[i].height += d; 257 | } 258 | } 259 | } 260 | 261 | static 262 | int 263 | tableheight(Table *t, int sep) 264 | { 265 | Tablecell *c; 266 | Lay *lay; 267 | int i, h, toth; 268 | 269 | for(i=0; inrow; i++){ 270 | h = 0; 271 | for(c=t->rows[i].cells; c!=nil; c=c->nextinrow){ 272 | if(c->rowspan != 1) 273 | continue; 274 | lay = layitems(c->content, Rect(0, 0, cellwidth(t, c, sep), 0), FALSE); 275 | h = max(h, max(Dy(lay->r), c->hspec)); 276 | layfree(lay); 277 | } 278 | t->rows[i].height = h; 279 | } 280 | fixrows(t, sep); 281 | toth = 0; 282 | for(i=0; inrow; i++) 283 | toth += t->rows[i].height; 284 | 285 | return toth; 286 | } 287 | 288 | void 289 | tablesize(Table *t, int availw) 290 | { 291 | int w, sep, hsep, vsep; 292 | 293 | t->availw = availw; 294 | sep = 2*(t->border+t->cellpadding); 295 | hsep = t->cellspacing*(t->ncol+1) + 2*t->border + t->ncol*sep; 296 | vsep = t->cellspacing*(t->nrow+1) + 2*t->border + t->nrow*sep; 297 | w = dimwidth(t->width, availw); 298 | w -= hsep; 299 | w = w>0 ? w : 0; 300 | t->totw = tablewidth(t, w, sep); 301 | t->totw += hsep; 302 | t->toth = tableheight(t, sep); 303 | t->toth += vsep; 304 | } 305 | 306 | void 307 | laytable(Itable *it, Rectangle r) 308 | { 309 | Rectangle cr; 310 | Tablecell *c; 311 | Table *t; 312 | int x, y, h, w; 313 | int sep, i; 314 | 315 | t = it->table; 316 | 317 | sep = (t->cellpadding+t->border) * 2; 318 | r = insetrect(r, t->cellspacing+t->border); 319 | for(c=t->cells; c!=nil; c=c->next){ 320 | w = cellwidth(t, c, sep); 321 | h = cellheight(t, c, sep); 322 | x = r.min.x; 323 | if(c->col > 0) 324 | for(i=0; icol; i++) 325 | x += t->cols[i].width + sep + t->cellspacing; 326 | y = r.min.y; 327 | if(c->row > 0) 328 | for(i=0;i row; i++) 329 | y += t->rows[i].height + sep + t->cellspacing; 330 | cr = Rect(x, y, x+w+sep, y+h+sep); 331 | c->lay = layitems(c->content, insetrect(cr, sep/2), TRUE); 332 | c->lay->r = cr; 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /abaco/scrl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | static Image *vscrtmp; 18 | static Image *hscrtmp; 19 | 20 | ulong 21 | umuldiv(ulong a, ulong b, ulong c) 22 | { 23 | double d; 24 | 25 | d = ((double)a * (double)b) / (double)c; 26 | if(d >= 4294967296.) 27 | abort(); 28 | return d; 29 | } 30 | 31 | long 32 | muldiv(long a, long b, long c) 33 | { 34 | int s; 35 | long v; 36 | 37 | s = 0; 38 | if(a < 0) { 39 | s = !s; 40 | a = -a; 41 | } 42 | if(b < 0) { 43 | s = !s; 44 | b = -b; 45 | } 46 | if(c < 0) { 47 | s = !s; 48 | c = -c; 49 | } 50 | v = umuldiv(a, b, c); 51 | if(s) 52 | v = -v; 53 | return v; 54 | } 55 | 56 | void 57 | scrlresize(void) 58 | { 59 | freeimage(vscrtmp); 60 | freeimage(hscrtmp); 61 | vscrtmp = eallocimage(display, Rect(0, 0, 32, screen->r.max.y), screen->chan, 0, DNofill); 62 | hscrtmp = eallocimage(display, Rect(0, 0, screen->r.max.x, 32), screen->chan, 0, DNofill); 63 | } 64 | 65 | static 66 | Rectangle 67 | scrpos(Rectangle r, uint p0, uint p1, uint tot) 68 | { 69 | Rectangle q; 70 | int h; 71 | 72 | q = r; 73 | h = q.max.y-q.min.y; 74 | if(tot == 0) 75 | return q; 76 | if(tot > 1024*1024){ 77 | tot>>=10; 78 | p0>>=10; 79 | p1>>=10; 80 | } 81 | if(p0 > 0) 82 | q.min.y += h*p0/tot; 83 | if(p1 < tot) 84 | q.max.y -= h*(tot-p1)/tot; 85 | if(q.max.y < q.min.y+2){ 86 | if(q.min.y+2 <= r.max.y) 87 | q.max.y = q.min.y+2; 88 | else 89 | q.min.y = q.max.y-2; 90 | } 91 | return q; 92 | } 93 | 94 | void 95 | textscrdraw(Text *t) 96 | { 97 | Rectangle r, r1, r2; 98 | Image *b; 99 | 100 | if(vscrtmp == nil) 101 | scrlresize(); 102 | r = t->scrollr; 103 | b = vscrtmp; 104 | r1 = r; 105 | r1.min.x = 0; 106 | r1.max.x = Dx(r); 107 | r2 = scrpos(r1, t->org, t->org+t->nchars, t->rs.nr); 108 | if(!eqrect(r2, t->lastsr)){ 109 | t->lastsr = r2; 110 | draw(b, r1, t->cols[BORD], nil, ZP); 111 | draw(b, r2, t->cols[BACK], nil, ZP); 112 | r2.min.x = r2.max.x-1; 113 | draw(b, r2, t->cols[BORD], nil, ZP); 114 | draw(t->b, r, b, nil, Pt(0, r1.min.y)); 115 | /*flushimage(display, 1);/*BUG?*/ 116 | } 117 | } 118 | 119 | void 120 | scrsleep(uint dt) 121 | { 122 | Timer *timer; 123 | static Alt alts[3]; 124 | 125 | timer = timerstart(dt); 126 | alts[0].c = timer->c; 127 | alts[0].v = nil; 128 | alts[0].op = CHANRCV; 129 | alts[1].c = mousectl->c; 130 | alts[1].v = &mousectl->m; 131 | alts[1].op = CHANRCV; 132 | alts[2].op = CHANEND; 133 | for(;;) 134 | switch(alt(alts)){ 135 | case 0: 136 | timerstop(timer); 137 | return; 138 | case 1: 139 | timercancel(timer); 140 | return; 141 | } 142 | } 143 | 144 | void 145 | textscroll(Text *t, int but) 146 | { 147 | uint p0, oldp0; 148 | Rectangle s; 149 | int x, y, my, h, first; 150 | 151 | s = insetrect(t->scrollr, 1); 152 | h = s.max.y-s.min.y; 153 | x = (s.min.x+s.max.x)/2; 154 | oldp0 = ~0; 155 | first = TRUE; 156 | do{ 157 | flushimage(display, 1); 158 | my = mouse->xy.y; 159 | if(my < s.min.y) 160 | my = s.min.y; 161 | if(my >= s.max.y) 162 | my = s.max.y; 163 | if(!eqpt(mouse->xy, Pt(x, my))){ 164 | moveto(mousectl, Pt(x, my)); 165 | readmouse(mousectl); /* absorb event generated by moveto() */ 166 | } 167 | if(but == 2){ 168 | y = my; 169 | p0 = (vlong)t->rs.nr*(y-s.min.y)/h; 170 | if(p0 >= t->q1) 171 | p0 = textbacknl(t, p0, 2); 172 | if(oldp0 != p0) 173 | textsetorigin(t, p0, FALSE); 174 | oldp0 = p0; 175 | readmouse(mousectl); 176 | continue; 177 | } 178 | if(but == 1) 179 | p0 = textbacknl(t, t->org, (my-s.min.y)/t->font->height); 180 | else 181 | p0 = t->org+frcharofpt(&t->Frame, Pt(s.max.x, my)); 182 | if(oldp0 != p0) 183 | textsetorigin(t, p0, TRUE); 184 | oldp0 = p0; 185 | /* debounce */ 186 | if(first){ 187 | flushimage(display, 1); 188 | sleep(200); 189 | nbrecv(mousectl->c, &mousectl->m); 190 | first = FALSE; 191 | } 192 | scrsleep(80); 193 | }while(mouse->buttons & (1<<(but-1))); 194 | while(mouse->buttons) 195 | readmouse(mousectl); 196 | } 197 | 198 | enum 199 | { 200 | Scrbord = 1, 201 | }; 202 | 203 | void 204 | pagescrldraw(Page *p) 205 | { 206 | Rectangle r1; 207 | int t, d, l, h; 208 | 209 | if(vscrtmp == nil) 210 | scrlresize(); 211 | 212 | r1 = Rect(0,0,Dx(p->hscrollr), Dy(p->hscrollr)); 213 | d = Dx(r1); 214 | t = Dx(p->lay->r); 215 | l = muldiv(p->pos.x, d, t); 216 | h = muldiv(p->pos.x+d, d, t); 217 | draw(hscrtmp, r1, tagcols[HIGH], nil, ZP); 218 | r1.max.x = r1.min.x+h; 219 | r1.min.x += l; 220 | r1.min.y += Scrbord; 221 | r1.max.y -= Scrbord; 222 | draw(hscrtmp, r1, tagcols[BACK], nil, ZP); 223 | 224 | r1 = Rect(0,0,Dx(p->vscrollr), Dy(p->vscrollr)); 225 | d = Dy(r1); 226 | t = Dy(p->lay->r); 227 | l = muldiv(p->pos.y, d, t); 228 | h = muldiv(p->pos.y+d, d, t); 229 | draw(vscrtmp, r1, tagcols[HIGH], nil, ZP); 230 | r1.max.y = r1.min.y+h; 231 | r1.min.y += l; 232 | r1.max.x -= Scrbord; 233 | r1.min.x += Scrbord; 234 | draw(vscrtmp, r1, tagcols[BACK], nil, ZP); 235 | 236 | draw(screen, p->vscrollr, vscrtmp, nil, vscrtmp->r.min); 237 | draw(screen, p->hscrollr, hscrtmp, nil, hscrtmp->r.min); 238 | } 239 | 240 | void 241 | pagescroll(Page *p, int but, int horizontal) 242 | { 243 | uint oldp0, p0; 244 | Rectangle s; 245 | Point mxy; 246 | int i, m, om, first, d, size; 247 | int smin, smax, ss, *pos; 248 | Timer *timer; 249 | static Alt alts[3]; 250 | 251 | alts[0].v = nil; 252 | alts[0].op = CHANRCV; 253 | alts[1].c = mousectl->c; 254 | alts[1].v = &mousectl->m; 255 | alts[1].op = CHANRCV; 256 | alts[2].op = CHANEND; 257 | 258 | if(horizontal){ 259 | s = insetrect(p->hscrollr, 1); 260 | ss = s.max.x - s.min.x; 261 | i = (s.min.y+s.max.y)/2; 262 | d = Dx(p->r); 263 | size = Dx(p->lay->r); 264 | p0 = p->pos.x; 265 | pos = &p->pos.x; 266 | smin = s.min.x; 267 | smax = s.max.x; 268 | m = om = mouse->xy.x; 269 | }else{ 270 | s = insetrect(p->vscrollr, 1); 271 | ss = s.max.y-s.min.y; 272 | i = (s.min.x+s.max.x)/2; 273 | d = Dy(p->r); 274 | size = Dy(p->lay->r); 275 | p0 = p->pos.y; 276 | pos = &p->pos.y; 277 | smin = s.min.y; 278 | smax = s.max.y; 279 | m = om = mouse->xy.y; 280 | } 281 | oldp0 = ~0; 282 | first = TRUE; 283 | do{ 284 | flushimage(display, 1); 285 | 286 | timer = timerstart(15); 287 | alts[0].c = timer->c; 288 | for(;;) 289 | switch(alt(alts)){ 290 | case 0: 291 | timerstop(timer); 292 | if(m == om) { 293 | timer = timerstart(15); 294 | alts[0].c = timer->c; 295 | continue; 296 | } 297 | goto endalt; 298 | case 1: 299 | if(horizontal) 300 | m = mouse->xy.x; 301 | else 302 | m = mouse->xy.y; 303 | 304 | if(m < smin) 305 | m = smin; 306 | if(m > smax) 307 | m = smax; 308 | 309 | if(horizontal) 310 | mxy = Pt(m, i); 311 | else 312 | mxy = Pt(i, m); 313 | if(!eqpt(mouse->xy, mxy)) 314 | moveto(mousectl, mxy); 315 | readmouse(mousectl); 316 | 317 | if(!(mouse->buttons & (1<<(but-1)))) 318 | goto endalt; 319 | break; 320 | } 321 | endalt: 322 | om = m; 323 | if(but == 2){ 324 | p0 = muldiv(m-smin, size, ss); 325 | p0 = max(p0, 0); 326 | p0 = min(p0,size-d); 327 | if(oldp0 != p0){ 328 | *pos = p0; 329 | pageredraw(p); 330 | } 331 | oldp0 = p0; 332 | readmouse(mousectl); 333 | continue; 334 | } 335 | if(but == 1) 336 | p0 -= (d/2); 337 | else 338 | p0 += d/2; 339 | p0 = min(p0, size-d); 340 | p0 = max(p0, 0); 341 | if(oldp0 != p0){ 342 | *pos = p0; 343 | pageredraw(p); 344 | } 345 | oldp0 = p0; 346 | /* debounce */ 347 | if(first){ 348 | flushimage(display, 1); 349 | sleep(200); 350 | nbrecv(mousectl->c, &mousectl->m); 351 | first = FALSE; 352 | } 353 | scrsleep(80); 354 | }while(mouse->buttons & (1<<(but-1))); 355 | while(mouse->buttons) 356 | readmouse(mousectl); 357 | } 358 | 359 | int 360 | pagescrollxy(Page *p, int x, int y) 361 | { 362 | int scrled; 363 | 364 | scrled = FALSE; 365 | if(x != 0){ 366 | p->pos.x += x; 367 | p->pos.x = max(p->pos.x, 0); 368 | p->pos.x = min(p->pos.x, Dx(p->lay->r)-Dx(p->r)); 369 | scrled =TRUE; 370 | } 371 | if(y != 0){ 372 | p->pos.y += y; 373 | p->pos.y = max(p->pos.y, 0); 374 | p->pos.y = min(p->pos.y, Dy(p->lay->r)-Dy(p->r)); 375 | scrled =TRUE; 376 | } 377 | return scrled; 378 | } 379 | -------------------------------------------------------------------------------- /webfs/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include <9p.h> 9 | #include "dat.h" 10 | #include "fns.h" 11 | 12 | int nclient; 13 | Client **client; 14 | 15 | static void clientthread(void*); 16 | int 17 | newclient(int plumbed) 18 | { 19 | int i; 20 | Client *c; 21 | 22 | for(i=0; iref==0) 24 | return i; 25 | 26 | c = emalloc(sizeof(Client)); 27 | c->plumbed = plumbed; 28 | c->creq = chancreate(sizeof(Req*), 8); 29 | threadcreate(clientthread, c, STACK); 30 | 31 | c->io = ioproc(); 32 | c->num = nclient; 33 | c->ctl = globalctl; 34 | clonectl(&c->ctl); 35 | if(nclient%16 == 0) 36 | client = erealloc(client, (nclient+16)*sizeof(client[0])); 37 | client[nclient++] = c; 38 | return nclient-1; 39 | } 40 | 41 | void 42 | closeclient(Client *c) 43 | { 44 | if(--c->ref == 0){ 45 | if(c->bodyopened){ 46 | if(c->url && c->url->close) 47 | (*c->url->close)(c); 48 | c->bodyopened = 0; 49 | } 50 | free(c->contenttype); 51 | c->contenttype = nil; 52 | free(c->postbody); 53 | c->postbody = nil; 54 | freeurl(c->url); 55 | c->url = nil; 56 | free(c->redirect); 57 | c->redirect = nil; 58 | free(c->authenticate); 59 | c->authenticate = nil; 60 | c->npostbody = 0; 61 | c->havepostbody = 0; 62 | c->bodyopened = 0; 63 | } 64 | } 65 | 66 | void 67 | clonectl(Ctl *c) 68 | { 69 | if(c->useragent) 70 | c->useragent = estrdup(c->useragent); 71 | } 72 | 73 | void 74 | clientbodyopen(Client *c, Req *r) 75 | { 76 | char e[ERRMAX], *next; 77 | int i; 78 | Url *u; 79 | 80 | next = nil; 81 | for(i=0; i<=c->ctl.redirectlimit; i++){ 82 | if(c->url == nil){ 83 | werrstr("nil url"); 84 | goto Error; 85 | } 86 | if(c->url->open == nil){ 87 | werrstr("unsupported url type"); 88 | goto Error; 89 | } 90 | if(fsdebug) 91 | fprint(2, "try %s\n", c->url->url); 92 | if(c->url->open(c, c->url) < 0){ 93 | Error: 94 | if(next) 95 | fprint(2, "next %s (but for error)\n", next); 96 | free(next); 97 | rerrstr(e, sizeof e); 98 | c->iobusy = 0; 99 | if(r != nil) 100 | r->fid->omode = -1; 101 | closeclient(c); /* not opening */ 102 | if(r != nil) 103 | respond(r, e); 104 | return; 105 | } 106 | if (c->authenticate) 107 | continue; 108 | if(!c->redirect) 109 | break; 110 | next = c->redirect; 111 | c->redirect = nil; 112 | if(i==c->ctl.redirectlimit){ 113 | werrstr("redirect limit reached"); 114 | goto Error; 115 | } 116 | if((u = parseurl(next, c->url)) == nil) 117 | goto Error; 118 | if(urldebug) 119 | fprint(2, "parseurl %s got scheme %d\n", next, u->ischeme); 120 | if(u->ischeme == USunknown){ 121 | werrstr("redirect with unknown URL scheme"); 122 | goto Error; 123 | } 124 | if(u->ischeme == UScurrent){ 125 | werrstr("redirect to URL relative to current document"); 126 | goto Error; 127 | } 128 | freeurl(c->url); 129 | c->url = u; 130 | } 131 | free(next); 132 | c->iobusy = 0; 133 | if(r != nil) 134 | respond(r, nil); 135 | } 136 | 137 | void 138 | plumburl(char *url, char *base) 139 | { 140 | int i; 141 | Client *c; 142 | Url *ubase, *uurl; 143 | 144 | ubase = nil; 145 | if(base){ 146 | ubase = parseurl(base, nil); 147 | if(ubase == nil) 148 | return; 149 | } 150 | uurl = parseurl(url, ubase); 151 | if(uurl == nil){ 152 | freeurl(ubase); 153 | return; 154 | } 155 | i = newclient(1); 156 | c = client[i]; 157 | c->ref++; 158 | c->baseurl = ubase; 159 | c->url = uurl; 160 | sendp(c->creq, nil); 161 | } 162 | 163 | void 164 | clientbodyread(Client *c, Req *r) 165 | { 166 | char e[ERRMAX]; 167 | 168 | if(c->url->read == nil){ 169 | respond(r, "unsupported url type"); 170 | return; 171 | } 172 | if(c->url->read(c, r) < 0){ 173 | rerrstr(e, sizeof e); 174 | c->iobusy = 0; 175 | respond(r, e); 176 | return; 177 | } 178 | c->iobusy = 0; 179 | respond(r, nil); 180 | } 181 | 182 | static void 183 | clientthread(void *a) 184 | { 185 | Client *c; 186 | Req *r; 187 | 188 | c = a; 189 | if(c->plumbed) { 190 | recvp(c->creq); 191 | if(c->url == nil){ 192 | fprint(2, "bad url got plumbed\n"); 193 | return; 194 | } 195 | clientbodyopen(c, nil); 196 | replumb(c); 197 | } 198 | while((r = recvp(c->creq)) != nil){ 199 | if(fsdebug) 200 | fprint(2, "clientthread %F\n", &r->ifcall); 201 | switch(r->ifcall.type){ 202 | case Topen: 203 | if(c->plumbed) { 204 | c->plumbed = 0; 205 | c->ref--; /* from plumburl() */ 206 | respond(r, nil); 207 | } 208 | else 209 | clientbodyopen(c, r); 210 | break; 211 | case Tread: 212 | clientbodyread(c, r); 213 | break; 214 | case Tflush: 215 | respond(r, nil); 216 | } 217 | if(fsdebug) 218 | fprint(2, "clientthread finished req\n"); 219 | } 220 | } 221 | 222 | enum 223 | { 224 | Bool, 225 | Int, 226 | String, 227 | XUrl, 228 | Fn, 229 | }; 230 | 231 | typedef struct Ctab Ctab; 232 | struct Ctab { 233 | char *name; 234 | int type; 235 | void *offset; 236 | }; 237 | 238 | Ctab ctltab[] = { 239 | "acceptcookies", Bool, (void*)offsetof(Ctl, acceptcookies), 240 | "sendcookies", Bool, (void*)offsetof(Ctl, sendcookies), 241 | "redirectlimit", Int, (void*)offsetof(Ctl, redirectlimit), 242 | "useragent", String, (void*)offsetof(Ctl, useragent), 243 | }; 244 | 245 | Ctab globaltab[] = { 246 | "chatty9p", Int, &chatty9p, 247 | "fsdebug", Int, &fsdebug, 248 | "cookiedebug", Int, &cookiedebug, 249 | "urldebug", Int, &urldebug, 250 | "httpdebug", Int, &httpdebug, 251 | }; 252 | 253 | Ctab clienttab[] = { 254 | "baseurl", XUrl, (void*)offsetof(Client, baseurl), 255 | "url", XUrl, (void*)offsetof(Client, url), 256 | }; 257 | 258 | static Ctab* 259 | findcmd(char *cmd, Ctab *tab, int ntab) 260 | { 261 | int i; 262 | 263 | for(i=0; ioffset); 315 | parseas(r, arg, t->type, a); 316 | return 1; 317 | } 318 | 319 | int 320 | clientctlwrite(Req *r, Client *c, char *cmd, char *arg) 321 | { 322 | void *a; 323 | Ctab *t; 324 | 325 | if((t = findcmd(cmd, clienttab, nelem(clienttab))) == nil) 326 | return 0; 327 | a = (void*)((uintptr)c+(uintptr)t->offset); 328 | parseas(r, arg, t->type, a); 329 | return 1; 330 | } 331 | 332 | int 333 | globalctlwrite(Req *r, char *cmd, char *arg) 334 | { 335 | void *a; 336 | Ctab *t; 337 | 338 | if((t = findcmd(cmd, globaltab, nelem(globaltab))) == nil) 339 | return 0; 340 | a = t->offset; 341 | parseas(r, arg, t->type, a); 342 | return 1; 343 | } 344 | 345 | static void 346 | ctlfmt(Ctl *c, char *s) 347 | { 348 | int i; 349 | void *a; 350 | char *t; 351 | 352 | for(i=0; i100 ? "..." : ""); 365 | break; 366 | } 367 | } 368 | } 369 | 370 | void 371 | ctlread(Req *r, Client *c) 372 | { 373 | char buf[1024]; 374 | 375 | sprint(buf, "%11d \n", c->num); 376 | ctlfmt(&c->ctl, buf+strlen(buf)); 377 | readstr(r, buf); 378 | respond(r, nil); 379 | } 380 | 381 | void 382 | globalctlread(Req *r) 383 | { 384 | char buf[1024], *s; 385 | int i; 386 | 387 | s = buf; 388 | for(i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | void 18 | wininit(Window *w, Window *notused, Rectangle r) 19 | { 20 | Rectangle r1, br; 21 | 22 | incref(&w->ref); 23 | w->r = r; 24 | w->tag.w = w; 25 | w->url.w = w; 26 | w->page.w = w; 27 | w->status.w = w; 28 | r1 = r; 29 | r1.max.y = r1.min.y + font->height; 30 | textinit(&w->tag, screen, r1, font, tagcols); 31 | w->tag.what = Tag; 32 | r1.min.y = r1.max.y++; 33 | draw(screen, r1, tagcols[BORD], nil, ZP); 34 | br.min = w->tag.scrollr.min; 35 | br.max.x = br.min.x + Dx(button->r); 36 | br.max.y = br.min.y + Dy(button->r); 37 | draw(screen, br, button, nil, button->r.min); 38 | r1.min.y = r1.max.y; 39 | r1.max.y += font->height; 40 | textinit(&w->url, screen, r1, font, tagcols); 41 | w->url. 42 | w->url.what = Urltag; 43 | r1.min.y = r1.max.y++; 44 | draw(screen, r1, tagcols[BORD], nil, ZP); 45 | r1.min.y = r1.max.y; 46 | r1.max.y = r.max.y - font->height - 1; 47 | w->page.all = r1; 48 | w->page.b = screen; 49 | draw(screen, r1, display->white, nil, ZP); 50 | r1.min.y = r1.max.y++; 51 | draw(screen, r1, tagcols[BORD], nil, ZP); 52 | r1.min.y = r1.max.y; 53 | r1.max.y += font->height; 54 | textinit(&w->status, screen, r1, font, tagcols); 55 | w->status.what = Statustag; 56 | } 57 | 58 | int 59 | winresize(Window *w, Rectangle r, int safe) 60 | { 61 | Rectangle r1, br; 62 | 63 | w->r = r; 64 | r1 = r; 65 | r1.max.y = r1.min.y + font->height; 66 | if(!safe || !eqrect(w->tag.r, r1)){ 67 | textresize(&w->tag, screen, r1); 68 | br.min = w->tag.scrollr.min; 69 | br.max.x = r1.min.x + Dx(button->r); 70 | br.max.y = r1.min.y + Dy(button->r); 71 | draw(screen, br, button, nil, button->r.min); 72 | r1.min.y = r1.max.y++; 73 | draw(screen, r1, tagcols[BORD], nil, ZP); 74 | r1.min.y = r1.max.y; 75 | r1.max.y += font->height; 76 | textresize(&w->url, screen, r1); 77 | r1.min.y = r1.max.y++; 78 | draw(screen, r1, tagcols[BORD], nil, ZP); 79 | } 80 | r1.min.y = r1.max.y; 81 | r1.max.y = r.max.y - font->height - 1; 82 | w->page.b = screen; 83 | if(!safe || !eqrect(w->page.all, r1)){ 84 | if(Dy(r1) <= 0){ 85 | w->page.all = ZR; 86 | pagerender(&w->page); 87 | w->r = r; 88 | w->r.max.y = r1.min.y; 89 | return w->r.max.y; 90 | } 91 | draw(screen, r1, display->white, nil, ZP); 92 | w->page.all = r1; 93 | pagerender(&w->page); 94 | r1.min.y = r1.max.y++; 95 | draw(screen, r1, tagcols[BORD], nil, ZP); 96 | r1.min.y = r1.max.y; 97 | r1.max.y = r.max.y; 98 | textresize(&w->status, screen, r1); 99 | } 100 | return w->r.max.y; 101 | } 102 | 103 | void 104 | winclose1(Window *w) 105 | { 106 | int i; 107 | 108 | if(decref(&w->ref) == 0){ 109 | textclose(&w->tag); 110 | textclose(&w->url); 111 | textclose(&w->status); 112 | if(w->history.url){ 113 | for(i=0; ihistory.nurl; i++) 114 | urlfree(w->history.url[i]); 115 | free(w->history.url); 116 | } 117 | free(w); 118 | } 119 | } 120 | 121 | void 122 | winclose(Window *w) 123 | { 124 | pageclose(&w->page); 125 | winclose1(w); 126 | } 127 | 128 | void 129 | winlock(Window *w, int owner) 130 | { 131 | incref(&w->ref); 132 | qlock(&w->lock); 133 | w->owner = owner; 134 | } 135 | 136 | void 137 | winunlock(Window *w) 138 | { 139 | w->owner = 0; 140 | qunlock(&w->lock); 141 | winclose1(w); 142 | } 143 | 144 | void 145 | winsettag1(Window *w) 146 | { 147 | int i, j, k, n, bar; 148 | Rune *new, *r; 149 | Image *b; 150 | uint q0, q1; 151 | Rectangle br; 152 | Runestr old; 153 | 154 | memset(&old, 0, sizeof(Runestr)); 155 | copyrunestr(&old, &w->tag.rs); 156 | for(i=0; itag.rs.nr; i++) 157 | if(old.r[i]==' ' || old.r[i]=='\t') 158 | break; 159 | 160 | if(runestreq(old, w->page.title) == FALSE){ 161 | textdelete(&w->tag, 0, i); 162 | textinsert(&w->tag, 0, w->page.title.r, w->page.title.nr); 163 | closerunestr(&old); 164 | copyrunestr(&old, &w->tag.rs); 165 | } 166 | new = runemalloc(w->page.title.nr+100); 167 | i = 0; 168 | runemove(new+i, ((Rune[]){ ' ','D','e','l',' ','S','n','a','r','f',0}) , 10); 169 | i += 10; 170 | if(w->history.nurl){ 171 | if(w->history.cid > 0){ 172 | runemove(new+i, ((Rune[]){ ' ','B','a','c','k',0}), 5); 173 | i += 5; 174 | } 175 | if(w->history.cid < w->history.nurl-1){ 176 | runemove(new+i, ((Rune[]){ ' ','N','e','x','t',0}), 5); 177 | i += 5; 178 | } 179 | if(w->page.loading){ 180 | runemove(new+i, ((Rune[]){ ' ','S','t','o','p',0}), 5); 181 | i += 5; 182 | } 183 | } 184 | runemove(new+i, ((Rune[]){ ' ','G','e','t',0}), 4); 185 | i += 4; 186 | runemove(new+i, ((Rune[]){ ' ','|',' ',0}), 3); 187 | i += 3; 188 | runemove(new+i, w->page.title.r, w->page.title.nr); 189 | i += w->page.title.nr; 190 | /* 191 | r = runestrchr(old.r, '|'); 192 | r = nil; 193 | if(r) 194 | k = r-old.r+1; 195 | else{ 196 | k = w->tag.rs.nr; 197 | if(w->page.url){ 198 | runemove(new+i, (Rune[]){ ' ','L','o','o','k',' ',0} , 6); 199 | i += 6; 200 | } 201 | } 202 | */ 203 | k = w->tag.rs.nr; 204 | if(runeeq(new, i, old.r, k) == FALSE){ 205 | n = k; 206 | if(n > i) 207 | n = i; 208 | for(j=0; jtag.q0; 212 | q1 = w->tag.q1; 213 | textdelete(&w->tag, j, k); 214 | textinsert(&w->tag, j, new+j, i-j); 215 | /* try to preserve user selection */ 216 | r = runestrchr(old.r, '|'); 217 | if(r){ 218 | bar = r-old.r; 219 | if(q0 > bar){ 220 | bar = (runestrchr(new, '|')-new)-bar; 221 | w->tag.q0 = q0+bar; 222 | w->tag.q1 = q1+bar; 223 | } 224 | } 225 | } 226 | closerunestr(&old); 227 | free(new); 228 | n = w->tag.rs.nr; 229 | if(w->tag.q0 > n) 230 | w->tag.q0 = n; 231 | if(w->tag.q1 > n) 232 | w->tag.q1 = n; 233 | textsetselect(&w->tag, w->tag.q0, w->tag.q1); 234 | b = button; 235 | br.min = w->tag.scrollr.min; 236 | br.max.x = br.min.x + Dx(b->r); 237 | br.max.y = br.min.y + Dy(b->r); 238 | draw(screen, br, b, nil, b->r.min); 239 | } 240 | 241 | 242 | void 243 | winsettag(Window *w) 244 | { 245 | if(w->col && w->col->safe) 246 | winsettag1(w); 247 | } 248 | 249 | void 250 | winseturl(Window *w) 251 | { 252 | if(w->page.url && runestreq(w->url.rs, w->page.url->act)==FALSE) 253 | textset(&w->url, w->page.url->act.r, w->page.url->act.nr); 254 | } 255 | 256 | void 257 | winsetstatus(Window *w, Rune *r) 258 | { 259 | if(w->col && w->col->safe) 260 | textset(&w->status, r, runestrlen(r)); 261 | } 262 | 263 | void 264 | winaddhist(Window *w, Url *u) 265 | { 266 | Url **url; 267 | int cid, n, i; 268 | 269 | url = w->history.url; 270 | n = w->history.nurl; 271 | cid = w->history.cid; 272 | if(cid < n-1){ 273 | for(i=cid+1; ihistory.url = erealloc(w->history.url, ++n*sizeof(Url *)); 278 | w->history.url[n-1] = u; 279 | w->history.cid = u->id = n-1; 280 | w->history.nurl = n; 281 | incref(&u->ref); 282 | } 283 | 284 | Text * 285 | wintext(Window *w, Point xy) 286 | { 287 | w->inpage = FALSE; 288 | if(ptinrect(xy, w->tag.all)) 289 | return &w->tag; 290 | if(ptinrect(xy, w->url.all)) 291 | return &w->url; 292 | if(ptinrect(xy, w->status.all)) 293 | return &w->status; 294 | if(ptinrect(xy, w->page.all)) 295 | w->inpage = TRUE; 296 | 297 | return nil; 298 | } 299 | 300 | Text * 301 | wintype(Window *w, Point xy, Rune r) 302 | { 303 | Text *t; 304 | 305 | t = wintext(w, xy); 306 | if(t && !ptinrect(xy, t->scrollr)) 307 | return t; 308 | if(w->inpage) 309 | pagetype(&w->page, r, xy); 310 | 311 | return nil; 312 | } 313 | 314 | Text * 315 | winmouse(Window *w, Point xy, int but) 316 | { 317 | Text *t; 318 | 319 | t = wintext(w, xy); 320 | if(t) 321 | return t; 322 | if(w->inpage) 323 | pagemouse(&w->page, xy, but); 324 | 325 | return nil; 326 | } 327 | 328 | void 329 | winmousebut(Window *w) 330 | { 331 | moveto(mousectl, divpt(addpt(w->tag.scrollr.min, w->tag.scrollr.max), 2)); 332 | } 333 | 334 | int 335 | winclean(Window *notused, int notused2) 336 | { 337 | return TRUE; 338 | } 339 | 340 | void 341 | windebug(Window *w) 342 | { 343 | Page *p; 344 | int i; 345 | 346 | p = &w->page; 347 | fprint(2, "title:\t%S\n", p->title.r); 348 | fprint(2, "url:\t%.*S\n",w->url.rs.nr, w->url.rs.r); 349 | fprint(2, "aborting:\t%s\n", istrue(p->aborting)); 350 | fprint(2, "changed:\t%s\n", istrue(p->changed)); 351 | fprint(2, "loading:\t%s\n", istrue(p->loading)); 352 | fprint(2, "status:\t%S\n", p->status); 353 | fprint(2, "HISTORY:\n"); 354 | for(i=0; ihistory.nurl; i++) 355 | fprint(2, "url[%d]: %S\n", i, w->history.url[i]->act.r); 356 | 357 | if(p->kidinfo) 358 | fprint(2, "name: %S\n", p->kidinfo->name); 359 | } 360 | -------------------------------------------------------------------------------- /abaco/dat.h: -------------------------------------------------------------------------------- 1 | typedef struct Box Box; 2 | typedef struct Cimage Cimage; 3 | typedef struct Column Column; 4 | typedef struct Exec Exec; 5 | typedef struct Line Line; 6 | typedef struct Page Page; 7 | typedef struct Row Row; 8 | typedef struct Runestr Runestr; 9 | typedef struct Text Text; 10 | typedef struct Timer Timer; 11 | typedef struct Url Url; 12 | typedef struct Window Window; 13 | 14 | struct Runestr 15 | { 16 | Rune *r; 17 | int nr; 18 | }; 19 | 20 | enum 21 | { 22 | Rowtag, 23 | Columntag, 24 | Tag, 25 | Urltag, 26 | Statustag, 27 | Entry, 28 | Textarea, 29 | }; 30 | 31 | struct Text 32 | { 33 | union { 34 | struct { 35 | Font *font; /* of chars in the frame */ 36 | Display *display; /* on which frame appears */ 37 | Image *b; /* on which frame appears */ 38 | Image *cols[NCOL]; /* text and background colors */ 39 | Rectangle r; /* in which text appears */ 40 | Rectangle entire; /* of full frame */ 41 | void (*scroll)(Frame*, int); /* scroll function provided by application */ 42 | Frbox *box; 43 | ulong p0, p1; /* selection */ 44 | ushort nbox, nalloc; 45 | ushort maxtab; /* max size of tab, in pixels */ 46 | ushort nchars; /* # runes in frame */ 47 | ushort nlines; /* # lines with text */ 48 | ushort maxlines; /* total # lines in frame */ 49 | ushort lastlinefull; /* last line fills frame */ 50 | ushort modified; /* changed since frselect() */ 51 | Image *tick; /* typing tick */ 52 | Image *tickback; /* saved image under tick */ 53 | int ticked; /* flag: is tick onscreen? */ 54 | int noredraw; /* don't draw on the screen */ 55 | }; 56 | Frame Frame; 57 | }; 58 | uint org; 59 | uint q0; 60 | uint q1; 61 | int what; 62 | Window *w; 63 | Rectangle scrollr; 64 | Rectangle lastsr; 65 | Rectangle all; 66 | Row *row; 67 | Column *col; 68 | Runestr rs; 69 | }; 70 | 71 | uint textbacknl(Text*, uint, uint); 72 | int textbswidth(Text*, Rune); 73 | int textclickmatch(Text*, int, int, int, uint*); 74 | void textclose(Text*); 75 | void textdelete(Text*, uint, uint); 76 | void textdoubleclick(Text*, uint*, uint*); 77 | void textfill(Text*); 78 | void textframescroll(Text*, int); 79 | void textinit(Text *, Image *, Rectangle, Font *, Image **); 80 | void textinsert(Text*, uint, Rune*, uint); 81 | void textredraw(Text *, Rectangle, Font *, Image *); 82 | int textresize(Text *, Image *, Rectangle); 83 | void textscrdraw(Text*); 84 | void textscroll(Text*, int); 85 | void textselect(Text*); 86 | int textselect2(Text *, uint *, uint *, Text **); 87 | int textselect3(Text *, uint *, uint *); 88 | void textset(Text *, Rune *, int); 89 | void textsetorigin(Text*, uint, int); 90 | void textsetselect(Text*, uint, uint); 91 | void textshow(Text*, uint, uint, int); 92 | void texttype(Text*, Rune); 93 | void textmouse(Text *, Point, int); 94 | 95 | struct Line 96 | { 97 | Rectangle r; 98 | int state; 99 | int hastext; 100 | int hastable; 101 | Box *boxes; 102 | Box *lastbox; 103 | Line *prev; 104 | Line *next; 105 | }; 106 | 107 | struct Box 108 | { 109 | Item *i; 110 | Rectangle r; 111 | 112 | void (*draw)(Box *, Page *, Image *); 113 | void (*mouse)(Box *, Page *, int); 114 | void (*key)(Box *, Page *, Rune); 115 | Box *prev; 116 | Box *next; 117 | }; 118 | 119 | Box* boxalloc(Line *, Item *, Rectangle); 120 | void boxinit(Box *); 121 | 122 | struct Lay 123 | { 124 | Rectangle r; 125 | int width; 126 | int xwall; 127 | Line *lines; 128 | Line *lastline; 129 | Font *font; 130 | Ifloat *floats; 131 | int laying; 132 | }; 133 | 134 | void laypage(Page *p); 135 | Lay* layitems(Item *, Rectangle, int); 136 | void laydraw(Page *, Image *, Lay *); 137 | void layfree(Lay *); 138 | 139 | struct Cimage 140 | { 141 | Ref ref; 142 | Image *i; 143 | Memimage *mi; 144 | Url *url; 145 | Cimage *next; 146 | }; 147 | 148 | struct Url 149 | { 150 | Ref ref; /* urls in window.url[] are not freed */ 151 | int id; 152 | int method; /* HGet or HPost */ 153 | Runestr src; /* requested url */ 154 | Runestr act; /* actual url (redirection) */ 155 | Runestr post; /* only set if method==HPost */ 156 | Runestr ctype; /* content type */ 157 | }; 158 | 159 | Url* urlalloc(Runestr *, Runestr *, int); 160 | void urlfree(Url *); 161 | Url* urldup(Url *); 162 | int urlopen(Url *); 163 | 164 | struct Page 165 | { 166 | Url *url; 167 | Runestr title; 168 | Window *w; 169 | Image *b; 170 | 171 | Rectangle r; 172 | Rectangle all; 173 | Rectangle vscrollr; 174 | Rectangle hscrollr; 175 | Row *row; 176 | Column *col; 177 | 178 | Docinfo *doc; 179 | Kidinfo *kidinfo; 180 | Item *items; 181 | Lay *lay; 182 | Point pos; 183 | 184 | int selecting; 185 | Point top, bot; 186 | Box *topbx, *botbx; 187 | 188 | int aborting; 189 | int changed; 190 | int loading; 191 | Rune *status; 192 | 193 | Page *parent; 194 | Page *child; 195 | Page *next; 196 | 197 | Cimage **cimage; 198 | int ncimage; 199 | }; 200 | 201 | void pageget(Page *, Runestr *, Runestr *, int, int); 202 | void pageload(Page *, Url *, int); 203 | void pageclose(Page *); 204 | void pageredraw(Page *); 205 | void pagerender(Page *); 206 | void pagemouse(Page *, Point, int); 207 | void pagetype(Page *, Rune, Point); 208 | void pagescrldraw(Page *); 209 | void pagescroll(Page *, int, int); 210 | int pagescrollxy(Page *, int, int); 211 | int pageabort(Page *); 212 | void pagesnarf(Page *); 213 | void pagerefresh(Page *); 214 | 215 | struct Window 216 | { 217 | Ref ref; 218 | QLock lock; 219 | Text tag; 220 | Text url; 221 | Page page; 222 | Text status; 223 | int owner; 224 | int inpage; 225 | Rectangle r; 226 | Column *col; 227 | struct{ 228 | Url **url; 229 | int nurl; 230 | int cid; 231 | }history; 232 | }; 233 | 234 | void wininit(Window *, Window *, Rectangle); 235 | int winclean(Window *, int); 236 | void winclose(Window *); 237 | int winresize(Window *, Rectangle, int); 238 | Text* wintext(Window *, Point); 239 | void winlock(Window *, int); 240 | void winunlock(Window *); 241 | void winaddhist(Window *, Url *); 242 | void winsettag(Window *); 243 | void winseturl(Window *); 244 | void winsetstatus(Window *, Rune *); 245 | Text* wintype(Window *, Point, Rune); 246 | Text* winmouse(Window *, Point, int); 247 | void winmousebut(Window *w); 248 | void windebug(Window *w); 249 | 250 | struct Column 251 | { 252 | Rectangle r; 253 | Text tag; 254 | Row *row; 255 | Window **w; 256 | int nw; 257 | int safe; 258 | }; 259 | 260 | void colinit(Column*, Rectangle); 261 | Window* coladd(Column*, Window*, Window*, int); 262 | void colclose(Column*, Window*, int); 263 | void colcloseall(Column*); 264 | void colresize(Column*, Rectangle); 265 | Text* colwhich(Column*, Point, Rune, int); 266 | void coldragwin(Column*, Window*, int); 267 | void colgrow(Column*, Window*, int); 268 | int colclean(Column*); 269 | void colsort(Column*); 270 | void colmousebut(Column*); 271 | 272 | struct Row 273 | { 274 | QLock lock; 275 | Rectangle r; 276 | Text tag; 277 | Column **col; 278 | int ncol; 279 | 280 | }; 281 | 282 | void rowinit(Row*, Rectangle); 283 | Column* rowadd(Row*, Column *c, int); 284 | void rowclose(Row*, Column*, int); 285 | Text* rowwhich(Row*, Point, Rune, int); 286 | Column* rowwhichcol(Row*, Point); 287 | void rowresize(Row*, Rectangle); 288 | void rowdragcol(Row*, Column*, int but); 289 | 290 | struct Exec 291 | { 292 | char *cmd; 293 | int p[2]; /* p[1] is write to program; p[0] set to prog fd 0*/ 294 | int q[2]; /* q[0] is read from program; q[1] set to prog fd 1 */ 295 | Channel *sync; /* chan(ulong) */ 296 | }; 297 | 298 | struct Timer 299 | { 300 | int dt; 301 | int cancel; 302 | Channel *c; /* chan(int) */ 303 | Timer *next; 304 | }; 305 | 306 | enum 307 | { 308 | Scrollsize = 12, 309 | Scrollgap = 4, 310 | Margin = 4, 311 | Border = 2, 312 | Space = 2, 313 | Tabspace = 30, 314 | Boxsize = 12, 315 | WFont = FntR*NumSize+Tiny, 316 | 317 | Panspeed = 4, 318 | Maxtab = 8, 319 | 320 | BUFSIZE = 1024*8, 321 | RBUFSIZE = BUFSIZE/sizeof(Rune), 322 | STACK = 64*1024, 323 | }; 324 | 325 | enum 326 | { 327 | FALSE, 328 | TRUE, 329 | XXX, 330 | }; 331 | 332 | enum 333 | { 334 | Light = 0xEEEEEE, 335 | Dark = 0x666666, 336 | Red = 0xBB0000, 337 | Back = 0xCCCCCC, 338 | }; 339 | 340 | Mouse *mouse; 341 | Mousectl *mousectl; 342 | Keyboardctl *keyboardctl; 343 | Image *tagcols[NCOL]; 344 | Image *textcols[NCOL]; 345 | Image *but2col; 346 | Image *but3col; 347 | Image *button; 348 | Image *colbutton; 349 | Font *passfont; 350 | Cursor boxcursor; 351 | Row row; 352 | Text *argtext; 353 | Text *seltext; 354 | Text *typetext; 355 | Page *selpage; 356 | Column *activecol; 357 | CFid *plumbsendfid; 358 | CFsys *webfs; 359 | CFid *webctlfid; 360 | char *charset; 361 | int procstderr; 362 | 363 | enum 364 | { 365 | Kscrolloneup = KF|0x20, 366 | Kscrollonedown = KF|0x21, 367 | }; 368 | 369 | Channel *cplumb; /* chan(Plumbmsg*) */ 370 | Channel *cexit; /* chan(int) */ 371 | Channel *crefresh; /* chan(page *) */ 372 | -------------------------------------------------------------------------------- /abaco/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include <9pclient.h> 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "dat.h" 18 | #include "fns.h" 19 | 20 | void mousethread(void *); 21 | void keyboardthread(void *); 22 | void iconinit(void); 23 | void plumbproc(void*); 24 | 25 | 26 | Channel *cexit; 27 | Channel *cplumb; 28 | Mousectl *mousectl; 29 | 30 | char *fontnames[2] = { 31 | #ifdef STANDALONE 32 | "/usr/share/abaco/freefont/sans/sans.13.font", 33 | "/usr/share/abaco/feefont/sans/sans.13.font" 34 | #else 35 | "/usr/local/plan9/font/lucsans/unicode.8.font", 36 | "/usr/local/plan9/font/lucsans/passwd.6.font" 37 | #endif 38 | }; 39 | 40 | int snarffd = -1; 41 | int mainpid; 42 | CFid *plumbwebfid; 43 | CFid *plumbsendfid; 44 | char *charset = "iso-8859-1"; 45 | int mainstacksize = STACK; 46 | 47 | void readpage(Column *, char *); 48 | int shutdown(void *, char *); 49 | 50 | void 51 | derror(Display *notused, char *s) 52 | { 53 | error(s); 54 | } 55 | 56 | void 57 | threadmain(int argc, char *argv[]) 58 | { 59 | Column *c; 60 | char *p; 61 | int i, ncol; 62 | 63 | rfork(RFENVG|RFNAMEG); 64 | notifyoff("sys: window size change"); 65 | 66 | ncol = 1; 67 | ARGBEGIN{ 68 | case 'c': 69 | p = ARGF(); 70 | if(p == nil) 71 | goto Usage; 72 | ncol = atoi(p); 73 | if(ncol <= 0) 74 | goto Usage; 75 | break; 76 | case 'p': 77 | procstderr++; 78 | break; 79 | case 't': 80 | charset = ARGF(); 81 | if(charset == nil) 82 | goto Usage; 83 | break; 84 | default: 85 | Usage: 86 | fprint(2, "usage: abaco [-c ncol] [-t charset] [url...]\n"); 87 | threadexitsall("usage"); 88 | }ARGEND 89 | 90 | /* 91 | int fds[3], fd[2]; 92 | pipe(fd); 93 | fds[0] = fd[0]; 94 | fds[1] = dup(2, 0); 95 | fds[2] = dup(2, 0); 96 | //threadspawnl(fds, "emu", "emu", "/dis/sh.dis", "-c", "styxmon {os dial unix!/tmp/ns.kris.:0/web >[1=0]}", nil); 97 | threadspawnl(fds, "sh", "sh", "-c", "dial unix!/tmp/ns.kris.:0/wmii >[1=0]", nil); 98 | */ 99 | webfs = nsmount("web", ""); 100 | if(webfs == nil){ 101 | fprint(2, "abaco: can't connect to webfs: %r\n"); 102 | threadexitsall("webfs"); 103 | } 104 | webctlfid = fsopen(webfs, "ctl", ORDWR); 105 | if(webctlfid == nil){ 106 | fprint(2, "abaco: can't initialize webfs: %r\n"); 107 | threadexitsall("webfs"); 108 | } 109 | 110 | if(initdraw(derror, fontnames[0], "abaco") < 0){ 111 | fprint(2, "abaco: can't open display: %r\n"); 112 | threadexitsall("initdraw"); 113 | } 114 | memimageinit(); 115 | iconinit(); 116 | timerinit(); 117 | initfontpaths(); 118 | cexit = chancreate(sizeof(int), 0); 119 | crefresh = chancreate(sizeof(Page *), 0); 120 | if(cexit==nil || crefresh==nil){ 121 | fprint(2, "abaco: can't create initial channels: %r\n"); 122 | threadexitsall("channels"); 123 | } 124 | 125 | mousectl = initmouse(nil, screen); 126 | if(mousectl == nil){ 127 | fprint(2, "abaco: can't initialize mouse: %r\n"); 128 | threadexitsall("mouse"); 129 | } 130 | mouse = &mousectl->m; 131 | keyboardctl = initkeyboard(nil); 132 | if(keyboardctl == nil){ 133 | fprint(2, "abaco: can't initialize keyboard: %r\n"); 134 | threadexitsall("keyboard"); 135 | } 136 | mainpid = getpid(); 137 | plumbwebfid = plumbopenfid("web", OREAD|OCEXEC); 138 | if(plumbwebfid){ 139 | cplumb = chancreate(sizeof(Plumbmsg*), 0); 140 | proccreate(plumbproc, nil, STACK); 141 | } 142 | plumbsendfid = plumbopenfid("send", OWRITE|OCEXEC); 143 | 144 | rowinit(&row, screen->clipr); 145 | for(i=0; i= row.ncol) 154 | readpage(c, argv[i]); 155 | else 156 | readpage(row.col[i/WPERCOL], argv[i]); 157 | } 158 | flushimage(display, 1); 159 | threadcreate(keyboardthread, nil, STACK); 160 | threadcreate(mousethread, nil, STACK); 161 | 162 | threadnotify(shutdown, 1); 163 | recvul(cexit); 164 | threadexitsall(nil); 165 | } 166 | 167 | void 168 | readpage(Column *c, char *s) 169 | { 170 | Window *w; 171 | Runestr rs; 172 | 173 | w = coladd(c, nil, nil, -1); 174 | bytetorunestr(s, &rs); 175 | pageget(&w->page, &rs, nil, HGet, TRUE); 176 | closerunestr(&rs); 177 | } 178 | 179 | char *oknotes[] = { 180 | "delete", 181 | "hangup", 182 | "kill", 183 | "exit", 184 | nil 185 | }; 186 | 187 | int 188 | shutdown(void *notused, char *msg) 189 | { 190 | int i; 191 | 192 | for(i=0; oknotes[i]; i++) 193 | if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0) 194 | threadexitsall(msg); 195 | print("abaco: %s\n", msg); 196 | abort(); 197 | return 0; 198 | } 199 | 200 | void 201 | plumbproc(void *notused) 202 | { 203 | Plumbmsg *m; 204 | 205 | threadsetname("plumbproc"); 206 | for(;;){ 207 | m = plumbrecvfid(plumbwebfid); 208 | if(m == nil) 209 | threadexits(nil); 210 | sendp(cplumb, m); 211 | } 212 | } 213 | 214 | void 215 | keyboardthread(void *notused) 216 | { 217 | Timer *timer; 218 | Text *t; 219 | Rune r; 220 | 221 | enum { KTimer, KKey, NKALT }; 222 | static Alt alts[NKALT+1]; 223 | 224 | alts[KTimer].c = nil; 225 | alts[KTimer].v = nil; 226 | alts[KTimer].op = CHANNOP; 227 | alts[KKey].c = keyboardctl->c; 228 | alts[KKey].v = &r; 229 | alts[KKey].op = CHANRCV; 230 | alts[NKALT].op = CHANEND; 231 | 232 | timer = nil; 233 | threadsetname("keyboardthread"); 234 | for(;;){ 235 | switch(alt(alts)){ 236 | case KTimer: 237 | timerstop(timer); 238 | alts[KTimer].c = nil; 239 | alts[KTimer].op = CHANNOP; 240 | break; 241 | case KKey: 242 | casekeyboard: 243 | typetext = rowwhich(&row, mouse->xy, r, TRUE); 244 | t = typetext; 245 | if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright)) /* scrolling doesn't change activecol */ 246 | activecol = t->col; 247 | if(timer != nil) 248 | timercancel(timer); 249 | if(t!=nil){ 250 | texttype(t, r); 251 | timer = timerstart(500); 252 | alts[KTimer].c = timer->c; 253 | alts[KTimer].op = CHANRCV; 254 | }else{ 255 | timer = nil; 256 | alts[KTimer].c = nil; 257 | alts[KTimer].op = CHANNOP; 258 | } 259 | if(nbrecv(keyboardctl->c, &r) > 0) 260 | goto casekeyboard; 261 | flushimage(display, 1); 262 | break; 263 | } 264 | } 265 | } 266 | 267 | void 268 | mousethread(void *notused) 269 | { 270 | Plumbmsg *pm; 271 | Mouse m; 272 | Text *t; 273 | int but; 274 | enum { MResize, MMouse, MPlumb, MRefresh, NMALT }; 275 | static Alt alts[NMALT+1]; 276 | 277 | threadsetname("mousethread"); 278 | alts[MResize].c = mousectl->resizec; 279 | alts[MResize].v = nil; 280 | alts[MResize].op = CHANRCV; 281 | alts[MMouse].c = mousectl->c; 282 | alts[MMouse].v = &mousectl->m; 283 | alts[MMouse].op = CHANRCV; 284 | alts[MPlumb].c = cplumb; 285 | alts[MPlumb].v = ± 286 | alts[MPlumb].op = CHANRCV; 287 | alts[MRefresh].c = crefresh; 288 | alts[MRefresh].v = nil; 289 | alts[MRefresh].op = CHANRCV; 290 | if(cplumb == nil) 291 | alts[MPlumb].op = CHANNOP; 292 | alts[NMALT].op = CHANEND; 293 | 294 | for(;;){ 295 | qlock(&row.lock); 296 | flushrefresh(); 297 | qunlock(&row.lock); 298 | flushimage(display, 1); 299 | switch(alt(alts)){ 300 | case MResize: 301 | if(getwindow(display, Refnone) < 0) 302 | error("resized"); 303 | scrlresize(); 304 | tmpresize(); 305 | rowresize(&row, screen->clipr); 306 | break; 307 | case MPlumb: 308 | plumblook(pm); 309 | plumbfree(pm); 310 | break; 311 | case MRefresh: 312 | break; 313 | case MMouse: 314 | m = mousectl->m; 315 | if(m.buttons == 0) 316 | continue; 317 | 318 | qlock(&row.lock); 319 | but = 0; 320 | if(m.buttons == 1) 321 | but = 1; 322 | else if(m.buttons == 2) 323 | but = 2; 324 | else if(m.buttons == 4) 325 | but = 3; 326 | 327 | if(m.buttons & (8|16)){ 328 | if(m.buttons & 8) 329 | but = Kscrolloneup; 330 | else 331 | but = Kscrollonedown; 332 | rowwhich(&row, m.xy, but, TRUE); 333 | }else if(but){ 334 | t = rowwhich(&row, m.xy, but, FALSE); 335 | if(t) 336 | textmouse(t, m.xy, but); 337 | } 338 | qunlock(&row.lock); 339 | break; 340 | } 341 | } 342 | } 343 | 344 | Cursor boxcursor = { 345 | {-7, -7}, 346 | {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 347 | 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 348 | 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, 349 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 350 | {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 351 | 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 352 | 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 353 | 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} 354 | }; 355 | 356 | void 357 | iconinit(void) 358 | { 359 | Rectangle r; 360 | 361 | /* Green */ 362 | tagcols[BACK] = allocimagemix(display, DPalegreen, DWhite); 363 | if(tagcols[BACK] == nil) 364 | error("allocimagemix"); 365 | tagcols[HIGH] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen); 366 | tagcols[BORD] = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen); 367 | tagcols[TEXT] = display->black; 368 | tagcols[HTEXT] = display->black; 369 | 370 | /* Grey */ 371 | textcols[BACK] = display->white; 372 | textcols[HIGH] = eallocimage(display, Rect(0,0,1,1), CMAP8,1, 0xCCCCCCFF); 373 | textcols[BORD] = display->black; 374 | textcols[TEXT] = display->black; 375 | textcols[HTEXT] = display->black; 376 | 377 | r = Rect(0, 0, Scrollsize+2, font->height+1); 378 | button = eallocimage(display, r, screen->chan, 0, DNofill); 379 | draw(button, r, tagcols[BACK], nil, r.min); 380 | r.max.x -= 2; 381 | border(button, r, 2, tagcols[BORD], ZP); 382 | 383 | r = button->r; 384 | colbutton = eallocimage(display, r, screen->chan, 0, 0x00994CFF); 385 | 386 | but2col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0xAA0000FF); 387 | but3col = eallocimage(display, Rect(0,0,1,2), screen->chan, 1, 0x444488FF); 388 | 389 | passfont = openfont(display, fontnames[1]); 390 | if(passfont == nil) 391 | error("openfont"); 392 | } 393 | 394 | /* 395 | * /dev/snarf updates when the file is closed, so we must open our own 396 | * fd here rather than use snarffd 397 | */ 398 | 399 | /* rio truncates larges snarf buffers, so this avoids using the 400 | * service if the string is huge */ 401 | 402 | enum 403 | { 404 | NSnarf = 1000, 405 | MAXSNARF = 100*1024, 406 | }; 407 | 408 | /* 409 | void 410 | putsnarf(Runestr *rs) 411 | { 412 | int fd, i, n; 413 | 414 | if(snarffd<0 || rs->nr==0) 415 | return; 416 | if(rs->nr > MAXSNARF) 417 | return; 418 | fd = open("/dev/snarf", OWRITE); 419 | if(fd < 0) 420 | return; 421 | for(i=0; inr; i+=n){ 422 | n = rs->nr-i; 423 | if(n > NSnarf) 424 | n =NSnarf; 425 | if(fprint(fd, "%.*S", n, rs->r) < 0) 426 | break; 427 | } 428 | close(fd); 429 | } 430 | 431 | void 432 | getsnarf(Runestr *rs) 433 | { 434 | int i, n, nb, nulls; 435 | char *sn, buf[BUFSIZE]; 436 | 437 | if(snarffd < 0) 438 | return; 439 | sn = nil; 440 | i = 0; 441 | seek(snarffd, 0, 0); 442 | while((n=read(snarffd, buf, sizeof(buf))) > 0){ 443 | sn = erealloc(sn, i+n+1); 444 | memmove(sn+i, buf, n); 445 | i += n; 446 | sn[i] = 0; 447 | } 448 | if(i > 0){ 449 | rs->r = runemalloc(i+1); 450 | cvttorunes(sn, i, rs->r, &nb, &rs->nr, &nulls); 451 | free(sn); 452 | } 453 | } 454 | */ 455 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Lucent Public License Version 1.02 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE 5 | PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original 12 | Program, and 13 | b. in the case of each Contributor, 14 | 15 | i. changes to the Program, and 16 | ii. additions to the Program; 17 | 18 | where such changes and/or additions to the Program were added to the 19 | Program by such Contributor itself or anyone acting on such 20 | Contributor's behalf, and the Contributor explicitly consents, in 21 | accordance with Section 3C, to characterization of the changes and/or 22 | additions as Contributions. 23 | 24 | "Contributor" means LUCENT and any other entity that has Contributed a 25 | Contribution to the Program. 26 | 27 | "Distributor" means a Recipient that distributes the Program, 28 | modifications to the Program, or any part thereof. 29 | 30 | "Licensed Patents" mean patent claims licensable by a Contributor 31 | which are necessarily infringed by the use or sale of its Contribution 32 | alone or when combined with the Program. 33 | 34 | "Original Program" means the original version of the software 35 | accompanying this Agreement as released by LUCENT, including source 36 | code, object code and documentation, if any. 37 | 38 | "Program" means the Original Program and Contributions or any part 39 | thereof 40 | 41 | "Recipient" means anyone who receives the Program under this 42 | Agreement, including all Contributors. 43 | 44 | 2. GRANT OF RIGHTS 45 | 46 | a. Subject to the terms of this Agreement, each Contributor hereby 47 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 48 | license to reproduce, prepare derivative works of, publicly display, 49 | publicly perform, distribute and sublicense the Contribution of such 50 | Contributor, if any, and such derivative works, in source code and 51 | object code form. 52 | 53 | b. Subject to the terms of this Agreement, each Contributor hereby 54 | grants Recipient a non-exclusive, worldwide, royalty-free patent 55 | license under Licensed Patents to make, use, sell, offer to sell, 56 | import and otherwise transfer the Contribution of such Contributor, if 57 | any, in source code and object code form. The patent license granted 58 | by a Contributor shall also apply to the combination of the 59 | Contribution of that Contributor and the Program if, at the time the 60 | Contribution is added by the Contributor, such addition of the 61 | Contribution causes such combination to be covered by the Licensed 62 | Patents. The patent license granted by a Contributor shall not apply 63 | to (i) any other combinations which include the Contribution, nor to 64 | (ii) Contributions of other Contributors. No hardware per se is 65 | licensed hereunder. 66 | 67 | c. Recipient understands that although each Contributor grants the 68 | licenses to its Contributions set forth herein, no assurances are 69 | provided by any Contributor that the Program does not infringe the 70 | patent or other intellectual property rights of any other entity. Each 71 | Contributor disclaims any liability to Recipient for claims brought by 72 | any other entity based on infringement of intellectual property rights 73 | or otherwise. As a condition to exercising the rights and licenses 74 | granted hereunder, each Recipient hereby assumes sole responsibility 75 | to secure any other intellectual property rights needed, if any. For 76 | example, if a third party patent license is required to allow 77 | Recipient to distribute the Program, it is Recipient's responsibility 78 | to acquire that license before distributing the Program. 79 | 80 | d. Each Contributor represents that to its knowledge it has sufficient 81 | copyright rights in its Contribution, if any, to grant the copyright 82 | license set forth in this Agreement. 83 | 84 | 3. REQUIREMENTS 85 | 86 | A. Distributor may choose to distribute the Program in any form under 87 | this Agreement or under its own license agreement, provided that: 88 | 89 | a. it complies with the terms and conditions of this Agreement; 90 | 91 | b. if the Program is distributed in source code or other tangible 92 | form, a copy of this Agreement or Distributor's own license agreement 93 | is included with each copy of the Program; and 94 | 95 | c. if distributed under Distributor's own license agreement, such 96 | license agreement: 97 | 98 | i. effectively disclaims on behalf of all Contributors all warranties 99 | and conditions, express and implied, including warranties or 100 | conditions of title and non-infringement, and implied warranties or 101 | conditions of merchantability and fitness for a particular purpose; 102 | ii. effectively excludes on behalf of all Contributors all liability 103 | for damages, including direct, indirect, special, incidental and 104 | consequential damages, such as lost profits; and 105 | iii. states that any provisions which differ from this Agreement are 106 | offered by that Contributor alone and not by any other party. 107 | 108 | B. Each Distributor must include the following in a conspicuous 109 | location in the Program: 110 | 111 | Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights 112 | Reserved. 113 | 114 | C. In addition, each Contributor must identify itself as the 115 | originator of its Contribution in a manner that reasonably allows 116 | subsequent Recipients to identify the originator of the Contribution. 117 | Also, each Contributor must agree that the additions and/or changes 118 | are intended to be a Contribution. Once a Contribution is contributed, 119 | it may not thereafter be revoked. 120 | 121 | 4. COMMERCIAL DISTRIBUTION 122 | 123 | Commercial distributors of software may accept certain 124 | responsibilities with respect to end users, business partners and the 125 | like. While this license is intended to facilitate the commercial use 126 | of the Program, the Distributor who includes the Program in a 127 | commercial product offering should do so in a manner which does not 128 | create potential liability for Contributors. Therefore, if a 129 | Distributor includes the Program in a commercial product offering, 130 | such Distributor ("Commercial Distributor") hereby agrees to defend 131 | and indemnify every Contributor ("Indemnified Contributor") against 132 | any losses, damages and costs (collectively"Losses") arising from 133 | claims, lawsuits and other legal actions brought by a third party 134 | against the Indemnified Contributor to the extent caused by the acts 135 | or omissions of such Commercial Distributor in connection with its 136 | distribution of the Program in a commercial product offering. The 137 | obligations in this section do not apply to any claims or Losses 138 | relating to any actual or alleged intellectual property infringement. 139 | In order to qualify, an Indemnified Contributor must: a) promptly 140 | notify the Commercial Distributor in writing of such claim, and b) 141 | allow the Commercial Distributor to control, and cooperate with the 142 | Commercial Distributor in, the defense and any related settlement 143 | negotiations. The Indemnified Contributor may participate in any such 144 | claim at its own expense. 145 | 146 | For example, a Distributor might include the Program in a commercial 147 | product offering, Product X. That Distributor is then a Commercial 148 | Distributor. If that Commercial Distributor then makes performance 149 | claims, or offers warranties related to Product X, those performance 150 | claims and warranties are such Commercial Distributor's responsibility 151 | alone. Under this section, the Commercial Distributor would have to 152 | defend claims against the Contributors related to those performance 153 | claims and warranties, and if a court requires any Contributor to pay 154 | any damages as a result, the Commercial Distributor must pay those 155 | damages. 156 | 157 | 5. NO WARRANTY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 160 | PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 161 | KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY 162 | WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 163 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 164 | responsible for determining the appropriateness of using and 165 | distributing the Program and assumes all risks associated with its 166 | exercise of rights under this Agreement, including but not limited to 167 | the risks and costs of program errors, compliance with applicable 168 | laws, damage to or loss of data, programs or equipment, and 169 | unavailability or interruption of operations. 170 | 171 | 6. DISCLAIMER OF LIABILITY 172 | 173 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR 174 | ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 175 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 176 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 177 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 178 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 179 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 180 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 181 | 182 | 7. EXPORT CONTROL 183 | 184 | Recipient agrees that Recipient alone is responsible for compliance 185 | with the United States export administration regulations (and the 186 | export control laws and regulation of any other countries). 187 | 188 | 8. GENERAL 189 | 190 | If any provision of this Agreement is invalid or unenforceable under 191 | applicable law, it shall not affect the validity or enforceability of 192 | the remainder of the terms of this Agreement, and without further 193 | action by the parties hereto, such provision shall be reformed to the 194 | minimum extent necessary to make such provision valid and enforceable. 195 | 196 | If Recipient institutes patent litigation against a Contributor with 197 | respect to a patent applicable to software (including a cross-claim or 198 | counterclaim in a lawsuit), then any patent licenses granted by that 199 | Contributor to such Recipient under this Agreement shall terminate as 200 | of the date such litigation is filed. In addition, if Recipient 201 | institutes patent litigation against any entity (including a 202 | cross-claim or counterclaim in a lawsuit) alleging that the Program 203 | itself (excluding combinations of the Program with other software or 204 | hardware) infringes such Recipient's patent(s), then such Recipient's 205 | rights granted under Section 2(b) shall terminate as of the date such 206 | litigation is filed. 207 | 208 | All Recipient's rights under this Agreement shall terminate if it 209 | fails to comply with any of the material terms or conditions of this 210 | Agreement and does not cure such failure in a reasonable period of 211 | time after becoming aware of such noncompliance. If all Recipient's 212 | rights under this Agreement terminate, Recipient agrees to cease use 213 | and distribution of the Program as soon as reasonably practicable. 214 | However, Recipient's obligations under this Agreement and any licenses 215 | granted by Recipient relating to the Program shall continue and 216 | survive. 217 | 218 | LUCENT may publish new versions (including revisions) of this 219 | Agreement from time to time. Each new version of the Agreement will be 220 | given a distinguishing version number. The Program (including 221 | Contributions) may always be distributed subject to the version of the 222 | Agreement under which it was received. In addition, after a new 223 | version of the Agreement is published, Contributor may elect to 224 | distribute the Program (including its Contributions) under the new 225 | version. No one other than LUCENT has the right to modify this 226 | Agreement. Except as expressly stated in Sections 2(a) and 2(b) above, 227 | Recipient receives no rights or licenses to the intellectual property 228 | of any Contributor under this Agreement, whether expressly, by 229 | implication, estoppel or otherwise. All rights in the Program not 230 | expressly granted under this Agreement are reserved. 231 | 232 | This Agreement is governed by the laws of the State of New York and 233 | the intellectual property laws of the United States of America. No 234 | party to this Agreement will bring a legal action under this Agreement 235 | more than one year after the cause of action arose. Each party waives 236 | its rights to a jury trial in any resulting litigation. 237 | 238 | -------------------------------------------------------------------------------- /webfs/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include <9p.h> 9 | #include 10 | #include 11 | #include "dat.h" 12 | #include "fns.h" 13 | 14 | char PostContentType[] = "application/x-www-form-urlencoded"; 15 | int httpdebug; 16 | 17 | typedef struct HttpState HttpState; 18 | struct HttpState 19 | { 20 | int fd; 21 | Client *c; 22 | char *location; 23 | char *setcookie; 24 | char *netaddr; 25 | char *credentials; 26 | char autherror[ERRMAX]; 27 | Ibuf b; 28 | }; 29 | 30 | static void 31 | location(HttpState *hs, char *value) 32 | { 33 | if(hs->location == nil) 34 | hs->location = estrdup(value); 35 | } 36 | 37 | static void 38 | contenttype(HttpState *hs, char *value) 39 | { 40 | if(hs->c->contenttype != nil) 41 | free(hs->c->contenttype); 42 | hs->c->contenttype = estrdup(value); 43 | } 44 | 45 | static void 46 | setcookie(HttpState *hs, char *value) 47 | { 48 | char *s, *t; 49 | Fmt f; 50 | 51 | s = hs->setcookie; 52 | fmtstrinit(&f); 53 | if(s) 54 | fmtprint(&f, "%s", s); 55 | fmtprint(&f, "set-cookie: "); 56 | fmtprint(&f, "%s", value); 57 | fmtprint(&f, "\n"); 58 | t = fmtstrflush(&f); 59 | if(t){ 60 | free(s); 61 | hs->setcookie = t; 62 | } 63 | } 64 | 65 | static char* 66 | unquote(char *s, char **ps) 67 | { 68 | char *p; 69 | 70 | if(*s != '"'){ 71 | p = strpbrk(s, " \t\r\n"); 72 | *p++ = 0; 73 | *ps = p; 74 | return s; 75 | } 76 | for(p=s+1; *p; p++){ 77 | if(*p == '\"'){ 78 | *p++ = 0; 79 | break; 80 | } 81 | if(*p == '\\' && *(p+1)){ 82 | p++; 83 | continue; 84 | } 85 | } 86 | memmove(s, s+1, p-(s+1)); 87 | s[p-(s+1)] = 0; 88 | *ps = p; 89 | return s; 90 | } 91 | 92 | static char* 93 | servername(char *addr) 94 | { 95 | char *p; 96 | 97 | if(strncmp(addr, "tcp!", 4) == 0 98 | || strncmp(addr, "net!", 4) == 0) 99 | addr += 4; 100 | addr = estrdup(addr); 101 | p = addr+strlen(addr); 102 | if(p>addr && *(p-1) == 's') 103 | p--; 104 | if(p>addr+5 && strcmp(p-5, "!http") == 0) 105 | p[-5] = 0; 106 | return addr; 107 | } 108 | 109 | void 110 | wwwauthenticate(HttpState *hs, char *line) 111 | { 112 | char cred[64], *user, *pass, *realm, *s, *spec, *name; 113 | Fmt fmt; 114 | UserPasswd *up; 115 | 116 | spec = nil; 117 | up = nil; 118 | cred[0] = 0; 119 | hs->autherror[0] = 0; 120 | if(cistrncmp(line, "basic ", 6) != 0){ 121 | werrstr("unknown auth: %s", line); 122 | goto error; 123 | } 124 | line += 6; 125 | if(cistrncmp(line, "realm=", 6) != 0){ 126 | werrstr("missing realm: %s", line); 127 | goto error; 128 | } 129 | line += 6; 130 | user = hs->c->url->user; 131 | pass = hs->c->url->passwd; 132 | if(user==nil || pass==nil){ 133 | realm = unquote(line, &line); 134 | fmtstrinit(&fmt); 135 | name = servername(hs->netaddr); 136 | fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm); 137 | free(name); 138 | if(hs->c->url->user) 139 | fmtprint(&fmt, " user=%q", hs->c->url->user); 140 | spec = fmtstrflush(&fmt); 141 | if(spec == nil) 142 | goto error; 143 | if((up = auth_getuserpasswd(nil, "%s", spec)) == nil) 144 | goto error; 145 | user = up->user; 146 | pass = up->passwd; 147 | } 148 | if((s = smprint("%s:%s", user, pass)) == nil) 149 | goto error; 150 | free(up); 151 | enc64(cred, sizeof(cred), (uchar*)s, strlen(s)); 152 | memset(s, 0, strlen(s)); 153 | free(s); 154 | hs->credentials = smprint("Basic %s", cred); 155 | if(hs->credentials == nil) 156 | goto error; 157 | return; 158 | 159 | error: 160 | free(up); 161 | free(spec); 162 | snprint(hs->autherror, sizeof hs->autherror, "%r"); 163 | } 164 | 165 | struct { 166 | char *name; /* Case-insensitive */ 167 | void (*fn)(HttpState *hs, char *value); 168 | } hdrtab[] = { 169 | { "location:", location }, 170 | { "content-type:", contenttype }, 171 | { "set-cookie:", setcookie }, 172 | { "www-authenticate:", wwwauthenticate }, 173 | }; 174 | 175 | static int 176 | httprcode(HttpState *hs) 177 | { 178 | int n; 179 | char *p; 180 | char buf[256]; 181 | 182 | n = readline(&hs->b, buf, sizeof(buf)-1); 183 | if(n <= 0) 184 | return n; 185 | if(httpdebug) 186 | fprint(2, "-> %s\n", buf); 187 | p = strchr(buf, ' '); 188 | if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){ 189 | werrstr("bad response from server"); 190 | return -1; 191 | } 192 | buf[n] = 0; 193 | return atoi(p+1); 194 | } 195 | 196 | /* 197 | * read a single mime header, collect continuations. 198 | * 199 | * this routine assumes that there is a blank line twixt 200 | * the header and the message body, otherwise bytes will 201 | * be lost. 202 | */ 203 | static int 204 | getheader(HttpState *hs, char *buf, int n) 205 | { 206 | char *p, *e; 207 | int i; 208 | 209 | n--; 210 | p = buf; 211 | for(e = p + n; ; p += i){ 212 | i = readline(&hs->b, p, e-p); 213 | if(i < 0) 214 | return i; 215 | 216 | if(p == buf){ 217 | /* first line */ 218 | if(strchr(buf, ':') == nil) 219 | break; /* end of headers */ 220 | } else { 221 | /* continuation line */ 222 | if(*p != ' ' && *p != '\t'){ 223 | unreadline(&hs->b, p); 224 | *p = 0; 225 | break; /* end of this header */ 226 | } 227 | } 228 | } 229 | 230 | if(httpdebug) 231 | fprint(2, "-> %s\n", buf); 232 | return p-buf; 233 | } 234 | 235 | static int 236 | httpheaders(HttpState *hs) 237 | { 238 | char buf[2048]; 239 | char *p; 240 | int i, n; 241 | 242 | for(;;){ 243 | n = getheader(hs, buf, sizeof(buf)); 244 | if(n < 0) 245 | return -1; 246 | if(n == 0) 247 | return 0; 248 | // print("http header: '%s'\n", buf); 249 | for(i = 0; i < nelem(hdrtab); i++){ 250 | n = strlen(hdrtab[i].name); 251 | if(cistrncmp(buf, hdrtab[i].name, n) == 0){ 252 | /* skip field name and leading white */ 253 | p = buf + n; 254 | while(*p == ' ' || *p == '\t') 255 | p++; 256 | (*hdrtab[i].fn)(hs, p); 257 | break; 258 | } 259 | } 260 | } 261 | } 262 | 263 | int 264 | httpopen(Client *c, Url *url) 265 | { 266 | int fd, code, redirect, authenticate; 267 | char *cookies; 268 | Ioproc *io; 269 | HttpState *hs; 270 | 271 | if(httpdebug) 272 | fprint(2, "httpopen\n"); 273 | io = c->io; 274 | hs = emalloc(sizeof(*hs)); 275 | hs->c = c; 276 | hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme)); 277 | c->aux = hs; 278 | if(httpdebug) 279 | fprint(2, "dial %s\n", hs->netaddr); 280 | fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps); 281 | if(fd < 0){ 282 | Error: 283 | if(httpdebug) 284 | fprint(2, "iodial: %r\n"); 285 | free(hs->netaddr); 286 | close(hs->fd); 287 | hs->fd = -1; 288 | free(hs); 289 | c->aux = nil; 290 | return -1; 291 | } 292 | hs->fd = fd; 293 | if(httpdebug) 294 | fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n", 295 | c->havepostbody? "POST": " GET", url->http.page_spec, url->host); 296 | ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n", 297 | c->havepostbody? "POST" : "GET", url->http.page_spec, url->host); 298 | if(httpdebug) 299 | fprint(2, "<- User-Agent: %s\n", c->ctl.useragent); 300 | if(c->ctl.useragent) 301 | ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent); 302 | if(c->ctl.sendcookies){ 303 | /* should we use url->page here? sometimes it is nil. */ 304 | cookies = httpcookies(url->host, url->http.page_spec, 0); 305 | if(cookies && cookies[0]) 306 | ioprint(io, fd, "%s", cookies); 307 | if(httpdebug) 308 | fprint(2, "<- %s", cookies); 309 | free(cookies); 310 | } 311 | if(c->havepostbody){ 312 | ioprint(io, fd, "Content-type: %s\r\n", PostContentType); 313 | ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody); 314 | if(httpdebug){ 315 | fprint(2, "<- Content-type: %s\n", PostContentType); 316 | fprint(2, "<- Content-length: %ud\n", c->npostbody); 317 | } 318 | } 319 | if(c->authenticate){ 320 | ioprint(io, fd, "Authorization: %s\r\n", c->authenticate); 321 | if(httpdebug) 322 | fprint(2, "<- Authorization: %s\n", c->authenticate); 323 | } 324 | ioprint(io, fd, "\r\n"); 325 | if(c->havepostbody) 326 | if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody) 327 | goto Error; 328 | 329 | c->havepostbody = 0; 330 | redirect = 0; 331 | authenticate = 0; 332 | initibuf(&hs->b, io, fd); 333 | code = httprcode(hs); 334 | 335 | switch(code){ 336 | case -1: /* connection timed out */ 337 | goto Error; 338 | 339 | /* 340 | case Eof: 341 | werrstr("EOF from HTTP server"); 342 | goto Error; 343 | */ 344 | 345 | case 200: /* OK */ 346 | case 201: /* Created */ 347 | case 202: /* Accepted */ 348 | case 204: /* No Content */ 349 | case 205: /* Reset Content */ 350 | #ifdef NOT_DEFINED 351 | if(ofile == nil && r->start != 0) 352 | sysfatal("page changed underfoot"); 353 | #endif 354 | break; 355 | 356 | case 206: /* Partial Content */ 357 | werrstr("Partial Content (206)"); 358 | goto Error; 359 | 360 | case 301: /* Moved Permanently */ 361 | case 302: /* Moved Temporarily */ 362 | case 303: /* See Other */ 363 | case 307: /* Temporary Redirect */ 364 | redirect = 1; 365 | break; 366 | 367 | case 304: /* Not Modified */ 368 | break; 369 | 370 | case 400: /* Bad Request */ 371 | werrstr("Bad Request (400)"); 372 | goto Error; 373 | 374 | case 401: /* Unauthorized */ 375 | if(c->authenticate){ 376 | werrstr("Authentication failed (401)"); 377 | goto Error; 378 | } 379 | authenticate = 1; 380 | break; 381 | case 402: /* Payment Required */ 382 | werrstr("Payment Required (402)"); 383 | goto Error; 384 | 385 | case 403: /* Forbidden */ 386 | werrstr("Forbidden by server (403)"); 387 | goto Error; 388 | 389 | case 404: /* Not Found */ 390 | werrstr("Not found on server (404)"); 391 | goto Error; 392 | 393 | case 405: /* Method Not Allowed */ 394 | werrstr("Method not allowed (405)"); 395 | goto Error; 396 | 397 | case 406: /* Not Acceptable */ 398 | werrstr("Not Acceptable (406)"); 399 | goto Error; 400 | 401 | case 407: /* Proxy auth */ 402 | werrstr("Proxy authentication required (407)"); 403 | goto Error; 404 | 405 | case 408: /* Request Timeout */ 406 | werrstr("Request Timeout (408)"); 407 | goto Error; 408 | 409 | case 409: /* Conflict */ 410 | werrstr("Conflict (409)"); 411 | goto Error; 412 | 413 | case 410: /* Gone */ 414 | werrstr("Gone (410)"); 415 | goto Error; 416 | 417 | case 411: /* Length Required */ 418 | werrstr("Length Required (411)"); 419 | goto Error; 420 | 421 | case 412: /* Precondition Failed */ 422 | werrstr("Precondition Failed (412)"); 423 | goto Error; 424 | 425 | case 413: /* Request Entity Too Large */ 426 | werrstr("Request Entity Too Large (413)"); 427 | goto Error; 428 | 429 | case 414: /* Request-URI Too Long */ 430 | werrstr("Request-URI Too Long (414)"); 431 | goto Error; 432 | 433 | case 415: /* Unsupported Media Type */ 434 | werrstr("Unsupported Media Type (415)"); 435 | goto Error; 436 | 437 | case 416: /* Requested Range Not Satisfiable */ 438 | werrstr("Requested Range Not Satisfiable (416)"); 439 | goto Error; 440 | 441 | case 417: /* Expectation Failed */ 442 | werrstr("Expectation Failed (417)"); 443 | goto Error; 444 | 445 | case 500: /* Internal server error */ 446 | werrstr("Server choked (500)"); 447 | goto Error; 448 | 449 | case 501: /* Not implemented */ 450 | werrstr("Server can't do it (501)"); 451 | goto Error; 452 | 453 | case 502: /* Bad gateway */ 454 | werrstr("Bad gateway (502)"); 455 | goto Error; 456 | 457 | case 503: /* Service unavailable */ 458 | werrstr("Service unavailable (503)"); 459 | goto Error; 460 | 461 | default: 462 | /* Bogus: we should treat unknown code XYZ as code X00 */ 463 | werrstr("Unknown response code %d", code); 464 | goto Error; 465 | } 466 | 467 | if(httpheaders(hs) < 0) 468 | goto Error; 469 | if(c->ctl.acceptcookies && hs->setcookie) 470 | httpsetcookie(hs->setcookie, url->host, url->path); 471 | if(authenticate){ 472 | if(!hs->credentials){ 473 | if(hs->autherror[0]) 474 | werrstr("%s", hs->autherror); 475 | else 476 | werrstr("unauthorized; no www-authenticate: header"); 477 | return -1; 478 | } 479 | c->authenticate = hs->credentials; 480 | hs->credentials = nil; 481 | } 482 | if(redirect){ 483 | if(!hs->location){ 484 | werrstr("redirection without Location: header"); 485 | return -1; 486 | } 487 | c->redirect = hs->location; 488 | hs->location = nil; 489 | } 490 | return 0; 491 | } 492 | 493 | int 494 | httpread(Client *c, Req *r) 495 | { 496 | HttpState *hs; 497 | long n; 498 | 499 | hs = c->aux; 500 | n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count); 501 | if(n < 0) 502 | return -1; 503 | 504 | r->ofcall.count = n; 505 | return 0; 506 | } 507 | 508 | void 509 | httpclose(Client *c) 510 | { 511 | HttpState *hs; 512 | 513 | hs = c->aux; 514 | if(hs == nil) 515 | return; 516 | ioclose(c->io, hs->fd); 517 | hs->fd = -1; 518 | free(hs->location); 519 | free(hs->setcookie); 520 | free(hs->netaddr); 521 | free(hs->credentials); 522 | free(hs); 523 | c->aux = nil; 524 | } 525 | 526 | -------------------------------------------------------------------------------- /abaco/exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | void del(Text *, Text *, int, int, Rune *, int); 18 | void delcol(Text *, Text *, int, int, Rune *, int); 19 | void cut(Text *, Text *, int, int, Rune *, int); 20 | void doexit(Text *, Text *, int, int, Rune *, int); 21 | void get(Text *, Text *, int, int, Rune *, int); 22 | void go(Text *,Text *, int, int, Rune *, int); 23 | void google(Text *, Text *, int, int, Rune *, int); 24 | void new(Text*, Text *, int, int, Rune *, int); 25 | void newcol(Text*, Text *, int, int, Rune *, int); 26 | void paste(Text *, Text *, int, int, Rune *, int); 27 | void sort(Text *, Text *, int, int, Rune *, int); 28 | void stop(Text *, Text *, int, int, Rune *, int); 29 | void debug(Text *, Text *, int, int, Rune *, int); 30 | 31 | typedef struct Exectab Exectab; 32 | struct Exectab 33 | { 34 | Rune *name; 35 | void (*fn)(Text *, Text *, int, int, Rune *, int); 36 | int flag1; 37 | int flag2; 38 | }; 39 | 40 | Exectab exectab[] = { 41 | { (Rune[]){ 'B','a','c','k',0} , go, FALSE, XXX }, 42 | { (Rune[]){ 'C','u','t',0} , cut, TRUE, TRUE }, 43 | { (Rune[]){ 'D','e','b','u','g',0} , debug, XXX, XXX }, 44 | { (Rune[]){ 'D','e','l',0} , del, XXX, XXX }, 45 | { (Rune[]){ 'D','e','l','c','o','l',0} ,delcol, FALSE, TRUE }, 46 | { (Rune[]){ 'E','x','i','t',0} , doexit, XXX, XXX }, 47 | { (Rune[]){ 'G','e','t',0} , get, XXX, XXX }, 48 | { (Rune[]){ 'G','o','o','g','l','e',0} ,google, XXX, XXX }, 49 | { (Rune[]){ 'N','e','w',0} , new, XXX, XXX }, 50 | { (Rune[]){ 'N','e','w','c','o','l',0} ,newcol, XXX, XXX }, 51 | { (Rune[]){ 'N','e','x','t',0} , go, TRUE, XXX }, 52 | { (Rune[]){ 'P','a','s','t','e',0} , paste, TRUE, XXX }, 53 | { (Rune[]){ 'S','n','a','r','f',0} , cut, TRUE, FALSE }, 54 | { (Rune[]){ 'S','t','o','p',0} , stop, XXX, XXX }, 55 | { (Rune[]){ 'S','o','r','t',0} , sort, XXX, XXX }, 56 | { nil, nil, 0, 0 }, 57 | }; 58 | 59 | static 60 | Exectab* 61 | lookup(Rune *r, int n) 62 | { 63 | Exectab *e; 64 | int nr; 65 | 66 | r = skipbl(r, n, &n); 67 | if(n == 0) 68 | return nil; 69 | findbl(r, n, &nr); 70 | nr = n-nr; 71 | for(e=exectab; e->name; e++) 72 | if(runeeq(r, nr, e->name, runestrlen(e->name)) == TRUE) 73 | return e; 74 | return nil; 75 | } 76 | 77 | int 78 | isexecc(int c) 79 | { 80 | if(isalnum(c)) 81 | return 1; 82 | return c=='<' || c=='|' || c=='>'; 83 | } 84 | 85 | void 86 | execute(Text *t, uint aq0, uint aq1, Text *notused) 87 | { 88 | uint q0, q1; 89 | Rune *r, *s; 90 | Exectab *e; 91 | int c, n; 92 | 93 | q0 = aq0; 94 | q1 = aq1; 95 | if(q1 == q0){ /* expand to find word (actually file name) */ 96 | /* if in selection, choose selection */ 97 | if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 98 | q0 = t->q0; 99 | q1 = t->q1; 100 | }else{ 101 | while(q1rs.nr && isexecc(c=t->rs.r[q1]) && c!=':') 102 | q1++; 103 | while(q0>0 && isexecc(c=t->rs.r[q0-1]) && c!=':') 104 | q0--; 105 | if(q1 == q0) 106 | return; 107 | } 108 | } 109 | r = runemalloc(q1-q0); 110 | runemove(r, t->rs.r+q0, q1-q0); 111 | e = lookup(r, q1-q0); 112 | if(e){ 113 | s = skipbl(r, q1-q0, &n); 114 | s = findbl(s, n, &n); 115 | s = skipbl(s, n, &n); 116 | (*e->fn)(t, seltext, e->flag1, e->flag2, s, n); 117 | } 118 | free(r); 119 | } 120 | 121 | void 122 | newcol(Text *et, Text *notused3, int notused, int notused2, Rune *notused4, int notused5) 123 | { 124 | Column *c; 125 | 126 | c = rowadd(et->row, nil, -1); 127 | if(c) 128 | winsettag(coladd(c, nil, nil, -1)); 129 | } 130 | 131 | void 132 | delcol(Text *t, Text *notused3, int notused, int notused2, Rune *notused4, int notused5) 133 | { 134 | Column *c; 135 | 136 | c = t->col; 137 | if(c==nil || colclean(c)==0) 138 | return; 139 | 140 | rowclose(c->row, c, TRUE); 141 | } 142 | 143 | void 144 | del(Text *et, Text * notused3, int flag1, int notused, Rune *notused4, int notused5) 145 | { 146 | if(et->w==nil) 147 | return; 148 | 149 | if(flag1 || winclean(et->w, FALSE)) 150 | colclose(et->col, et->w, TRUE); 151 | } 152 | 153 | void 154 | sort(Text *et, Text *notused, int notused2, int notused3, Rune *notused4, int notused5) 155 | { 156 | if(et->col) 157 | colsort(et->col); 158 | } 159 | 160 | void 161 | doexit(Text *notused, Text *notused4, int notused2, int notused3, Rune *notused6, int notused5) 162 | { 163 | sendul(cexit, 0); 164 | threadexits(nil); 165 | } 166 | 167 | void 168 | debug(Text *notused, Text *notused2, int notused3, int notused4, Rune *notused6, int notused5) 169 | { 170 | Column *c; 171 | int i, j; 172 | 173 | for(j=0; jnw; i++){ 176 | fprint(2, "Col: %d; Win: %d\n", j, i); 177 | windebug(c->w[i]); 178 | } 179 | } 180 | } 181 | 182 | void 183 | stop(Text *t, Text *notused, int notused2, int notused3, Rune *notused4, int notused5) 184 | { 185 | if(t==nil || t->w==nil) 186 | return; 187 | 188 | pageabort(&t->w->page); 189 | } 190 | 191 | void 192 | get(Text *t, Text *notused, int notused2, int notused3, Rune *notused4, int notused5) 193 | { 194 | Window *w; 195 | int dohist; 196 | 197 | if(t==nil || t->w==nil) 198 | return; 199 | w = t->w; 200 | if(w->url.rs.nr == 0) 201 | return; 202 | 203 | dohist = FALSE; 204 | if(w->page.url==nil || runestreq(w->page.url->act, w->url.rs)==FALSE) 205 | dohist = TRUE; 206 | 207 | pageget(&w->page, &w->url.rs, nil, HGet, dohist); 208 | } 209 | 210 | void 211 | go(Text *et, Text *t, int isnext, int notused, Rune *notused4, int notused5) 212 | { 213 | Window *w; 214 | Page *p; 215 | int n, id; 216 | 217 | if(et!=nil && et->w!=nil) 218 | t = et; 219 | if(t==nil || t->w==nil) 220 | return; 221 | 222 | w = t->w; 223 | n = w->history.nurl; 224 | p = &w->page; 225 | if(!p->url) 226 | return; 227 | 228 | id = p->url->id; 229 | 230 | if(isnext) 231 | id++; 232 | else 233 | id--; 234 | 235 | if(n==0 || id<0 || id==n) 236 | return; 237 | 238 | incref(&w->history.url[id]->ref); 239 | pageload(p, w->history.url[id], FALSE); 240 | w->history.cid = id; 241 | } 242 | 243 | void 244 | cut(Text *notused, Text *t, int dosnarf, int docut, Rune *notused4, int notused5) 245 | { 246 | char *s; 247 | uint u; 248 | if(selpage){ 249 | if(dosnarf && !docut && !eqpt(selpage->top, selpage->bot)) 250 | pagesnarf(selpage); 251 | return; 252 | } 253 | if(t==nil){ 254 | /* can only happen if seltext == nil */ 255 | return; 256 | } 257 | if(t->q0 > t->q1){ 258 | u = t->q0; 259 | t->q0 = t->q1; 260 | t->q1 =u; 261 | } 262 | if(t->q0 == t->q1) 263 | return; 264 | 265 | if(dosnarf){ 266 | s = smprint("%.*S", t->q1-t->q0, t->rs.r+t->q0); 267 | putsnarf(s); 268 | free(s); 269 | } 270 | if(docut){ 271 | textdelete(t, t->q0, t->q1); 272 | textsetselect(t, t->q0, t->q0); 273 | if(t->w) 274 | textscrdraw(t); 275 | }else if(dosnarf) /* Snarf command */ 276 | argtext = t; 277 | } 278 | 279 | void 280 | paste(Text *notused, Text *t, int selectall, int notused2, Rune *notused4, int notused5) 281 | { 282 | Runestr rs; 283 | char *s; 284 | uint q1; 285 | 286 | if(t == nil) 287 | return; 288 | 289 | s = getsnarf(); 290 | if(s == nil) 291 | return; 292 | if(*s == '\0') { 293 | free(s); 294 | return; 295 | } 296 | 297 | bytetorunestr(s, &rs); 298 | free(s); 299 | 300 | cut(t, t, FALSE, TRUE, nil, 0); 301 | textinsert(t, t->q0, rs.r, rs.nr); 302 | q1 = t->q0+rs.nr; 303 | if(selectall) 304 | textsetselect(t, t->q0, q1); 305 | else 306 | textsetselect(t, q1, q1); 307 | if(t->w) 308 | textscrdraw(t); 309 | 310 | closerunestr(&rs); 311 | } 312 | 313 | typedef struct Expand Expand; 314 | 315 | struct Expand 316 | { 317 | uint q0; 318 | uint q1; 319 | Rune *name; 320 | int nname; 321 | int jump; 322 | union{ 323 | Text *at; 324 | Rune *ar; 325 | }; 326 | int (*agetc)(void*, uint); 327 | int a0; 328 | int a1; 329 | }; 330 | 331 | int 332 | expand(Text *t, uint q0, uint q1, Expand *e) 333 | { 334 | memset(e, 0, sizeof *e); 335 | 336 | /* if in selection, choose selection */ 337 | e->jump = TRUE; 338 | if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 339 | q0 = t->q0; 340 | q1 = t->q1; 341 | if(t->what == Tag) 342 | e->jump = FALSE; 343 | } 344 | if(q0 == q1){ 345 | while(q1rs.nr && isalnum(t->rs.r[q1])) 346 | q1++; 347 | while(q0>0 && isalnum(t->rs.r[q0-1])) 348 | q0--; 349 | } 350 | e->q0 = q0; 351 | e->q1 = q1; 352 | return q1 > q0; 353 | } 354 | 355 | void 356 | look3(Text *t, uint q0, uint q1) 357 | { 358 | Expand e; 359 | Text *ct; 360 | Runestr rs; 361 | char buf[32]; 362 | Rune *r, c; 363 | uint p; 364 | int n; 365 | 366 | ct = seltext; 367 | if(ct == nil) 368 | ct = seltext = t; 369 | if(expand(t, q0, q1, &e) == FALSE) 370 | return; 371 | if(plumbsendfid){ 372 | /* send whitespace-delimited word to plumber */ 373 | buf[0] = '\0'; 374 | if(q1 == q0){ 375 | if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){ 376 | q0 = t->q0; 377 | q1 = t->q1; 378 | }else{ 379 | p = q0; 380 | while(q0>0 && (c=t->rs.r[q0-1])!=L' ' && c!=L'\t' && c!=L'\n') 381 | q0--; 382 | while(q1rs.nr && (c=t->rs.r[q1])!=L' ' && c!=L'\t' && c!=L'\n') 383 | q1++; 384 | if(q1 == q0) 385 | return; 386 | sprint(buf, "click=%d", p-q0); 387 | } 388 | } 389 | rs.r = runemalloc(q1-q0); 390 | runemove(rs.r, t->rs.r+q0, q1-q0); 391 | rs.nr = q1-q0; 392 | if(plumbrunestr(&rs, buf) >= 0){ 393 | closerunestr(&rs); 394 | return; 395 | } 396 | /* plumber failed to match; fall through */ 397 | } 398 | if(t == ct) 399 | textsetselect(ct, e.q1, e.q1); 400 | n = e.q1 - e.q0; 401 | r = runemalloc(n); 402 | runemove(r, t->rs.r+e.q0, n); 403 | if(search(ct, r, n) && e.jump) 404 | moveto(mousectl, addpt(frptofchar(&ct->Frame, ct->p0), Pt(4, ct->font->height-4))); 405 | 406 | free(r); 407 | } 408 | 409 | int 410 | search(Text *ct, Rune *r, uint n) 411 | { 412 | uint q, nb, maxn; 413 | int around; 414 | Rune *s, *b, *c; 415 | 416 | if(n==0 || n>ct->rs.nr || 2*n>RBUFSIZE) 417 | return FALSE; 418 | 419 | maxn = max(n*2, RBUFSIZE); 420 | s = runemalloc(RBUFSIZE); 421 | b = s; 422 | nb = 0; 423 | b[nb] = 0; 424 | around = 0; 425 | q = ct->q1; 426 | for(;;){ 427 | if(q >= ct->rs.nr){ 428 | q = 0; 429 | around = 1; 430 | nb = 0; 431 | b[nb] = 0; 432 | } 433 | if(nb > 0){ 434 | c = runestrchr(b, r[0]); 435 | if(c == nil){ 436 | q += nb; 437 | nb = 0; 438 | b[nb] = 0; 439 | if(around && q>=ct->q1) 440 | break; 441 | continue; 442 | } 443 | q += (c-b); 444 | nb -= (c-b); 445 | b = c; 446 | } 447 | /* reload if buffer covers neither string nor rest of file */ 448 | if(nbrs.nr-q){ 449 | nb = ct->rs.nr-q; 450 | if(nb >= maxn) 451 | nb = maxn-1; 452 | runemove(s, ct->rs.r+q, nb); 453 | b = s; 454 | b[nb] = '\0'; 455 | } 456 | /* this runeeq is fishy but the null at b[nb] makes it safe */ 457 | if(runeeq(b, n, r, n) == TRUE){ 458 | if(ct->w) 459 | textshow(ct, q, q+n, 1); 460 | else{ 461 | ct->q0 = q; 462 | ct->q1 = q+n; 463 | } 464 | seltext = ct; 465 | free(s); 466 | return TRUE; 467 | } 468 | if(around && q>=ct->q1) 469 | break; 470 | --nb; 471 | b++; 472 | q++; 473 | } 474 | free(s); 475 | return FALSE; 476 | } 477 | 478 | Window* 479 | lookpage(Rune *s, int n) 480 | { 481 | int i, j; 482 | Window *w; 483 | Column *c; 484 | Page *p; 485 | 486 | /* avoid terminal slash on directories */ 487 | if(n>1 && s[n-1] == '/') 488 | --n; 489 | for(j=0; jnw; i++){ 492 | w = c->w[i]; 493 | p = &w->page; 494 | if(p->url && runeeq(p->url->src.r, p->url->src.nr, s, n)) 495 | if(w->col != nil) 496 | return w; 497 | } 498 | } 499 | return nil; 500 | } 501 | 502 | Window * 503 | openpage(Page *p, Runestr *rs) 504 | { 505 | Window *w; 506 | 507 | if(!validurl(rs->r)) 508 | return nil; 509 | 510 | w = lookpage(rs->r, rs->nr); 511 | if(w){ 512 | p = &w->page; 513 | if(!p->col->safe && Dy(p->r)==0) /* window is obscured by full-column window */ 514 | colgrow(p->col, p->col->w[0], 1); 515 | }else{ 516 | w = makenewwindow(p); 517 | winsettag(w); 518 | pageget(&w->page, rs, nil, HGet, TRUE); 519 | } 520 | return w; 521 | } 522 | 523 | void 524 | plumblook(Plumbmsg *m) 525 | { 526 | Runestr rs; 527 | 528 | if(m->ndata >= BUFSIZE){ 529 | fprint(2, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data); 530 | return; 531 | } 532 | if(m->data[0] == '\0') 533 | return; 534 | 535 | bytetorunestr(m->data, &rs); 536 | openpage(nil, &rs); 537 | closerunestr(&rs); 538 | } 539 | 540 | void 541 | new(Text *et, Text *notused, int notused2, int notused3, Rune *notused4, int notused5) 542 | { 543 | if(et->col != nil) 544 | winsettag(coladd(et->col, nil, nil, -1)); 545 | } 546 | 547 | void 548 | google(Text *et, Text *notused, int notused2, int notused3, Rune *arg, int narg) 549 | { 550 | Runestr rs; 551 | Rune *s; 552 | 553 | s = ucvt(arg); 554 | rs.r = runesmprint("http://www.google.com/search?hl=en&ie=UTF-8&q=%.*S", narg, s); 555 | rs.nr = runestrlen(rs.r); 556 | openpage(nil, &rs); 557 | free(s); 558 | closerunestr(&rs); 559 | } 560 | 561 | -------------------------------------------------------------------------------- /abaco/cols.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | void 18 | colinit(Column *c, Rectangle r) 19 | { 20 | Rectangle r1; 21 | Text *t; 22 | 23 | draw(screen, r, display->white, nil, ZP); 24 | c->r = r; 25 | c->w = nil; 26 | c->nw = 0; 27 | t = &c->tag; 28 | t->w = nil; 29 | t->col = c; 30 | r1 = r; 31 | r1.max.y = r1.min.y + font->height; 32 | textinit(t, screen, r1, font, tagcols); 33 | t->what = Columntag; 34 | r1.min.y = r1.max.y; 35 | r1.max.y += Border; 36 | draw(screen, r1, display->black, nil, ZP); 37 | textinsert(t, 0, (Rune[]){ 'N','e','w',' ','C','u','t',' ','P','a','s','t','e',' ','S','n','a','r','f',' ','S','o','r','t',' ','D','e','l','c','o','l',' ',0} , 32); 38 | textsetselect(t, t->rs.nr, t->rs.nr); 39 | draw(screen, t->scrollr, colbutton, nil, colbutton->r.min); 40 | c->safe = TRUE; 41 | } 42 | 43 | Window* 44 | coladd(Column *c, Window *w, Window *clone, int y) 45 | { 46 | Rectangle r, r1; 47 | Window *v; 48 | int i, t; 49 | 50 | v = nil; 51 | r = c->r; 52 | r.min.y = c->tag.r.max.y+Border; 53 | if(ynw>0){ /* steal half of last window by default */ 54 | v = c->w[c->nw-1]; 55 | y = v->page.all.min.y+Dy(v->page.all)/2; 56 | } 57 | /* look for window we'll land on */ 58 | for(i=0; inw; i++){ 59 | v = c->w[i]; 60 | if(y < v->r.max.y) 61 | break; 62 | } 63 | if(c->nw > 0){ 64 | if(i < c->nw) 65 | i++; /* new window will go after v */ 66 | /* 67 | * if v's too small, grow it first. 68 | */ 69 | if(!c->safe || Dy(v->page.all)<=3 ){ 70 | colgrow(c, v, 1); 71 | y = v->page.all.min.y+Dy(v->page.all)/2; 72 | } 73 | r = v->r; 74 | if(i == c->nw) 75 | t = c->r.max.y; 76 | else 77 | t = c->w[i]->r.min.y-Border; 78 | r.max.y = t; 79 | r1 = r; 80 | y = min(y, t-(Dy(v->r)-Dy(v->page.all))); 81 | r1.max.y = min(y, v->page.all.min.y+Dy(v->page.all)); 82 | r1.min.y = winresize(v, r1, FALSE); 83 | r1.max.y = r1.min.y+Border; 84 | draw(screen, r1, display->black, nil, ZP); 85 | r.min.y = r1.max.y; 86 | } 87 | if(w == nil){ 88 | w = emalloc(sizeof(Window)); 89 | w->col = c; 90 | wininit(w, clone, r); 91 | }else{ 92 | w->col = c; 93 | winresize(w, r, FALSE); 94 | } 95 | w->tag.col = c; 96 | w->tag.row = c->row; 97 | w->url.col = c; 98 | w->url.row = c->row; 99 | w->page.col = c; 100 | w->page.row = c->row; 101 | w->status.col = c; 102 | w->status.row = c->row; 103 | c->w = realloc(c->w, (c->nw+1)*sizeof(Window*)); 104 | memmove(c->w+i+1, c->w+i, (c->nw-i)*sizeof(Window*)); 105 | c->nw++; 106 | c->w[i] = w; 107 | savemouse(w); 108 | /* near but not on the button */ 109 | moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3))); 110 | c->safe = TRUE; 111 | return w; 112 | } 113 | 114 | void 115 | colclose(Column *c, Window *w, int dofree) 116 | { 117 | Rectangle r; 118 | int i; 119 | 120 | /* w is locked */ 121 | if(!c->safe) 122 | colgrow(c, w, 1); 123 | for(i=0; inw; i++) 124 | if(c->w[i] == w) 125 | goto Found; 126 | error("can't find window"); 127 | Found: 128 | r = w->r; 129 | w->tag.col = nil; 130 | w->url.col = nil; 131 | w->page.col = nil; 132 | w->col = nil; 133 | restoremouse(w); 134 | if(dofree) 135 | winclose(w); 136 | memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*)); 137 | c->nw--; 138 | c->w = realloc(c->w, c->nw*sizeof(Window*)); 139 | if(c->nw == 0){ 140 | draw(screen, r, display->white, nil, ZP); 141 | return; 142 | } 143 | if(i == c->nw){ /* extend last window down */ 144 | w = c->w[i-1]; 145 | r.min.y = w->r.min.y; 146 | r.max.y = c->r.max.y; 147 | }else{ /* extend next window up */ 148 | w = c->w[i]; 149 | r.max.y = w->r.max.y; 150 | } 151 | if(c->safe) 152 | winresize(w, r, FALSE); 153 | } 154 | 155 | void 156 | colcloseall(Column *c) 157 | { 158 | int i; 159 | Window *w; 160 | 161 | if(c == activecol) 162 | activecol = nil; 163 | if(seltext && c==seltext->col) 164 | seltext = nil; 165 | textclose(&c->tag); 166 | for(i=0; inw; i++){ 167 | w = c->w[i]; 168 | w->tag.col = nil; 169 | w->url.col = nil; 170 | w->page.col = nil; 171 | w->col = nil; 172 | winclose(w); 173 | } 174 | c->nw = 0; 175 | free(c->w); 176 | free(c); 177 | clearmouse(); 178 | } 179 | 180 | void 181 | colmousebut(Column *c) 182 | { 183 | moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2)); 184 | } 185 | 186 | void 187 | colresize(Column *c, Rectangle r) 188 | { 189 | int i; 190 | Rectangle r1, r2; 191 | Window *w; 192 | 193 | clearmouse(); 194 | r1 = r; 195 | r1.max.y = r1.min.y + c->tag.font->height; 196 | textresize(&c->tag, screen, r1); 197 | draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min); 198 | r1.min.y = r1.max.y; 199 | r1.max.y += Border; 200 | draw(screen, r1, display->black, nil, ZP); 201 | r1.max.y = r.max.y; 202 | for(i=0; inw; i++){ 203 | w = c->w[i]; 204 | if(i == c->nw-1) 205 | r1.max.y = r.max.y; 206 | else 207 | r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r); 208 | r2 = r1; 209 | r2.max.y = r2.min.y+Border; 210 | draw(screen, r2, display->black, nil, ZP); 211 | r1.min.y = r2.max.y; 212 | r1.min.y = winresize(w, r1, FALSE); 213 | } 214 | c->r = r; 215 | } 216 | 217 | static 218 | int 219 | colcmp(const void *a, const void *b) 220 | { 221 | Rune *r1, *r2; 222 | int i, nr1, nr2; 223 | 224 | r1 = (*(Window**)a)->page.title.r; 225 | nr1 = (*(Window**)a)->page.title.nr; 226 | r2 = (*(Window**)b)->page.title.r; 227 | nr2 = (*(Window**)b)->page.title.nr; 228 | for(i=0; inw == 0) 245 | return; 246 | clearmouse(); 247 | rp = emalloc(c->nw*sizeof(Rectangle)); 248 | wp = emalloc(c->nw*sizeof(Window*)); 249 | memmove(wp, c->w, c->nw*sizeof(Window*)); 250 | qsort(wp, c->nw, sizeof(Window*), colcmp); 251 | for(i=0; inw; i++) 252 | rp[i] = wp[i]->r; 253 | r = c->r; 254 | y = c->tag.r.max.y; 255 | for(i=0; inw; i++){ 256 | w = wp[i]; 257 | r.min.y = y; 258 | if(i == c->nw-1) 259 | r.max.y = c->r.max.y; 260 | else 261 | r.max.y = r.min.y+Dy(w->r)+Border; 262 | r1 = r; 263 | r1.max.y = r1.min.y+Border; 264 | draw(screen, r1, display->black, nil, ZP); 265 | r.min.y = r1.max.y; 266 | y = winresize(w, r, FALSE); 267 | } 268 | free(rp); 269 | free(c->w); 270 | c->w = wp; 271 | } 272 | 273 | void 274 | colgrow(Column *c, Window *w, int but) 275 | { 276 | Rectangle r, cr; 277 | int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h, wnl; 278 | Window *v; 279 | 280 | for(i=0; inw; i++) 281 | if(c->w[i] == w) 282 | goto Found; 283 | error("can't find window"); 284 | 285 | Found: 286 | cr = c->r; 287 | if(but < 0){ /* make sure window fills its own space properly */ 288 | r = w->r; 289 | if(i==c->nw-1 || c->safe==FALSE) 290 | r.max.y = cr.max.y; 291 | else 292 | r.max.y = c->w[i+1]->r.min.y; 293 | winresize(w, r, FALSE); 294 | return; 295 | } 296 | cr.min.y = c->w[0]->r.min.y; 297 | if(but == 3){ /* full size */ 298 | if(i != 0){ 299 | v = c->w[0]; 300 | c->w[0] = w; 301 | c->w[i] = v; 302 | } 303 | for(j=1; jnw; j++) 304 | c->w[j]->page.all = ZR; 305 | winresize(w, cr, FALSE); 306 | c->safe = FALSE; 307 | return; 308 | } 309 | /* store old #lines for each window */ 310 | wnl = Dy(w->page.all); 311 | onl = wnl; 312 | nl = emalloc(c->nw * sizeof(int)); 313 | ny = emalloc(c->nw * sizeof(int)); 314 | tot = 0; 315 | for(j=0; jnw; j++){ 316 | l = Dy(c->w[j]->page.all); 317 | nl[j] = l; 318 | tot += l; 319 | } 320 | /* approximate new #lines for this window */ 321 | if(but == 2){ /* as big as can be */ 322 | memset(nl, 0, c->nw * sizeof(int)); 323 | goto Pack; 324 | } 325 | nnl = min(onl + max(min(5, wnl), onl/2), tot); 326 | if(nnl < wnl) 327 | nnl = (wnl+nnl)/2; 328 | if(nnl == 0) 329 | nnl = 2; 330 | dnl = nnl - onl; 331 | /* compute new #lines for each window */ 332 | for(k=1; knw; k++){ 333 | /* prune from later window */ 334 | j = i+k; 335 | if(jnw && nl[j]){ 336 | l = min(dnl, max(1, nl[j]/2)); 337 | nl[j] -= l; 338 | nl[i] += l; 339 | dnl -= l; 340 | } 341 | /* prune from earlier window */ 342 | j = i-k; 343 | if(j>=0 && nl[j]){ 344 | l = min(dnl, max(1, nl[j]/2)); 345 | nl[j] -= l; 346 | nl[i] += l; 347 | dnl -= l; 348 | } 349 | } 350 | Pack: 351 | /* pack everyone above */ 352 | y1 = cr.min.y; 353 | for(j=0; jw[j]; 355 | r = v->r; 356 | r.min.y = y1; 357 | r.max.y = y1+Dy(v->tag.all)+Border+Dy(v->url.all); 358 | if(nl[j]) 359 | r.max.y += 1 + nl[j]; 360 | if(!c->safe || !eqrect(v->r, r)) 361 | winresize(v, r, c->safe); 362 | 363 | r.min.y = v->r.max.y; 364 | r.max.y += Border; 365 | draw(screen, r, display->black, nil, ZP); 366 | y1 = r.max.y; 367 | } 368 | /* scan to see new size of everyone below */ 369 | y2 = c->r.max.y; 370 | for(j=c->nw-1; j>i; j--){ 371 | v = c->w[j]; 372 | r = v->r; 373 | r.min.y = y2-Dy(v->tag.all)-Border-Dy(v->url.all); 374 | if(nl[j]) 375 | r.min.y -= 1 + nl[j]; 376 | r.min.y -= Border; 377 | ny[j] = r.min.y; 378 | y2 = r.min.y; 379 | } 380 | /* compute new size of window */ 381 | r = w->r; 382 | r.min.y = y1; 383 | if(i == c->nw-1) 384 | r.max.y = c->r.max.y; 385 | else{ 386 | r.max.y = r.min.y+Dy(w->tag.all)+Border+Dy(w->url.all); 387 | h = font->height; 388 | if(y2-r.max.y >= 2*(1+h+Border)){ 389 | r.max.y += 1; 390 | r.max.y += h*((y2-r.max.y)/h); 391 | } 392 | } 393 | /* draw window */ 394 | if(!c->safe || !eqrect(w->r, r)) 395 | winresize(w, r, c->safe); 396 | 397 | if(i < c->nw-1){ 398 | r.min.y = r.max.y; 399 | r.max.y += Border; 400 | draw(screen, r, display->black, nil, ZP); 401 | for(j=i+1; jnw; j++) 402 | ny[j] -= (y2-r.max.y); 403 | } 404 | /* pack everyone below */ 405 | y1 = r.max.y; 406 | for(j=i+1; jnw; j++){ 407 | v = c->w[j]; 408 | r = v->r; 409 | r.min.y = y1; 410 | if(j == c->nw-1) 411 | r.max.y = c->r.max.y; 412 | else{ 413 | r.max.y = y1+Dy(v->tag.all)+Border+Dy(v->url.all); 414 | if(nl[j]) 415 | r.max.y += 1 + nl[j]; 416 | } 417 | if(!c->safe || !eqrect(v->r, r)) 418 | winresize(v, r, c->safe); 419 | if(j < c->nw-1){ /* no border on last window */ 420 | r.min.y = v->r.max.y; 421 | r.max.y += Border; 422 | draw(screen, r, display->black, nil, ZP); 423 | } 424 | y1 = r.max.y; 425 | } 426 | free(nl); 427 | free(ny); 428 | c->safe = TRUE; 429 | winmousebut(w); 430 | } 431 | 432 | void 433 | coldragwin(Column *c, Window *w, int but) 434 | { 435 | Rectangle r; 436 | int i, b; 437 | Point p, op; 438 | Window *v; 439 | Column *nc; 440 | 441 | clearmouse(); 442 | setcursor(mousectl, &boxcursor); 443 | b = mouse->buttons; 444 | op = mouse->xy; 445 | while(mouse->buttons == b) 446 | readmouse(mousectl); 447 | setcursor(mousectl, nil); 448 | if(mouse->buttons){ 449 | while(mouse->buttons) 450 | readmouse(mousectl); 451 | return; 452 | } 453 | 454 | for(i=0; inw; i++) 455 | if(c->w[i] == w) 456 | goto Found; 457 | error("can't find window"); 458 | 459 | Found: 460 | p = mouse->xy; 461 | if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){ 462 | colgrow(c, w, but); 463 | winmousebut(w); 464 | return; 465 | } 466 | /* is it a flick to the right? */ 467 | if(abs(p.y-op.y)<10 && p.x>op.x+30 && rowwhichcol(c->row, p)==c) 468 | p.x = op.x+Dx(w->r); /* yes: toss to next column */ 469 | nc = rowwhichcol(c->row, p); 470 | if(nc!=nil && nc!=c){ 471 | colclose(c, w, FALSE); 472 | coladd(nc, w, nil, p.y); 473 | winmousebut(w); 474 | return; 475 | } 476 | if(i==0 && c->nw==1) 477 | return; /* can't do it */ 478 | if((i>0 && p.yw[i-1]->r.min.y) || (inw-1 && p.y>w->r.max.y) 479 | || (i==0 && p.y>w->r.max.y)){ 480 | /* shuffle */ 481 | colclose(c, w, FALSE); 482 | coladd(c, w, nil, p.y); 483 | winmousebut(w); 484 | return; 485 | } 486 | if(i == 0) 487 | return; 488 | v = c->w[i-1]; 489 | if(p.y < v->url.all.max.y) 490 | p.y = v->url.all.max.y; 491 | if(p.y > w->r.max.y-Dy(w->tag.all)-Border-Dy(w->url.all)) 492 | p.y = w->r.max.y-Dy(w->tag.all)-Border-Dy(w->url.all); 493 | r = v->r; 494 | r.max.y = p.y; 495 | if(r.max.y > v->page.all.min.y){ 496 | r.max.y -= (r.max.y-v->page.all.min.y)%font->height; 497 | if(v->page.all.min.y == v->page.all.max.y) 498 | r.max.y++; 499 | } 500 | if(!eqrect(v->r, r)) 501 | winresize(v, r, c->safe); 502 | r.min.y = v->r.max.y; 503 | r.max.y = r.min.y+Border; 504 | draw(screen, r, display->black, nil, ZP); 505 | r.min.y = r.max.y; 506 | if(i == c->nw-1) 507 | r.max.y = c->r.max.y; 508 | else 509 | r.max.y = c->w[i+1]->r.min.y-Border; 510 | if(!eqrect(w->r, r)) 511 | winresize(w, r, c->safe); 512 | c->safe = TRUE; 513 | winmousebut(w); 514 | } 515 | 516 | Text* 517 | colwhich(Column *c, Point p, Rune r, int key) 518 | { 519 | Window *w; 520 | Text *t; 521 | int i; 522 | 523 | if(!ptinrect(p, c->r)) 524 | return nil; 525 | if(ptinrect(p, c->tag.all)) 526 | return &c->tag; 527 | for(i=0; inw; i++){ 528 | w = c->w[i]; 529 | if(ptinrect(p, w->r)){ 530 | winlock(w, key ? 'K' : 'M'); 531 | if(key) 532 | t = wintype(w, p, r); 533 | else 534 | t = winmouse(w, p, r); 535 | winunlock(w); 536 | return t; 537 | } 538 | } 539 | return nil; 540 | } 541 | 542 | int 543 | colclean(Column *c) 544 | { 545 | int i, clean; 546 | 547 | clean = TRUE; 548 | for(i=0; inw; i++) 549 | clean &= winclean(c->w[i], TRUE); 550 | return clean; 551 | } 552 | -------------------------------------------------------------------------------- /webfs/fs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Web file system. Conventionally mounted at /mnt/web 3 | * 4 | * ctl send control messages (might go away) 5 | * cookies list of cookies, editable 6 | * clone open and read to obtain new connection 7 | * n connection directory 8 | * ctl control messages (like get url) 9 | * body retrieved data 10 | * content-type mime content-type of body 11 | * postbody data to be posted 12 | * parsed parsed version of url 13 | * url entire url 14 | * scheme http, ftp, etc. 15 | * host hostname 16 | * path path on host 17 | * query query after path 18 | * fragment #foo anchor reference 19 | * user user name (ftp) 20 | * password password (ftp) 21 | * ftptype transfer mode (ftp) 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include <9p.h> 32 | #include "dat.h" 33 | #include "fns.h" 34 | 35 | int fsdebug; 36 | 37 | enum 38 | { 39 | Qroot, 40 | Qrootctl, 41 | Qclone, 42 | Qcookies, 43 | Qclient, 44 | Qctl, 45 | Qbody, 46 | Qbodyext, 47 | Qcontenttype, 48 | Qpostbody, 49 | Qparsed, 50 | Qurl, 51 | Qscheme, 52 | Qschemedata, 53 | Quser, 54 | Qpasswd, 55 | Qhost, 56 | Qport, 57 | Qpath, 58 | Qquery, 59 | Qfragment, 60 | Qftptype, 61 | Qend, 62 | }; 63 | 64 | #define PATH(type, n) ((type)|((n)<<8)) 65 | #define TYPE(path) ((int)(path) & 0xFF) 66 | #define NUM(path) ((uint)(path)>>8) 67 | 68 | Channel *creq; 69 | Channel *creqwait; 70 | Channel *cclunk; 71 | Channel *cclunkwait; 72 | 73 | typedef struct Tab Tab; 74 | struct Tab 75 | { 76 | char *name; 77 | ulong mode; 78 | int offset; 79 | }; 80 | 81 | Tab tab[] = 82 | { 83 | "/", DMDIR|0555, 0, 84 | "ctl", 0666, 0, 85 | "clone", 0666, 0, 86 | "cookies", 0666, 0, 87 | "XXX", DMDIR|0555, 0, 88 | "ctl", 0666, 0, 89 | "body", 0444, 0, 90 | "XXX", 0444, 0, 91 | "contenttype", 0444, 0, 92 | "postbody", 0666, 0, 93 | "parsed", DMDIR|0555, 0, 94 | "url", 0444, offsetof(Url, url), 95 | "scheme", 0444, offsetof(Url, scheme), 96 | "schemedata", 0444, offsetof(Url, schemedata), 97 | "user", 0444, offsetof(Url, user), 98 | "passwd", 0444, offsetof(Url, passwd), 99 | "host", 0444, offsetof(Url, host), 100 | "port", 0444, offsetof(Url, port), 101 | "path", 0444, offsetof(Url, path), 102 | "query", 0444, offsetof(Url, query), 103 | "fragment", 0444, offsetof(Url, fragment), 104 | "ftptype", 0444, offsetof(Url, ftp.type), 105 | }; 106 | 107 | ulong time0; 108 | 109 | static void 110 | fillstat(Dir *d, uvlong path, ulong length, char *ext) 111 | { 112 | Tab *t; 113 | int type; 114 | char buf[32]; 115 | 116 | memset(d, 0, sizeof(*d)); 117 | d->uid = estrdup("web"); 118 | d->gid = estrdup("web"); 119 | d->qid.path = path; 120 | d->atime = d->mtime = time0; 121 | d->length = length; 122 | type = TYPE(path); 123 | t = &tab[type]; 124 | if(type == Qbodyext) { 125 | snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext); 126 | d->name = estrdup(buf); 127 | } 128 | else if(t->name) 129 | d->name = estrdup(t->name); 130 | else{ /* client directory */ 131 | snprint(buf, sizeof buf, "%ud", NUM(path)); 132 | d->name = estrdup(buf); 133 | } 134 | d->qid.type = t->mode>>24; 135 | d->mode = t->mode; 136 | } 137 | 138 | static void 139 | fsstat(Req *r) 140 | { 141 | fillstat(&r->d, r->fid->qid.path, 0, nil); 142 | respond(r, nil); 143 | } 144 | 145 | static int 146 | rootgen(int i, Dir *d, void *notused) 147 | { 148 | char buf[32]; 149 | 150 | i += Qroot+1; 151 | if(i < Qclient){ 152 | fillstat(d, i, 0, nil); 153 | return 0; 154 | } 155 | i -= Qclient; 156 | if(i < nclient){ 157 | fillstat(d, PATH(Qclient, i), 0, nil); 158 | snprint(buf, sizeof buf, "%d", i); 159 | free(d->name); 160 | d->name = estrdup(buf); 161 | return 0; 162 | } 163 | return -1; 164 | } 165 | 166 | static int 167 | clientgen(int i, Dir *d, void *aux) 168 | { 169 | Client *c; 170 | 171 | c = aux; 172 | i += Qclient+1; 173 | if(i <= Qparsed){ 174 | fillstat(d, PATH(i, c->num), 0, c->ext); 175 | return 0; 176 | } 177 | return -1; 178 | } 179 | 180 | static int 181 | parsedgen(int i, Dir *d, void *aux) 182 | { 183 | Client *c; 184 | 185 | c = aux; 186 | i += Qparsed+1; 187 | if(i < Qend){ 188 | fillstat(d, PATH(i, c->num), 0, nil); 189 | return 0; 190 | } 191 | return -1; 192 | } 193 | 194 | static void 195 | fsread(Req *r) 196 | { 197 | char *s; 198 | char e[ERRMAX]; 199 | Client *c; 200 | ulong path; 201 | 202 | path = r->fid->qid.path; 203 | switch(TYPE(path)){ 204 | default: 205 | snprint(e, sizeof e, "bug in webfs path=%lux\n", path); 206 | respond(r, e); 207 | break; 208 | 209 | case Qroot: 210 | dirread9p(r, rootgen, nil); 211 | respond(r, nil); 212 | break; 213 | 214 | case Qrootctl: 215 | globalctlread(r); 216 | break; 217 | 218 | case Qcookies: 219 | cookieread(r); 220 | break; 221 | 222 | case Qclient: 223 | dirread9p(r, clientgen, client[NUM(path)]); 224 | respond(r, nil); 225 | break; 226 | 227 | case Qctl: 228 | ctlread(r, client[NUM(path)]); 229 | break; 230 | 231 | case Qcontenttype: 232 | c = client[NUM(path)]; 233 | if(c->contenttype == nil) 234 | r->ofcall.count = 0; 235 | else 236 | readstr(r, c->contenttype); 237 | respond(r, nil); 238 | break; 239 | 240 | case Qpostbody: 241 | c = client[NUM(path)]; 242 | readbuf(r, c->postbody, c->npostbody); 243 | respond(r, nil); 244 | break; 245 | 246 | case Qbody: 247 | case Qbodyext: 248 | c = client[NUM(path)]; 249 | if(c->iobusy){ 250 | respond(r, "already have i/o pending"); 251 | break; 252 | } 253 | c->iobusy = 1; 254 | sendp(c->creq, r); 255 | break; 256 | 257 | case Qparsed: 258 | dirread9p(r, parsedgen, client[NUM(path)]); 259 | respond(r, nil); 260 | break; 261 | 262 | case Qurl: 263 | case Qscheme: 264 | case Qschemedata: 265 | case Quser: 266 | case Qpasswd: 267 | case Qhost: 268 | case Qport: 269 | case Qpath: 270 | case Qquery: 271 | case Qfragment: 272 | case Qftptype: 273 | c = client[NUM(path)]; 274 | r->ofcall.count = 0; 275 | if(c->url != nil 276 | && (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil) 277 | readstr(r, s); 278 | respond(r, nil); 279 | break; 280 | } 281 | } 282 | 283 | static void 284 | fswrite(Req *r) 285 | { 286 | int m; 287 | ulong path; 288 | char e[ERRMAX], *buf, *cmd, *arg; 289 | Client *c; 290 | 291 | path = r->fid->qid.path; 292 | switch(TYPE(path)){ 293 | default: 294 | snprint(e, sizeof e, "bug in webfs path=%lux\n", path); 295 | respond(r, e); 296 | break; 297 | 298 | case Qcookies: 299 | cookiewrite(r); 300 | break; 301 | 302 | case Qrootctl: 303 | case Qctl: 304 | if(r->ifcall.count >= 1024){ 305 | respond(r, "ctl message too long"); 306 | return; 307 | } 308 | buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count); 309 | cmd = buf; 310 | arg = strpbrk(cmd, "\t "); 311 | if(arg){ 312 | *arg++ = '\0'; 313 | arg += strspn(arg, "\t "); 314 | }else 315 | arg = ""; 316 | r->ofcall.count = r->ifcall.count; 317 | if(TYPE(path)==Qrootctl){ 318 | if(!ctlwrite(r, &globalctl, cmd, arg) 319 | && !globalctlwrite(r, cmd, arg)) 320 | respond(r, "unknown control command"); 321 | }else{ 322 | c = client[NUM(path)]; 323 | if(!ctlwrite(r, &c->ctl, cmd, arg) 324 | && !clientctlwrite(r, c, cmd, arg)) 325 | respond(r, "unknown control command"); 326 | } 327 | free(buf); 328 | break; 329 | 330 | case Qpostbody: 331 | c = client[NUM(path)]; 332 | if(c->bodyopened){ 333 | respond(r, "cannot write postbody after opening body"); 334 | break; 335 | } 336 | if(r->ifcall.offset >= 128*1024*1024){ /* >128MB is probably a mistake */ 337 | respond(r, "offset too large"); 338 | break; 339 | } 340 | m = r->ifcall.offset + r->ifcall.count; 341 | if(c->npostbody < m){ 342 | c->postbody = erealloc(c->postbody, m); 343 | memset(c->postbody+c->npostbody, 0, m-c->npostbody); 344 | c->npostbody = m; 345 | } 346 | memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count); 347 | r->ofcall.count = r->ifcall.count; 348 | respond(r, nil); 349 | break; 350 | } 351 | } 352 | 353 | static void 354 | fsopen(Req *r) 355 | { 356 | static int need[4] = { 4, 2, 6, 1 }; 357 | ulong path; 358 | int n; 359 | Client *c; 360 | Tab *t; 361 | 362 | /* 363 | * lib9p already handles the blatantly obvious. 364 | * we just have to enforce the permissions we have set. 365 | */ 366 | path = r->fid->qid.path; 367 | t = &tab[TYPE(path)]; 368 | n = need[r->ifcall.mode&3]; 369 | if((n&t->mode) != n){ 370 | respond(r, "permission denied"); 371 | return; 372 | } 373 | 374 | switch(TYPE(path)){ 375 | case Qcookies: 376 | cookieopen(r); 377 | break; 378 | 379 | case Qpostbody: 380 | c = client[NUM(path)]; 381 | c->havepostbody++; 382 | c->ref++; 383 | respond(r, nil); 384 | break; 385 | 386 | case Qbody: 387 | case Qbodyext: 388 | c = client[NUM(path)]; 389 | if(c->url == nil){ 390 | respond(r, "url is not yet set"); 391 | break; 392 | } 393 | c->bodyopened = 1; 394 | c->ref++; 395 | sendp(c->creq, r); 396 | break; 397 | 398 | case Qclone: 399 | n = newclient(0); 400 | path = PATH(Qctl, n); 401 | r->fid->qid.path = path; 402 | r->ofcall.qid.path = path; 403 | if(fsdebug) 404 | fprint(2, "open clone => path=%lux\n", path); 405 | t = &tab[Qctl]; 406 | /* fall through */ 407 | default: 408 | if(t-tab >= Qclient) 409 | client[NUM(path)]->ref++; 410 | respond(r, nil); 411 | break; 412 | } 413 | } 414 | 415 | static void 416 | fsdestroyfid(Fid *fid) 417 | { 418 | sendp(cclunk, fid); 419 | recvp(cclunkwait); 420 | } 421 | 422 | static void 423 | fsattach(Req *r) 424 | { 425 | if(r->ifcall.aname && r->ifcall.aname[0]){ 426 | respond(r, "invalid attach specifier"); 427 | return; 428 | } 429 | r->fid->qid.path = PATH(Qroot, 0); 430 | r->fid->qid.type = QTDIR; 431 | r->fid->qid.vers = 0; 432 | r->ofcall.qid = r->fid->qid; 433 | respond(r, nil); 434 | } 435 | 436 | static char* 437 | fswalk1(Fid *fid, char *name, Qid *qid) 438 | { 439 | int i, n; 440 | ulong path; 441 | char buf[32], *ext; 442 | 443 | path = fid->qid.path; 444 | if(!(fid->qid.type&QTDIR)) 445 | return "walk in non-directory"; 446 | 447 | if(strcmp(name, "..") == 0){ 448 | switch(TYPE(path)){ 449 | case Qparsed: 450 | qid->path = PATH(Qclient, NUM(path)); 451 | qid->type = tab[Qclient].mode>>24; 452 | return nil; 453 | case Qclient: 454 | case Qroot: 455 | qid->path = PATH(Qroot, 0); 456 | qid->type = tab[Qroot].mode>>24; 457 | return nil; 458 | default: 459 | return "bug in fswalk1"; 460 | } 461 | } 462 | 463 | i = TYPE(path)+1; 464 | for(; ipath = PATH(i, n); 470 | qid->type = tab[i].mode>>24; 471 | return nil; 472 | } 473 | break; 474 | } 475 | if(i==Qbodyext){ 476 | ext = client[NUM(path)]->ext; 477 | snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext); 478 | if(strcmp(buf, name) == 0){ 479 | qid->path = PATH(i, NUM(path)); 480 | qid->type = tab[i].mode>>24; 481 | return nil; 482 | } 483 | } 484 | else if(strcmp(name, tab[i].name) == 0){ 485 | qid->path = PATH(i, NUM(path)); 486 | qid->type = tab[i].mode>>24; 487 | return nil; 488 | } 489 | if(tab[i].mode&DMDIR) 490 | break; 491 | } 492 | return "directory entry not found"; 493 | } 494 | 495 | static void 496 | fsflush(Req *r) 497 | { 498 | Req *or; 499 | int t; 500 | Client *c; 501 | ulong path; 502 | 503 | or=r; 504 | while(or->ifcall.type==Tflush) 505 | or = or->oldreq; 506 | 507 | if(or->ifcall.type != Tread && or->ifcall.type != Topen) 508 | abort(); 509 | 510 | path = or->fid->qid.path; 511 | t = TYPE(path); 512 | if(t != Qbody && t != Qbodyext) 513 | abort(); 514 | 515 | c = client[NUM(path)]; 516 | sendp(c->creq, r); 517 | iointerrupt(c->io); 518 | } 519 | 520 | static void 521 | fsthread(void *notused) 522 | { 523 | ulong path; 524 | Alt a[3]; 525 | Fid *fid; 526 | Req *r; 527 | 528 | threadsetname("fsthread"); 529 | plumbstart(); 530 | 531 | a[0].op = CHANRCV; 532 | a[0].c = cclunk; 533 | a[0].v = &fid; 534 | a[1].op = CHANRCV; 535 | a[1].c = creq; 536 | a[1].v = &r; 537 | a[2].op = CHANEND; 538 | 539 | for(;;){ 540 | switch(alt(a)){ 541 | case 0: 542 | path = fid->qid.path; 543 | if(TYPE(path)==Qcookies) 544 | cookieclunk(fid); 545 | if(fid->omode != -1 && TYPE(path) >= Qclient) 546 | closeclient(client[NUM(path)]); 547 | sendp(cclunkwait, nil); 548 | break; 549 | case 1: 550 | switch(r->ifcall.type){ 551 | case Tattach: 552 | fsattach(r); 553 | break; 554 | case Topen: 555 | fsopen(r); 556 | break; 557 | case Tread: 558 | fsread(r); 559 | break; 560 | case Twrite: 561 | fswrite(r); 562 | break; 563 | case Tstat: 564 | fsstat(r); 565 | break; 566 | case Tflush: 567 | fsflush(r); 568 | break; 569 | default: 570 | respond(r, "bug in fsthread"); 571 | break; 572 | } 573 | sendp(creqwait, 0); 574 | break; 575 | } 576 | } 577 | } 578 | 579 | static void 580 | fssend(Req *r) 581 | { 582 | sendp(creq, r); 583 | recvp(creqwait); /* avoids need to deal with spurious flushes */ 584 | } 585 | 586 | void 587 | initfs(void) 588 | { 589 | time0 = time(0); 590 | creq = chancreate(sizeof(void*), 0); 591 | creqwait = chancreate(sizeof(void*), 0); 592 | cclunk = chancreate(sizeof(void*), 0); 593 | cclunkwait = chancreate(sizeof(void*), 0); 594 | proccreate(fsthread, nil, STACK); 595 | } 596 | 597 | void 598 | takedown(Srv *notused) 599 | { 600 | closecookies(); 601 | threadexitsall("done"); 602 | } 603 | 604 | Srv fs = 605 | { 606 | .attach= fssend, 607 | .destroyfid= fsdestroyfid, 608 | .walk1= fswalk1, 609 | .open= fssend, 610 | .read= fssend, 611 | .write= fssend, 612 | .stat= fssend, 613 | .flush= fssend, 614 | .end= takedown, 615 | }; 616 | 617 | -------------------------------------------------------------------------------- /abaco/page.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | static void pageload1(Page *, Url *, int); 18 | 19 | static 20 | void 21 | addchild(Page *p, Page *c) 22 | { 23 | Page *t; 24 | 25 | c->parent = p; 26 | c->w = p->w; 27 | c->b = p->b; 28 | c->col = p->col; 29 | c->row = p->row; 30 | if(p->child == nil) 31 | p->child = c; 32 | else{ 33 | for(t=p->child; t->next!=nil; t=t->next) 34 | ; 35 | t->next = c; 36 | } 37 | } 38 | 39 | static 40 | void 41 | loadchilds(Page *p, Kidinfo *k) 42 | { 43 | Runestr rs; 44 | Kidinfo *t; 45 | Page *c; 46 | 47 | addrefresh(p, "loading frames..."); 48 | p->kidinfo = k; 49 | for(t=k->kidinfos; t!=nil; t=t->next){ 50 | c = emalloc(sizeof(Page)); 51 | addchild(p, c); 52 | if(t->isframeset){ 53 | c->url = urldup(p->url); 54 | loadchilds(c, t); 55 | }else{ 56 | c->kidinfo = t; 57 | /* this check shouldn't be necessary, but... */ 58 | if(t->src){ 59 | rs.r = urlcombine(p->url->act.r, t->src); 60 | rs.nr = runestrlen(rs.r); 61 | pageload1(c, urlalloc(&rs, nil, HGet), FALSE); 62 | closerunestr(&rs); 63 | } 64 | } 65 | } 66 | } 67 | 68 | static struct { 69 | char *mime; 70 | char *filter; 71 | }filtertab[] = { 72 | {"image/gif", "gif -t9"}, 73 | {"image/jpeg", "jpg -t9"}, 74 | {"image/jpg", "jpg -t9"}, 75 | {"image/pjpeg", "jpg -t9"}, 76 | {"image/png", "png -t9"}, 77 | {"image/ppm", "ppm -t9"}, 78 | {nil, nil}, 79 | }; 80 | 81 | char * 82 | getfilter(Rune *r, int x, int y) 83 | { 84 | char buf[128]; 85 | int i; 86 | 87 | snprint(buf, sizeof(buf), "%S", r); 88 | for(i=0; filtertab[i].mime!=nil; i++) 89 | if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0) 90 | break; 91 | if(filtertab[i].filter == nil) 92 | return nil; 93 | if(x==0 && y==0) 94 | return smprint("%s", filtertab[i].filter); 95 | if(x!=0 && y!=0) 96 | return smprint("%s | resample -x %d -y %d", filtertab[i].filter, x, y); 97 | if(x != 0) 98 | return smprint("%s | resample -x %d", filtertab[i].filter, x); 99 | /* y != 0 */ 100 | return smprint("%s | resample -y %d", filtertab[i].filter, y); 101 | } 102 | 103 | static Cimage *cimages = nil; 104 | static QLock cimagelock; 105 | 106 | static 107 | void 108 | freecimage(Cimage *ci) 109 | { 110 | Cimage *ci1; 111 | 112 | qlock(&cimagelock); 113 | if(decref(&ci->ref) == 0){ 114 | if(ci->i) 115 | freeimage(ci->i); 116 | else if(ci->mi) 117 | freememimage(ci->mi); 118 | urlfree(ci->url); 119 | ci1 = cimages; 120 | if(ci1 == ci) 121 | cimages = ci->next; 122 | else{ 123 | while(ci1){ 124 | if(ci1->next == ci){ 125 | ci1->next = ci->next; 126 | break; 127 | } 128 | ci1 = ci1->next; 129 | } 130 | } 131 | free(ci); 132 | } 133 | qunlock(&cimagelock); 134 | } 135 | 136 | static 137 | void 138 | closeimages(Page *p) 139 | { 140 | int i; 141 | 142 | for(i=0; incimage; i++) 143 | freecimage(p->cimage[i]); 144 | free(p->cimage); 145 | p->cimage =nil; 146 | p->ncimage = 0; 147 | } 148 | 149 | static 150 | Cimage * 151 | loadimg(Rune *src, int x, int y) 152 | { 153 | Channel *sync; 154 | Cimage *ci; 155 | Runestr rs; 156 | Exec *e; 157 | char *filter; 158 | int fd, p[2], q[2]; 159 | 160 | ci = emalloc(sizeof(Cimage)); 161 | rs. r = src; 162 | rs.nr = runestrlen(rs.r); 163 | ci->url = urlalloc(&rs, nil, HGet); 164 | fd = urlopen(ci->url); 165 | if(fd < 0){ 166 | Err1: 167 | return ci; 168 | } 169 | filter = getfilter(ci->url->ctype.r, x, y); 170 | if(filter == nil){ 171 | werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r); 172 | Err2: 173 | close(fd); 174 | goto Err1; 175 | } 176 | 177 | if(pipe(q)<0) 178 | error("can't create pipe"); 179 | p[0] = fd; 180 | p[1] = open("/dev/null", ORDWR); 181 | sync = chancreate(sizeof(ulong), 0); 182 | if(sync == nil) 183 | error("can't create channel"); 184 | e = emalloc(sizeof(Exec)); 185 | e->p[0] = p[0]; 186 | e->p[1] = p[1]; 187 | e->q[0] = q[0]; 188 | e->q[1] = q[1]; 189 | e->cmd = filter; 190 | e->sync = sync; 191 | proccreate(execproc, e, STACK); 192 | recvul(sync); 193 | chanfree(sync); 194 | close(p[0]); 195 | close(q[1]); 196 | 197 | ci->mi = readmemimage(q[0]); 198 | close(q[0]); 199 | if(ci->mi == nil){ 200 | werrstr("can't read image"); 201 | goto Err2; 202 | } 203 | free(filter); 204 | return ci; 205 | } 206 | 207 | static 208 | Cimage * 209 | findimg(Rune *s) 210 | { 211 | Cimage *ci; 212 | 213 | qlock(&cimagelock); 214 | for(ci=cimages; ci!=nil; ci=ci->next) 215 | if(runestrcmp(ci->url->src.r, s) == 0) 216 | break; 217 | 218 | qunlock(&cimagelock); 219 | return ci; 220 | } 221 | 222 | void 223 | loadimages(Page *p) 224 | { 225 | Cimage *ci; 226 | Iimage *i; 227 | Rune *src; 228 | 229 | addrefresh(p, "loading images..."); 230 | reverseimages(&p->doc->images); 231 | for(i=p->doc->images; i!=nil; i=i->nextimage){ 232 | if(p->aborting) 233 | break; 234 | src = urlcombine(getbase(p), i->imsrc); 235 | ci = findimg(src); 236 | if(ci == nil){ 237 | ci = loadimg(src, i->imwidth, i->imheight); 238 | qlock(&cimagelock); 239 | ci->next = cimages; 240 | cimages = ci; 241 | qunlock(&cimagelock); 242 | } 243 | free(src); 244 | incref(&ci->ref); 245 | i->aux = ci; 246 | p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *)); 247 | p->cimage[p->ncimage-1] = ci; 248 | p->changed = TRUE; 249 | addrefresh(p, ""); 250 | } 251 | } 252 | 253 | static char *mimetab[] = { 254 | "text/html", 255 | "application/xhtml", 256 | nil, 257 | }; 258 | 259 | static 260 | void 261 | pageloadproc(void *v) 262 | { 263 | Page *p; 264 | char buf[BUFSIZE], *s; 265 | long n, l; 266 | int fd, i, ctype; 267 | 268 | threadsetname("pageloadproc"); 269 | 270 | p = v; 271 | addrefresh(p, "opening: %S...", p->url->src.r); 272 | fd = urlopen(p->url); 273 | if(fd < 0){ 274 | addrefresh(p, "%S: %r", p->url->src.r); 275 | Err: 276 | p->loading = FALSE; 277 | return; 278 | } 279 | if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */ 280 | goto Html; 281 | 282 | snprint(buf, sizeof(buf), "%S", p->url->ctype.r); 283 | for(i=0; mimetab[i]!=nil; i++) 284 | if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0) 285 | break; 286 | 287 | if(mimetab[i]){ 288 | Html: 289 | ctype = TextHtml; 290 | }else if(cistrncmp(buf, "text/", 5) == 0) 291 | ctype = TextPlain; 292 | else{ 293 | close(fd); 294 | addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r); 295 | goto Err; 296 | } 297 | addrefresh(p, "loading: %S...", p->url->src.r); 298 | s = nil; 299 | l = 0; 300 | while((n=read(fd, buf, sizeof(buf))) > 0){ 301 | if(p->aborting){ 302 | if(s){ 303 | free(s); 304 | s = nil; 305 | } 306 | break; 307 | } 308 | s = erealloc(s, l+n+1); 309 | memmove(s+l, buf, n); 310 | l += n; 311 | s[l] = '\0'; 312 | } 313 | close(fd); 314 | n = l; 315 | if(s) { 316 | s = convert(p->url->ctype, s, &n); 317 | p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc); 318 | free(s); 319 | fixtext(p); 320 | if(ctype==TextHtml && p->aborting==FALSE) 321 | p->changed = TRUE; 322 | addrefresh(p, ""); 323 | if(p->doc->doctitle){ 324 | p->title.r = erunestrdup(p->doc->doctitle); 325 | p->title.nr = runestrlen(p->title.r); 326 | } 327 | p->loading = XXX; 328 | if(p->doc->kidinfo) 329 | loadchilds(p, p->doc->kidinfo); 330 | else if(p->doc->images) 331 | loadimages(p); 332 | p->changed = TRUE; 333 | p->loading = FALSE; 334 | addrefresh(p, ""); 335 | } 336 | } 337 | 338 | static 339 | void 340 | pageload1(Page *p, Url *u, int dohist) 341 | { 342 | pageclose(p); 343 | p->loading = TRUE; 344 | p->url = u; 345 | if(dohist) 346 | winaddhist(p->w, p->url); 347 | proccreate(pageloadproc, p, STACK); 348 | } 349 | 350 | void pageload(Page *p, Url *u, int dohist) 351 | { 352 | if(p->parent == nil) 353 | textset(&p->w->url, u->src.r, u->src.nr); 354 | draw(p->b, p->all, display->white, nil, ZP); 355 | pageload1(p, u, dohist); 356 | } 357 | 358 | void 359 | pageget(Page *p, Runestr *src, Runestr *post, int m, int dohist) 360 | { 361 | pageload(p, urlalloc(src, post, m), dohist); 362 | } 363 | 364 | void 365 | pageclose(Page *p) 366 | { 367 | Page *c, *nc; 368 | 369 | if(p == selpage) 370 | selpage = nil; 371 | pageabort(p); 372 | closeimages(p); 373 | urlfree(p->url); 374 | p->url = nil; 375 | if(p->doc){ 376 | freedocinfo(p->doc); 377 | p->doc = nil; 378 | } 379 | layfree(p->lay); 380 | p->lay = nil; 381 | freeitems(p->items); 382 | p->items = nil; 383 | for(c=p->child; c!=nil; c=nc){ 384 | nc = c->next; 385 | pageclose(c); 386 | free(c); 387 | } 388 | p->child = nil; 389 | closerunestr(&p->title); 390 | p->pos = ZP; 391 | p->top = ZP; 392 | p->bot = ZP; 393 | p->loading = p->aborting = FALSE; 394 | } 395 | 396 | int 397 | pageabort(Page *p) 398 | { 399 | Page *c; 400 | 401 | for(c=p->child; c!=nil; c=c->next) 402 | pageabort(c); 403 | 404 | p->aborting = TRUE; 405 | while(p->loading) 406 | sleep(100); 407 | 408 | p->aborting = FALSE; 409 | return TRUE; 410 | } 411 | 412 | 413 | static Image *tmp; 414 | 415 | void 416 | tmpresize(void) 417 | { 418 | if(tmp) 419 | freeimage(tmp); 420 | 421 | tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1); 422 | } 423 | 424 | static 425 | void 426 | renderchilds(Page *p) 427 | { 428 | Rectangle r; 429 | Kidinfo *k; 430 | Page *c; 431 | int i, j, x, y, *w, *h; 432 | 433 | draw(p->b, p->all, display->white, nil, ZP); 434 | r = p->all; 435 | y = r.min.y; 436 | c = p->child; 437 | k = p->kidinfo; 438 | frdims(k->rows, k->nrows, Dy(r), &h); 439 | frdims(k->cols, k->ncols, Dx(r), &w); 440 | for(i=0; inrows; i++){ 441 | x = r.min.x; 442 | for(j=0; jncols; j++){ 443 | if(c->aborting) 444 | return; 445 | c->b = p->b; 446 | c->all = Rect(x,y,x+w[j],y+h[i]); 447 | c->w = p->w; 448 | pagerender(c); 449 | c = c->next; 450 | x += w[j]; 451 | } 452 | y += h[i]; 453 | } 454 | free(w); 455 | free(h); 456 | } 457 | 458 | static 459 | void 460 | pagerender1(Page *p) 461 | { 462 | Rectangle r; 463 | 464 | r = p->all; 465 | p->hscrollr = r; 466 | p->hscrollr.min.y = r.max.y-Scrollsize; 467 | p->vscrollr = r; 468 | p->vscrollr.max.x = r.min.x+Scrollsize; 469 | r.max.y -= Scrollsize; 470 | r.min.x += Scrollsize; 471 | p->r = r; 472 | p->vscrollr.max.y = r.max.y; 473 | p->hscrollr.min.x = r.min.x; 474 | laypage(p); 475 | pageredraw(p); 476 | } 477 | 478 | void 479 | pagerender(Page *p) 480 | { 481 | if(p->child && p->loading==FALSE) 482 | renderchilds(p); 483 | else if(p->doc) 484 | pagerender1(p); 485 | } 486 | 487 | void 488 | pageredraw(Page *p) 489 | { 490 | Rectangle r; 491 | 492 | r = p->lay->r; 493 | if(Dx(r)==0 || Dy(r)==0){ 494 | draw(p->b, p->r, display->white, nil, ZP); 495 | return; 496 | } 497 | if(tmp == nil) 498 | tmpresize(); 499 | 500 | p->selecting = FALSE; 501 | draw(tmp, tmp->r, getbg(p), nil, ZP); 502 | laydraw(p, tmp, p->lay); 503 | draw(p->b, p->r, tmp, nil, tmp->r.min); 504 | r = p->vscrollr; 505 | r.min.y = r.max.y; 506 | r.max.y += Scrollsize; 507 | draw(p->b, r, tagcols[HIGH], nil, ZP); 508 | draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP); 509 | pagescrldraw(p); 510 | } 511 | 512 | static 513 | void 514 | pageselect1(Page *p) /* when called, button 1 is down */ 515 | { 516 | Point mp, npos, opos; 517 | int b, scrled, x, y; 518 | 519 | b = mouse->buttons; 520 | mp = mousectl->m.xy; 521 | opos = getpt(p, mp); 522 | do{ 523 | x = y = 0; 524 | if(mp.x < p->r.min.x) 525 | x -= p->r.min.x-mp.x; 526 | else if(mp.x > p->r.max.x) 527 | x += mp.x-p->r.max.x; 528 | if(mp.y < p->r.min.y) 529 | y -= (p->r.min.y-mp.y)*Panspeed; 530 | else if(mp.y > p->r.max.y) 531 | y += (mp.y-p->r.max.y)*Panspeed; 532 | 533 | scrled = pagescrollxy(p, x, y); 534 | npos = getpt(p, mp); 535 | if(opos.y < npos.y){ 536 | p->top = opos; 537 | p->bot = npos; 538 | }else{ 539 | p->top = npos; 540 | p->bot = opos; 541 | } 542 | pageredraw(p); 543 | if(scrled == TRUE) 544 | scrsleep(100); 545 | else 546 | readmouse(mousectl); 547 | 548 | mp = mousectl->m.xy; 549 | }while(mousectl->m.buttons == b); 550 | } 551 | 552 | /* 553 | static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; 554 | static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; 555 | static Rune left2[] = { L'\'', L'"', L'`', 0 }; 556 | */ 557 | 558 | /* 559 | static 560 | Rune *left[] = { 561 | left1, 562 | left2, 563 | nil 564 | }; 565 | static 566 | Rune *right[] = { 567 | right1, 568 | left2, 569 | nil 570 | }; 571 | */ 572 | 573 | void 574 | pagedoubleclick(Page *p) 575 | { 576 | Point xy; 577 | Line *l; 578 | Box *b; 579 | 580 | xy = getpt(p, mouse->xy); 581 | l = linewhich(p->lay, xy); 582 | if(l==nil || l->hastext==FALSE) 583 | return; 584 | 585 | if(xy.xboxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */ 586 | p->top = l->boxes->r.min; 587 | if(l->next && !hasbrk(l->next->state)){ 588 | for(l=l->next; l->next!=nil; l=l->next) 589 | if(hasbrk(l->next->state)) 590 | break; 591 | } 592 | p->bot = l->lastbox->r.max;; 593 | }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){ /* end of line? */ 594 | p->bot = l->lastbox->r.max; 595 | if(!hasbrk(l->state) && l->prev!=nil){ 596 | for(l=l->prev; l->prev!=nil; l=l->prev) 597 | if(hasbrk(l->state)) 598 | break; 599 | } 600 | p->top = l->boxes->r.min; 601 | }else{ 602 | b = pttobox(l, xy); 603 | if(b!=nil && b->i->tag==Itexttag){ 604 | p->top = b->r.min; 605 | p->bot = b->r.max; 606 | } 607 | } 608 | p->top.y += 2; 609 | p->bot.y -= 2; 610 | pageredraw(p); 611 | } 612 | 613 | static uint clickmsec; 614 | 615 | void 616 | pageselect(Page *p) 617 | { 618 | int b, x, y; 619 | 620 | 621 | selpage = p; 622 | /* 623 | * To have double-clicking and chording, we double-click 624 | * immediately if it might make sense. 625 | */ 626 | b = mouse->buttons; 627 | if(mouse->msec-clickmsec<500){ 628 | pagedoubleclick(p); 629 | x = mouse->xy.x; 630 | y = mouse->xy.y; 631 | /* stay here until something interesting happens */ 632 | do 633 | readmouse(mousectl); 634 | while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); 635 | mouse->xy.x = x; /* in case we're calling pageselect1 */ 636 | mouse->xy.y = y; 637 | } 638 | if(mousectl->m.buttons == b) 639 | pageselect1(p); 640 | 641 | if(eqpt(p->top, p->bot)){ 642 | if(mouse->msec-clickmsec<500) 643 | pagedoubleclick(p); 644 | else 645 | clickmsec = mouse->msec; 646 | } 647 | while(mouse->buttons){ 648 | mouse->msec = 0; 649 | b = mouse->buttons; 650 | if(b & 2) /* snarf only */ 651 | cut(nil, nil, TRUE, FALSE, nil, 0); 652 | while(mouse->buttons == b) 653 | readmouse(mousectl); 654 | } 655 | } 656 | 657 | Page * 658 | pagewhich(Page *p, Point xy) 659 | { 660 | Page *c; 661 | 662 | if(p->child == nil) 663 | return p; 664 | 665 | for(c=p->child; c!=nil; c=c->next) 666 | if(ptinrect(xy, c->all)) 667 | return pagewhich(c, xy); 668 | 669 | return nil; 670 | } 671 | 672 | void 673 | pagemouse(Page *p, Point xy, int but) 674 | { 675 | Box *b; 676 | 677 | p = pagewhich(p, xy); 678 | if(p==nil || p->lay==nil) 679 | return; 680 | 681 | if(ptinrect(xy, p->vscrollr)){ 682 | pagescroll(p, but, FALSE); 683 | return; 684 | } 685 | if(ptinrect(xy, p->hscrollr)){ 686 | pagescroll(p, but, TRUE); 687 | return; 688 | } 689 | xy = getpt(p, xy); 690 | b = boxwhich(p->lay, xy); 691 | if(b && b->mouse) 692 | b->mouse(b, p, but); 693 | else if(but == 1) 694 | pageselect(p); 695 | } 696 | 697 | void 698 | pagetype(Page *p, Rune r, Point xy) 699 | { 700 | Box *b; 701 | int x, y; 702 | 703 | p = pagewhich(p, xy); 704 | if(p==nil || p->lay==nil) 705 | return; 706 | 707 | /* text field? */ 708 | xy = getpt(p, xy); 709 | b = boxwhich(p->lay, xy); 710 | if(b && b->key){ 711 | b->key(b, p, r); 712 | return; 713 | } 714 | 715 | x = 0; 716 | y = 0; 717 | switch(r){ 718 | case Kleft: 719 | x -= Dx(p->r)/2; 720 | break; 721 | case Kright: 722 | x += Dx(p->r)/2; 723 | break; 724 | case Kdown: 725 | case Kscrollonedown: 726 | y += Dy(p->r)/2; 727 | break; 728 | case Kpgdown: 729 | y += Dy(p->r); 730 | break; 731 | case Kup: 732 | case Kscrolloneup: 733 | y -= Dy(p->r)/2; 734 | break; 735 | case Kpgup: 736 | y -= Dy(p->r); 737 | break; 738 | case Khome: 739 | y -= Dy(p->lay->r); /* force p->pos.y = 0 */ 740 | break; 741 | case Kend: 742 | y = Dy(p->lay->r) - Dy(p->r); 743 | break; 744 | default: 745 | return; 746 | } 747 | if(pagescrollxy(p, x, y)) 748 | pageredraw(p); 749 | } 750 | 751 | void 752 | pagesnarf(Page *p) 753 | { 754 | Runestr rs; 755 | char *s; 756 | 757 | memset(&rs, 0, sizeof(Runestr)); 758 | laysnarf(p, p->lay, &rs); 759 | s = smprint("%.*S", rs.nr, rs.r); 760 | putsnarf(s); 761 | free(s); 762 | closerunestr(&rs); 763 | } 764 | 765 | void 766 | pagerefresh(Page *p) 767 | { 768 | Rune *q, *t; 769 | Runestr rs; 770 | int n; 771 | 772 | if(!p->doc || !p->doc->refresh) 773 | return; 774 | 775 | q = runestrchr(p->doc->refresh, L'='); 776 | if(q == nil) 777 | return; 778 | q++; 779 | if(!q) 780 | return; 781 | n = runestrlen(q); 782 | if(*q == L'\''){ 783 | q++; 784 | n -= 2; 785 | } 786 | if(n <= 0) 787 | return; 788 | t = runesmprint("%.*S", n, q); 789 | rs.r = urlcombine(getbase(p), t); 790 | rs.nr = runestrlen(rs.r); 791 | pageget(p, &rs, nil, HGet, FALSE); 792 | closerunestr(&rs); 793 | free(t); 794 | } 795 | -------------------------------------------------------------------------------- /abaco/text.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include <9pclient.h> 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "dat.h" 15 | #include "fns.h" 16 | 17 | Image *tagcols[NCOL]; 18 | Image *textcols[NCOL]; 19 | 20 | 21 | void 22 | textinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOL]) 23 | { 24 | t->all = r; 25 | t->scrollr = r; 26 | t->scrollr.max.x = r.min.x+Scrollsize; 27 | t->lastsr = ZR; 28 | r.min.x += Scrollsize+Scrollgap; 29 | t->rs.nr = 0; 30 | memmove(t->Frame.cols, cols, sizeof t->Frame.cols); 31 | textredraw(t, r, f, b); 32 | } 33 | 34 | void 35 | textredraw(Text *t, Rectangle r, Font *f, Image *b) 36 | { 37 | Rectangle r1; 38 | 39 | frinit(&t->Frame, r, f, b, t->Frame.cols); 40 | r1 = t->r; 41 | r1.min.x -= Scrollsize+Scrollgap; /* back fill to scroll bar */ 42 | draw(t->b, r1, t->cols[BACK], nil, ZP); 43 | t->maxtab = Maxtab*stringwidth(f, "0"); 44 | textfill(t); 45 | textsetselect(t, t->q0, t->q1); 46 | } 47 | 48 | int 49 | textresize(Text *t, Image *b, Rectangle r) 50 | { 51 | if(Dy(r) > 0) 52 | r.max.y -= Dy(r)%t->font->height; 53 | else 54 | r.max.y = r.min.y; 55 | 56 | t->all = r; 57 | t->scrollr = r; 58 | t->scrollr.max.x = r.min.x+Scrollsize; 59 | t->lastsr = ZR; 60 | r.min.x += Scrollsize+Scrollgap; 61 | frclear(&t->Frame, 0); 62 | textredraw(t, r, t->font, b); 63 | if(t->what == Textarea) 64 | textscrdraw(t); 65 | return r.max.y; 66 | } 67 | 68 | void 69 | textclose(Text *t) 70 | { 71 | closerunestr(&t->rs); 72 | frclear(&t->Frame, 1); 73 | } 74 | 75 | void 76 | textinsert(Text *t, uint q0, Rune *r, uint n) 77 | { 78 | if(n == 0) 79 | return; 80 | 81 | t->rs.r = runerealloc(t->rs.r, t->rs.nr+n); 82 | runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0); 83 | runemove(t->rs.r+q0, r, n); 84 | t->rs.nr += n; 85 | if(q0 < t->q1) 86 | t->q1 += n; 87 | if(q0 < t->q0) 88 | t->q0 += n; 89 | if(q0 < t->org) 90 | t->org += n; 91 | else if(q0 <= t->org+t->nchars) 92 | frinsert(&t->Frame, r, r+n, q0-t->org); 93 | } 94 | 95 | void 96 | textfill(Text *t) 97 | { 98 | Rune *rp; 99 | int i, n, m, nl; 100 | 101 | if(t->lastlinefull) 102 | return; 103 | rp = runemalloc(BUFSIZE*8); 104 | do{ 105 | n = t->rs.nr-(t->org+t->nchars); 106 | if(n == 0) 107 | break; 108 | if(n > 2000) /* educated guess at reasonable amount */ 109 | n = 2000; 110 | runemove(rp, t->rs.r+(t->org+t->nchars), n); 111 | /* 112 | * it's expensive to frinsert more than we need, so 113 | * count newlines. 114 | */ 115 | nl = t->maxlines-t->nlines; 116 | m = 0; 117 | for(i=0; i= nl) 121 | break; 122 | } 123 | } 124 | frinsert(&t->Frame, rp, rp+i, t->nchars); 125 | }while(t->lastlinefull == FALSE); 126 | free(rp); 127 | } 128 | 129 | void 130 | textdelete(Text *t, uint q0, uint q1) 131 | { 132 | uint n, p0, p1; 133 | 134 | n = q1-q0; 135 | if(n == 0) 136 | return; 137 | 138 | runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1); 139 | t->rs.nr -= n; 140 | if(q0 < t->q0) 141 | t->q0 -= min(n, t->q0-q0); 142 | if(q0 < t->q1) 143 | t->q1 -= min(n, t->q1-q0); 144 | if(q1 <= t->org) 145 | t->org -= n; 146 | else if(q0 < t->org+t->nchars){ 147 | p1 = q1 - t->org; 148 | if(p1 > t->nchars) 149 | p1 = t->nchars; 150 | if(q0 < t->org){ 151 | t->org = q0; 152 | p0 = 0; 153 | }else 154 | p0 = q0 - t->org; 155 | frdelete(&t->Frame, p0, p1); 156 | textfill(t); 157 | } 158 | t->rs.r[t->rs.nr] = L'\0'; 159 | } 160 | 161 | int 162 | textbswidth(Text *t, Rune c) 163 | { 164 | uint q, eq; 165 | Rune r; 166 | int skipping; 167 | 168 | /* there is known to be at least one character to erase */ 169 | if(c == 0x08) /* ^H: erase character */ 170 | return 1; 171 | q = t->q0; 172 | skipping = TRUE; 173 | while(q > 0){ 174 | r = t->rs.r[q-1]; 175 | if(r == '\n'){ /* eat at most one more character */ 176 | if(q == t->q0) /* eat the newline */ 177 | --q; 178 | break; 179 | } 180 | if(c == 0x17){ 181 | eq = isalnum(r); 182 | if(eq && skipping) /* found one; stop skipping */ 183 | skipping = FALSE; 184 | else if(!eq && !skipping) 185 | break; 186 | } 187 | --q; 188 | } 189 | return t->q0-q; 190 | } 191 | 192 | void 193 | texttype(Text *t, Rune r) 194 | { 195 | uint q0, q1; 196 | int nb, n; 197 | int nr; 198 | Rune *rp; 199 | 200 | if(t->what!=Textarea && r=='\n'){ 201 | if(t->what==Urltag) 202 | get(t, t, XXX, XXX, nil, 0); 203 | return; 204 | } 205 | if(t->what==Tag && (r==Kscrollonedown || r==Kscrolloneup)) 206 | return; 207 | 208 | nr = 1; 209 | rp = &r; 210 | switch(r){ 211 | case Kleft: 212 | if(t->q0 > 0) 213 | textshow(t, t->q0-1, t->q0-1, TRUE); 214 | return; 215 | case Kright: 216 | if(t->q1 < t->rs.nr) 217 | textshow(t, t->q1+1, t->q1+1, TRUE); 218 | return; 219 | case Kdown: 220 | n = t->maxlines/3; 221 | goto case_Down; 222 | case Kscrollonedown: 223 | n = mousescrollsize(t->maxlines); 224 | if(n <= 0) 225 | n = 1; 226 | goto case_Down; 227 | case Kpgdown: 228 | n = 2*t->maxlines/3; 229 | case_Down: 230 | q0 = t->org+frcharofpt(&t->Frame, Pt(t->r.min.x, t->r.min.y+n*t->font->height)); 231 | textsetorigin(t, q0, TRUE); 232 | return; 233 | case Kup: 234 | n = t->maxlines/3; 235 | goto case_Up; 236 | case Kscrolloneup: 237 | n = mousescrollsize(t->maxlines); 238 | goto case_Up; 239 | case Kpgup: 240 | n = 2*t->maxlines/3; 241 | case_Up: 242 | q0 = textbacknl(t, t->org, n); 243 | textsetorigin(t, q0, TRUE); 244 | return; 245 | case Khome: 246 | textshow(t, 0, 0, FALSE); 247 | return; 248 | case Kend: 249 | textshow(t, t->rs.nr, t->rs.nr, FALSE); 250 | return; 251 | case 0x01: /* ^A: beginning of line */ 252 | /* go to where ^U would erase, if not already at BOL */ 253 | nb = 0; 254 | if(t->q0>0 && t->rs.r[t->q0-1]!='\n') 255 | nb = textbswidth(t, 0x15); 256 | textshow(t, t->q0-nb, t->q0-nb, TRUE); 257 | return; 258 | case 0x05: /* ^E: end of line */ 259 | q0 = t->q0; 260 | while(q0rs.nr && t->rs.r[q0]!='\n') 261 | q0++; 262 | textshow(t, q0, q0, TRUE); 263 | return; 264 | } 265 | if(t->q1 > t->q0) 266 | cut(t, t, TRUE, TRUE, nil, 0); 267 | 268 | textshow(t, t->q0, t->q0, TRUE); 269 | switch(r){ 270 | case 0x08: /* ^H: erase character */ 271 | case 0x15: /* ^U: erase line */ 272 | case 0x17: /* ^W: erase word */ 273 | if(t->q0 == 0) /* nothing to erase */ 274 | return; 275 | nb = textbswidth(t, r); 276 | q1 = t->q0; 277 | q0 = q1-nb; 278 | /* if selection is at beginning of window, avoid deleting invisible text */ 279 | if(q0 < t->org){ 280 | q0 = t->org; 281 | nb = q1-q0; 282 | } 283 | if(nb > 0){ 284 | textdelete(t, q0, q0+nb); 285 | textsetselect(t, q0, q0); 286 | } 287 | return; 288 | } 289 | /* otherwise ordinary character; just insert */ 290 | textinsert(t, t->q0, &r, 1); 291 | if(rp != &r) 292 | free(rp); 293 | textsetselect(t, t->q0+nr, t->q0+nr); 294 | if(t->what == Textarea) 295 | textscrdraw(t); 296 | } 297 | 298 | static Text *clicktext; 299 | static uint clickmsec; 300 | static Text *selecttext; 301 | static uint selectq; 302 | 303 | /* 304 | * called from frame library 305 | */ 306 | void 307 | framescroll(Frame *f, int dl) 308 | { 309 | if(f != &selecttext->Frame) 310 | error("frameselect not right frame"); 311 | textframescroll(selecttext, dl); 312 | } 313 | 314 | void 315 | textframescroll(Text *t, int dl) 316 | { 317 | uint q0; 318 | 319 | if(dl == 0){ 320 | scrsleep(100); 321 | return; 322 | } 323 | if(dl < 0){ 324 | q0 = textbacknl(t, t->org, -dl); 325 | if(selectq > t->org+t->p0) 326 | textsetselect(t, t->org+t->p0, selectq); 327 | else 328 | textsetselect(t, selectq, t->org+t->p0); 329 | }else{ 330 | if(t->org+t->nchars == t->rs.nr) 331 | return; 332 | q0 = t->org+frcharofpt(&t->Frame, Pt(t->r.min.x, t->r.min.y+dl*t->font->height)); 333 | if(selectq > t->org+t->p1) 334 | textsetselect(t, t->org+t->p1, selectq); 335 | else 336 | textsetselect(t, selectq, t->org+t->p1); 337 | } 338 | textsetorigin(t, q0, TRUE); 339 | } 340 | 341 | void 342 | textselect(Text *t) 343 | { 344 | uint q0, q1; 345 | int b, x, y; 346 | int state; 347 | 348 | selecttext = t; 349 | /* 350 | * To have double-clicking and chording, we double-click 351 | * immediately if it might make sense. 352 | */ 353 | b = mouse->buttons; 354 | q0 = t->q0; 355 | q1 = t->q1; 356 | selectq = t->org+frcharofpt(&t->Frame, mouse->xy); 357 | if(clicktext==t && mouse->msec-clickmsec<500) 358 | if(q0==q1 && selectq==q0){ 359 | textdoubleclick(t, &q0, &q1); 360 | textsetselect(t, q0, q1); 361 | flushimage(display, 1); 362 | x = mouse->xy.x; 363 | y = mouse->xy.y; 364 | /* stay here until something interesting happens */ 365 | do 366 | readmouse(mousectl); 367 | while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); 368 | mouse->xy.x = x; /* in case we're calling frselect */ 369 | mouse->xy.y = y; 370 | q0 = t->q0; /* may have changed */ 371 | q1 = t->q1; 372 | selectq = q0; 373 | } 374 | if(mouse->buttons == b){ 375 | t->Frame.scroll = framescroll; 376 | frselect(&t->Frame, mousectl); 377 | /* horrible botch: while asleep, may have lost selection altogether */ 378 | if(selectq > t->rs.nr) 379 | selectq = t->org + t->p0; 380 | t->Frame.scroll = nil; 381 | if(selectq < t->org) 382 | q0 = selectq; 383 | else 384 | q0 = t->org + t->p0; 385 | if(selectq > t->org+t->nchars) 386 | q1 = selectq; 387 | else 388 | q1 = t->org+t->p1; 389 | } 390 | if(q0 == q1){ 391 | if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){ 392 | textdoubleclick(t, &q0, &q1); 393 | clicktext = nil; 394 | }else{ 395 | clicktext = t; 396 | clickmsec = mouse->msec; 397 | } 398 | }else 399 | clicktext = nil; 400 | textsetselect(t, q0, q1); 401 | flushimage(display, 1); 402 | state = 0; /* undo when possible; +1 for cut, -1 for paste */ 403 | while(mouse->buttons){ 404 | mouse->msec = 0; 405 | b = mouse->buttons; 406 | if((b&1) && (b&6)){ 407 | if(b & 2){ 408 | if(state==-1 && t->what==Textarea){ 409 | textsetselect(t, q0, t->q0); 410 | state = 0; 411 | }else if(state != 1){ 412 | cut(t, t, TRUE, TRUE, nil, 0); 413 | state = 1; 414 | } 415 | }else{ 416 | if(state==1 && t->what==Textarea){ 417 | textsetselect(t, q0, t->q1); 418 | state = 0; 419 | }else if(state != -1){ 420 | paste(t, t, TRUE, FALSE, nil, 0); 421 | state = -1; 422 | } 423 | } 424 | textscrdraw(t); 425 | } 426 | flushimage(display, 1); 427 | while(mouse->buttons == b) 428 | readmouse(mousectl); 429 | clicktext = nil; 430 | } 431 | } 432 | 433 | void 434 | textshow(Text *t, uint q0, uint q1, int doselect) 435 | { 436 | int qe; 437 | int nl; 438 | uint q; 439 | 440 | if(t->what != Textarea){ 441 | if(doselect) 442 | textsetselect(t, q0, q1); 443 | return; 444 | } 445 | if(doselect) 446 | textsetselect(t, q0, q1); 447 | qe = t->org+t->nchars; 448 | if(t->org<=q0 && (q0rs.nr))) 449 | textscrdraw(t); 450 | else{ 451 | nl = t->maxlines/4; 452 | q = textbacknl(t, q0, nl); 453 | /* avoid going backwards if trying to go forwards - long lines! */ 454 | if(!(q0>t->org && qorg)) 455 | textsetorigin(t, q, TRUE); 456 | while(q0 > t->org+t->nchars) 457 | textsetorigin(t, t->org+1, FALSE); 458 | } 459 | } 460 | 461 | static 462 | int 463 | region(int a, int b) 464 | { 465 | if(a < b) 466 | return -1; 467 | if(a == b) 468 | return 0; 469 | return 1; 470 | } 471 | 472 | void 473 | selrestore(Frame *f, Point pt0, uint p0, uint p1) 474 | { 475 | if(p1<=f->p0 || p0>=f->p1){ 476 | /* no overlap */ 477 | frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]); 478 | return; 479 | } 480 | if(p0>=f->p0 && p1<=f->p1){ 481 | /* entirely inside */ 482 | frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); 483 | return; 484 | } 485 | 486 | /* they now are known to overlap */ 487 | 488 | /* before selection */ 489 | if(p0 < f->p0){ 490 | frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]); 491 | p0 = f->p0; 492 | pt0 = frptofchar(f, p0); 493 | } 494 | /* after selection */ 495 | if(p1 > f->p1){ 496 | frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]); 497 | p1 = f->p1; 498 | } 499 | /* inside selection */ 500 | frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); 501 | } 502 | 503 | void 504 | textsetselect(Text *t, uint q0, uint q1) 505 | { 506 | int p0, p1; 507 | 508 | /* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */ 509 | t->q0 = q0; 510 | t->q1 = q1; 511 | /* compute desired p0,p1 from q0,q1 */ 512 | p0 = q0-t->org; 513 | p1 = q1-t->org; 514 | if(p0 < 0) 515 | p0 = 0; 516 | if(p1 < 0) 517 | p1 = 0; 518 | if(p0 > t->nchars) 519 | p0 = t->nchars; 520 | if(p1 > t->nchars) 521 | p1 = t->nchars; 522 | if(p0==t->p0 && p1==t->p1) 523 | return; 524 | /* screen disagrees with desired selection */ 525 | if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){ 526 | /* no overlap or too easy to bother trying */ 527 | frdrawsel(&t->Frame, frptofchar(&t->Frame, t->p0), t->p0, t->p1, 0); 528 | frdrawsel(&t->Frame, frptofchar(&t->Frame, p0), p0, p1, 1); 529 | goto Return; 530 | } 531 | /* overlap; avoid unnecessary painting */ 532 | if(p0 < t->p0){ 533 | /* extend selection backwards */ 534 | frdrawsel(&t->Frame, frptofchar(&t->Frame, p0), p0, t->p0, 1); 535 | }else if(p0 > t->p0){ 536 | /* trim first part of selection */ 537 | frdrawsel(&t->Frame, frptofchar(&t->Frame, t->p0), t->p0, p0, 0); 538 | } 539 | if(p1 > t->p1){ 540 | /* extend selection forwards */ 541 | frdrawsel(&t->Frame, frptofchar(&t->Frame, t->p1), t->p1, p1, 1); 542 | }else if(p1 < t->p1){ 543 | /* trim last part of selection */ 544 | frdrawsel(&t->Frame, frptofchar(&t->Frame, p1), p1, t->p1, 0); 545 | } 546 | 547 | Return: 548 | t->p0 = p0; 549 | t->p1 = p1; 550 | } 551 | 552 | 553 | /* 554 | * Release the button in less than DELAY ms and it's considered a null selection 555 | * if the mouse hardly moved, regardless of whether it crossed a char boundary. 556 | */ 557 | enum { 558 | DELAY = 2, 559 | MINMOVE = 4, 560 | }; 561 | 562 | uint 563 | xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */ 564 | { 565 | uint p0, p1, q, tmp; 566 | ulong msec; 567 | Point mp, pt0, pt1, qt; 568 | int reg, b; 569 | 570 | mp = mc->m.xy; 571 | b = mc->m.buttons; 572 | msec = mc->m.msec; 573 | 574 | /* remove tick */ 575 | if(f->p0 == f->p1) 576 | frtick(f, frptofchar(f, f->p0), 0); 577 | p0 = p1 = frcharofpt(f, mp); 578 | pt0 = frptofchar(f, p0); 579 | pt1 = frptofchar(f, p1); 580 | reg = 0; 581 | frtick(f, pt0, 1); 582 | do{ 583 | q = frcharofpt(f, mc->m.xy); 584 | if(p1 != q){ 585 | if(p0 == p1) 586 | frtick(f, pt0, 0); 587 | if(reg != region(q, p0)){ /* crossed starting point; reset */ 588 | if(reg > 0) 589 | selrestore(f, pt0, p0, p1); 590 | else if(reg < 0) 591 | selrestore(f, pt1, p1, p0); 592 | p1 = p0; 593 | pt1 = pt0; 594 | reg = region(q, p0); 595 | if(reg == 0) 596 | frdrawsel0(f, pt0, p0, p1, col, display->white); 597 | } 598 | qt = frptofchar(f, q); 599 | if(reg > 0){ 600 | if(q > p1) 601 | frdrawsel0(f, pt1, p1, q, col, display->white); 602 | 603 | else if(q < p1) 604 | selrestore(f, qt, q, p1); 605 | }else if(reg < 0){ 606 | if(q > p1) 607 | selrestore(f, pt1, p1, q); 608 | else 609 | frdrawsel0(f, qt, q, p1, col, display->white); 610 | } 611 | p1 = q; 612 | pt1 = qt; 613 | } 614 | if(p0 == p1) 615 | frtick(f, pt0, 1); 616 | flushimage(f->display, 1); 617 | readmouse(mc); 618 | }while(mc->m.buttons == b); 619 | if(mc->m.msec-msec < DELAY && p0!=p1 620 | && abs(mp.x-mc->m.xy.x)m.xy.y) 0) 623 | selrestore(f, pt0, p0, p1); 624 | else if(reg < 0) 625 | selrestore(f, pt1, p1, p0); 626 | p1 = p0; 627 | } 628 | if(p1 < p0){ 629 | tmp = p0; 630 | p0 = p1; 631 | p1 = tmp; 632 | } 633 | pt0 = frptofchar(f, p0); 634 | if(p0 == p1) 635 | frtick(f, pt0, 0); 636 | selrestore(f, pt0, p0, p1); 637 | /* restore tick */ 638 | if(f->p0 == f->p1) 639 | frtick(f, frptofchar(f, f->p0), 1); 640 | flushimage(f->display, 1); 641 | *p1p = p1; 642 | return p0; 643 | } 644 | 645 | int 646 | textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask) 647 | { 648 | uint p0, p1; 649 | int buts; 650 | 651 | p0 = xselect(&t->Frame, mousectl, high, &p1); 652 | buts = mousectl->m.buttons; 653 | if((buts & mask) == 0){ 654 | *q0 = p0+t->org; 655 | *q1 = p1+t->org; 656 | } 657 | 658 | while(mousectl->m.buttons) 659 | readmouse(mousectl); 660 | return buts; 661 | } 662 | 663 | int 664 | textselect2(Text *t, uint *q0, uint *q1, Text **tp) 665 | { 666 | int buts; 667 | 668 | *tp = nil; 669 | buts = textselect23(t, q0, q1, but2col, 4); 670 | if(buts & 4) 671 | return 0; 672 | if(buts & 1){ /* pick up argument */ 673 | *tp = argtext; 674 | return 1; 675 | } 676 | return 1; 677 | } 678 | 679 | int 680 | textselect3(Text *t, uint *q0, uint *q1) 681 | { 682 | int h; 683 | 684 | h = (textselect23(t, q0, q1, but3col, 1|2) == 0); 685 | return h; 686 | } 687 | 688 | static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; 689 | static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; 690 | static Rune left2[] = { L'\n', 0 }; 691 | static Rune left3[] = { L'\'', L'"', L'`', 0 }; 692 | 693 | static 694 | Rune *left[] = { 695 | left1, 696 | left2, 697 | left3, 698 | nil 699 | }; 700 | static 701 | Rune *right[] = { 702 | right1, 703 | left2, 704 | left3, 705 | nil 706 | }; 707 | 708 | void 709 | textdoubleclick(Text *t, uint *q0, uint *q1) 710 | { 711 | int c, i; 712 | Rune *r, *l, *p; 713 | uint q; 714 | 715 | if(t->rs.nr == 0) 716 | return; 717 | 718 | for(i=0; left[i]!=nil; i++){ 719 | q = *q0; 720 | l = left[i]; 721 | r = right[i]; 722 | /* try matching character to left, looking right */ 723 | if(q == 0) 724 | c = '\n'; 725 | else 726 | c = t->rs.r[q-1]; 727 | p = runestrchr(l, c); 728 | if(p != nil){ 729 | if(textclickmatch(t, c, t->rs.r[p-l], 1, &q)) 730 | *q1 = q-(c!='\n'); 731 | return; 732 | } 733 | /* try matching character to right, looking left */ 734 | if(q == t->rs.nr) 735 | c = '\n'; 736 | else 737 | c = t->rs.r[q]; 738 | p = runestrchr(r, c); 739 | if(p != nil){ 740 | if(textclickmatch(t, c, l[p-r], -1, &q)){ 741 | *q1 = *q0+(*q0rs.nr && c=='\n'); 742 | *q0 = q; 743 | if(c!='\n' || q!=0 || t->rs.r[0]=='\n') 744 | (*q0)++; 745 | } 746 | return; 747 | } 748 | } 749 | /* try filling out word to right */ 750 | while(*q1rs.nr && isalnum(t->rs.r[*q1])) 751 | (*q1)++; 752 | /* try filling out word to left */ 753 | while(*q0>0 && isalnum(t->rs.r[*q0-1])) 754 | (*q0)--; 755 | } 756 | 757 | int 758 | textclickmatch(Text *t, int cl, int cr, int dir, uint *q) 759 | { 760 | Rune c; 761 | int nest; 762 | 763 | nest = 1; 764 | for(;;){ 765 | if(dir > 0){ 766 | if(*q == t->rs.nr) 767 | break; 768 | c = t->rs.r[*q]; 769 | (*q)++; 770 | }else{ 771 | if(*q == 0) 772 | break; 773 | (*q)--; 774 | c = t->rs.r[*q]; 775 | } 776 | if(c == cr){ 777 | if(--nest==0) 778 | return 1; 779 | }else if(c == cl) 780 | nest++; 781 | } 782 | return cl=='\n' && nest==1; 783 | } 784 | 785 | uint 786 | textbacknl(Text *t, uint p, uint n) 787 | { 788 | int i, j; 789 | 790 | /* look for start of this line if n==0 */ 791 | if(n==0 && p>0 && t->rs.r[p-1]!='\n') 792 | n = 1; 793 | i = n; 794 | while(i-->0 && p>0){ 795 | --p; /* it's at a newline now; back over it */ 796 | if(p == 0) 797 | break; 798 | /* at 128 chars, call it a line anyway */ 799 | for(j=128; --j>0 && p>0; p--) 800 | if(t->rs.r[p-1]=='\n') 801 | break; 802 | } 803 | return p; 804 | } 805 | 806 | void 807 | textsetorigin(Text *t, uint org, int exact) 808 | { 809 | int i, a, fixup; 810 | Rune *r; 811 | uint n; 812 | 813 | if(org>0 && !exact){ 814 | /* org is an estimate of the char posn; find a newline */ 815 | /* don't try harder than 256 chars */ 816 | for(i=0; i<256 && orgrs.nr; i++){ 817 | if(t->rs.r[org] == '\n'){ 818 | org++; 819 | break; 820 | } 821 | org++; 822 | } 823 | } 824 | a = org-t->org; 825 | fixup = 0; 826 | if(a>=0 && anchars){ 827 | frdelete(&t->Frame, 0, a); 828 | fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */ 829 | }else if(a<0 && -anchars){ 830 | n = t->org - org; 831 | r = runemalloc(n); 832 | runemove(r, t->rs.r+org, n); 833 | frinsert(&t->Frame, r, r+n, 0); 834 | free(r); 835 | }else 836 | frdelete(&t->Frame, 0, t->nchars); 837 | t->org = org; 838 | textfill(t); 839 | textscrdraw(t); 840 | textsetselect(t, t->q0, t->q1); 841 | if(fixup && t->p1 > t->p0) 842 | frdrawsel(&t->Frame, frptofchar(&t->Frame, t->p1-1), t->p1-1, t->p1, 1); 843 | } 844 | 845 | void 846 | textset(Text *t, Rune*r, int n) 847 | { 848 | textdelete(t, 0, t->rs.nr); 849 | textinsert(t, 0, r, n); 850 | textsetselect(t, t->q0, t->q1); 851 | } 852 | 853 | void 854 | textmouse(Text *t, Point xy, int but) 855 | { 856 | Text *argt; 857 | uint q0, q1; 858 | 859 | if(ptinrect(xy, t->scrollr)){ 860 | if(t->what == Columntag) 861 | rowdragcol(&row, t->col, but); 862 | else if(t->what == Tag) 863 | coldragwin(t->col, t->w, but); 864 | else if(t->what == Textarea) 865 | textscroll(t, but); 866 | if(t->col) 867 | activecol = t->col; 868 | return; 869 | } 870 | if(but == 1){ 871 | selpage = nil; 872 | textselect(t); 873 | argtext = t; 874 | seltext = t; 875 | }else if(but == 2){ 876 | if(textselect2(t, &q0, &q1, &argt)) 877 | execute(t, q0, q1, argt); 878 | }else if(but == 3){ 879 | if(textselect3(t, &q0, &q1)) 880 | look3(t, q0, q1); 881 | } 882 | } 883 | --------------------------------------------------------------------------------