shithub: livedoc

Download patch

ref: 8daa731518254608fc35d69f39d7a0a9994b6d83
author: sirjofri <[email protected]>
date: Mon Jul 1 12:54:42 EDT 2024

adds files

--- /dev/null
+++ b/README
@@ -1,0 +1,24 @@
+Livedoc
+
+What does it do:
+
+- In your currently opened document, it finds the page corresponding to your current cursor location.
+- It builds your document, and plumbs the location to the "image" channel (optional).
+- Repeats every N seconds.
+
+
+Requirements needed to use this program:
+
+- Your document directory needs a proper mkfile.
+- The mkfile needs a rule named "tout" that outputs troff intermediate language output.
+- Your edited documents needs to be directly inside the that directory, not within a subdirectory.
+
+
+Under the hood:
+
+- It creates a memory copy of the whole directory using dircp(1)
+- It copies the buffer contents of your open file to the cloned file in memory
+- It places a specific symbol (default: center dot "·") at the cursor location
+- It scans the troff output to find that symbol, figuring out the page
+- It builds the pdf using mk
+- It copies the pdf to the /tmp directory and plumbs it, using the page number
--- /dev/null
+++ b/livedoc.c
@@ -1,0 +1,411 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <plumb.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-hqp] [-t sleep] [-m mark]\n", argv0);
+	exits("usage");
+}
+
+char Enoacme[] = "not in acme";
+char Enofile[] = "no file";
+
+typedef struct Winpos Winpos;
+struct Winpos {
+	long m;
+	long n;
+};
+
+int winid;
+int ctlfd, addrfd, datafd;
+int sctlfd, stagfd;
+char *stag = nil;
+char *file, *filedir, *targetfile;
+char *ldpath = "/mnt/livedoc";
+char *mark = "·";
+
+int noplumb = 0;
+
+void
+runcmd(char *cmd, char **args)
+{
+	int p[2];
+	
+	if (pipe(p) < 0)
+		sysfatal("runcmd: %r");
+	
+	switch (fork()) {
+	case -1: /* error */
+		sysfatal("unable to fork for %s: %r", cmd);
+	case 0: /* child */
+		chdir(ldpath); /* ramfs call can silently fail */
+		dup(p[1], 1);
+		close(p[1]);
+		close(p[0]);
+		exec(cmd, args);
+		sysfatal("unable to exec: %r");
+	default: /* parent */
+		close(p[1]);
+		break;
+	}
+	
+	wait();
+}
+
+Winpos
+finddot(void)
+{
+	int n;
+	Winpos pos;
+	char data[25]; /* m[11] space[1] n[11] */
+	char *e;
+	
+	pos.m = pos.n = -1;
+	
+	fprint(ctlfd, "addr=dot");
+	
+	n = pread(addrfd, data, 24, 0);
+	if (n != 24) {
+		werrstr("error reading addr file");
+		return pos;
+	}
+	
+	data[24] = 0;
+	pos.m = strtol(data, &e, 10);
+	pos.n = strtol(e, nil, 10);
+	return pos;
+}
+
+char *dircpargs[] = {
+	"dircp",
+	nil,
+	"/mnt/livedoc",
+	nil,
+};
+
+char *cleanargs[] = {
+	"rm",
+	"-rf",
+	"/mnt/livedoc/*",
+	nil,
+};
+
+void
+copydata(void)
+{
+	dircpargs[1] = filedir;
+	
+//	runcmd("/bin/rm", cleanargs);
+	runcmd("/bin/dircp", dircpargs);
+}
+
+void
+dumpfile(Winpos pos)
+{
+	int fd;
+	Biobuf *bin, *bout;
+	char *s;
+	
+	fprint(addrfd, "#0,#%ld", pos.m);
+	
+	fd = open(targetfile, OWRITE);
+	if (fd < 0)
+		sysfatal("error opening file: %r");
+	
+	bin = Bfdopen(datafd, OREAD);
+	bout = Bfdopen(fd, OWRITE);
+	
+	while (s = Brdstr(bin, '\n', 0))
+		if (Bprint(bout, "%s", s) == -1)
+			break;
+	
+	Bprint(bout, "%s", mark);
+	fprint(addrfd, "#%ld,$", pos.m);
+	
+	while (s = Brdstr(bin, '\n', 0)) {
+		if (Bprint(bout, "%s", s) == -1)
+			break;
+	}
+	Bterm(bout);
+}
+
+char *mkpdargs[] = {
+	"mk",
+	"tout",
+	nil,
+};
+
+int
+pagedot(void)
+{
+	int p[2];
+	Biobuf *bin;
+	char *s;
+	int page, ignore;
+	
+	if (pipe(p) < 0)
+		sysfatal("unable to pipe: %r");
+	
+	switch (fork()) {
+	case -1: /* error */
+		sysfatal("unable to fork for pagedot: %r");
+	case 0: /* child */
+		chdir(ldpath);
+		dup(p[1], 1);
+		close(p[1]);
+		close(p[0]);
+		exec("/bin/mk", mkpdargs);
+		sysfatal("error: %r");
+	default: /* parent */
+		close(p[1]);
+	}
+	
+	page = 1;
+	ignore = 0;
+	bin = Bfdopen(p[0], OREAD);
+	while (s = Brdstr(bin, '\n', 1)) {
+		if (ignore)
+			continue;
+		
+		if (s[0] == 'p') {
+			page = atoi(&s[1]);
+		}
+		if (strstr(s, mark)) {
+			ignore = 1;
+		}
+		free(s);
+	}
+	Bterm(bin);
+	waitpid();
+	
+	return ignore ? page : 1;
+}
+
+char *mkargs[] = {
+	"mk",
+	nil,
+};
+
+void
+compile(void)
+{
+	runcmd("/bin/mk", mkargs);
+}
+
+int round = 0;
+char *pdffile = nil;
+
+void
+copyfile(void)
+{
+	char *s;
+	int to, from, dirfd;
+	int n, l;
+	long r;
+	Dir *dirs;
+	char buf[8192];
+	
+	dirfd = open(ldpath, OREAD);
+	if (dirfd < 0)
+		sysfatal("unable to open dir: %r");
+	
+	s = nil;
+	while ((n = dirread(dirfd, &dirs)) > 0) {
+		for (int i = 0; i < n; i++) {
+			l = strlen(dirs[i].name);
+			if (strcmp(&dirs[i].name[l-4], ".pdf") == 0) {
+				s = strdup(dirs[i].name);
+				free(dirs);
+				goto Found;
+			}
+		}
+		free(dirs);
+	}
+Found: /* s is pdf filename or nil */
+	close(dirfd);
+	if (!s) {
+		fprint(2, "no pdf file found!\n");
+		return;
+	}
+	from = open(s, OREAD);
+	if (from < 0)
+		sysfatal("cannot open source pdf file: %r");
+	free(s);
+	
+	if (pdffile) {
+		remove(pdffile);
+		free(pdffile);
+		pdffile = nil;
+	}
+	
+	round++;
+	
+	pdffile = smprint("/tmp/livedoc.%d.pdf", round);
+	to = create(pdffile, OWRITE, 0666);
+	if (to < 0)
+		sysfatal("unable to create file: %r");
+	
+	seek(from, 0, 0);
+	while (r = read(from, buf, sizeof(buf))) {
+		if (write(to, buf, r) != r)
+			sysfatal("write error");
+	}
+	
+	close(from);
+	close(to);
+}
+
+void
+plumbpage(int page)
+{
+	char *s;
+	int fd;
+	
+	if (!pdffile)
+		return;
+	
+	if (noplumb) {
+		print("%s!%d\n", pdffile, page);
+		return;
+	}
+	
+	fd = plumbopen("send", OWRITE|OCEXEC);
+	if (fd < 0) {
+		fprint(2, "unable to open plumber: %r\n");
+		return;
+	}
+	
+	s = smprint("%s!%d", pdffile, page);
+	plumbsendtext(fd, "Livedoc", "image", "/tmp", s);
+	free(s);
+	close(fd);
+}
+
+int singlerun = 0;
+
+int
+tick(void)
+{
+	Winpos pos;
+	int p;
+	int tfd;
+	
+	/* access(2) doesn't work in this case for some reason */
+	tfd = open(stag, OREAD);
+	if (tfd < 0) {
+		return 0;
+	}
+	close(tfd);
+	
+	fprint(sctlfd, "dirty");
+	
+	copydata();
+	pos = finddot();
+	dumpfile(pos);
+	p = pagedot();
+	compile();
+	copyfile();
+	plumbpage(p);
+	
+	fprint(sctlfd, "clean");
+	
+	return !singlerun;
+}
+
+char *ramfsargs[] = {
+	"ramfs",
+	"-m",
+	"/mnt/livedoc",
+	nil
+};
+
+void
+main(int argc, char **argv)
+{
+	char *s, sidstr[13];
+	char *winctl, *winaddr, *windata;
+	int sleeptimer = 5000;
+	int n, sid;
+	
+	ARGBEGIN{
+	case 'h':
+		usage();
+		return;
+	case 't':
+		sleeptimer = abs(atoi(EARGF(usage())));
+		break;
+	case 'm':
+		mark = EARGF(usage());
+		break;
+	case 'p':
+		noplumb++;
+		break;
+	case 'q':
+		singlerun++;
+		break;
+	}ARGEND;
+	
+	s = getenv("winid");
+	if (!s) {
+		fprint(2, Enoacme);
+		exits(Enoacme);
+	}
+	winid = atoi(s);
+	
+	file = getenv("%");
+	if (!file) {
+		fprint(2, Enofile);
+		exits(Enofile);
+	}
+	
+	filedir = strdup(file);
+	*strrchr(filedir, '/') = 0;
+	
+	targetfile = strrchr(file, '/') + 1;
+	targetfile = smprint("%s/%s", ldpath, targetfile);
+	
+	winctl = smprint("/mnt/acme/%d/ctl", winid);
+	winaddr = smprint("/mnt/acme/%d/addr", winid);
+	windata = smprint("/mnt/acme/%d/xdata", winid);
+	
+	ctlfd = open(winctl, ORDWR);
+	addrfd = open(winaddr, ORDWR);
+	datafd = open(windata, OREAD);
+	
+	free(winctl);
+	free(winaddr);
+	free(windata);
+	
+	sctlfd = open("/mnt/acme/new/ctl", ORDWR);
+	if (sctlfd < 0)
+		sysfatal("unable to open file: %r");
+	
+	read(sctlfd, sidstr, sizeof(sidstr));
+	sid = atoi(sidstr);
+	stag = smprint("/mnt/acme/%d/tag", sid);
+	stagfd = open(stag, ORDWR);
+	
+	fprint(sctlfd, "scratch\n");
+	fprint(sctlfd, "name %s/-Livedoc\n", filedir);
+	
+	if (ctlfd < 0 || addrfd < 0 || datafd < 0) {
+		sysfatal("unable to open file: %r");
+	}
+	
+	runcmd("/bin/ramfs", ramfsargs);
+	
+	n = 0;
+	while (tick() && n != -1)
+		n = sleep(sleeptimer);
+	
+	close(ctlfd);
+	close(addrfd);
+	close(datafd);
+	free(filedir);
+	free(targetfile);
+	free(stag);
+	exits(nil);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+BIN=/acme/bin/$objtype
+TARG=Livedoc
+OFILES=livedoc.$O
+
+</sys/src/cmd/mkone