|
DataMuseum.dkPresents historical artifacts from the history of: CP/M |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about CP/M Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 20352 (0x4f80) Types: TextFile Names: »TELNET.C«
└─⟦23f778bf6⟧ Bits:30005378 BDS C v1.46 & Pascal/MT+ v5.5 (Callan format) └─ ⟦this⟧ »TELNET.C« └─⟦4ada80662⟧ Bits:30005446 Pascal/MT+ v5.5 & XREF & BDS C v1.46 └─ ⟦this⟧ »TELNET.C«
#define TITLE "BDS Telnet version 2.3 (July 1980)" /* Written by Leor Zolman and Leo Kenen July 1980 This version has been modified to obtain all hardware- dependent information from bdscio.h, which must contain the correct hardware specifications for your console and modem ports. If you intend to use this program for high speed (i.e, greater than 300 baud) data transfers, such as maybe over RS232 lines between two machines directly, then the speed of transfer will be limited by the processors involved instead of the baud rate; UNDER SUCH CIRCUMSTANCES, A TRANSFER WILL ONLY WORK IF THESE TWO CONDITIONS ARE MET: 1) The transfer must always performd in BINARY mode, never in TEXT mode, and 2) The receiving processor must be as fast or FASTER than the transmitting processor. That is, a 2 MHz machine may transmit to a 4 MHz machine at, say, 9600 baud, BUT NOT VICE- VERSA. * Telnet assumes that your CP/M console * * I/O device is much faster than your * * modem. On a 2MHz 8080, the modem can * * be receiving at up to 300 baud as long * * as your console whips along (at the * * very least) at about 1200. 4 MHz mach- * * ines might be able to get away with * * slower terminals, but not much slower. * "Telnet" is a program which interacts with a modem to turn your microcomputer into a very versatile terminal. Special commands are entered to the program by typing the character you designate as "SPECIAL", i.e, some character (such as the null or ^A ) which you wouldn't be likely to need transmitted, and then entering the appropriate command letter. Incoming data may be buffered up in RAM memory and dumped to disk whenever you desire (via the "o", "d", "c" and "k" commands), data may be transmitted from disk to modem (via "t" and "a"), or files can be formally transferred in an alternate "checksum" mode which handles handshaking and buffering automatically when interacting with the same program on the other end of the line. During file transfers, you may temporarily pause and later resume the transmission (via the "p" and "r" commands.) There are also various options you can control (see "n", "7", "h" and "l") to adapt operation toward the type of file you wish to transfer. The "q" command closes the output file (if open) and quits to CP/M. The "s" command displays the status of the program. "z" clears the console screen. Any other command letter (such as, for example, "?") causes a list of legal commands to be displayed. Telnet considers "half-duplex" to be any situation in which an echo is received FROM the modem for each character sent TO the modem (whether it is the modem generating the echo OR the remote system being connected to), and "full-duplex" is when no echo comes back. In order to transmit or receive files in the checksum mode, each party must make sure that his modem is operating in FULL-DUPLEX, i.e., that the modem is not generating an automatic echo. Checksumming and handshaking is not allowed under half-duplex operation, since the echoes would mess up handshaking. When you run telnet, it will ask you whether or not you are running half-duplex, and perform accordingly. If you switch from half to full or vice-versa while running the program, use the "h" option to inform telnet of the fact. You must be in "half-duplex" mode to perform checksumed transfers. To perform checksummed file transfer, a connection must first be established between the two parties. If both parties are operating in full duplex, one originating and the other answering, then telnet will both display what each types to the console and send it to the modem. If a file then needs to be transferred, then one user would give the "t" command (to transmit) and the other would give the "o" command (to open an output file.) If both users indicate checksum mode (rather than only one specifing checksum mode which will abort almost immediatly), then telnet will take it from there and perform the transfer. If the sender (transmitter) wants to suspend the transfer temporarily and continue later, he can use the "p" command. When the receiver sees that transmission has been suspended (when no data has been sent for a long time), then HE gives the "p" command also, and both users may type to each other. When ready to resume, the "r" command must be given by the RECEIVER first, and then the sender, to prevent data from being lost. */ #include "bdscio.h" /* System, h'ware constants */ #define SPECIAL 0x1e /* The character you type to signal a Telnet command (should be obscure...I use a "control-shift-uparrow") */ /* The following #defines need not be changed: */ #define ACK 0x06 /* Ascii ACK for handshaking */ #define NAK 0x15 #define EOT 0x04 /* End of transmission */ #define ETX 0x03 /* Abort Transmission */ /* External variable declarations: */ char rflag; /* receiving file open flag */ char tflag; /* transmitting file open flag */ char chflag; /* checksumming enabled flag */ char cflag; /* text-collection enabled flag */ char pflag; /* pausing flag */ char spflag; /* stripping parity bit flag */ char lflag; /* list device enabled flag */ char nflag; /* recognizing nulls flag */ char fflag; /* true if changing CR-LF's into just CR when transmitting */ char lastc; /* last char xmitted */ char dodflag; /* true if displaying outging data */ char didflag; /* true if displaying incoming data */ char hdflag; /* true if effectively working in half-duplex */ char abortf; /* true when file I/O aborted */ char rbufÆBUFSIZÅ; /* file I/O buffer for incoming data file */ char tbufÆSECSIZÅ; /* sector buffer for file being transmitted */ char rnameÆ20Å; /* name of receiving file */ char tnameÆ20Å; /* name of transmitting file */ int rfd, tfd; /* file descriptors */ char *cptr; /* pointer to free space in buf */ unsigned free; /* number of bytes free in buf */ int bcount; /* counts bytes in current block when checksumming */ int scount; /* Number of sectors sent/received */ int checksum; /* the checksum value itself */ char timoutf; /* true if time-out happens while waiting for modem data */ char *i; /* odd-job char pointer */ int dod_sav, did_sav; /* scratch variables */ unsigned bufspace; /* # of bytes available for text collection buffer in ram */ char *buf; /* text collection pointer; will point to the location just after itself */ char toupper(); /* This makes for better code than if we let it default to "int" */ /* Routine to return true if input is present on the modem: */ miready() æ return (inp(MSTAT) & MIMASK) == (MAHI ? MIMASK : 0); å /* Routine to return true if modem is ready to output a byte: */ moready() æ return (inp(MSTAT) & MOMASK) == (MAHI ? MOMASK : 0); å main() æ char c, c2; int n; init(); loop: if (abortf) æ if (rflag) rclose(); if (tflag) tabort(); abortf = 0; å if (tflag && xmit()) æ printf("ØnTransmission complete.Øn"); close(tfd); reset(); å if (abortf) goto loop; if (miready()) æ c = c2 = getmod(); if (spflag) c &= 0x7f; if (tflag && (c == ETX)) æ printf("Reciever has aborted;Øn"); abortf = 1; goto loop; å if (didflag && (c øø nflag) && (c != CPMEOF)) display(c); if (cflag && !pflag) æ if (c øø nflag) if (!free) printf("**BUFFER FULL**Ø007Ø007"); else æ *cptr++ = c; free--; å if (chflag) æ checksum += c2; bcount++; if (bcount == SECSIZ) æ bcount = 0; outmod(checksum >> 8); outmod(checksum); checksum = 0; c = getmod(); if (c == EOT) æ rdump(0); rclose(); printf("Øn%s recieved OKØn",rname); å else if (c == ACK) æ if (cptr > buf+1000) rdump(0); if (!didflag) printf("Good sector <%d>Øn",++scount); outmod(0xFD); å else æ cptr -= SECSIZ; free += SECSIZ; printf("ØnChecksum error. Retrying <%d>Øn",scount+1); outmod(0xFD); timoutf = 0; å å å å å if (kbready()) æ c = getch(); if (c != SPECIAL) æ if (pflag øø (!tflag && !(rflag && chflag))) æ outmod(c); if (dodflag) display(c); å å else special(); å goto loop; å /* Handle special Telnet command: */ special() æ char c; int n; printf("ØnSpecial: "); if ( (c = getchar()) != 'Øn') printf(" "); switch (toupper(c)) æ case 'Øn': return; case SPECIAL: outmod(SPECIAL); printf("Special char sentØn"); break; case '7': spflag = ask("Strip parity"); break; case 'N': nflag = ask("Recognize incoming nulls"); break; case 'F': fflag = ask("Transmit CR-LF pairs as CR only"); break; case 'H': if (rflag øø tflag) æ printf( "Must abort transfer firstØn"); break; å printf("ØnAre you either at half"); printf("-duplex or getting an "); hdflag = ask ("echo"); reset(); break; case 'L': lflag = ask("List incoming data"); break; case 'Z': printf(CLEARS); break; case 'P': if (pflag) printf("Already pausing"); else if (!(tflag øø rflag)) printf("Not transmitting or receiving"); else æ pflag = 1; dod_sav = dodflag; did_sav = didflag; dodflag = !hdflag; didflag = 1; printf("Ok, pausing from %s", tflag ? "transmission" : "collection"); å goto lf; case 'R': if (!pflag) printf("Not pausing"); else æ pflag = 0; dodflag = dod_sav; didflag = did_sav; printf("%s now enabled again.", tflag ? "transmission" : "collection"); å goto lf; case 'K': printf("Text buffer !ZAPPED!"); free = bufspace; cptr = buf; goto lf; case 'V': if (rflag) æ putchar('Øn'); i = buf; while (i < cptr) putchar(*i++); printf("Øn%u bytes free",free); å else printf("No recieving file open"); goto lf; case 'O': if (rflag) rclose(); if (tflag) tabort(); printf("ØnOutput filename? "); gets(rname); rflag = 1; if (!askstuff()) æ rflag = 0; return; å printf("Creating %s...",rname); rfd = fcreat(rname,rbuf); if (rfd == ERROR) æ printf("Cannot create %sØn",rname); reset(); break; å putchar('Øn'); cptr = buf; free = bufspace; rflag = cflag = 1; pflag = checksum = bcount = 0; if (chflag) æ printf("Trying to link..."); do æ c = getmod(); if (abortf) æ printf("aborting...Øn"); unlink(rname); reset(); return; å timoutf = 0; å while (c & 0x7f); printf("linked.Øn"); outmod(0); å break; case 'D': if (rflag) rdump(1); else printf("No output file"); goto lf; case 'C': if (rflag) rclose(); else printf("No output file"); goto lf; case 'Q': if (tflag) tabort(); if (rflag) rclose(); exit(); case 'A': if (tflag øø rflag) æ if (chflag) outmod(ETX); abortf = 1; break; å printf("No transfer to abort.Øn"); goto lf; case 'T': if (tflag) tabort(); if (rflag) rclose(); printf("ØnFile to transmit? "); gets(tname); tflag = 1; if (!askstuff()) æ tflag = 0; return; å tfd = open(tname,0); if (tfd == ERROR) æ printf("Cannot open %sØn",tname); reset(); goto lf; å pflag = checksum = bcount = 0; if (read(tfd,tbuf,1) <=0) æ printf("Read error from %sØn", tname); abortf = 1; return; å if (chflag) æ printf("Trying to link..."); while (1) æ outmod(0); for (n=0; n<5000; n++) if (miready()) æ if( !(getmod() & 0x7f)) æ printf("linked.Øn"); return; å å else if (kbabort()) æ printf("aborting.Øn"); return; å å å break; case 'S': dostat(); goto lf; default: prcoms(); lf: putchar('Øn'); å å /* Print out legal Telnet commands: */ prcoms() æ printf("ØnBDS Telnet commands are:Øn"); printf("Double SPECIAL: send SPECIALØn"); printf("o: Open output file, start collectionØn"); printf("p: Pause (suspend collection or transmission)Øn"); printf("r: Resume after pausingØn"); printf("d: Dump (append) text buffer to output fileØn"); printf("c: Close output file (after dumping buffer)Øn"); printf("v: View contents of text bufferØn"); printf("k: Kill (erase) contents of text bufferØn"); printf("t: Transmit a file to modemØn"); printf("a: Abort transfer of fileØn"); printf("n: accept or ignore NullsØn"); printf("7: select policy regarding Parity bitsØn"); printf("f: select whether to transmit CR-LF as just CRØn"); printf("h: set Half/full duplex modeØn"); printf("l: control CP/M List deviceØn"); printf("z: clear console terminal screenØn"); printf("s: display Status of TelnetØn"); printf("q: dump & close output file (if open) and Quit to CP/M"); å /* Print opening message and initialize program: */ init() æ printf(TITLE); timoutf = cflag = nflag = lflag = pflag = abortf = fflag = 0; spflag = 1; lastc = 0; buf = &buf + 1; bufspace = buf + 500 - topofmem(); /* compute space available */ bufspace = -bufspace; /* for text collection buf */ printf("ØnØnAnswer `y' if either your modem is set to half-duplex,Øn"); printf("or you expect an echo from the system on the"); printf(" other endØn"); printf("of the line; else answer `n':Øn"); hdflag = ask("Do you expect an echo"); reset(); printf("OK; you're on line...ØnØn"); å /* Get all the info pertinent to a file transfer; i.e, whether or not the file is text (and needs parity stripped, nulls ignored, echoing to console, etc.), whether or not checksumming and handshaking are required (they always go together), and make sure the user is in full duplex mode. */ askstuff() æ printf("Øn%s ",rflag ? "recieving" : "transmitting"); if (ask("text (y) or binary data (n) ")) æ nflag = 0; spflag = didflag = 1; dodflag = !hdflag; printf("Stripping parity, ignoring nulls,Øn"); printf(" %sdisplaying %s data.Øn", (rflag ? didflag : dodflag) ? "" : "not ", rflag ? "incoming" : "outgoing"); å else æ spflag = didflag = dodflag = 0; nflag = 1; printf("%s all data verbatim, and notØn", rflag ? "Recieving" : "Sending"); printf("displaying it on the console.Øn"); å putchar('Øn'); printf("Handshaking & checksumming can only happenØn"); printf("if the other computer has this same programØn"); printf("running. Do you want handshaking & checksumming"); chflag = ask(""); if (chflag && hdflag) æ printf("Can't do it unless you can eliminate"); printf(" the echo! Aborting.Øn"); return 0; å scount = 0; return ask("OK...type y to begin, n to abort:"); å /* Routine to print out a string and return true if the user responds positively */ int ask(s) char *s; æ char c; while (1) æ printf("%s ",s); printf("(y/n)? "); c = toupper(getchar()); if (c == 'Y') æ printf("esØn"); return 1; å else if (c == 'N') æ printf("oØn"); return 0; å else putchar('Øn'); å å /* Print out state of Telnet program: */ dostat() æ putchar('Øn'); if (rflag) æ printf("Output file = %sØn",rname); printf("Text buffer has %u bytes free", free); printf("ØnText collection: "); if (cflag) if (pflag) printf("on, but pausingØn"); else printf("onØn"); else printf("offØn"); å else printf("No output fileØn"); if (tflag) æ printf("Transmitting: %s ", tname); if (pflag) printf("(but pausing)"); putchar('Øn'); å else printf("Not transmitting any fileØn"); printf("Incoming nulls are being %sØn", nflag ?"collected" : "ignored"); printf("Parity bits are being %sØn", spflag ?"stripped" : "preserved"); printf("Half-duplex mode: %s", hdflag ? "on" : "off"); å /* Routine to dump contents of the memory text buffer to the output file and clear the buffer for more data: (Note that the "else putchar('Ø0');" clause may not be necessary on your system; this is here only to make up for a strange "feature" of Lifeboat's Northstar CBIOS where disk polling happens during console output, potentially causing bytes to be missed from the modem.) */ rdump(n) æ for (i=buf; i<cptr; i++) putc(*i,rbuf); cptr = buf; free = bufspace; if (n) printf("ØnBuffer writtenØn"); else putchar('Ø0'); å /* Routine to dump and close the output file: */ rclose() æ rdump(1); printf(" Closing %s ",rname); if (!chflag) putc(CPMEOF,rbuf); fflush(rbuf); close(rfd); reset(); putchar('Øn'); å /* Routine to reset telnet */ reset() æ timoutf = rflag = tflag = chflag = cflag = 0; scount = 0; spflag = 1; dodflag = !hdflag; didflag = 1; å /* Get a byte from the modem: */ getmod() æ char c; unsigned n; if (timoutf) return; for (n=20000; !miready() && n; n--) if (kbabort()) return; if (!n) æ timoutf = 1; return 1; å c = inp(MDATA); if (MRESET) outp(MSTAT,MRESETVAL); return c; å /* Output a byte to the modem: */ outmod(c) char c; æ while (!moready()) if (kbabort()) return; outp(MDATA,c); å kbready() æ return bios(2); å /* Get a character from the keyboard: (Uses a direct BIOS instead of going through the BDOS. By naming this the same as the library version of "getchar", we insure that THIS version is used by things like "gets" instead of the library version.) */ getchar() æ char c; c = getch(); if (c == 'Ør') c = 'Øn'; putchar(c); return c; å getch() æ return bios(3); å /* Return true if keyboard hit and SPECIAL typed: */ kbabort() æ if (kbready() && getch() == SPECIAL) æ abortf = 1; return 1; å return 0; å /* Write a character to the console. */ putchar(c) char c; æ if (c == 'Øn') putch2('Ør'); putch2(c); å putch2(c) char c; æ bios(4,c); å /* Write character to console display, and also to system list device if that is enabled: */ display(c) char c; æ if (c==CPMEOF) return; putch2(c); if (lflag) bdos(5,c); å xmit() æ int incheck; int n; char c; if (pflag øø !moready()) return 0; c = tbufÆbcount++Å; checksum += c; if ((!(spflag && (c&0x7f)==CPMEOF && !chflag)) && (!(!chflag && c=='Øn' && lastc=='Ør' && !nflag && fflag))) outmod(c); lastc = c; if (dodflag) display(c); if (bcount != SECSIZ) return 0; bcount = 0; if (!chflag) return !read1(); incheck = (getmod() << 8) + getmod(); if (incheck != checksum) æ for (n=0; n<20000; n++); /* let line settle down */ printf("ØnError. Resending sector %d...Øn",scount+1); outmod(NAK); å else if (read1()) æ if (!dodflag) printf("Good sector <%d>Øn",++scount); outmod(ACK); å else æ outmod(EOT); return 1; å checksum = 0; if (getmod() != 0xFD) æ printf("ØnPhase error; aborting..."); abortf = 1; å return 0; å /* Read a sector of the transmission file: */ read1() æ int i; i = read(tfd, tbuf, 1); if ( i == ERROR) æ printf("ØnRead error from %s; Aborting.Øn", tname); tabort(); å return i; å tabort() æ if (chflag) while (bcount++ != 133) outmod(ETX); printf("ØnTransmission of %s aborted.Øn",tname); close(tfd); reset(); å «eof»