conx.cc
//*************************************************************************
// MODULE : CONX - Operator's console process (see EXEC.HPP for class) *
// AUTHOR : Ron Chernich *
// PURPOSE: The operator's console is started by magic as PID ZERO when *
// the RCOS kernel first runs. It reads command lines and will *
// fork to create and other procs (which are its childern). *
// Aside: I wanted to call this module "con" but under DOS, you *
// can't name a file "con" anything! End of Aside. *
// HISTORY: *
// 26-MAR-93 First version of Command parser *
// 20-APR-93 Re-coded as a high proirity, priviledged process *
// 14-MAY-93 Rolling log a'la ICL 2903 added *
// 17-MAY-93 Tricky bug tracked to "delete[]" required on arrays! *
// 04-JAN-94 Testing constant expression for "Priority" + a mem leak! *
// 17-Sep-94 Console said OK to start of an invalid PID slot number. *
// 22-MAR-95 Happy Birthday, Ron! FIND command new reports process ID *
//*************************************************************************
#include "exec.hh"
#include "tty.hh"
#include "kernel.hh"
/////////////
// globals define ansi sequences for output formatting..
//
#define CONX_PID 0
#define ROW 2
#define COL 4
#define LEN_GOTO 6
#define LEN_EREOL 3
#define LEN_PROMPT 2
#define LEN_NEWECHO (LEN_GOTO+LEN_EREOL)
#define LEN_NEWPROMPT (LEN_GOTO+LEN_PROMPT+LEN_EREOL)
///////////////////
// Legal operator commands (don't change order of first 3, or of the
// corresponding sting array )..
//
enum { CMD_Die, CMD_Find, CMD_Batch, CMD_Delete,
CMD_Go, CMD_Suspend, CMD_Resume, CMD_Priority };
////////////////
// Operator error message indicies..
//
enum { CMND_OK, ERR_WHAT, ERR_NOT_FOUND, ERR_BAD_PID, ERR_BAD_PRIORITY,
ERR_XTRA_PARAM, ERR_NO_START, ERR_NO_CAN_DIE };
/////////////////
// local globals (ho ho)..
//
static UINT16 uCon; // ID of terminal given us
static INT16 nRows, nCols, nDispRow; // Size of terminal & output row#
static char arrPrmpt[] = { '>', ' ' };
static char arrErEol[] = { ESC, '[', 'K' };
static char arrGoto[] = { ESC, '[', 0, ';', 0, 'H' };
static char *pszFnar[] = { "DIE", "FIND", "BATCH", "DELETE", "GO",
"SUSPEND", "RESUME", "PRIORITY", "" };
//////////////////
// Protos local to this module..
//
static void ConEcho (char*, Knl*);
static BOOL FileCheck (char*, INT16, MMU_MSG&);
//////////////////////////////////////////////////////////////////////////
// At this point, we are a process (pid zero), with a Line Protocol Driver
// (which has a tty device allocated to it whose ID we don't know, or need
// to know). Since we don't know what "size" the tty is (rows/cols) and
// since this will be compiler font and graphic layout dependant, we'll
// ask for some details, store them, and turn off the "break" detect on
// the line driver.
//
void Exec::InitCon (void)
{
uCon = ID_LNDRV+arrPCB[0].uPid;
MSG mess(CONX_PID, KM_IoCtrl, DM_GetSize);
pTx->SendMsg(uCon, &mess);
nRows = mess.wParam >> 8;
nCols = mess.wParam & 0x00ff;
nDispRow = 0;
mess = message(CONX_PID, KM_IoCtrl, DM_BreakOff);
pTx->SendMsg(uCon, &mess);
}
///////////////////
// Here we have the console process. All other processes are forked off it.
// We arrive here every time the Kernel (Exec, actually) dispatches process
// ZERO (us) to the CPU. This probably means that a message we ware blocked
// on has been received - ie a reply from the line protocol driver. We will
// action the user text, then re-display the prompt and ask the line driver
// for another block, effectively blocking ourselves again.
//
void Exec::RunCon (PCB &Pcb)
{
if (Pcb.pReply) {
char *pst;
if (pst = new char[Pcb.pReply->wParam + 1]) {
UINT16 wMsg = Pcb.pReply->wMsgType & ~MM_Sync;
strncpy(pst, (char*)Pcb.pReply->pBody, Pcb.pReply->wParam);
*(pst + Pcb.pReply->wParam) = '\0';
DELETE_ARRAY Pcb.pReply->pBody;
delete Pcb.pReply;
Pcb.pReply = NULL;
switch (wMsg) {
case KM_ReadBlk:
ParseCon(pst);
DELETE_ARRAY pst;
break;
case KM_WriteBlk:
ConEcho(pst, pTx);
DELETE_ARRAY pst;
return;
default:
DELETE_ARRAY pst;
return;
}
}
}
arrGoto[COL] = 0;
arrGoto[ROW] = nRows - 1;
char st[LEN_NEWPROMPT];
memcpy(st, arrGoto, LEN_GOTO);
memcpy(st + LEN_GOTO, arrErEol, LEN_EREOL);
memcpy(st + LEN_GOTO + LEN_EREOL, arrPrmpt, LEN_PROMPT);
MSG mess(CONX_PID, KM_WriteBlk, LEN_NEWPROMPT, (void*)st);
pTx->SendMsg(uCon, &mess);
mess = message(CONX_PID, KM_ReadBlk, 0, NULL);
pTx->PostMsg(uCon, &mess);
}
//////////////////////
// Command parser .. implements the OS "operator's console" based on the
// good old ICL 1900 (DME) Exec. All Upper case chars for valid commands
// are mandatory; the rest (if present) must be correct but are ignored.
//
// DIe Orderly Termination of operating system
// FInd Load program
// BAtch Execute batch of operator commands
// GO Begin execution
// SUspend Suspend process
// DElete Delete process
// REsume Resume process (after suspension)
// PRiority Revise process priority
//
// Note that all chars in the input string are guaranteed upper case and any
// "new" char arrays sent to the line driver are deleted by the tty device.
//
void Exec::ParseCon (char* pCmnd)
{
char *cp;
INT16 n1, n2, nErr = ERR_WHAT, idx = 0;
MMU_MSG memmsg = { NULL, arrPCB[0].uSP, 0 };
static char *szRsp[] = { " - O.K.", " - ERROR %c", " Loaded %d" };
while (pszFnar[idx]) {
INT16 nLen = (pCmnd[2] == ' ') ? 2 : strlen(pszFnar[idx]);
if (strncmp(pCmnd, pszFnar[idx], nLen))
++idx;
else {
nErr = CMND_OK;
cp = pCmnd + nLen;
if ((idx == CMD_Find) || (idx == CMD_Batch))
nErr = (FileCheck(cp, idx, memmsg) ? CMND_OK : ERR_NOT_FOUND);
if (idx > CMD_Batch) {
n1 = n2 = -1;
sscanf(cp, " %d %d", &n1, &n2);
if ((n1 < 0) || (n1 > MAX_PROC))
nErr = ERR_BAD_PID;
if ((n2 != -1) && (idx != CMD_Priority))
nErr = ERR_XTRA_PARAM;
}
if (nErr == CMND_OK) {
MSG msg;
switch(idx) {
case CMD_Die:
if (uProcCnt == 1)
KbdIn.KeyPut(_ALT, ALT_F4);
else
nErr = ERR_NO_CAN_DIE;
break;
case CMD_Go:
if (!LoSked(n1))
nErr = ERR_BAD_PID;
break;
case CMD_Delete:
Kill(n1);
break;
case CMD_Find:
msg = message(CONX_PID, KM_WriteBlk,
arrMCB[0].hStack, (void*)&memmsg);
pTx->SendMsg(ID_MMU, &msg);
DELETE_ARRAY memmsg.pData;
arrPCB[0].uSP += (memmsg.uLen - 1);
n1 = Fork();
nErr = (NO_PROC == n1) ? ERR_NO_START : CMND_OK;
arrPCB[0].uSP -= (memmsg.uLen - 1);
break;
case CMD_Suspend:
if (!Suspend(n1))
nErr = ERR_BAD_PID;
break;
case CMD_Resume:
if (!Resume(n1))
nErr = ERR_BAD_PID;
break;
case CMD_Priority:
nErr = ((n2 < MIN_PRIORITY) || (n2 > MAX_PRIORITY)) ?
ERR_BAD_PRIORITY : (arrPCB[n1].uPid == NO_PROC) ?
ERR_BAD_PID : CMND_OK;
if (nErr == CMND_OK)
arrPCB[n1].nPriority = n2;
break;
}
}
break; // while
}
}
char *pst;
if (pst = new char[nCols+1]) {
char szRes[16];
INT16 nLim = MIN((INT16)strlen(pCmnd), nCols);
strncpy(pst, pCmnd, MIN((INT16)strlen(pCmnd), nCols-2));
if ((CMD_Find == idx) && (CMND_OK == nErr)) {
sprintf(pst + nLim, szRsp[2], n1);
nLim = strlen(pst);
}
sprintf(szRes, ((nErr == CMND_OK) ? szRsp[0] : szRsp[1]), nErr+64);
INT16 nReq = strlen(szRes);
while ((nLim + nReq) > nCols)
--nLim;
strcpy(pst + nLim, szRes);
ConEcho(pst, pTx);
DELETE_ARRAY pst;
}
}
///////////////
// Ensure a file with the passed name exists. Extension and mode dependant
// on param thus:
// CMD_FI: .PCD extension in binary mode
// CMD_BA: .BAT extension in text mode
// Since the string may contain an extension, over-ride it with the
// correct extension, provided it's within 4 chars of the string end,
// since if it's not, it may be the part of a path ID. If successful, copy
// the name and its length into the data area of the memory request message
// passed as .
// RETURNS: TRUE .. file exists
// FALSE .. no it don't
//
BOOL FileCheck(char *st, INT16 n, MMU_MSG &memreq)
{
char *cp, szName[128];
while (st && (*st == ' '))
++st;
strcpy(szName, st);
cp = szName + MAX(0, strlen(szName) - 1);
while ((*cp == ' ') && (cp >= szName))
*(cp--) = '\0';
if (szName) {
if ((cp = strrchr(szName, '.')) && (strlen(cp) <= 4))
*cp = '\0';
strcat(szName, ((n == CMD_Find) ? ".PCD" : ".BAT"));
FILE *pf;
if (pf = fopen(szName, ((n == CMD_Find) ? "rb" : "rt"))) {
fclose(pf);
if (memreq.pData = new UINT16[strlen(szName)+1]) {
UINT16 idx = 0;
while (szName[idx]) {
memreq.pData[idx] = (UINT16)szName[idx];
++idx;
}
memreq.pData[idx] = idx;
memreq.uLen = (idx + 1);
return TRUE;
}
}
}
return FALSE;
}
/////////////
// Format the passed message to effect a "rolling" message area above the
// console command line (jus' like the good ol' ICL 2903). Since the array
// needed to build the text block is of variable length we will use the
// heap to allocate it, and since the WriteBlk message is "sent" we are
// responsible for deleting it.
//
static void ConEcho (char *pszTxt, Knl *pTx)
{
char *pst;
INT16 nLen = MIN(nCols-2, (INT16)strlen(pszTxt));
if (pst = new char[nLen+(LEN_NEWECHO*2)]) {
arrGoto[COL] = 2;
arrGoto[ROW] = nDispRow++;
if (nDispRow >= nRows - 1)
nDispRow = 0;
memcpy(pst, arrGoto, LEN_GOTO);
memcpy(pst+LEN_GOTO, arrErEol, LEN_EREOL);
arrGoto[ROW] = nDispRow;
memcpy(pst+LEN_NEWECHO+nLen, arrGoto, LEN_GOTO);
memcpy(pst+LEN_NEWECHO+nLen+LEN_GOTO, arrErEol, LEN_EREOL);
memcpy(pst+LEN_NEWECHO, pszTxt, nLen);
MSG mess(CONX_PID, KM_WriteBlk, nLen+(LEN_NEWECHO<<1), (void*)pst);
pTx->SendMsg(uCon, &mess);
DELETE_ARRAY pst;
}
}
/********************************** eof **********************************/