2022-08-28 09:35:46 +01:00
|
|
|
/*
|
2022-11-14 14:02:28 +00:00
|
|
|
Copyright 2001-2022 John Wiseman G8BPQ
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
This file is part of LinBPQ/BPQ32.
|
|
|
|
|
|
|
|
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
|
|
|
|
*/
|
|
|
|
|
|
|
|
//
|
|
|
|
// Interface to allow G8BPQ switch to use Serial TNC in character mode
|
|
|
|
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#ifndef MACBPQ
|
|
|
|
#ifndef FREEBSD
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/serial.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include "CHeaders.h"
|
|
|
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
|
2022-08-28 09:35:46 +01:00
|
|
|
extern int (WINAPI FAR *EnumProcessesPtr)();
|
|
|
|
|
|
|
|
#include "bpq32.h"
|
|
|
|
|
|
|
|
#include "tncinfo.h"
|
|
|
|
|
|
|
|
static int Socket_Data(int sock, int error, int eventcode);
|
|
|
|
|
|
|
|
VOID MoveWindows(struct TNCINFO * TNC);
|
|
|
|
static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen);
|
|
|
|
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
|
|
|
|
BOOL SerialWriteCommBlock(struct TNCINFO * TNC);
|
|
|
|
void SerialCheckRX(struct TNCINFO * TNC);
|
|
|
|
int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen);
|
|
|
|
int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data);
|
|
|
|
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
|
|
|
|
VOID SendInitScript(struct TNCINFO * TNC);
|
|
|
|
int SerialGetLine(char * buf);
|
|
|
|
int ProcessEscape(UCHAR * TXMsg);
|
|
|
|
BOOL KAMStartPort(struct PORTCONTROL * PORT);
|
|
|
|
BOOL KAMStopPort(struct PORTCONTROL * PORT);
|
|
|
|
|
|
|
|
static char ClassName[]="SERIALSTATUS";
|
|
|
|
static char WindowTitle[] = "SERIAL";
|
|
|
|
static int RigControlRow = 165;
|
|
|
|
|
|
|
|
#ifndef LINBPQ
|
|
|
|
#include <commctrl.h>
|
|
|
|
#endif
|
|
|
|
|
2023-05-25 14:17:53 +01:00
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
extern int SemHeldByAPI;
|
|
|
|
|
|
|
|
static RECT Rect;
|
|
|
|
|
|
|
|
static int ProcessLine(char * buf, int Port);
|
|
|
|
|
|
|
|
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
|
|
|
|
|
|
|
|
static int ProcessLine(char * buf, int Port)
|
|
|
|
{
|
|
|
|
UCHAR * ptr;
|
|
|
|
int len=510;
|
|
|
|
struct TNCINFO * TNC = TNCInfo[Port];
|
|
|
|
char errbuf[256];
|
|
|
|
|
|
|
|
// Read Initialisation lines
|
|
|
|
|
|
|
|
while(TRUE)
|
|
|
|
{
|
|
|
|
if (SerialGetLine(buf) == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
strcpy(errbuf, buf);
|
|
|
|
|
|
|
|
if (memcmp(buf, "****", 4) == 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
ptr = strchr(buf, ';');
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
*ptr++ = 13;
|
|
|
|
*ptr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_memicmp(buf, "LOGDIR ", 7) == 0)
|
|
|
|
TNC->LogPath = _strdup(&buf[7]);
|
|
|
|
else if (_memicmp(buf, "DRAGON", 6) == 0)
|
|
|
|
TNC->Dragon = TRUE;
|
|
|
|
else
|
|
|
|
strcat (TNC->InitScript, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
char * Config;
|
|
|
|
static char * ptr1, * ptr2;
|
|
|
|
|
|
|
|
int SerialGetLine(char * buf)
|
|
|
|
{
|
|
|
|
loop:
|
|
|
|
|
|
|
|
if (ptr2 == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memcpy(buf, ptr1, ptr2 - ptr1 + 2);
|
|
|
|
buf[ptr2 - ptr1 + 2] = 0;
|
|
|
|
ptr1 = ptr2 + 2;
|
|
|
|
ptr2 = strchr(ptr1, 13);
|
|
|
|
|
|
|
|
if (buf[0] < 0x20) goto loop;
|
|
|
|
if (buf[0] == '#') goto loop;
|
|
|
|
if (buf[0] == ';') goto loop;
|
|
|
|
|
|
|
|
if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
|
|
|
|
if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
|
|
|
|
buf[strlen(buf)] = 13;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL SerialReadConfigFile(int Port, int ProcLine())
|
|
|
|
{
|
|
|
|
char buf[256],errbuf[256];
|
|
|
|
|
|
|
|
Config = PortConfig[Port];
|
|
|
|
|
|
|
|
if (Config)
|
|
|
|
{
|
|
|
|
// Using config from bpq32.cfg
|
|
|
|
|
|
|
|
if (strlen(Config) == 0)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr1 = Config;
|
|
|
|
ptr2 = strchr(ptr1, 13);
|
|
|
|
|
|
|
|
if (!ProcLine(buf, Port))
|
|
|
|
{
|
|
|
|
WritetoConsoleLocal("\n");
|
|
|
|
WritetoConsoleLocal("Bad config record ");
|
|
|
|
WritetoConsoleLocal(errbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(buf," ** Error - No Configuration info in bpq32.cfg");
|
|
|
|
WritetoConsoleLocal(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
|
|
|
|
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);
|
|
|
|
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
|
|
|
|
|
|
|
|
static time_t ltime;
|
|
|
|
|
|
|
|
static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen)
|
|
|
|
{
|
|
|
|
if (TNC->hDevice)
|
|
|
|
{
|
|
|
|
// Serial mode. Queue to Hostmode driver
|
|
|
|
|
|
|
|
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
|
|
|
|
|
|
|
|
if (buffptr == 0) return; // No buffers, so ignore
|
|
|
|
|
|
|
|
buffptr->Len = EncLen;
|
|
|
|
memcpy(&buffptr->Data[0], Encoded, EncLen);
|
|
|
|
|
|
|
|
C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
|
|
|
|
TNC->Streams[Stream].FramesQueued++;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID SerialChangeMYC(struct TNCINFO * TNC, char * Call)
|
|
|
|
{
|
|
|
|
UCHAR TXMsg[100];
|
|
|
|
int datalen;
|
|
|
|
|
|
|
|
if (strcmp(Call, TNC->CurrentMYC) == 0)
|
|
|
|
return; // No Change
|
|
|
|
|
|
|
|
strcpy(TNC->CurrentMYC, Call);
|
|
|
|
|
|
|
|
datalen = sprintf(TXMsg, "MYCALL %s\r", Call);
|
|
|
|
SerialSendCommand(TNC, TXMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
|
|
|
|
{
|
|
|
|
int datalen;
|
|
|
|
PMSGWITHLEN buffptr;
|
|
|
|
// char txbuff[500];
|
|
|
|
unsigned int bytes,txlen = 0;
|
|
|
|
UCHAR * TXMsg;
|
|
|
|
|
|
|
|
size_t Param;
|
|
|
|
int Stream = 0;
|
|
|
|
HKEY hKey=0;
|
|
|
|
struct TNCINFO * TNC = TNCInfo[port];
|
|
|
|
struct STREAMINFO * STREAM = &TNC->Streams[0];
|
|
|
|
struct ScanEntry * Scan;
|
|
|
|
|
|
|
|
if (TNC == NULL)
|
|
|
|
return 0; // Port not defined
|
|
|
|
|
|
|
|
if (TNC->hDevice == 0)
|
|
|
|
{
|
|
|
|
// Clear anything from UI_Q
|
|
|
|
|
|
|
|
while (TNC->PortRecord->UI_Q)
|
|
|
|
{
|
|
|
|
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
|
|
|
|
ReleaseBuffer(buffptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to reopen every 30 secs
|
|
|
|
|
|
|
|
if (fn > 3 && fn < 7)
|
|
|
|
goto ok;
|
|
|
|
|
|
|
|
TNC->ReopenTimer++;
|
|
|
|
|
|
|
|
if (TNC->ReopenTimer < 300)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TNC->ReopenTimer = 0;
|
|
|
|
|
|
|
|
if (TNC->PortRecord->PORTCONTROL.PortStopped == 0)
|
|
|
|
OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE);
|
|
|
|
|
|
|
|
if (TNC->hDevice == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#ifndef MACBPQ
|
|
|
|
#ifndef FREEBSD
|
|
|
|
|
|
|
|
if (TNC->Dragon)
|
|
|
|
{
|
|
|
|
struct serial_struct sstruct;
|
|
|
|
|
|
|
|
// Need to set custom baud rate
|
|
|
|
|
|
|
|
if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0)
|
|
|
|
{
|
|
|
|
Debugprintf("Error: Dragon could not get comm ioctl\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// set custom divisor to get 829440 baud
|
|
|
|
|
|
|
|
sstruct.custom_divisor = 29;
|
|
|
|
sstruct.flags |= ASYNC_SPD_CUST;
|
|
|
|
|
|
|
|
// set serial_struct
|
|
|
|
|
|
|
|
if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0)
|
|
|
|
Debugprintf("Error: Dragon could not set custom comm baud divisor\n");
|
|
|
|
else
|
|
|
|
Debugprintf("Dragon custom baud rate set\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
SendInitScript(TNC);
|
|
|
|
|
|
|
|
}
|
|
|
|
ok:
|
|
|
|
|
|
|
|
switch (fn)
|
|
|
|
{
|
|
|
|
case 7:
|
|
|
|
|
|
|
|
// 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances
|
|
|
|
|
|
|
|
SerialCheckRX(TNC);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 1: // poll
|
|
|
|
|
|
|
|
while (TNC->PortRecord->UI_Q)
|
|
|
|
{
|
|
|
|
char FECMsg[512];
|
|
|
|
char Call[12] = " ";
|
|
|
|
struct _MESSAGE * buffptr;
|
|
|
|
char * ptr = FECMsg;
|
|
|
|
|
|
|
|
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
|
|
|
|
|
|
|
|
ReleaseBuffer(buffptr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (TNC->DiscPending)
|
|
|
|
{
|
|
|
|
TNC->DiscPending--;
|
|
|
|
|
|
|
|
if (TNC->DiscPending == 0)
|
|
|
|
{
|
|
|
|
// Too long in Disc Pending - Kill and Restart TNC
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (Stream = 0; Stream <= 2; Stream++)
|
|
|
|
{
|
|
|
|
STREAM = &TNC->Streams[Stream];
|
|
|
|
|
|
|
|
if (STREAM->NeedDisc)
|
|
|
|
{
|
|
|
|
STREAM->NeedDisc--;
|
|
|
|
|
|
|
|
if (STREAM->NeedDisc == 0)
|
|
|
|
{
|
|
|
|
// Send the DISCONNECT
|
|
|
|
|
|
|
|
SerialSendCommand(TNC, "DISCONNECT\r");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
|
|
|
|
{
|
|
|
|
// New Attach
|
|
|
|
|
|
|
|
int calllen;
|
|
|
|
char Msg[80];
|
|
|
|
|
|
|
|
Debugprintf("Serial New Attach Stream %d", Stream);
|
|
|
|
|
|
|
|
STREAM->Attached = TRUE;
|
|
|
|
|
|
|
|
calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall);
|
|
|
|
TNC->Streams[Stream].MyCall[calllen] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
SerialChangeMYC(TNC, TNC->Streams[0].MyCall);
|
|
|
|
|
|
|
|
// Stop other ports in same group
|
|
|
|
|
|
|
|
SuspendOtherPorts(TNC);
|
|
|
|
|
|
|
|
//sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
|
|
|
|
//MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
|
|
|
|
|
|
|
|
// Stop Scanning
|
|
|
|
|
|
|
|
sprintf(Msg, "%d SCANSTOP", TNC->Port);
|
|
|
|
|
2023-05-16 16:40:12 +01:00
|
|
|
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
|
2022-08-28 09:35:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (STREAM->Attached)
|
|
|
|
CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if any frames for this port
|
|
|
|
|
|
|
|
for (Stream = 0; Stream <= 2; Stream++)
|
|
|
|
{
|
|
|
|
STREAM = &TNC->Streams[Stream];
|
|
|
|
|
|
|
|
if (STREAM->BPQtoPACTOR_Q)
|
|
|
|
{
|
|
|
|
PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q);
|
|
|
|
UCHAR * data = &buffptr->Data[0];
|
|
|
|
STREAM->FramesQueued--;
|
|
|
|
txlen = (int)buffptr->Len;
|
|
|
|
STREAM->BytesTXed += txlen;
|
|
|
|
|
|
|
|
bytes=SerialSendData(TNC, data, txlen);
|
|
|
|
WritetoTrace(TNC, data, txlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREAM->PACTORtoBPQ_Q != 0)
|
|
|
|
{
|
|
|
|
buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q);
|
|
|
|
|
|
|
|
datalen = (int)buffptr->Len;
|
|
|
|
|
|
|
|
buff->PORT = Stream; // Compatibility with Kam Driver
|
|
|
|
buff->PID = 0xf0;
|
|
|
|
memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte
|
|
|
|
datalen += sizeof(void *) + 4;
|
|
|
|
|
|
|
|
PutLengthinBuffer(buff, datalen);
|
|
|
|
|
|
|
|
ReleaseBuffer(buffptr);
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREAM->ReportDISC) // May need a delay so treat as a counter
|
|
|
|
{
|
|
|
|
STREAM->ReportDISC--;
|
|
|
|
if (STREAM->ReportDISC == 0)
|
|
|
|
{
|
|
|
|
buff->PORT = Stream;
|
|
|
|
// STREAM->Connected = 0;
|
|
|
|
// STREAM->Attached = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
case 2: // send
|
|
|
|
|
|
|
|
Stream = buff->PORT;
|
|
|
|
|
|
|
|
if (!TNC->hDevice)
|
|
|
|
{
|
|
|
|
// Send Error Response
|
|
|
|
|
|
|
|
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
|
|
|
|
|
|
|
|
if (buffptr == 0) return (0); // No buffers, so ignore
|
|
|
|
|
|
|
|
buffptr->Len = 36;
|
|
|
|
memcpy(&buffptr->Data[0], "No Connection to TNC\r", 36);
|
|
|
|
|
|
|
|
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
|
|
|
|
|
|
|
|
return 0; // Don't try if not connected
|
|
|
|
}
|
|
|
|
|
|
|
|
STREAM = &TNC->Streams[Stream];
|
|
|
|
|
|
|
|
if (TNC->SwallowSignon)
|
|
|
|
{
|
|
|
|
TNC->SwallowSignon = FALSE; // Discard *** connected
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID
|
|
|
|
TXMsg = &buff->L2DATA[0];
|
|
|
|
TXMsg[txlen] = 0;
|
|
|
|
|
|
|
|
// for now just send, but allow sending control
|
|
|
|
// characters with \\ or ^ escape
|
|
|
|
|
|
|
|
// if (STREAM->Connected)
|
|
|
|
{
|
|
|
|
STREAM->PacketsSent++;
|
|
|
|
|
|
|
|
if (strstr(TXMsg, "\\\\") || strchr(TXMsg, '^'))
|
|
|
|
{
|
|
|
|
txlen = ProcessEscape(TXMsg);
|
|
|
|
|
|
|
|
if (txlen == 0) // Must be \\D
|
|
|
|
{
|
|
|
|
STREAM->ReportDISC = TRUE; // Tell Node
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes=SerialSendData(TNC, TXMsg, txlen);
|
|
|
|
TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response
|
|
|
|
STREAM->BytesTXed += bytes;
|
|
|
|
// WritetoTrace(TNC, &buff->L2DATA[0], txlen);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0)
|
|
|
|
{
|
|
|
|
STREAM->ReportDISC = TRUE; // Tell Node
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// See if Local command (eg RADIO)
|
|
|
|
|
|
|
|
if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0)
|
|
|
|
{
|
|
|
|
sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]);
|
|
|
|
|
2023-05-16 16:40:12 +01:00
|
|
|
if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0]))
|
2022-08-28 09:35:46 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
|
|
|
|
|
|
|
|
if (buffptr == 0) return 1; // No buffers, so ignore
|
|
|
|
|
|
|
|
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]);
|
|
|
|
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0)
|
|
|
|
{
|
|
|
|
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
|
|
|
|
|
|
|
|
TNC->OverrideBusy = TRUE;
|
|
|
|
|
|
|
|
if (buffptr)
|
|
|
|
{
|
|
|
|
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r");
|
|
|
|
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if a Connect Command. If so, start codec and set Connecting
|
|
|
|
|
|
|
|
if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect
|
|
|
|
{
|
|
|
|
char Connect[80];
|
|
|
|
char * ptr = strchr(&buff->L2DATA[2], 13);
|
|
|
|
|
|
|
|
if (ptr)
|
|
|
|
*ptr = 0;
|
|
|
|
|
|
|
|
_strupr(&buff->L2DATA[2]);
|
|
|
|
|
|
|
|
if (strlen(&buff->L2DATA[2]) > 9)
|
|
|
|
buff->L2DATA[11] = 0;
|
|
|
|
|
|
|
|
txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]);
|
|
|
|
|
|
|
|
SerialChangeMYC(TNC, TNC->Streams[0].MyCall);
|
|
|
|
|
|
|
|
// See if Busy
|
|
|
|
|
|
|
|
if (InterlockedCheckBusy(TNC))
|
|
|
|
{
|
|
|
|
// Channel Busy. Unless override set, wait
|
|
|
|
|
|
|
|
if (TNC->OverrideBusy == 0)
|
|
|
|
{
|
|
|
|
// Save Command, and wait up to 10 secs
|
|
|
|
|
|
|
|
sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
|
|
|
|
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
|
|
|
|
|
|
|
|
TNC->ConnectCmd = _strdup(Connect);
|
|
|
|
TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TNC->OverrideBusy = FALSE;
|
|
|
|
|
|
|
|
memset(TNC->Streams[0].RemoteCall, 0, 10);
|
|
|
|
strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]);
|
|
|
|
|
|
|
|
sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall);
|
|
|
|
SerialSendCommand(TNC, Connect);
|
|
|
|
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
|
|
|
|
|
|
|
|
STREAM->Connecting = TRUE;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normal data. Send to TNC
|
|
|
|
|
|
|
|
|
|
|
|
SerialSendData(TNC, TXMsg, txlen);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
*/
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
// CHECK IF OK TO SEND (And check TNC Status)
|
|
|
|
|
|
|
|
Stream = (int)(size_t)buff;
|
|
|
|
|
|
|
|
// I think we should check buffer space for all comms modes
|
|
|
|
|
|
|
|
{
|
|
|
|
int Queued;
|
|
|
|
int Outstanding = TNC->Streams[Stream].BytesOutstanding;
|
|
|
|
|
|
|
|
if (Stream == 0)
|
|
|
|
Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue
|
|
|
|
else
|
|
|
|
Queued = TNC->Streams[Stream].FramesQueued;
|
|
|
|
|
|
|
|
Outstanding = Queued = 0;
|
|
|
|
|
|
|
|
if (TNC->Mode == 'O') // OFDM version has more buffer space
|
|
|
|
{
|
|
|
|
if (Queued > 4 || Outstanding > 8500)
|
|
|
|
return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Queued > 4 || Outstanding > 2000)
|
|
|
|
return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (TNC->Streams[Stream].Attached == 0)
|
|
|
|
return (TNC->hDevice != 0) << 8 | 1;
|
|
|
|
|
|
|
|
return ((TNC->hDevice != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK
|
|
|
|
|
|
|
|
|
|
|
|
case 4: // reinit7
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 5: // Close
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 6: // Scan Stop Interface
|
|
|
|
|
|
|
|
Param = (size_t)buff;
|
|
|
|
|
|
|
|
if (Param == 2) // Check Permission (Shouldn't happen)
|
|
|
|
{
|
|
|
|
Debugprintf("Scan Check Permission called on ARDOP");
|
|
|
|
return 1; // OK to change
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Param == 1) // Request Permission
|
|
|
|
{
|
|
|
|
if (TNC->ARDOPCommsMode == 'T') // TCP Mode
|
|
|
|
{
|
|
|
|
if (!TNC->CONNECTED)
|
|
|
|
return 0; // No connection so no interlock
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Serial Modes
|
|
|
|
|
|
|
|
if (!TNC->HostMode)
|
|
|
|
return 0; // No connection so no interlock
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (TNC->ConnectPending == 0 && TNC->PTTState == 0)
|
|
|
|
{
|
|
|
|
SerialSendCommand(TNC, "CONOK OFF");
|
|
|
|
TNC->GavePermission = TRUE;
|
|
|
|
return 0; // OK to Change
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TNC->ConnectPending)
|
|
|
|
TNC->ConnectPending--; // Time out if set too long
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Param == 3) // Release Permission
|
|
|
|
{
|
|
|
|
if (TNC->GavePermission)
|
|
|
|
{
|
|
|
|
TNC->GavePermission = FALSE;
|
|
|
|
if (TNC->ARDOPCurrentMode[0] != 'S') // Skip
|
|
|
|
SerialSendCommand(TNC, "CONOK ON");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Param is Address of a struct ScanEntry
|
|
|
|
|
|
|
|
Scan = (struct ScanEntry *)buff;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SerialReleaseTNC(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
// Set mycall back to Node or Port Call, and Start Scanner
|
|
|
|
|
|
|
|
UCHAR TXMsg[1000];
|
|
|
|
|
|
|
|
SerialChangeMYC(TNC, TNC->NodeCall);
|
|
|
|
|
|
|
|
// Start Scanner
|
|
|
|
|
|
|
|
sprintf(TXMsg, "%d SCANSTART 15", TNC->Port);
|
|
|
|
|
2023-05-16 16:40:12 +01:00
|
|
|
Rig_Command( (TRANSPORTENTRY *) -1, TXMsg);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
ReleaseOtherPorts(TNC);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
VOID SerialSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC)
|
2022-08-28 09:35:46 +01:00
|
|
|
{
|
|
|
|
SerialSendCommand(TNC, "CONOK OFF\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SerialReleasePort(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
SerialSendCommand(TNC, "CONOK ON\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID * SerialExtInit(EXTPORTDATA * PortEntry)
|
|
|
|
{
|
|
|
|
int port;
|
|
|
|
char Msg[255];
|
|
|
|
char * ptr;
|
|
|
|
struct TNCINFO * TNC;
|
|
|
|
char * TempScript;
|
|
|
|
|
|
|
|
sprintf(Msg,"Serial TNC %s\n", PortEntry->PORTCONTROL.SerialPortName);
|
|
|
|
WritetoConsole(Msg);
|
|
|
|
|
|
|
|
port=PortEntry->PORTCONTROL.PORTNUMBER;
|
|
|
|
|
|
|
|
if (TNCInfo[port]) // If restarting, free old config
|
|
|
|
free(TNCInfo[port]);
|
|
|
|
|
|
|
|
TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO));
|
|
|
|
memset(TNC, 0, sizeof(struct TNCINFO));
|
|
|
|
|
|
|
|
TNC->InitScript = malloc(1000);
|
|
|
|
TNC->InitScript[0] = 0;
|
|
|
|
|
|
|
|
if (PortConfig[port]) // May not have config
|
|
|
|
SerialReadConfigFile(port, ProcessLine);
|
|
|
|
|
|
|
|
TNC = TNCInfo[port];
|
|
|
|
|
|
|
|
if (TNC == NULL)
|
|
|
|
{
|
|
|
|
// Not defined in Config file
|
|
|
|
|
|
|
|
sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n");
|
|
|
|
WritetoConsole(Msg);
|
|
|
|
|
|
|
|
return ExtProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
TNC->Port = port;
|
|
|
|
TNC->Hardware = H_SERIAL;
|
|
|
|
|
|
|
|
TNC->PortRecord = PortEntry;
|
|
|
|
|
|
|
|
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
|
|
|
|
memcpy(TNC->NodeCall, MYNODECALL, 10);
|
|
|
|
else
|
|
|
|
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
|
|
|
|
|
|
|
|
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
|
|
|
|
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
|
|
|
|
|
|
|
|
PortEntry->PORTCONTROL.PROTOCOL = 10;
|
|
|
|
PortEntry->PORTCONTROL.PORTQUALITY = 0;
|
|
|
|
|
|
|
|
if (TNC->PacketChannels > 1)
|
|
|
|
TNC->PacketChannels = 1;
|
|
|
|
|
|
|
|
PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1;
|
|
|
|
|
|
|
|
PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only
|
|
|
|
PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream
|
|
|
|
|
|
|
|
PortEntry->PORTCONTROL.UICAPABLE = TRUE;
|
|
|
|
|
|
|
|
if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
|
|
|
|
PortEntry->PORTCONTROL.PORTPACLEN = 236;
|
|
|
|
|
|
|
|
TNC->SuspendPortProc = SerialSuspendPort;
|
|
|
|
TNC->ReleasePortProc = SerialReleasePort;
|
|
|
|
|
|
|
|
PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort;
|
|
|
|
PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort;
|
|
|
|
|
|
|
|
|
|
|
|
ptr=strchr(TNC->NodeCall, ' ');
|
|
|
|
if (ptr) *(ptr) = 0; // Null Terminate
|
|
|
|
|
|
|
|
// Set Essential Params and MYCALL
|
|
|
|
|
|
|
|
// Put overridable ones on front, essential ones on end
|
|
|
|
|
|
|
|
TempScript = zalloc(1000);
|
|
|
|
|
|
|
|
// cant think of any yet
|
|
|
|
|
|
|
|
if (TNC->InitScript)
|
|
|
|
{
|
|
|
|
strcat(TempScript, TNC->InitScript);
|
|
|
|
free(TNC->InitScript);
|
|
|
|
}
|
|
|
|
|
|
|
|
TNC->InitScript = TempScript;
|
|
|
|
|
|
|
|
// Set MYCALL
|
|
|
|
|
|
|
|
sprintf(Msg, "MYCALL %s\r", TNC->NodeCall);
|
|
|
|
strcat(TNC->InitScript, Msg);
|
|
|
|
|
|
|
|
strcpy(TNC->CurrentMYC, TNC->NodeCall);
|
|
|
|
|
|
|
|
if (TNC->WL2K == NULL)
|
|
|
|
if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded
|
|
|
|
TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo;
|
|
|
|
|
|
|
|
OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE);
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#ifndef MACBPQ
|
|
|
|
#ifndef FREEBSD
|
|
|
|
|
|
|
|
if (TNC->Dragon)
|
|
|
|
{
|
|
|
|
struct serial_struct sstruct;
|
|
|
|
|
|
|
|
// Need to set custom baud rate
|
|
|
|
|
|
|
|
if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0)
|
|
|
|
{
|
|
|
|
printf("Error: Dragon could not get comm ioctl\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// set custom divisor to get 829440 baud
|
|
|
|
|
|
|
|
sstruct.custom_divisor = 29;
|
|
|
|
sstruct.flags |= ASYNC_SPD_CUST;
|
|
|
|
|
|
|
|
// set serial_struct
|
|
|
|
|
|
|
|
if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0)
|
|
|
|
Debugprintf("Error: Dragon could not set custom comm baud divisor\n");
|
|
|
|
else
|
|
|
|
Debugprintf("Dragon custom baud rate set\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SendInitScript(TNC);
|
|
|
|
|
|
|
|
time(&TNC->lasttime); // Get initial time value
|
|
|
|
|
|
|
|
return ExtProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID TidyClose(struct TNCINFO * TNC, int Stream)
|
|
|
|
{
|
|
|
|
// If all acked, send disc
|
|
|
|
|
|
|
|
if (TNC->Streams[Stream].BytesOutstanding == 0)
|
|
|
|
SerialSendCommand(TNC, "DISCONNECT\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID ForcedClose(struct TNCINFO * TNC, int Stream)
|
|
|
|
{
|
|
|
|
SerialSendCommand(TNC, "DISCONNECT\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID CloseComplete(struct TNCINFO * TNC, int Stream)
|
|
|
|
{
|
|
|
|
if (Stream == 0)
|
|
|
|
{
|
|
|
|
SerialReleaseTNC(TNC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SerialAbort(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
SerialSendCommand(TNC, "ABORT\r");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Host Mode Stuff (we reuse some routines in SCSPactor)
|
|
|
|
|
|
|
|
VOID SerialDoTermModeTimeout(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
UCHAR * Poll = TNC->TXBuffer;
|
|
|
|
|
|
|
|
if (TNC->ReinitState == 0)
|
|
|
|
{
|
|
|
|
//Checking if in Terminal Mode - Try to set back to Term Mode
|
|
|
|
|
|
|
|
TNC->ReinitState = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TNC->ReinitState == 1)
|
|
|
|
{
|
|
|
|
// Forcing back to Term Mode
|
|
|
|
|
|
|
|
TNC->ReinitState = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TNC->ReinitState == 3)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID SendInitScript(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
UCHAR * Poll = TNC->TXBuffer;
|
|
|
|
char * ptr1, * ptr2;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
// Send INIT script
|
|
|
|
|
|
|
|
ptr1 = &TNC->InitScript[0];
|
|
|
|
|
|
|
|
while (ptr1 && ptr1[0])
|
|
|
|
{
|
|
|
|
ptr2 = strchr(ptr1, 13);
|
|
|
|
|
|
|
|
if (ptr2 == 0)
|
|
|
|
{
|
|
|
|
TNC->ReinitState = 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = (int)(ptr2 - ptr1) + 1;
|
|
|
|
|
|
|
|
memcpy(Poll, ptr1, len);
|
|
|
|
|
|
|
|
TNC->TXLen = len;
|
|
|
|
SerialWriteCommBlock(TNC);
|
|
|
|
|
|
|
|
Sleep(50);
|
|
|
|
|
|
|
|
if (ptr2)
|
|
|
|
*(ptr2++) = 13; // Put CR back for next time
|
|
|
|
|
|
|
|
ptr1 = ptr2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID SerialProcessTNCMessage(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
PMSGWITHLEN buffptr;
|
|
|
|
struct STREAMINFO * STREAM = &TNC->Streams[0];
|
|
|
|
|
|
|
|
buffptr = GetBuff();
|
|
|
|
|
|
|
|
if (buffptr == 0)
|
|
|
|
{
|
|
|
|
return; // No buffers, so ignore
|
|
|
|
}
|
|
|
|
|
|
|
|
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer);
|
|
|
|
|
|
|
|
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SerialCheckRX(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
int Length, Len = 0;
|
|
|
|
|
|
|
|
if (TNC->RXLen == 250)
|
|
|
|
TNC->RXLen = 0;
|
|
|
|
|
|
|
|
if (TNC->hDevice)
|
|
|
|
Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 250 - TNC->RXLen);
|
|
|
|
|
|
|
|
if (Len == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
TNC->RXLen += Len;
|
|
|
|
|
|
|
|
Length = TNC->RXLen;
|
|
|
|
|
|
|
|
TNC->RXBuffer[TNC->RXLen] = 0;
|
|
|
|
|
|
|
|
// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':')
|
|
|
|
|
|
|
|
if (strlen(TNC->RXBuffer) < TNC->RXLen)
|
|
|
|
TNC->RXLen = 0;
|
|
|
|
|
|
|
|
// Also need timeout for incomplete lines
|
|
|
|
|
|
|
|
// if ((strchr(TNC->RXBuffer, 10) == 0) && (strchr(TNC->RXBuffer, 13) == 0))
|
|
|
|
// return; // Wait for rest of frame
|
|
|
|
|
|
|
|
|
|
|
|
// OpenLogFile(TNC->Port);
|
|
|
|
// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer));
|
|
|
|
// CloseLogFile(TNC->Port);
|
|
|
|
|
|
|
|
TNC->RXLen = 0; // Ready for next frame
|
|
|
|
|
|
|
|
SerialProcessTNCMessage(TNC);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SerialWriteCommBlock(struct TNCINFO * TNC)
|
|
|
|
{
|
|
|
|
if (TNC->hDevice)
|
|
|
|
return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen)
|
|
|
|
{
|
|
|
|
if (TNC->hDevice)
|
|
|
|
return WriteCOMBlock(TNC->hDevice, data, txlen);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data)
|
|
|
|
{
|
|
|
|
if (TNC->hDevice)
|
|
|
|
return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ProcessEscape(UCHAR * TXMsg)
|
|
|
|
{
|
|
|
|
UCHAR * Orig = TXMsg;
|
|
|
|
UCHAR * ptr1 = TXMsg;
|
|
|
|
UCHAR * ptr2 = strstr(TXMsg, "\\\\");
|
|
|
|
UCHAR * ptr3 = strchr(TXMsg, '^');
|
|
|
|
|
|
|
|
BOOL HexEscape = FALSE;
|
|
|
|
int NewLen;
|
|
|
|
|
|
|
|
// Now using ^C for ctrl/c, etc
|
|
|
|
// Still use \\d
|
|
|
|
// ^^ for ^, \\\ for \\
|
|
|
|
|
|
|
|
while (ptr2)
|
|
|
|
{
|
|
|
|
UCHAR Next;
|
|
|
|
|
|
|
|
ptr1 = ptr2; // over stuff before escape
|
|
|
|
|
|
|
|
ptr2 += 2; // over \\
|
|
|
|
|
|
|
|
Next = *ptr2;
|
|
|
|
|
|
|
|
if (Next == '\\')
|
|
|
|
{
|
|
|
|
ptr1 = ptr2; // put \\ in message
|
|
|
|
memmove(ptr2, ptr2 + 1, strlen(ptr2));
|
|
|
|
}
|
|
|
|
else if (Next == 'd' || Next == 'D')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ptr2 = strstr(ptr2, "\\\\");
|
|
|
|
}
|
|
|
|
|
|
|
|
// now look for ^
|
|
|
|
|
|
|
|
ptr1 = Orig;
|
|
|
|
ptr2 = strchr(Orig, '^');
|
|
|
|
|
|
|
|
while (ptr2)
|
|
|
|
{
|
|
|
|
UCHAR Next;
|
|
|
|
|
|
|
|
ptr1 = ptr2; // over stuff before escape
|
|
|
|
ptr2 ++; // over ^
|
|
|
|
|
|
|
|
Next = *ptr2;
|
|
|
|
|
|
|
|
if (Next != '^')
|
|
|
|
{
|
|
|
|
Next &= 0x1F; // Mask to control char
|
|
|
|
HexEscape = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptr1 = Next;
|
|
|
|
|
|
|
|
memmove(ptr2, ptr2 + 1, strlen(ptr2));
|
|
|
|
|
|
|
|
ptr2 = strchr(ptr2, '^');
|
|
|
|
}
|
|
|
|
|
|
|
|
NewLen = (int)strlen(Orig);
|
|
|
|
|
|
|
|
if (HexEscape)
|
|
|
|
{
|
|
|
|
// remove trailing CR
|
|
|
|
|
|
|
|
NewLen --;
|
|
|
|
|
|
|
|
Orig[NewLen] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewLen;
|
|
|
|
}
|
|
|
|
|