shithub: vexed

Download patch

ref: 4eb183a5f487d9647216767fcf93c794c4c13157
author: phil9 <telephil9@gmail.com>
date: Mon Dec 27 15:40:17 EST 2021

initial import

--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 phil9 <telephil9@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null
+++ b/README.md
@@ -1,0 +1,22 @@
+# vexed
+A visual hex editor for plan9.
+
+Move using the arrow keys or select using the mouse.
+Scroll with page up/down, the mouse wheel or using the scrollbar.
+Home and End keys go to the beginning and end of the file respectively.
+Del or q exit the program.
+
+**THIS IS WORK IN PROGRESS. USE AT YOUR OWN RISK**
+
+## Usage: 
+```sh
+% mk install
+% vexed <file>
+```
+
+## License:
+MIT
+
+## Bugs:
+Nope, only features!
+
--- /dev/null
+++ b/a.h
@@ -1,0 +1,26 @@
+typedef struct Buffer Buffer;
+
+struct Buffer
+{
+	uchar *data;
+	usize count;
+	usize size;
+};
+
+int readfile(Buffer*, char*);
+int writefile(Buffer*, char*);
+
+/* COLORS */
+enum
+{
+	BACK,
+	ADDR,
+	HEX,
+	ASCII,
+	SCROLL,
+	NCOLS,
+};
+
+Image* cols[NCOLS];
+
+void initcols(int);
--- /dev/null
+++ b/buf.c
@@ -1,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "a.h"
+
+int
+readfile(Buffer *buf, char *filename)
+{
+	int fd;
+	long r;
+
+	buf->count = 0;
+	buf->size  = 8192;
+	buf->data  = malloc(buf->size);
+	if(buf->data == nil)
+		return -1;
+	fd = open(filename, OREAD);
+	if(fd < 0)
+		return -1;
+	for(;;){
+		r = read(fd, buf->data + buf->count, buf->size - buf->count);
+		if(r < 0)
+			return -1;
+		if(r == 0)
+			break;
+		buf->count += r;
+		if(buf->count == buf->size){
+			buf->size *= 1.5;
+			buf->data = realloc(buf->data, buf->size);
+			if(buf->data == nil)
+				return -1;
+		}
+	}
+	buf->data[buf->count] = 0;
+	close(fd);
+	return 0;
+}
+
+int writefile(Buffer *buf, char *filename)
+{
+	USED(buf);
+	USED(filename);
+	return -1;
+}
--- /dev/null
+++ b/cols.c
@@ -1,0 +1,29 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "a.h"
+
+void
+initcols(int reverse)
+{
+	if(reverse){
+		cols[BACK]   = display->black;
+		cols[ADDR]   = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+		cols[HEX]    = display->white;
+		cols[ASCII]  = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+		cols[SCROLL] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF^reverse);
+	}else{
+		cols[BACK]   = display->white;
+		cols[ADDR]   = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreygreen);
+		cols[HEX]    = display->black;
+		cols[ASCII]  = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreygreen);
+		cols[SCROLL] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF);
+	}
+/*
+	bg  = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x282828ff);
+	fg  = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xebdbb2ff);
+	ofg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x83a598ff);
+	scrlbg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x504945ff); 
+*/
+}
+
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,9 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=vexed
+OFILES=vexed.$O buf.$O cols.$O
+HFILES=a.h
+
+</sys/src/cmd/mkone
+
--- /dev/null
+++ b/vexed.c
@@ -1,0 +1,333 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <ctype.h>
+#include "a.h"
+
+enum
+{
+	Emouse,
+	Eresize,
+	Ekeyboard,
+};
+
+enum
+{
+	Padding = 4,
+	Spacing = 8,
+	Scrollwidth = 12,
+};
+
+const char	*filename;
+Buffer buf;
+int blines;
+Mousectl	*mctl;
+Keyboardctl	*kctl;
+int sw;
+int scrollsize;
+Rectangle scrollr;
+Rectangle viewr;
+Rectangle statusr;
+int nlines;
+int offset;
+int sel = 0;
+
+int
+selvisible(void)
+{
+	int l;
+
+	l = sel/16;
+	return offset <= l && l < offset+nlines;
+}
+
+int
+ensureselvisible(void)
+{
+	if(selvisible())
+		return 0;
+	offset = sel/16;
+	return 1;
+}
+
+void
+drawline(int y, int base)
+{
+	int index, i, n;
+	char b[8] = {0}, *s;
+	Point p;
+	Point p2;
+	
+	index = base + offset*16;
+	if(index >= buf.count)
+		return;
+	p = Pt(viewr.min.x, y);
+	if(index/16 == sel/16){
+		n = snprint(b, sizeof b, "%06X", sel);
+		p = stringnbg(screen, p, cols[BACK], ZP, font, b, n, cols[ADDR], ZP);
+	}else{
+		n = snprint(b, sizeof b, "%06X", index);
+		p = stringn(screen, p, cols[ADDR], ZP, font, b, n);
+	}
+	p.x += 2*Spacing;
+	p2 = addpt(p, Pt(16*3*sw - sw + 2*Spacing, 0));
+	for(i = 0; i < 16; i++) {
+		if(index + i >= buf.count)
+			break;
+		n = snprint(b, sizeof b, "%02X", buf.data[index + i]);
+		s = isprint(buf.data[index + i]) ? (char*)&buf.data[index + i] : ".";
+		if(index + i == sel){
+			p = stringnbg(screen, p, cols[BACK], ZP, font, b, n, cols[HEX], ZP);
+			p2 = stringnbg(screen, p2, cols[BACK], ZP, font, s, 1, cols[ASCII], ZP);
+		}else{
+			p = stringn(screen, p, cols[HEX], ZP, font, b, n);
+			p2 = stringn(screen, p2, cols[ASCII], ZP, font, s, 1);
+		}
+		p = stringn(screen, p, cols[BACK], ZP, font, " ", 1);
+	}
+}
+
+void
+redraw(void)
+{
+	int i, h, y, ye;
+	Rectangle scrposr;
+	char b[16] = {0};
+
+	draw(screen, screen->r, cols[BACK], nil, ZP);
+	draw(screen, scrollr, cols[SCROLL], nil, ZP);
+	border(screen, scrollr, 0, cols[HEX], ZP);
+	if(blines > 0){
+		h = ((double)nlines / blines) * Dy(scrollr);
+		y = ((double)offset / blines) * Dy(scrollr);
+		ye = scrollr.min.y + y + h - 1;
+		if(ye >= scrollr.max.y)
+			ye = scrollr.max.y - 1;
+		scrposr = Rect(scrollr.min.x + 1, scrollr.min.y + y + 1, scrollr.max.x - 1, ye);
+	}else
+		scrposr = insetrect(scrollr, -1);
+	draw(screen, scrposr, cols[BACK], nil, ZP);
+	for(i = 0; i < nlines; i++)
+		drawline(viewr.min.y + i * font->height, i*16);
+	string(screen, Pt(statusr.min.x + Padding, statusr.min.y), cols[HEX], ZP, font, filename);
+	snprint(b, sizeof b, "%d%%", (int)((100.0 * sel) / buf.count + 0.5));
+	y = statusr.max.x - stringwidth(font, b) - Padding;
+	string(screen, Pt(y, statusr.min.y), cols[HEX], ZP, font, b);
+	flushimage(display, 1);
+}
+
+int
+scroll(int lines)
+{
+	if(blines <= nlines)
+		return 0;
+	if(lines < 0 && offset == 0)
+		return 0;
+	if(lines > 0 && offset + nlines >= blines){
+		return 0;
+	}
+	offset += lines;
+	if(offset < 0)
+		offset = 0;
+	if(offset + blines%nlines >= blines)
+		offset = blines - blines%nlines;
+	sel += lines * 16;
+	if(sel < 0)
+		sel = 0;
+	else if(sel >= buf.count)
+		sel = buf.count - 1;
+	redraw();
+	return 1;
+}
+
+int
+indexat(Point p)
+{
+	int row, col, index;
+
+	row = (p.y - viewr.min.y) / font->height;
+	col = ((p.x - viewr.min.x) / sw - 8);
+	if(col < 0 || col > 16*3*sw)
+		return -1;
+	index = col/3 + row*16 + offset*16;
+	if(index >= buf.count)
+		return -1;
+	return index;
+}
+
+void
+emouse(Mouse *m)
+{
+	int n;
+
+	if(ptinrect(m->xy, scrollr)){
+		if(m->buttons == 1){
+			n = (m->xy.y - scrollr.min.y) / font->height;
+			scroll(-n);
+		}else if(m->buttons == 2){
+			n = (m->xy.y - scrollr.min.y) * blines / Dy(scrollr);
+			offset = n;
+			sel = offset*16;
+			redraw();
+		}else if(m->buttons == 4){
+			n = (m->xy.y - scrollr.min.y) / font->height;
+			scroll(n);
+		}
+	} else if(ptinrect(m->xy, viewr)){
+		if(m->buttons == 1){
+			n = indexat(m->xy);
+			if(n >= 0){
+				sel = n;
+				redraw();
+			}
+		}else if(m->buttons == 8){
+			scroll(-scrollsize);
+		}else if(m->buttons == 16){
+			scroll(scrollsize);
+		}
+	}
+}
+
+void
+eresize(void)
+{
+	int w, x;
+
+	statusr = Rect(screen->r.min.x + Padding, screen->r.max.y - Padding - font->height, screen->r.max.x - Padding, screen->r.max.y - Padding);
+	scrollr = Rect(screen->r.min.x + Padding, screen->r.min.y + Padding, screen->r.min.x + Padding + Scrollwidth, statusr.min.y - Padding);
+	sw = stringwidth(font, " ");
+	w = Padding + 6*sw + 2*Spacing + 16*3*sw-sw + 2*Spacing + 16*sw + Padding;
+	x = scrollr.max.x + Padding;
+	viewr = Rect(x, screen->r.min.y + Padding, x + w, statusr.min.y - Padding);
+	nlines = Dy(viewr) / font->height;
+	scrollsize = mousescrollsize(nlines);
+	redraw();
+	flushimage(display, 1);
+}
+
+void
+ekeyboard(Rune k)
+{
+	switch(k){
+	case 'q':
+	case Kdel:
+		threadexitsall(nil);
+		break;
+	case Kpgup:
+		scroll(-nlines);
+		break;
+	case Kpgdown:
+		scroll(nlines);
+		break;
+	case Kleft:
+		if(sel > 0){
+			sel--;
+			ensureselvisible();
+			redraw();
+		}
+		break;
+	case Kright:
+		if(sel < (buf.count - 1)){
+			sel++;
+			ensureselvisible();
+			redraw();
+		}
+		break;
+	case Kup:
+		if(sel >= 16){
+			sel -= 16;
+			ensureselvisible();
+			redraw();
+		}
+		break;
+	case Kdown:
+		if(sel < (buf.count - 16)){
+			sel += 16;
+			ensureselvisible();
+			redraw();
+		}
+		break;
+	case Khome:
+		if(sel != 0){
+			sel = 0;
+			ensureselvisible();
+			redraw();
+		}
+		break;
+	case Kend:
+		if(sel != buf.count - 1){
+			sel = buf.count - 1;
+			if(!selvisible())
+				offset = blines - blines%nlines;
+			redraw();
+		}
+		break;
+	}
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-b] <filename>\n", argv0);
+	threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	Mouse m;
+	Rune k;
+	Alt alts[] = {
+		{ nil, &m,  CHANRCV },
+		{ nil, nil, CHANRCV },
+		{ nil, &k,  CHANRCV },
+		{ nil, nil, CHANEND },
+	};
+	int reverse;
+
+	reverse = 0;
+	ARGBEGIN{
+	case 'b':
+		reverse = 1;
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(*argv == nil)
+		usage();
+	filename = *argv;
+	if(readfile(&buf, filename) < 0)
+		sysfatal("readfile: %r");
+	blines = buf.count/16;
+	if(initdraw(nil, nil, argv0) < 0)
+		sysfatal("initdraw: %r");
+	display->locking = 0;
+	if((mctl = initmouse(nil, screen)) == nil)
+		sysfatal("initmouse: %r");
+	if((kctl = initkeyboard(nil)) == nil)
+		sysfatal("initkeyboard: %r");
+	initcols(reverse);
+	alts[Emouse].c = mctl->c;
+	alts[Eresize].c = mctl->resizec;
+	alts[Ekeyboard].c = kctl->c;
+	eresize();
+	for(;;){
+		switch(alt(alts)){
+		case Emouse:
+			emouse(&m);
+			break;
+		case Eresize:
+			if(getwindow(display, Refnone) < 0)
+				sysfatal("getwindow: %r");
+			eresize();
+			break;
+		case Ekeyboard:
+			ekeyboard(k);
+			break;
+		}
+	}
+}
+