/* filink.c - transfer files between unix and an Epson PX-8 */ /* This program is in the public domain. If it doesn't work or causes */ /* you grief in any way, blame the public, not me! */ /* Frank Cringle, August 1994 */ /* The best way to compile this is with gcc on a sysv-like machine. */ /* If that is not what you have available, try to get as close as */ /* possible - use the sys5 universe or /usr/5bin/cc or whatever. */ /* Version 0.01 */ #include #include #include #include #include #include struct termios cookedtio, rawtio; #define GOTTIO 1 #define ISATTY 2 #define ISRAW 4 int ttyflags; /* put the tty back to normal mode */ static void ttycooked(void) { tcflush(0, TCIOFLUSH); if (ttyflags & ISRAW) { tcsetattr(fileno(stdin), TCSAFLUSH, &cookedtio); putc('\n', stdout); ttyflags &= ~ISRAW; } } /* set up the tty (stdin/stdout) to transfer all 8-bit characters */ static void ttyraw(void) { if (!(ttyflags & GOTTIO) && isatty(fileno(stdin))) { ttyflags = ISATTY|GOTTIO; tcflush(0, TCIOFLUSH); if (tcgetattr(fileno(stdin), &cookedtio) != 0) { perror("tcgetattr"); exit(1); } rawtio = cookedtio; rawtio.c_iflag = 0; rawtio.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET); rawtio.c_lflag = 0; memset(rawtio.c_cc, 0, NCCS); rawtio.c_cc[VMIN] = 0; /* read returns as soon as data */ /* is available */ rawtio.c_cc[VTIME] = 1; /* or after 1 tenth of a */ /* second at the latest*/ } if ((ttyflags & (ISATTY | ISRAW)) == ISATTY) { tcsetattr(fileno(stdin), TCSAFLUSH, &rawtio); ttyflags |= ISRAW; } /* reset to normal before exit. If your C library does not */ /* have atexit(), you should install ttycooked as a handler */ /* for SIGINT, SIGHUP, etc. */ atexit(ttycooked); } /* print timeout message and quit */ static void noanswer(int state, int who) { fprintf(stderr, "%s not responding in state %d\r\n", who == 'R' ? "Receiver" : "Sender", state); exit(1); } /* send a character with error checking */ static void send(char c) { if (write(1, &c, 1) != 1) { perror("stdout"); exit(1); } } /* get a character (give up and return -1 after trys*0.1 secs if trys is positive) */ static int get(int trys) { char c; do { if (trys <= 0) trys = -1; switch (read(0, &c, 1)) { case 1: return c & 0xff; case 0: break; default: perror("stdin"); exit(1); } } while (--trys); return -1; } /* send named file */ static void sendfile(char *name) { FILE *f; char buf[128]; int bufc = 0; char fn[12]; char *p = name; int csum = 0, i, state = 1; if ((f = fopen(name, "r")) == NULL) { perror(name); return; } /* strip path */ if ((p = strrchr(name, '/')) != NULL) p++; else p = name; /* convert name to upper case (max 8 chars) */ for (i = 0; i < 8 && *p && *p != '.'; i++) { fn[i] = toupper(*p); p++; } while (i < 8) fn[i++] = ' '; /* skip to extension (if there is one) */ while (*p && *p != '.') p++; if (*p == '.') p++; /* copy and convert extension */ for (i = 8; i < 11 && *p; i++) { fn[i] = toupper(*p); p++; } while (i < 11) fn[i++] = ' '; fn[11] = 0; p = fn; while (1) switch (state) { case 1: send('R'); switch (get(50)) { case 'S': state = 2; break; case -1: fputs("Receiver not ready\r\n", stderr); } break; case 2: send('G'); state = 3; break; case 3: send(4); switch (get(20)) { case 8: state = 4; break; case -1: noanswer(state, 'R'); } break; case 4: if (*p == 0) state = 5; else { send(*p); if ((i = get(20)) == *p) p++; else if (i == -1) noanswer(state, 'R'); else state = 3; } break; case 5: send(5); switch (get(20)) { case 9: state = 6; break; case -1: noanswer(state, 'R'); default: state = 3; } break; case 6: if (bufc == 0 && feof(f)) { send(3); state = 9; } else { send(2); switch (get(20)) { case -1: noanswer(state, 'R'); case 'P': state = 7; } } break; case 7: if (bufc == 0) { memset(buf, 0x1a, 128); fread(buf, 128, 1, f); bufc = 128; csum = 0; } csum ^= buf[128 - bufc]; send(buf[128 - bufc--]); state = bufc ? 7 : 8; break; case 8: send(csum); switch (get(20)) { case 'B': state = 6; bufc = 128; break; case 'G': state = 6; bufc = 0; break; case -1: noanswer(state, 'R'); default: state = 8; break; } break; case 9: send(19); return; } } /* receive however many files the other guy wants to send */ static void receivefile(void) { FILE *f = NULL; char buf[128], name[13], *p = NULL; int bufc = 0; int c, csum = 0, i = 0, state = 1; while (1) switch (state) { case 1: switch (get(50)) { case 'R': send('S'); state = 2; break; case -1: fputs("Sender not ready\r\n", stderr); } break; case 2: switch (get(20)) { case 'G': state = 3; break; case -1: noanswer(state, 'S'); } break; case 3: switch (get(20)) { case 4: send(8); p = name; i = 0; state = 4; break; case 19: exit(0); case -1: noanswer(state, 'S'); default: send('X'); } break; case 4: if ((c = get(20)) == -1) noanswer(state, 'S'); /* pick up filename and convert it to */ /* lower case */ if (c < ' ' || i >= 11) { send('X'); state = 3; } else { if (++i == 8) *p++ = '.'; if (c != ' ') *p++ = tolower(c & 0x7f); send(c); } if (i == 11) { *p = 0; if (p > name && p[-1] == '.') p[-1] = 0; state = 5; } break; case 5: switch (get(20)) { case 5: if ((f = fopen(name, "w")) == NULL) { perror(name); send('X'); state = 3; /* protocol weakness: there is */ /* no way to say "this ain't */ /* never gonna work" */ } else { send(9); state = 6; } break; case -1: noanswer(state, 'S'); default: send('X'); state = 3; } break; case 6: switch (get(20)) { case 2: send('P'); p = buf; bufc = 0; csum = 0; state = 7; break; case 3: fclose(f); state = 3; break; case -1: noanswer(state, 'S'); default: send('N'); } break; case 7: if ((c = get(20)) == -1) noanswer(state, 'S'); *p++ = c; csum ^= c; if (++bufc == 128) state = 8; break; case 8: if ((c = get(20)) == -1) noanswer(state, 'S'); if (c != csum) send('B'); else { if (fwrite(buf, 128, 1, f) == 0) { perror(name); exit(1); } send('G'); } state = 6; break; } return; } int main(int argc, char **argv) { int i; ttyraw(); if (argc == 1) receivefile(); else for (i = 1; i < argc; i++) sendfile(argv[i]); ttycooked(); exit(0); }