/* TELEDIT.C v1.3 (see TELEDIT.DOC) Modified for BDS C v1.50 by Leor Zolman, 10/82, 2/83 A telecommunications program using Ward Christensen's "MODEM" File Transfer Protocol. Modified by Nigel Harrison and Leor Zolman from XMODEM.C, which was written by: Jack M. Wierda, modified by Roderick W. Hart and William D. Earnest Note that the Modem port interfaces are defined in BDSCIO.H, which must be configured with the correct values for your system. TELED now knows distinguishes between several different modes of operation: "terminal" mode (full duplex), "host" mode, and "half-duplex" mode, as follows (designed by Stephen Stuvner): ---------------------------------------------------------------- TERMINAL MODE: keyboard(char) ---> MoDem MoDem(char) ---> CRT ---------------------------------------------------------------- ECHO (HOST) MODE: keyboard(char) ---> MoDem `---> CRT If (char == CR).... LF ---> MoDem `---> CRT MoDem(char) ---> CRT `---> MoDem If (char == CR).... LF ---> CRT `---> MoDem ---------------------------------------------------------------- 1/2 DUPLEX MODE: keyboard(char) ---> MoDem `---> CRT If (char == CR).... LF ---> CRT MoDem(char) ---> CRT ---------------------------------------------------------------- */ #include #include /* The following three defines must be customized by the user: */ #define HC "\33H*" /* Home cursor and print a "*" */ #define SPECIAL '^'-0x40 /* Gets out of terminal mode */ #define CPUCLK 4 /* CPU clock rate, in MHz */ #define BELL 7 /* ASCII bell */ /* The rest of the defines need not be modified */ #define SOH 1 #define EOT 4 #define ACK 6 #define ERRORMAX 10 #define RETRYMAX 10 #define LF 10 #define CR 13 #define SPS 1500 /*loops per second */ #define NAK 21 #define TIMEOUT -1 #define TBFSIZ NSECTS*SECSIZ #define ML 1000 /* maximum source lines */ #define SEARCH_FIRST 17 /* BDOS calls */ #define SEARCH_NEXT 18 char linebuf[MAXLINE]; /* string buffer */ char fnambuf[50]; /* name of file for text collection */ int nlines, nl; /* number of lines */ int linect; /* current line */ char fflg; /* text collection file flag */ char halfdup; /* half-duplex mode */ char hostflg; /* true in host mode */ char sflg; /* transmit flag */ int kbhit() /* test for console character available: */ { return bios(2); } int getch() /* get char from console raw, no echo */ { while (!kbhit()); return bios(3) & 0x7f; } int getchar() /* get console char with echo, expand CR to CRLF locally: */ { char c; putchar(c = getch()); if (c == '\r') putchar('\n'); return c; } putchar(c) /* write char to console, expand LF to CRLF: */ { if (c == '\n') putchar('\r'); bios(4,c); } send(data) /* send char to modem */ char data; { while(!MOD_TBE) ; MOD_TDATA(data); } main(argc,argv) char **argv; { int i, j, k, l; /* scratch integers */ char *lineptr[ML]; /* allow ML source lines */ char *inl[ML]; /* inserted line pointers */ char *p; /* scratch pointer */ char buf[BUFSIZ]; /* i/o buffer */ int in; /* collect characters at in */ char *alloc(); /* storage allocator */ char *getm(); /* memory procuror */ int baudrate; _allocp = NULL; /* initialize alligator */ linect = nlines = i = 0; fflg = hostflg = halfdup = FALSE; lineptr[i] = NULL; baudrate = 0; /* unknown baud rate */ while(1) { puts(CLEARS); printf("Teledit ver 1.3 \n\n"); printf("T: Terminal mode - no text collection\n"); printf("X: terminal mode with teXt collection\n"); printf(" In terminal mode:\n"); printf(" SPECIAL (control-^): return to menu\n"); printf(" control-E: enter editor\n"); printf("H: toggle Host mode (for user-user typing) (currently: %s)\n", hostflg ? "ENABLED" : "disabled"); printf("G: toGgle half-duplex system mode (currently: %s duplex)\n", halfdup ? "HALF" : "full"); printf("E: Edit collected text\n"); printf("F: Flush text buffer to text collection file\n"); printf("C: flush and Close text collection file\n"); printf("U: select cp/m User area\n"); printf("V: select cp/m driVe\n"); printf("D: print Directory for current drive and user area\n"); printf("S: Send a file, MODEM protocol\n"); printf("R: Receive a file, MODEM protocol\n"); printf("B: select Baud rate (currently "); if (baudrate) printf("running at %d baud",baudrate); else printf("unchanged from last usage"); puts(")\n"); printf("Q: quit\n"); printf("SPECIAL: send SPECIAL char to modem\n"); printf("\nCommand: "); in = toupper(getchar()); putchar('\n'); switch(in) { case 'U': printf("Switch to user area: "); bdos(32,atoi(gets(linebuf))); break; case 'V': printf("Switch to drive: "); bdos(14,toupper(getchar()) - 'A'); break; case 'D': dodir(); goto wait; case SPECIAL: send(SPECIAL); break; case 'T': terminal(); break; case 'X': if (fflg) printf("Text collection already in effect (filename '%s')\n", fnambuf); else { puts("Collection text filename = "); getname(fnambuf); puts("Wait..."); if (fopen(fnambuf,buf) == ERROR) /* if it already exists, */ goto newfile; /* go create it */ else /* else query further */ { printf("\nFile already exists; do you want to "); if(ask("overwrite it")) { newfile: if (fcreat(fnambuf, buf) == ERROR) /* Make new version */ { printf("\nCan't create %s", fnambuf); goto wait; } } else if (ask("Do you want to append to it")) /* append to it */ { if (fappend(fnambuf,buf) == ERROR) { printf("\nCan't append for some reason\n"); goto wait; } } else { printf("File operations aborted.\n"); goto wait; } } fflg = TRUE; } printf("\nReady\n"); while(1) { if(MOD_RDA) { putchar (in = MOD_RDATA & 0x7F); if (hostflg) { send(in); if (in == '\r') { send('\n'); putchar('\n'); } } if(in >= ' ') linebuf[i++] = in; else { if(in == '\n') continue; if(in == CR) { linebuf[i++] = '\n'; linebuf[i] = NULL; p = getm(linebuf); if(p == NULL) continue; lineptr[nlines++] = p; lineptr[nlines] = NULL; if(nlines > 500) putchar(BELL); i = 0; } else if(in == '\t' || in == '\f') linebuf[i++] = in; else continue; } } if(kbhit()) { in = getch(); if(in == SPECIAL) break; else if (in == ('E' & 037)) { ed(lineptr); printf("Terminal mode:\n\n"); } else send(in); if (hostflg || halfdup) { putchar(in); if (in == '\r') { putchar('\n'); if (hostflg) send('\n'); } } } } break; case 'S': case 'R': printf("File to %s = ", (in == 'S') ? "send" : "receive"); getname(linebuf); if(in == 'R') readfile(linebuf); else { if(fopen(linebuf, buf) == ERROR) { printf("Can't open %s\n", linebuf); goto wait; } sendfile(linebuf); } putchar(BELL); /* beep when done */ goto wait; case 'E': ed(lineptr); break; case 'D': dump(lineptr, buf); goto wait; case 'G': if (halfdup = !halfdup) { puts("Going into half duplex mode\n"); if (hostflg) { puts("(HOST mode is being automatically disabled)\n"); hostflg = FALSE; } goto wait; } goto Hterm; case 'H': if (hostflg = !hostflg) { puts("Going into HOST (user-user communication) mode\n"); if (halfdup) { puts("(HALF DUPLEX mode is being automatically disabled)\n"); halfdup = FALSE; } goto wait; } Hterm: puts("Going back to normal terminal mode\n"); goto wait; case 'B': /**** This section is left for the user to configure **** * 1) ask the user what baudrate to switch to, * * 2) make the "baudrate" variable equal the new rate * * 3) actually set the baudrate in whichever way is * * appropriate to your hardware. * * Note that the "baudrate" variable comes up equal to * * 0 to indicate that no explicit baud rate has been * * selected by the user (i.e., the baudrate is in what- * * ever state it was in before TELED was invoked) * *********************************************************/ puts("Baudrate selection is left for the user to configure.\n"); goto wait; case 'Q': if (fflg) { printf("\nSave file \"%s\" to ",fnambuf); if (ask("disk")) { dump(lineptr, buf); putc(CPMEOF, buf); fclose(buf); } } exit(0); case 'C': if (fflg) { printf("Closing %s...",fnambuf); dump(lineptr, buf); putc(CPMEOF, buf); fclose(buf); fflg = FALSE; } else puts("No currently active text collection file\n"); wait: puts("\n\nHit any key to return to menu: "); getchar(); } } } dodir() { char dmapos; /* value returned by search calls */ char first_time; /* used in search routine */ char tmpfn[20]; /* temp filename buffer */ char fcb[36]; int colno; /* column count */ int i; char name[15]; int drive; bdos(26,BASE+0x80); /* ensure default DMA after read/write */ printf("\nFiles = "); if (getline(name,15) < 2) setfcb(fcb,"*.*"); else setfcb(fcb,name); drive= (fcb[0]==0 ? bdos(25) : fcb[0]-1 ) ; puts(CLEARS); printf("Directory for Drive %c, user area %d:\n\n", drive+'A', bdos(32,0xff)); colno = 1; first_time = TRUE; while (1) { dmapos = bdos(first_time ? SEARCH_FIRST : SEARCH_NEXT,fcb); if (dmapos == 255) break; first_time = FALSE; hackname(tmpfn,(BASE + 0x80 + dmapos * 32)); puts(tmpfn); for (i = strlen(tmpfn); i < 15; i++) putchar(' '); if ((colno += 15) > 65) { putchar('\n'); colno =1; } } } hackname(dest,source) char *dest, *source; { int i,j; j = 0; for (i = 1; i < 9; i++) { if (source[i] == ' ') break; dest[j++] = source[i]; } if (source[9] != ' ') dest[j++] = '.'; for (i = 9; i < 12; i++) { if (source[i] == ' ') break; dest[j++] = source[i]; } dest[j] = '\0'; return dest; } dump(lineptr, buf) /* dump text buffer */ char **lineptr, *buf; { int i; for(i = 0; lineptr[i] != NULL; i++) if(fputs(lineptr[i], buf) == ERROR) printf("\n\7Error writing txt, disk full?\n"); lineptr[0] = linect = nlines = _allocp = 0; } ed(lineptr) /* editor */ char **lineptr; { char in, *inl[ML], *p, buf[BUFSIZ]; int i, j, k, l; char *getm(), *alloc(); if(!fflg) { printf("\n\7No text buffer to edit.\n"); return; } printf("\nedit\n*"); nl = 22; while (in = getchar()) switch (tolower(in)) { case 'a': puts("Filename to yank = "); if(fopen(getname(linebuf), buf) == ERROR) { printf ("\r Cannot open %s.\r*", linebuf); continue; } for(i = 0; fgets(linebuf, buf); i++) { inl[i] = getm(linebuf); if(inl[i] == NULL) break; } inl[i] = NULL; iblock(lineptr, inl, linect); show (lineptr, linect, nl); continue; case 'b': linect = 0; show (lineptr, linect, nl); continue; case 'q': printf(CLEARS); return; case 'f': gets(linebuf); if((i = find(lineptr, linect)) >= 0) { linect = i; show (lineptr, linect, nl); } else if((i = find(lineptr, 0)) >= 0) { linect = i; show (lineptr, linect, nl); } else { printf(HC); printf(" not found\r*"); } continue; case 'i': j = min(linect, 5); if(j == 0) printf(CLEARS); else { show(lineptr, (linect - j), j); j++; printf("\b "); for(; j; j--) putchar('\n'); } while(1) { gets(linebuf); for(i = 0; linebuf[i]; i++) if(linebuf[i] == CPMEOF) break; if(linebuf[i] == CPMEOF) break; linebuf[i++] = '\n'; linebuf[i] = NULL; inl[j++] = getm(linebuf); } inl[j] = NULL; iblock(lineptr, inl, linect); show (lineptr, linect, nl); continue; case 'k': putchar('\n'); show1(lineptr, linect); kill(lineptr, linect, 1); continue; case 'l': gets(linebuf); if((i = lfind(lineptr, linect)) >= 0) { linect = i; show(lineptr, linect, nl); } else if((i = lfind(lineptr, 0)) >= 0) { linect = i; show(lineptr, linect, nl); } else { printf(HC); printf(" not found\r*"); } continue; case 'o': putchar('\n'); i = linect; while(gets(linebuf)) { for(j = 0; linebuf[j]; j++) if(linebuf[j] == CPMEOF) break; if(linebuf[j] == CPMEOF) break; linebuf[j++] = '\n'; linebuf[j] = NULL; if(lineptr[i]) free(lineptr[i]); lineptr[i++] = getm(linebuf); if(i > nlines) lineptr[++nlines] = NULL; } show (lineptr, linect, nl); continue; case 'p': linect = min((linect + nl), nlines); show (lineptr, linect, nl); continue; case 's': gets(linebuf); nl = max((atoi(linebuf)), 1); show (lineptr, linect, nl); continue; case 'z': linect = nlines; show(lineptr, linect, nl); continue; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': linebuf[0] = in; for (i = 1; (isdigit(in = getchar())); i++) linebuf[i] = in; linebuf[i] = NULL; i = atoi(linebuf); if (i < 0) j = max((linect + i), 0); else j = min((linect + i), nlines); in = tolower(in); if(j > linect && in == 'k') kill(lineptr, linect, (j - linect)); else linect = j; if (in == 'p') linect = max((linect-nl), 0); show (lineptr, linect, nl); continue; case '#': printf (" of lines: %d\r*", nlines); continue; case '\n': if (lineptr[linect] != NULL) { show1(lineptr, linect); linect++; } else printf (HC); continue; case ' ': if(linect) linect--; show(lineptr, linect, nl); continue; case ('E'&037): send(in); printf("\n^E sent\n"); return; default: printf(" ?\r*"); } } shocrt(sec,try,tot) int sec,try,tot; { if(sflg) printf("Sending #%d (Try=%d Errs=%d) \r", sec, try, tot); else printf("Awaiting #%d (Try=%d, Errs=%d) \r", sec, try, tot); if(try && tot) putchar('\n'); } receive(seconds) int seconds; { char data; int lpc,seccnt; for (lpc = 0; lpc < CPUCLK; lpc++) { seccnt = seconds * SPS; while (!MOD_RDA && seccnt--); if(seccnt >= 0) { data = MOD_RDATA; return(data); } } return(TIMEOUT); } char *getname(linbuf) /* get a reasonable filename from user */ char *linbuf; { int i; char c; while (1) { gets(linbuf); if (!*linbuf) /* reject null filename */ goto reject; for (i = 0; c = linbuf[i]; i++) { if (c < ' ' || c > 0x7F) goto reject; } break; reject: puts("\nFilename = "); continue; } return linbuf; } purgeline() { while (MOD_RDA) MOD_RDATA; /* purge the receive register */ } readfile(file) char *file; { int j, firstchar, sectnum, sectcurr, sectcomp, errors; int toterr,checksum; int errorflag, fd; int bufctr; char buffer[BUFSIZ]; sflg = FALSE; fd = creat(file); if(fd == -1) { printf("Teledit: cannot create %s\n", file); return; } printf("\nReady to receive %s\n", file); sectnum = 0; errors = 0; toterr = 0; bufctr = 0; purgeline(); shocrt(0,0,0); do { errorflag = FALSE; do firstchar = receive (10); while(firstchar != SOH && firstchar != EOT && firstchar != TIMEOUT); if(firstchar == TIMEOUT) errorflag = TRUE; if(firstchar == SOH) { sectcurr = receive (1); sectcomp = receive (1); if((sectcurr + sectcomp) == 255) { if(sectcurr == ((sectnum + 1) & 0xFF)) { checksum = 0; for(j = bufctr;j < (bufctr + SECSIZ);j++) { buffer[j] = receive (1); checksum = (checksum + buffer[j]) & 0xff; } if(checksum == receive (1)) { errors = 0; sectnum = sectcurr; bufctr = bufctr + SECSIZ; if(bufctr == TBFSIZ) { bufctr = 0; write(fd, buffer, NSECTS); } shocrt(sectnum,errors,toterr); send(ACK); } else errorflag = TRUE; } else if(sectcurr == sectnum) { do; while(receive (1) != TIMEOUT) ; send(ACK); } else errorflag = TRUE; } else errorflag = TRUE; } if(errorflag == TRUE) { errors++; if(sectnum) toterr++; while(receive (1) != TIMEOUT); shocrt(sectnum,errors,toterr); send(NAK); } } while(firstchar != EOT && errors != ERRORMAX); if((firstchar == EOT) && (errors < ERRORMAX)) { send(ACK); bufctr = (bufctr + SECSIZ - 1) / SECSIZ; write(fd, buffer, bufctr); close(fd); printf("\nDone -- returning to menu:\n"); } else printf("\n\7Aborting\n\n"); } sendfile(file) char *file; { char *npnt; int j, sectnum, sectors, attempts; int toterr,checksum; int bufctr, fd; char buffer[BUFSIZ]; sflg = TRUE; fd = open(file,0); if(fd == -1) { printf("\nTeledit: %s not found\n", file); return; } else printf("\nFile is %d sectors long.\n",cfsize(fd)); purgeline(); attempts=0; toterr = 0; shocrt(0,0,0); while((receive (10) != NAK) && (attempts != 8)) { attempts++; shocrt(0,attempts,0); } if (attempts == 8) { printf("\nTimed out awaiting initial NAK\n"); return; } attempts = 0; sectnum = 1; while((sectors = read(fd, buffer, NSECTS)) && (attempts != RETRYMAX)) { if(sectors == -1) { printf("\nFile read error.\n"); break; } else { bufctr = 0; do { attempts = 0; do { shocrt(sectnum,attempts,toterr); send(SOH); send(sectnum); send(-sectnum-1); checksum = 0; for(j = bufctr;j < (bufctr + SECSIZ);j++) { send(buffer[j]); checksum = (checksum + buffer[j]) & 0xff; } send(checksum); purgeline(); attempts++; toterr++; } while((receive (10) != ACK) && (attempts != RETRYMAX)); bufctr = bufctr + SECSIZ; sectnum++; sectors--; toterr--; } while((sectors != 0) && (attempts != RETRYMAX)); } } if(attempts == RETRYMAX) printf("\nNo ACK on sector, aborting\n"); else { attempts = 0; do { send(EOT); purgeline(); attempts++; } while((receive (10) != ACK) && (attempts != RETRYMAX)); if(attempts == RETRYMAX) printf("\nNo ACK on EOT, aborting\n"); } close(fd); printf("\nDone -- Returning to menu:\n"); } ask(s) char *s; { char c; again: printf("%s (y/n)? ", s); c = tolower(getchar()); if(c == 'y') { puts("es\n"); return 1; } else if(c == 'n') puts("o\n"); else { printf(" \7Yes or no, please...\n"); goto again; } return 0; } find(lineptr, linect) /*Find a line having the pattern in linebuf */ char *lineptr[]; int linect; { int i; for(i = linect; lineptr[i] != NULL; i++) if(pat(lineptr[i], linebuf) >= 0) return(i); return(-1); } kill(lineptr, linect, nl) /* erase lines */ char *lineptr[]; int linect, nl; { int i, j; for (i = linect; lineptr[i] != NULL && nl > 0; i++, nl--) { free(lineptr[i]); nlines--; } lineptr[linect] = NULL; if(lineptr[i] != NULL) { j = (nlines - linect) * 2; movmem(&lineptr[i], &lineptr[linect], j + 2); } } lfind(lineptr, linect) /* find pattern at beginning of a line */ char *lineptr[]; int linect; { int i, j; char line[MAXLINE]; j = strlen(linebuf); for (i = linect; lineptr[i] != NULL; i++) { strcpy(line, lineptr[i]); line[j] = NULL; if(strcmp(line, linebuf) == 0) return i; } return -1; } pat(s, t) /* pattern match..*/ char s[], t[]; { int i, j, k; for(i = 0; s[i] != '\0'; i++) { for(j = i, k=0; t[k] != '\0' && s[j] == t[k]; j++, k++) ; if(t[k] == '\0') return(i); } return(-1); } show (lineptr, linect, nl) /* screen current frame */ char *lineptr[]; int linect, nl; { int i; printf (CLEARS); putchar('\n'); for (i = linect; i < (linect+nl) && lineptr[i] != NULL; i++) printf("%s", lineptr[i]); printf (HC); return(i); } show1(lineptr, linect) char **lineptr; int linect; { int i; for(i = 0; i < nl; i++) putchar('\n'); if((linect + nl) >= nlines) putchar('\n'); else printf("%s", lineptr[linect+nl]); printf(HC); for(i = 0; i < 78; i++) putchar(' '); printf("\r*"); } terminal() /* terminal mode, no text */ { int in; while(1) { if(MOD_RDA) { putchar(in = MOD_RDATA); if (hostflg) { send(in); if (in == '\r') { send('\n'); putchar('\n'); } } } if(kbhit()) { in = getch(); if (in == SPECIAL) return; else send(in); if (hostflg || halfdup) { putchar(in); if (in == '\r') { putchar('\n'); if (hostflg) send('\n'); } } } } } char *getm(line) /* get memory for line, store it, return pointer */ char line[]; { char *p, *alloc(); if ((p = alloc(strlen(line) + 1)) != NULL) strcpy(p, line); return(p); } iblock(rb, ib, cl) /* insert block ib into rb at cl */ char *rb[], *ib[]; int cl; { int i, j; j = 0; if (rb[cl]) { for (i = 0; ib[i]; i++) ; j = (nlines - cl) * 2; movmem (&rb[cl], &rb[cl+i], j + 2); } for (i = 0; ib[i]; i++, cl++) rb[cl] = ib[i]; if(!j) rb[cl] = NULL; nlines += i; return cl; /* return new current line */ }