home *** CD-ROM | disk | FTP | other *** search
- /*
- pmp.c -- main program
-
- Poor Man's Packet (PMP)
- Copyright (c) 1991 by Andrew C. Payne All Rights Reserved.
-
- Permission to use, copy, modify, and distribute this software and its
- documentation without fee for NON-COMMERCIAL AMATEUR RADIO USE ONLY is hereby
- granted, provided that the above copyright notice appear in all copies.
- The author makes no representations about the suitability of this software
- for any purpose. It is provided "as is" without express or implied warranty.
-
- July, 1989
- Andrew C. Payne
- */
-
- /* ----- Includes ----- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <alloc.h>
- #include <mem.h>
- #include <bios.h>
- #include <dos.h>
- #include <io.h>
- #include <ctype.h>
- #include <time.h>
- #include <string.h>
-
- #define EXTERN
- #include "keys.h"
- #include "pmp.h"
- #include "ports.h"
-
- int holdmode;
-
- /* ----- Title Screen ----- */
-
- /* TitleScreen()
- Shows the title screen.
- */
- static void TitleScreen(void)
- {
- clrscr();
- normal();
- cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓ ▓▓ ▓▓ ▓▓ ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▓▓ oor ▓▓ ▓▓ an's ▓▓ acket ▄▄▄▄▄▄▄\r\n");
- cprintf(" ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄\r\n\n");
- bright();
- cprintf(" A Complete Packet Radio Program For The IBM PC\r\n\n");
- normal();
- cprintf(" Version %s of %s \r\n\n\n",VERSION,__DATE__);
- cprintf(" By Andrew C. Payne, N8KEI\r\n");
- cprintf(" with special thanks to Kevin Feeney, WB2EMS\r\n\n");
- cprintf(" Copyright (c) 1989, 1990, 1991 ACP\r\n");
- cprintf(" All rights reserved.\r\n");
- }
-
- /* ----- Subroutines ----- */
-
- /* stoupper(s)
- Convert string to upper case.
- */
- void stoupper(char *s)
- {
- while(*s) {
- *s = toupper(*s);
- s++;
- }
- }
-
- /* wait()
- Prompts user for keystroke.
- */
- void wait(void)
- {
- bright();
- gotoxy(1,25);
- cprintf(" ----- Press any key to continue ------");
- (void)getkey();
- }
-
- /* WaitKey(p)
- Waits for a keystroke, calling PeriodicHook() while waiting.
- Returns keystroke. If 'p' is non-zero, then a prompt is shown
- on line 'p'.
- */
- KEY WaitKey(int prompt)
- {
- if(prompt)
- CenterTitle(prompt," Press any key to resume ");
-
- while(!keypressed())
- PeriodicHook();
-
- return getkey();
- }
-
- /* ----- Sound Control ----- */
-
- /* StartSound(freq,time)
- Starts a sound of the given frequency and duration (in 50 ms BIOS
- clock ticks).
- */
- void StartSound(int freq, int time)
- {
- if(Sound) {
- sound(freq);
- SoundEnd = BiosTime() + time;
- }
- }
-
- /* ----- Upper Level User Commands ------ */
-
- /* DoHelp()
- Handles the help screen.
- */
- void DoHelp(void)
- {
- SaveScreen(TRUE,TRUE);
- CenterTitle(1," PMP Help ");
- GotoXY(1,2);
- _uputs(NormalAttr,"\n");
- _uputs(NormalAttr,"│ ALT-C Connect F1-F6 User defined macros\n");
- _uputs(NormalAttr,"│ ALT-B Send Beacon\n");
- _uputs(NormalAttr,"│ ALT-D Disconnect\n");
- _uputs(NormalAttr,"│ ALT-H Show help (this screen) UP,DOWN Scrollback line at a time\n");
- _uputs(NormalAttr,"│ ALT-J Screen shapshot to file PGUP/DN Scrollback page at a time\n");
- _uputs(NormalAttr,"│ ALT-L Download/capture a text file\n");
- _uputs(NormalAttr,"│ ALT-N Nodes recently heard\n");
- _uputs(NormalAttr,"│ ALT-P Pause screen\n");
- _uputs(NormalAttr,"│ ALT-S Show system status\n");
- _uputs(NormalAttr,"│ ALT-U Upload a text file\n");
- _uputs(NormalAttr,"│ ALT-W Write scrollback to disk\n");
- _uputs(NormalAttr,"│ ALT-X Exit PMP\n\n");
- _uputs(NormalAttr,"│ ────────────────────────────────────────────────────────────────────────\n");
- _uputs(NormalAttr,"│ Comments, questions, and money to:\n\n");
- _uputs(NormalAttr,"│ Andrew C. Payne\n");
- _uputs(NormalAttr,"│ Route 3, Box 78-Q\n");
- _uputs(NormalAttr,"│ Berkeley Springs, WV 25411");
- (void)WaitKey(23);
- RestoreScreen();
- }
-
- /* DoDebug()
- Toggle debug mode on and off.
- */
- void DoDebug(void)
- {
- DebugMode = !DebugMode;
- putstring(1,25,1,InvAttr, DebugMode ? "■" : " ");
- }
-
- /* DoConnect()
- Prompts user for connect path, starts connection.
- */
- static void DoConnect(void)
- {
- char path[80];
-
- /* if we are disconnected, prompt for connect path and initiate connect */
- if(AX25_Control.state == DISCONNECTED) {
- if(GetInput("Connect to --> ",path,60)) {
- memcpy(&AX25_Control.header.source,
- &MyCall,sizeof(struct ax25_addr));
- AX25_Control.header.pid = PID_TEXT;
- AX25_Control.type = TEXT;
- if(SetAX25Path(path,&AX25_Control.header))
- AX25_Open();
- }
- }
- }
-
- /* DoStatus()
- Shows the status of the node, primarily the health counters.
- */
- static void DoStatus(void)
- {
- char s[80];
- long t;
- struct tm *tm;
- int hours,minutes,secs;
- #ifdef TRACE
- extern int nlogs;
- #endif
- SaveScreen(TRUE,TRUE);
- CenterTitle(1," PMP Status ");
- GotoXY(1,4);
- time(&t);
- tm = localtime(&t);
- sprintf(s,"│ Status at %02d/%02d/%02d %02d:%02d:%02d ",
- tm->tm_mon+1, tm->tm_mday, tm->tm_year, tm->tm_hour,
- tm->tm_min, tm->tm_sec);
- _uputs(NormalAttr,s);
- t -= StartTime;
- secs = t % 60;
- t /= 60;
- minutes = t % 60;
- t /= 60;
- hours = t % 24;
- t /= 24;
- sprintf(s," Uptime %d %02d:%02d:%02d\n\n",
- (int)t, hours, minutes, secs);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld good frames received\n",RXCount);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld framing errors\n",RXFrameErr);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld checksum errors\n\n",RXCRCErr);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld receive queue overflows\n",RXQOverflow);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld receive buffer overflows\n",RXBOverflow);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld REJ frames received\n",RXREJ);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld FRMR frames received\n\n",RXFRMR);
- _uputs(NormalAttr,s);
- sprintf(s,"│ %8ld frames transmitted\n\n",TXCount);
- _uputs(NormalAttr,s);
- #ifdef TRACE
- sprintf(s,"│ %8ld frames in trace log\n\n",(long)nlogs);
- _uputs(NormalAttr,s);
- #endif
- sprintf(s,"│ %12ld bytes free\n\n",coreleft());
- _uputs(NormalAttr,s);
-
- if(Capturing()) {
- sprintf(s,"│ Capturing to %s (%ld bytes captured)",
- CaptureFile, CaptureSize);
- _uputs(NormalAttr,s);
- }
-
- (void)WaitKey(23);
- RestoreScreen();
- }
-
- /* DoPause()
- Pause the screen.
- */
- void DoPause(void)
- {
- SaveScreen(TRUE,FALSE);
- (void)WaitKey(23);
- RestoreScreen();
- }
-
- /* DoUpload()
- Uploads a file.
-
- Crude first version, error handling sucks.
- */
- void DoUpload(void)
- {
- char fname[40]; /* file name to upload */
- char inbuf[256]; /* input buffer */
- char text[90]; /* temp text area */
- long filesize; /* length of file in bytes */
- long starttime; /* start time of upload */
- int uploadtime; /* time used to upload */
- FILE *upfile;
- int cursor;
- int len; /* # of bytes acutally read */
-
- /* can't upload unless connected */
- if(!Connected()) {
- sound(400);
- delay(200);
- nosound();
- return;
- }
-
- /* get filename, open file */
- if(GetInput("Upload what file? --> ",fname,40)) {
- if(!*fname)
- return;
-
- stoupper(fname);
- upfile = fopen(fname,"r");
- if(upfile == NULL) { /* error on open */
- uprintf(InvAttr," Error opening '%s' : %s \n",fname, sys_errlist[errno]);
- return;
- }
-
- /* show upload status */
- filesize = filelength(fileno(upfile));
- cursor = cursave();
- curoff();
- sprintf(text," Uploading %-40s Press <ESC> to abort",fname);
- putstring(1,24,80,InvAttr,text);
- starttime = BiosTime();
-
- /* read lines from file */
- while(len = fread(inbuf, 1, 256, upfile)) {
-
- /* wait for space in AX.25 queue, then enqueue the data */
- do {
- PeriodicHook(); /* wait to empty */
- if(keypressed() && (getkey() == ESC)) {
- Notify(" Upload aborted.");
- goto done;
- }
- } while(AX25QFull());
-
- LinkSend((byte *)inbuf, len); /* send the data */
- uputtext(BrightAttr, inbuf, len);
- }
- uploadtime = (BiosTime() - starttime) / BIOSSEC;
- if(uploadtime == 0)
- uploadtime = 1;
-
- /* if terminated for any reason other than EOF, handle error */
- if(ferror(upfile)) {
- uprintf(InvAttr," Error reading '%s' : %s \n",
- fname, sys_errlist[errno]);
- } else {
- if(Sound) { /* two beeps */
- sound(2600);
- delay(100);
- nosound();
- delay(50);
- sound(2600);
- delay(100);
- nosound();
- }
- sprintf(text," Upload complete. (%d seconds for %ld bytes = %ld bytes/sec)",
- uploadtime, filesize, (long)(filesize/uploadtime));
- Notify(text);
- }
- done:
- clear_area(24,1,80); /* clean up */
- currest(cursor);
- fclose(upfile);
- }
- }
-
- /* DoCapture()
- Toggles file capture (log to disk).
-
- Note: The test for a file aready existing could be cleaned up
- using 'access'.
- */
- void DoCapture(void)
- {
- char fname[41]; /* capture filename */
- char text[80]; /* temp text area */
- FILE *test;
-
- /* If already capturing, close file */
- if(Capturing()) {
- sprintf(text," Closing capture file %s (%ld bytes)",
- CaptureFile, CaptureSize);
- Notify(text);
- CloseCapture();
- putstring(60,25,3,StatusAttr,"");
- return;
- }
-
- /* get filename */
- if(GetInput("Capture file? --> ",fname,40)) {
- if(!*fname)
- return;
-
- if((test = fopen(fname,"r")) != NULL) {
- fclose(test);
- if(!GetInput("File already exists. Overwrite? [Y/N]",text,2))
- return;
- if(toupper(*text) != 'Y')
- return;
- }
- stoupper(fname);
- OpenCapture(fname);
- putstring(60,25,3,StatusAttr,"Cap");
- }
- }
-
- /* DoSnapshot()
- Write a snapshot of the current screen to a file.
- */
- void DoSnapshot(void)
- {
- char fname[41]; /* capture snapshot */
- FILE *test;
- char temp[160]; /* temp area */
- char line[81]; /* output line */
- int i,l; /* screen line */
- char *p,*q;
-
- /* open snapshot file*/
- if(!GetInput("Snapshot to what file? --> ",fname,40))
- return;
- if(!*fname)
- return;
-
- if((test = fopen(fname,"r")) != NULL) {
- fclose(test);
- if(!GetInput("File already exists. Overwrite? [Y/N]",line,2))
- return;
- if(toupper(*line) != 'Y')
- return;
- }
- if((test = fopen(fname,"w")) == NULL) {
- Notify(" Can't open snapshot file.");
- return;
- }
-
- /* write contents of screen to file */
- for(l=1; l<24; l++) {
- gettext(1,l,80,l,temp);
- q = line;
- p = temp;
- for(i=0; i<80; i++) { /* extract contents */
- *q++ = *p++;
- p++;
- }
- do { /* trim line */
- q--;
- } while(q >= line && (*q == '\0' || *q == ' '));
- *++q = '\n';
- *++q = '\0';
- fputs(line, test); /* should error check this */
- }
- fclose(test);
- }
-
- /* DoWrite()
- Writes the contents of the scrollback buffer to the file specified.
- */
- void DoWrite(void)
- {
- char fname[41]; /* capture snapshot */
- FILE *test;
- char line[81]; /* output line */
-
- /* open snapshot file*/
- if(!GetInput("Write scrollback buffer to what file? --> ",fname,40))
- return;
- if(!*fname)
- return;
-
- if((test = fopen(fname,"r")) != NULL) {
- fclose(test);
- if(!GetInput("File already exists. Overwrite? [Y/N]",line,2))
- return;
- if(toupper(*line) != 'Y')
- return;
- }
- WriteScrollback(fname);
- }
-
- /* ----- Main Loop ----- */
-
- /* PeriodicHook()
- When in an idle loop, this routine should be called as much as
- possible. If the carrier is down, goes to RX, handles timers
- and flushes the TX queue.
- */
- void PeriodicHook(void)
- {
- long t;
-
- /* Go to RX if a carrier is detected */
- while(RXCarrier()) {
- if(RXLevel1()) /* receive incoming */
- RXProcess(); /* process incoming */
- Pwait = 0;
- }
-
- /* process AX.25 link transmit items, if any */
- AX25_Flush();
-
- /* check for expired timers */
- t = BiosTime();
- if(AX25_Control.t1 != 0 && AX25_Control.t1 <= t)
- AX25_Expire(1);
- if(AX25_Control.t3 != 0 && AX25_Control.t3 <= t)
- AX25_Expire(3);
- if(SoundEnd && t >= SoundEnd) { /* sound timeout */
- nosound();
- SoundEnd = 0;
- }
- if(BeaconEnd && t >= BeaconEnd) { /* beacon timeout */
- SendBeacon();
- StartBeacon();
- }
-
- /* do p-persistence, and flush anything in the TX queue */
- if(Pwait == 0 || t > Pwait) { /* attempt transmission */
- if(rand() < Ppersist) { /* we are a go? */
- Pwait = 0;
- TXQEmpty(); /* Transmit! */
- } else
- Pwait = t + Slottime; /* wait a bit */
- }
- }
-
- /* MainLoop()
- Main command handling loop.
- */
- static void MainLoop(void)
- {
- char buf[82]; /* input buffer */
- char temp; /* temp character storage */
- int p; /* position in input buffer */
- KEY k; /* keypress */
- byte c;
- int state; /* current connected state */
- int scrollback; /* TRUE if scrolling back */
- int i;
-
- /* initalize */
- InitKeybuffer();
- scrollback = FALSE;
- p = 0;
- gotoxy(1,24);
- state = Connected();
-
- /* main loop */
- while(TRUE) {
-
- /* loop until keypress or change in state */
- while(!keypressed() && (state == Connected()))
- PeriodicHook(); /* do RX, TX, timers */
- state = Connected();
-
- /* handle the cursor */
- if(Connected()) /* if connected, show cursor */
- curon();
- else {
- if(p) { /* clear any text */
- clear_area(24,1,80);
- p = 0;
- }
- curoff();
- }
-
- /* handle any user keystrokes */
- while(k = Nextkey()) {
-
- /* exit scrollback mode, if appropriate */
- if(scrollback && k != UP && k != DOWN
- && k != PGDN && k != PGUP && k != HOME) {
- EndScrollback();
- scrollback = FALSE;
- }
-
- /* ASCII keypress, show on bottom line */
- if((c = asciicode(k)) && Connected()) {
- bright();
- switch(c) {
- case '\r': /* send */
- buf[p++] = '\n';
- buf[p] = '\0';
- uputs(BrightAttr, buf);
- LinkSend((byte *)buf, p);
- clear_area(24,1,80);
- p = 0;
- break;
- case '\b': /* backspace */
- if(p>0) {
- cprintf("\b \b");
- p--;
- }
- break;
- case ' ': /* test for autowrap */
- if(AutoWrap && p > AutoWrap) {
- i = p-1;
- while(i && buf[i] != ' ')
- i--;
- if(i) { /* wrap word */
- buf[i++] = '\n';
- temp = buf[i];
- buf[i] = '\0';
- LinkSend((byte *)buf,i);
- uputs(BrightAttr,buf);
- buf[i] = temp;
- buf[p] = '\0';
- strcpy(buf, buf+i);
- p -= i;
- clear_area(24,1,80);
- if(p)
- putstring(1,24,80,BrightAttr,buf);
- }
- }
- /* note fall through */
- default: /* char */
- if(p < 79)
- putch(buf[p++] = c);
- }
-
- /* else, function key */
- } else {
- switch(k) {
- case DOWN: /* scroll */
- if(scrollback) {
- if(MoveScrollback(1)) {
- EndScrollback();
- scrollback = FALSE;
- }
- }
- break;
- case UP: /* scroll */
- if(!scrollback)
- scrollback = StartScrollback();
- if(scrollback)
- MoveScrollback(-1);
- break;
- case PGUP: /* scroll */
- if(!scrollback)
- scrollback = StartScrollback();
- if(scrollback)
- MoveScrollback(-23);
- break;
- case PGDN: /* scroll */
- if(scrollback) {
- if(MoveScrollback(23)) {
- EndScrollback();
- scrollback = FALSE;
- }
- }
- break;
- case HOME: /* scroll */
- if(!scrollback)
- scrollback = StartScrollback();
- if(scrollback)
- MoveScrollback(-32000);
- break;
- case ALTX: /* exit */
- if(Connected()) {
- sound(400);
- delay(200);
- nosound();
- } else
- return;
- break;
- case ALTB: /* beacon */
- SendBeacon();
- break;
- case ALTC: /* connect */
- DoConnect();
- break;
- case ALTD: /* discnnct */
- AX25_Close();
- break;
- case ALTH: /* help */
- DoHelp();
- break;
- case ALTJ: /* snapshot */
- SaveMessage();
- DoSnapshot();
- RestoreMessage();
- state = -1;
- break;
- case ALTN: /* nodes heard */
- DoHeard();
- break;
- case ALTP: /* pause */
- DoPause();
- break;
- case ALTS: /* node status */
- DoStatus();
- break;
- case ALTU: /* file upload */
- SaveMessage();
- DoUpload();
- RestoreMessage();
- state = -1;
- break;
- case ALTL: /* file download/capture */
- SaveMessage();
- DoCapture();
- RestoreMessage();
- state = -1;
- break;
- case ALTW: /* write scrollback */
- SaveMessage();
- DoWrite();
- RestoreMessage();
- state = -1;
- break;
- case F1:
- AddKeystrokes(fkeys[0]);
- break;
- case F2:
- AddKeystrokes(fkeys[1]);
- break;
- case F3:
- AddKeystrokes(fkeys[2]);
- break;
- case F4:
- AddKeystrokes(fkeys[3]);
- break;
- case F5:
- AddKeystrokes(fkeys[4]);
- break;
- case F6:
- AddKeystrokes(fkeys[5]);
- break;
- case F7:
- AddKeystrokes(fkeys[6]);
- break;
- case F8:
- AddKeystrokes(fkeys[7]);
- break;
- case F9: /* toggle PASSALL */
- PassMode = !PassMode;
- break;
- #ifdef TRACE
- case F10: /* debug */
- DoDebug();
- break;
- #endif
- }
- }
- gotoxy(p+1,24);
- }
- }
- }
-
- /* ----- Error handling ----- */
-
- /* OutOfMemory()
- Gets called during initialization routines to show an out of
- memory error. Prompts for user keystroke and exits.
- */
- void OutOfMemory(void)
- {
- cprintf(" Fatal Error -- Out of Memory! ");
- wait();
- CRTExit();
- exit(-1);
- }
-
- /* usage()
- Show the command line usage.
- */
- static void usage(void)
- {
- printf("Usage: PMP {-a} {-p<paramfile>}\n");
- }
-
- /* ----- Main Program ----- */
-
- void main(int argc, char **argv)
- {
- int i;
-
- /* process command line parameters */
- AutoMode = FALSE;
- strcpy(ParamFname, "PMP.CFG");
- for(i=1; i<argc; i++) {
- if(argv[i][0] != '-') {
- usage();
- exit(-1);
- } else switch(toupper(argv[i][1])) {
- case 'A':
- AutoMode = TRUE;
- break;
- case 'P':
- strcpy(ParamFname, argv[i]+2);
- break;
- default:
- printf("Unknown switch: %s\n",argv[i]);
- usage();
- exit(0);
- }
- }
-
- /* check to see if we've got enough memory to breathe */
- if(coreleft() < 65536L) {
- printf("Not enough free memory to run PMP.\n");
- exit(-1);
- }
-
- /* Startup */
- InitParameters();
- TXKey(FALSE); /* no transmitter */
- CRTInit();
- TitleScreen();
-
- /* read the parameter file */
- gotoxy(1,23);
- inverse(); /* set up for errors */
- if(ReadParameters()) { /* if error reading parameter file */
- wait();
- CRTExit();
- exit(-1);
- }
-
- /* initialize */
- RXInit(); /* Initialize RX */
- TXQInit(); /* Initialize TX */
- AX25_Init(); /* Initialize AX25 LAPB */
- HeardInit(); /* Initialize the heard lists */
- InitCapture(); /* Initialize capture */
-
- if(!AutoMode)
- wait();
-
-
- time(&StartTime);
- normal();
- clrscr();
- StatusLine();
-
- #ifdef TRACE
- LogInit(); /* Initialize the log */
- DebugMode = FALSE;
- DoDebug();
- #endif
-
- #ifdef DEBUG
- PassMode = FALSE;
- #endif
- holdmode = FALSE;
-
- /* crank up timers */
- StartBeacon();
- SoundEnd = 0;
-
- /* doit! */
- MainLoop();
- if(Capturing()) /* close capture file */
- DoCapture();
-
- /* bye, bye, clean things up... */
- CRTExit();
- #ifdef TRACE
- DumpLog(); /* dump the debug log */
- #endif
- exit(0);
- }