aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-01-04 16:42:42 -0800
committerH. Peter Anvin <hpa@zytor.com>2009-01-04 16:42:42 -0800
commitbc0fb6e12bd1999f38d39199164edb47ea56c6b2 (patch)
tree9d3363da80af4cdeb9f9769b21b811c5cfd769ff
parent81eed1d4265ae193cf334edfe5aeb0b68418ffcf (diff)
downloadabc80-bc0fb6e12bd1999f38d39199164edb47ea56c6b2.tar.gz
abc80-bc0fb6e12bd1999f38d39199164edb47ea56c6b2.tar.xz
abc80-bc0fb6e12bd1999f38d39199164edb47ea56c6b2.zip
abcprintd: initial implementation of a remote access protocol
Completely untested, but "at least it compiles" implementation of a remote file access protocol in abcprintd.
-rw-r--r--tools/Makefile11
-rw-r--r--tools/abcprint.txt54
-rw-r--r--tools/abcprintd.c67
-rw-r--r--tools/fileop.c422
4 files changed, 533 insertions, 21 deletions
diff --git a/tools/Makefile b/tools/Makefile
index ed6c2e6..e165044 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -11,11 +11,14 @@ endif
all : abcprintd$(X) z80asm z80dis$(X)
-abcprintd$(X) : abcprintd.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+.c.$(O):
+ $(CC) $(CFLAGS) -c -o $@ $<
-z80dis$(X) : z80dis.c
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+abcprintd$(X) : abcprintd.$(O) fileop.$(O)
+ $(CC) $(LDFLAGS) -o $@ $^
+
+z80dis$(X) : z80dis.$(O)
+ $(CC) $(LDFLAGS) -o $@ $<
.PHONY: z80asm
z80asm :
diff --git a/tools/abcprint.txt b/tools/abcprint.txt
new file mode 100644
index 0000000..6c16971
--- /dev/null
+++ b/tools/abcprint.txt
@@ -0,0 +1,54 @@
+abcprintd protocol
+
+FF 00 -> end of print job
+
+FF FF -> FF as part of print job
+
+All other bytes are part of a print job
+
+
+File sharing commands:
+----------------------
+
+All commands start with FF <opcode> <seq>, and all responses begin
+with FF <opcode> <seq> <err>, where <err> is 0 for OK and otherwise
+0x80+ABC80 error code.
+
+FF A0 ss ixix NNNNNNNNEEE OPEN TEXT
+
+ ixix = Address of IX-map (used as a handle)
+
+ Open an existing file for reading in text mode
+
+FF A1 ss ixix NNNNNNNNEEE OPEN BINARY
+
+ Open an existing file for reading in binary (block) mode
+
+FF A2 ss ixix NNNNNNNNEEE PREPARE TEXT
+
+ Create a new file for writing in text mode
+
+FF A3 ss ixix NNNNNNNNEEE PREPARE BINARY
+
+ Create a new file for writing in binary (block) mode
+
+FF A4 ss ixix INPUT
+
+ Read a text line; response followed by len (2 bytes) + data
+
+FF A5 ss ixix len READ BLOCK
+
+ Read a data block of specified len; response: len + data
+
+FF A6 ss ixix len data... PRINT
+
+ Write a text line or data block
+
+FF A7 ss ixix CLOSE
+
+ Close a file handle
+
+FF A8 ss xxxx CLOSEALL
+
+ All files are closed and forgotten. xxxx ignored.
+
diff --git a/tools/abcprintd.c b/tools/abcprintd.c
index b813bb4..5925e55 100644
--- a/tools/abcprintd.c
+++ b/tools/abcprintd.c
@@ -1,7 +1,6 @@
-#ident "$Id$"
/* ----------------------------------------------------------------------- *
*
- * Copyright 2004-2006 H. Peter Anvin - All Rights Reserved
+ * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,6 +23,7 @@
#include <termios.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include <sys/wait.h>
#include <fcntl.h>
@@ -93,8 +93,8 @@ static int open_port(const char *path)
tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|NOFLSH|TOSTOP|IEXTEN);
- tio.c_cc[VMIN] = 64;
- tio.c_cc[VTIME] = 1;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
cfsetispeed(&tio, B115200);
cfsetospeed(&tio, B115200);
@@ -167,6 +167,9 @@ static void output(int c, FILE *tf, enum print_state *psp)
}
}
+extern bool file_op(unsigned char, int);
+extern const char *file_op_dir;
+
#define BUF_SIZE 4096
int main(int argc, char *argv[])
@@ -176,8 +179,10 @@ int main(int argc, char *argv[])
enum {
st_normal, /* Normal operation */
st_ff, /* 0xFF received */
+ st_file, /* File operation in progress */
} state;
- char ibuf[BUF_SIZE], c;
+ char ibuf[BUF_SIZE];
+ unsigned char c;
int i, b;
enum print_state ps;
static const char *lpr_default_argv[] = {
@@ -185,27 +190,46 @@ int main(int argc, char *argv[])
"--non-printable-format=space",
NULL
};
+ int o;
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+ bool badopt = false;
+
+ file_op_dir = NULL;
+
+ while ((o = getopt(argc, argv, "d:")) != EOF) {
+ switch (o) {
+ case 'd':
+ file_op_dir = optarg;
+ break;
+ default:
+ badopt = true;
+ break;
+ }
+ }
- if ( argc < 2 ) {
- fprintf(stderr, "Usage: %s port [lpr_program [options...]]\n", argv[0]);
+ if ( badopt || optind >= argc ) {
+ fprintf(stderr,
+ "Usage: %s [options] port lpr_program [lpr_args...]]\n",
+ argv[0]);
exit(1);
}
- if ( (fd = open_port(argv[1])) < 0 ) {
- perror(argv[1]);
+ if ( (fd = open_port(argv[optind])) < 0 ) {
+ perror(argv[optind]);
exit(1);
}
- if ( argc == 2 ) {
+ if ( argc == optind ) {
lpr_argc = sizeof(lpr_default_argv)/sizeof(const char *) - 1;
lpr_argv = lpr_default_argv;
} else {
- lpr_argc = argc-2;
- if ( !(lpr_argv = malloc(sizeof(char *) * (lpr_argc+1))) ) {
+ lpr_argc = argc-optind-1;
+ if ( !(lpr_argv = malloc(sizeof(char *)*(lpr_argc+1))) ) {
perror("malloc");
exit(1);
}
- memcpy(lpr_argv, argv+2, sizeof(char *) * (argc-2));
+ memcpy(lpr_argv, argv+optind+1, sizeof(char *)*(lpr_argc+1));
}
daemon(0,0);
@@ -225,18 +249,27 @@ int main(int argc, char *argv[])
switch ( state ) {
case st_normal:
- if ( c == '\xff' )
+ if ( c == 0xff )
state = st_ff;
else
output(c, tf, &ps);
break;
case st_ff:
- if ( c == 0 )
+ if ( c == 0 ) {
print_setup(&tf, &ps); /* BREAK received, end of job */
- else
+ state = st_normal;
+ } else if ( c >= 0xa0 && c <= 0xbf ) {
+ /* Opcode range reserved for file ops */
+ state = file_op(c, fd) ? st_file : st_normal;
+ } else {
output(c, tf, &ps);
- state = st_normal;
+ state = st_normal;
+ }
+ break;
+
+ case st_file:
+ state = file_op(c, fd) ? st_file : st_normal;
break;
}
}
diff --git a/tools/fileop.c b/tools/fileop.c
new file mode 100644
index 0000000..cce13df
--- /dev/null
+++ b/tools/fileop.c
@@ -0,0 +1,422 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#ifndef O_TEXT
+# define O_TEXT 0
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+const char *file_op_dir;
+
+static ssize_t xwrite(int fd, const void *buf, size_t count)
+{
+ const char *p = buf;
+ ssize_t out = 0;
+ ssize_t rv;
+
+ while (count) {
+ rv = write(fd, p, count);
+ if (rv == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return out ? out : -1; /* Error */
+ } else if (rv == 0) {
+ return out; /* EOF */
+ }
+
+ p += rv;
+ out += rv;
+ count -= rv;
+ }
+
+ return out;
+}
+
+static enum {
+ st_op,
+ st_filename,
+ st_len,
+ st_data
+} state = st_op;
+static int byte_count = 4;
+static unsigned char cmd[4];
+static unsigned char *bytep = cmd;
+static struct file_data *filemap[65536];
+
+struct file_data {
+ FILE *f;
+};
+
+static void send_reply(int fd, int status)
+{
+ char reply[4];
+
+ reply[0] = 0xff;
+ reply[1] = cmd[0];
+ reply[2] = cmd[1];
+ reply[3] = status;
+
+ if (fd >= 0)
+ xwrite(fd, reply, 4);
+}
+
+static void do_close(int fd, uint16_t ix)
+{
+ struct file_data *fm = filemap[ix];
+
+ if (fm) {
+ fclose(fm->f);
+ free(fm);
+ filemap[ix] = NULL;
+ send_reply(fd, 0);
+ } else {
+ send_reply(fd, 128+45); /* "Fel logiskt filnummer" */
+ }
+}
+
+static void do_closeall(int fd)
+{
+ int i;
+
+ for (i = 0; i <= 65535; i++)
+ do_close(-1, i);
+
+ send_reply(fd, 0);
+}
+
+/* XXX: This assumes Latin-1; Unicode requires more sophistication */
+static const char my_tolower[256] =
+ "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
+ "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "\351abcdefghijklmnopqrstuvwxyz\344\366\345\374_"
+ "\351abcdefghijklmnopqrstuvwxyz\344\366\345\374\377"
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
+
+static void do_open(int fd, uint16_t ix, char *name)
+{
+ char *path, *p;
+ int opl;
+ int i;
+ bool nodot;
+ struct file_data *fm;
+ int err;
+ static const char *modes[6] = {"r+t", "r+b", "w+t", "w+b", "rt", "rb" };
+
+ name[11] = '\0';
+
+ if (!file_op_dir) {
+ send_reply(fd, 128+42); /* Skivan ej klar */
+ return;
+ }
+
+ do_close(-1, ix);
+
+ opl = strlen(file_op_dir);
+ path = alloca(opl+14);
+ memcpy(path, file_op_dir, opl);
+ p = path+opl;
+ *p++ = '/';
+
+ nodot = true;
+ for (i = 0; i < 11; i++) {
+ if (name[i] != ' ') {
+ if (i >= 8 && !nodot) {
+ *p++ = '.';
+ nodot = false;
+ }
+ *p++ = my_tolower[(unsigned char)name[i]];
+ }
+ }
+
+ if (p == path+opl+1) {
+ /* Empty filename */
+ send_reply(fd, 128+21); /* File not found */
+ return;
+ }
+
+ fm = calloc(1, sizeof(struct file_info *));
+ if (!fm) {
+ send_reply(fd, 128+42);
+ return;
+ }
+
+ fm->f = fopen(path, modes[cmd[0] & 3]);
+ if (!fm->f && errno == EACCES && !(cmd[0] & 2)) {
+ fm->f = fopen(path, modes[(cmd[0] & 1)+4]);
+ }
+ if (!fm->f) {
+ free(fm);
+ switch (errno) {
+ case EACCES:
+ err = 128+39;
+ break;
+ case EROFS:
+ err = 128+43;
+ break;
+ case EIO:
+ case ENOTDIR:
+ err = 128+48;
+ break;
+ default:
+ err = 128+21;
+ break;
+ }
+ send_reply(fd, 128+21);
+ return;
+ }
+
+ filemap[ix] = fm;
+
+ send_reply(fd, 0);
+}
+
+static void do_read_block(int fd, uint16_t ix, uint16_t len)
+{
+ struct file_data *fm = filemap[ix];
+ int err;
+ unsigned char *data;
+ int dlen;
+
+ if (!fm) {
+ send_reply(fd, 128+45);
+ return;
+ }
+
+ data = calloc(len+2, 1);
+ if (!data) {
+ send_reply(fd, 128+42); /* "Skivan ej klar" */
+ return;
+ }
+
+ clearerr(fm->f);
+ dlen = fread(data+2, 1, len, fm->f);
+ if (dlen == 0) {
+ if (ferror(fm->f)) {
+ switch (errno) {
+ case EBADF:
+ err = 128+44; /* Logisk fil ej öppen */
+ break;
+ case EIO:
+ err = 128+35; /* Checksummafel vid läsning */
+ break;
+ default:
+ err = 128+48; /* Fel i biblioteket */
+ break;
+ }
+ } else {
+ /* EOF */
+ err = 128+34; /* Slut på filen */
+ }
+ send_reply(fd, err);
+ free(data);
+ return;
+ }
+
+ send_reply(fd, 0);
+ data[0] = len;
+ data[1] = len >> 8;
+ xwrite(fd, data, len+2);
+ free(data);
+}
+
+#define BUF_SIZE 256
+
+static void do_input(int fd, uint16_t ix)
+{
+ struct file_data *fm = filemap[ix];
+ int err;
+ char data[BUF_SIZE], data1[2*BUF_SIZE];
+ char *p, *q;
+ int dlen;
+
+ if (!fm) {
+ send_reply(fd, 128+45);
+ return;
+ }
+
+ clearerr(fm->f);
+ if (!fgets(data, sizeof data, fm->f)) {
+ if (ferror(fm->f)) {
+ switch (errno) {
+ case EBADF:
+ err = 128+44; /* Logisk fil ej öppen */
+ break;
+ case EIO:
+ err = 128+35; /* Checksummafel vid läsning */
+ break;
+ default:
+ err = 128+48; /* Fel i biblioteket */
+ break;
+ }
+ } else {
+ /* EOF */
+ err = 128+34; /* Slut på filen */
+ }
+ send_reply(fd, err);
+ free(data);
+ return;
+ }
+
+ /* Strip CR and change LF -> CR LF */
+ for (p = data, q = data1+2 ; *p ; p++) {
+ switch (*p) {
+ case '\r':
+ break;
+ case '\n':
+ *q++ = '\r';
+ /* fall through */
+ default:
+ *q++ = *p;
+ break;
+ }
+ }
+ dlen = q - (data1+2);
+ data1[0] = dlen;
+ data1[1] = dlen >> 8;
+ send_reply(fd, 0);
+ xwrite(fd, data1, dlen+2);
+}
+
+static void do_print(int fd, uint16_t ix, uint16_t len, void *data)
+{
+ struct file_data *fm = filemap[ix];
+ int err;
+
+ if (!fm) {
+ send_reply(fd, 128+45);
+ return;
+ }
+
+ err = 0;
+ if (fwrite(data, 1, len, fm->f) != len || fflush(fm->f)) {
+ switch (errno) {
+ case EACCES:
+ err = 128+39; /* Filen skrivskyddad */
+ break;
+ case ENOSPC:
+ case EFBIG:
+ err = 128+41; /* Skivan full */
+ break;
+ case EROFS:
+ err = 128+43; /* Skivan skrivskyddad */
+ break;
+ case EBADF:
+ err = 128+44; /* Logisk fil ej öppen */
+ break;
+ case EIO:
+ err = 128+36; /* Checksummafel vid skrivning */
+ break;
+ default:
+ err = 128+48; /* Fel i biblioteket */
+ break;
+ }
+ }
+ send_reply(fd, err);
+}
+
+bool file_op(unsigned char c, int fd)
+{
+ static unsigned char *data;
+ static char namebuf[12];
+ static unsigned char lenbuf[2];
+ uint16_t ix, len;
+
+ *bytep++ = c;
+ if (--byte_count)
+ return true; /* More to do... */
+
+ /* Otherwise, we have a full deck of *something* */
+
+ ix = (cmd[3] << 8) + cmd[2];
+ len = (lenbuf[1] << 8) + lenbuf[0];
+
+ switch (state) {
+ case st_op:
+ switch (cmd[0]) {
+ case 0xA0: /* OPEN TEXT */
+ case 0xA1: /* PREPARE TEXT */
+ case 0xA2: /* OPEN BINARY */
+ case 0xA3: /* PREPARE BINARY */
+ bytep = (void *)namebuf;
+ byte_count = 11;
+ state = st_filename;
+ return true;
+
+ case 0xA4: /* INPUT */
+ do_input(fd, ix);
+ break;
+
+ case 0xA5: /* READ BLOCK */
+ case 0xA6: /* PRINT */
+ bytep = lenbuf;
+ byte_count = 2;
+ state = st_len;
+ return true;
+
+ case 0xA7: /* CLOSE */
+ do_close(fd, ix);
+ break;
+
+ case 0xA8: /* CLOSEALL */
+ do_closeall(fd);
+ break;
+
+ default:
+ /* Unknown command */
+ send_reply(fd, 128+11);
+ break;
+ }
+ break;
+
+ case st_filename:
+ do_open(fd, ix, namebuf);
+ break;
+
+ case st_len:
+ switch (cmd[0]) {
+ case 0xA5: /* READ BLOCK */
+ do_read_block(fd, ix, len);
+ break;
+ case 0xA6: /* PRINT */
+ if (data)
+ free(data);
+ data = malloc(len);
+ if (!data)
+ send_reply(fd, 128+42); /* "Skivan ej klar" */
+ bytep = data;
+ byte_count = len;
+ state = st_data;
+ return true;
+ }
+ break;
+
+ case st_data:
+ do_print(fd, ix, len, data);
+ free(data);
+ data = NULL;
+ break;
+ }
+
+ /* unless otherwise specified, back to command mode */
+ bytep = cmd;
+ byte_count = 4;
+ return false;
+}
+
+