gophra

gopher client for plan9
git clone git://nsmpr.xyz/gophra.git
Log | Files | Refs

commit 6d6e7d80139ccfe54742096df8fa7f51f445c761
Author: glenda <glenda@9front.local>
Date:   Tue, 28 Apr 2020 07:28:59 +0000

initial commit

Diffstat:
ATODO | 5+++++
Agophra.c | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amkfile | 9+++++++++
3 files changed, 462 insertions(+), 0 deletions(-)

diff --git a/TODO b/TODO @@ -0,0 +1,5 @@ +* README +* man page +* git +* history +* image view diff --git a/gophra.c b/gophra.c @@ -0,0 +1,447 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <cursor.h> +#include <keyboard.h> +#include <frame.h> + +enum { + DBorder = DBlue, + DWBG = DBlack, + DWFG = 0xCCCCCCFF, + DWFGL = DWhite, + DStatus = DYellow, +}; + +typedef struct Text Text; +struct Text { + char *data; + long size; +} text; + +Image *Iborder, *Iwbg, *Iwfg, *Iwfgl, *Istatus; + +Rectangle rstatus, rw; + +char status[256], path[256], addr[256], type; + +char history[64][256]; +int hc; + +char *tmpfile = "/tmp/gophra.tmp"; + +long scroll; + +void usage(void); +void back(void); +void clear(void); +void calcrects(void); +void drawborder(void); +void drawstatus(void); +void drawtext(void); +void drawmenu(void); +int extracturl(char*); +char* getline(long); +void handlelink(void); +int loadtext(char*, char*); +void runhold(void*); +void runpage(void*); +int save(void); + +void +threadmain(int argc, char **argv) +{ + Mousectl *mc; + Keyboardctl *kc; + Mouse mv; + Rune kv; + int rv[2]; + + long sline; + char *s; + int mpress; + + ARGBEGIN{ + default: + usage(); + break; + }ARGEND; + + if(initdraw(0, 0, "gophra") < 0) + sysfatal("inidraw failed: %r"); + + Iborder = allocimage(display, Rect(0,0,1,1), + RGB24, 1, DBorder); + Iwbg = allocimage(display, Rect(0,0,1,1), + RGB24, 1, DWBG); + Iwfg = allocimage(display, Rect(0,0,1,1), + RGB24, 1, DWFG); + Iwfgl = allocimage(display, Rect(0,0,1,1), + RGB24, 1, DWFGL); + Istatus = allocimage(display, Rect(0,0,1,1), + RGB24, 1, DStatus); + + mpress = 0; + scroll = 0; + hc = 0; + snprint(status, 255, "gophra!"); + + if (argv[0] != 0) loadtext(argv[0], ""); + + calcrects(); + drawborder(); + drawstatus(); + drawmenu(); + flushimage(display, 1); + + if((mc = initmouse(0, screen)) == nil) + sysfatal("initmouse failed: %r"); + if((kc = initkeyboard(0)) == nil) + sysfatal("initkeyboard failed: %r"); + + Alt alts[4] = { + {kc->c, &kv, CHANRCV}, + {mc->c, &mv, CHANRCV}, + {mc->resizec, rv, CHANRCV}, + {0, 0, CHANEND}, + }; + + for (;;) { + switch (alt(alts)) { + case 0: /* keyboard */ + if (kv == 0x7f) threadexitsall(nil); + if (kv == 'g') { + char buf[256]; + buf[0] = 0; + if (enter("goto:", buf, 255, mc, kc, 0) >=0) + if (loadtext(buf, "") == 0){ + drawmenu(); + flushimage(display, 1); + } + } + break; + case 1: /* mouse */ + if (ptinrect(mv.xy, rw) != 1) break; + if (mv.buttons == 0) mpress = 0; + if (mv.buttons == 8) { + mpress = 1; + scroll -= 1 + (mv.xy.y-rw.min.y)/font->height; + if (scroll < 0) scroll = 0; + drawmenu(); + flushimage(display, 1); + } + if (mv.buttons == 16) { + mpress = 1; + scroll += 1 + (mv.xy.y-rw.min.y)/font->height; + drawmenu(); + flushimage(display, 1); + } + if (mpress != 0) break; + if (mv.buttons == 1) { + mpress = 1; + sline = (mv.xy.y - rw.min.y) / font->height + + scroll; + s = getline(sline); + extracturl(s); + snprint(status, 255, "%s/%c%s", addr, type, path); + drawstatus(); + flushimage(display, 1); + if (s != 0) free(s); + } + if (mv.buttons == 2) { + mpress = 1; + sline = (mv.xy.y - rw.min.y) / font->height + + scroll; + s = getline(sline); + + handlelink(); + if (s != 0) free(s); + } + break; + case 2: /* resize */ + if(getwindow(display, Refnone) < 0) + sysfatal("resize failed: %r"); + calcrects(); + drawborder(); + drawstatus(); + drawmenu(); + flushimage(display, 1); + break; + } + } +} + +void +usage(void) +{ + fprint(2, "usage: %s", argv0); + exits("usage"); +} + +void +back(void) +{ + if (hc == 0) return; + hc--; + // todo: extract addr and path from history + //loadtext + drawmenu(); + flushimage(display, 1); +} + +void +calcrects(void) +{ + rw = Rect( + screen->r.min.x + 4, + screen->r.min.y + 8 + font->height, + screen->r.max.x - 4, + screen->r.max.y - 4); + rstatus = Rect( + screen->r.min.x + 4, + screen->r.min.y + 4, + screen->r.max.x - 4, + screen->r.min.y + 4 + font->height); +} + +void +drawborder(void) +{ + draw(screen, screen->r, Iborder, 0, ZP); + draw(screen, rw, Iwbg, 0, ZP); +} + +void +drawstatus(void) +{ + draw(screen, rstatus, Iborder, 0, ZP); + string(screen, rstatus.min, Istatus, ZP, font, status); +} + +void +drawtext(void) +{ + char sbuf[1024], *tp, *sp; + long i; + int fflag; + Point dp; + fflag = 0; + dp = rw.min; + tp = text.data; + sp = sbuf; + draw(screen, rw, Iwbg, 0, ZP); + if (sp == nil) return; + while (tp < text.data + text.size) { + *sp = *tp; + sp++; + tp++; + if (*tp == '\n'){ + *(sp-1) = 0; + fflag = 1; + } + if (dp.x + stringwidth(font, sbuf) > rw.max.x) { + *(sp-1) = 0; + tp--; + fflag = 1; + } + if (fflag) { + string(screen, dp, Iwfg, ZP, font, sbuf); + for (i = 0; i < 1024; i++) sbuf[i] = 0; + sp = sbuf; + dp.y += font->height; + if (dp.y + font->height > rw.max.y) break; + fflag = 0; + } + } +} + +void +drawmenu(void) +{ + char sbuf[1024], *tp, *sp; + Image *Ifg; + Point dp; + long n, lc; + lc = 0; + dp = rw.min; + tp = text.data; + sp = sbuf; + draw(screen, rw, Iwbg, 0, ZP); + if (sp == nil) return; + while (tp < text.data + text.size) { + *sp = *tp; + sp++; + tp++; + if (*tp == '\n'){ + *sp = 0; + tp++; + lc++; + sp = sbuf; + if (lc-1 < scroll) continue; + n = strchr(sbuf, '\t') - (sbuf + 1); + while (dp.x + stringnwidth(font, sbuf+1, n) > + rw.max.x) n--; + Ifg = Iwfgl; + if (sbuf[0] == 'i') Ifg = Iwfg; + stringn(screen, dp, Ifg, ZP, font, sbuf+1, n); + dp.y += font->height; + if (dp.y + font->height > rw.max.y) break; + } + } + if ((tp >= text.data + text.size) && (scroll > lc)) + scroll = lc; +} + +char* +getline(long ln) +{ + char *sbuf, *sp, *tp; + long lc; + sbuf = malloc(sizeof(char) * 1024); + sp = sbuf; + tp = text.data; + for (lc = 0; lc < ln; tp++) { + if (*tp == '\n') lc++; + if (tp > text.data + text.size) return 0; + } + while (*tp != '\n') { + *sp = *tp; + sp++; + *sp = 0; + tp++; + if (tp > text.data + text.size) return 0; + } + return sbuf; +} + +int +extracturl(char *s) +{ + char *sp, *ep, buf[1024], port[256]; + type = s[0]; + sp = strchr(s, '\t') + 1; + ep = strchr(sp, '\t'); + strncpy(path, sp, ep-sp); + path[ep-sp] = 0; + sp = ep + 1; + ep = strchr(sp, '\t'); + buf[0] = 0; + strncpy(buf, sp, ep-sp); + buf[ep-sp] = 0; + sp = ep + 1; + ep = strchr(sp, '\r'); + strncpy(port, sp, ep-sp); + port[ep-sp] = 0; + snprint(addr, 1024, "tcp!%s!%s", buf, port); + return 0; +} + +void +handlelink(void) +{ + switch (type) { + case '0': + save(); + proccreate(runhold, 0, 1024 * 8); + break; + case '1': + loadtext(addr, path); + scroll = 0; + drawmenu(); + flushimage(display, 1); + break; + case 'I': + save(); + proccreate(runpage, 0, 1024 * 8); + break; + default: + snprint(status, 255, "unknown type - %c", type); + drawstatus(); + flushimage(display, 1); + } +} + +int +loadtext(char *addr, char *path) +{ + int dcfd; + long n; + char buf[1024]; + + dcfd = dial(addr, 0, 0, 0); + if (dcfd > 0) { + write(dcfd, path, strlen(path)); + write(dcfd, "\n", 1); + } else { + snprint(status, 255, "failed to dial %s", addr); + drawstatus(); + flushimage(display, 1); + return -1; + } + + text.size = 0; + while ((n = read(dcfd, buf, 1024)) > 0) { + text.data = realloc(text.data, text.size + n); + memcpy(text.data + text.size, buf, n); + text.size += n; + snprint(status, 255, "loading %s: %ldB", path, text.size); + drawstatus(); + flushimage(display, 1); + } + snprint(status, 255, "done"); + drawstatus(); + flushimage(display, 1); + return 0; +} + + +void +runhold(void*) +{ + // TODO: this should be done through plumber + procexecl(nil, "/bin/window", "window", "-m", "hold", tmpfile, nil); +} + +void +runpage(void*) +{ + procexecl(nil, "/bin/window", "window", "-m", "page", tmpfile, nil); +} + +int +save(void) +{ + int dcfd, wfd; + long n; + char buf[1024]; + + dcfd = dial(addr, 0, 0, 0); + if (dcfd > 0) { + write(dcfd, path, strlen(path)); + write(dcfd, "\n", 1); + } else { + snprint(status, 255, "failed to dial %s", addr); + drawstatus(); + flushimage(display, 1); + return -1; + } + + wfd = create(tmpfile, OWRITE, 0666); + if (wfd <= 0) { + snprint(status, 255, "failed to create tmpfile"); + drawstatus(); + flushimage(display, 1); + return -1; + } + while ((n = read(dcfd, buf, 1024)) > 0) { + write(wfd, buf, n); + } + close(dcfd); + close(wfd); + snprint(status, 255, "saved"); + drawstatus(); + flushimage(display, 1); + return 0; +} +\ No newline at end of file diff --git a/mkfile b/mkfile @@ -0,0 +1,8 @@ +</$objtype/mkfile +BIN=/$objtype/bin + +TARG=gophra + +OFILES=gophra.$O + +</sys/src/cmd/mkone +\ No newline at end of file