679 lines
20 KiB
C
679 lines
20 KiB
C
/* share/src/util $Id$
|
|
* Author: Roger A. Cole
|
|
* Date: 10-24-90
|
|
*
|
|
* Experimental Physics and Industrial Control System (EPICS)
|
|
*
|
|
* Copyright 1991, the Regents of the University of California,
|
|
* and the University of Chicago Board of Governors.
|
|
*
|
|
* This software was produced under U.S. Government contracts:
|
|
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
|
|
* and (W-31-109-ENG-38) at Argonne National Laboratory.
|
|
*
|
|
* Initial development by:
|
|
* The Controls and Automation Group (AT-8)
|
|
* Ground Test Accelerator
|
|
* Accelerator Technology Division
|
|
* Los Alamos National Laboratory
|
|
*
|
|
* Co-developed with
|
|
* The Controls and Computing Group
|
|
* Accelerator Systems Division
|
|
* Advanced Photon Source
|
|
* Argonne National Laboratory
|
|
*
|
|
* Modification Log:
|
|
* -----------------
|
|
* .01 10-24-90 rac initial version
|
|
* .02 07-30-91 rac installed in SCCS; changed to use EPICS_ENV..
|
|
* .03 09-11-91 joh updated for v5 vxWorks
|
|
* .04 12-08-91 rac rewrite for Unix-only, without LWP
|
|
*
|
|
* make options
|
|
* -DNDEBUG don't compile assert() checking
|
|
*/
|
|
/*+/mod***********************************************************************
|
|
* TITLE cmdProto - prototype for a multi-threaded program with command interface
|
|
*
|
|
* DESCRIPTION
|
|
* This skeleton can be used as the basis for a multi-threaded program
|
|
* which interfaces to a user with text strings, as from a keyboard.
|
|
* Several generic capabilities are provided:
|
|
*
|
|
* o scanning of input control lines
|
|
* o re-directing of input to come from a file
|
|
* o running as a stand-alone program, or as a server or client,
|
|
* with socket-based communications
|
|
* o on-line help
|
|
* o handling cleanup on exception conditions (such as bus error)
|
|
*
|
|
* To run as a server, specify "server" as a command line option.
|
|
*
|
|
* To run as a client, specify "hostName" as a command line option.
|
|
*
|
|
* Under SunOS, running under dbx doesn't allow testing the error
|
|
* handling features of the code--the debugger catches the signals.
|
|
* To do such testing, run without dbx.
|
|
*
|
|
* To use this skeleton:
|
|
*
|
|
* Change Zzz, zzz, and ZZZ to a desired name
|
|
* Customize a context block
|
|
* Remove the "server" items, if they aren't going to be used
|
|
* Add additional commands in zzzCmdCrunch
|
|
* Add additional help info in zzzInitAtStartup. Usually, this
|
|
* will mean adding to helpCmdsSpec and helpUsage. For
|
|
* any commands needing an individual help topic, then a
|
|
* specific HELP_TOPIC will have to be defined.
|
|
* If you're using sockets, assign a port number in <ports.h>, and
|
|
* change glZzzIPPort to the IPPORT_xxx you've chosen.
|
|
*
|
|
* SunOS
|
|
* o link with genLib.a
|
|
*
|
|
* BUGS
|
|
* o this program should use tasks.h to establish priorities, stack
|
|
* sizes, etc.
|
|
* o not all signals are caught
|
|
* o using this skeleton, it isn't possible to have several invocations
|
|
* of the program running simultaneously
|
|
*
|
|
*-***************************************************************************/
|
|
#include <genDefs.h>
|
|
#include <cmdDefs.h>
|
|
#include <envDefs.h>
|
|
#include <ezsSockSubr.h>
|
|
#include <cadef.h>
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#include <setjmp.h>
|
|
|
|
|
|
/*/subhead ZZZ_CTX-------------------------------------------------------
|
|
* ZZZ_CTX - context information
|
|
*
|
|
* A zzz descriptor is the `master handle' which is used for
|
|
* handling business.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
typedef struct zzzCtx {
|
|
jmp_buf sigEnv; /* environment for longjmp at signal time */
|
|
int listenSock; /* fd for server's "listen" socket */
|
|
int inUse; /* 1 says "presently engaged" by a client */
|
|
int inUseInit; /* 1 says initialized */
|
|
char *inUseName; /* name of client's host */
|
|
char *oldPrompt; /* prompt prior to socket connect */
|
|
int stop; /* 1 says stop operations */
|
|
int clientSock; /* fd for socket to client */
|
|
int reopenIO; /* std... should be re-directed */
|
|
int reopenIOoption; /* 0,1,2; option for arFreopenAllStd */
|
|
int oldStdinFd; /* original fd for stdin, before redirect */
|
|
int oldStdoutFd; /* original fd for stdout, before redirect */
|
|
int oldStderrFd; /* original fd for stderr, before redirect */
|
|
} ZZZ_CTX;
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* prototypes
|
|
*----------------------------------------------------------------------------*/
|
|
int zzz();
|
|
void zzzCmdCrunch();
|
|
void zzzListenCheck();
|
|
void zzzListenSetReopen();
|
|
void zzzSigHandler();
|
|
long zzzInitAtStartup();
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* global definitions
|
|
*----------------------------------------------------------------------------*/
|
|
static CX_CMD glZzzCxCmd;
|
|
static CX_CMD *pglZzzCxCmd=NULL;
|
|
static ZZZ_CTX glZzzCtx;
|
|
static ZZZ_CTX *pglZzzCtx=NULL;
|
|
static char *glZzzId="zzz 11/27/90";
|
|
static int glZzzIPPort;
|
|
static int serverMode=0;
|
|
static chid pCh;
|
|
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME main
|
|
*
|
|
* DESCRIPTION
|
|
* This program is a prototype skeleton for building programs
|
|
* which interact with a keyboard/window environment. The program
|
|
* can be started with any of:
|
|
*
|
|
* % cmdProto (runs as normal interactive program)
|
|
* % cmdProto server (runs as a server)
|
|
* % cmdProto server& (runs as a server)
|
|
* % cmdProto hostName (runs as a client to cmdProto on hostName)
|
|
*
|
|
*-*/
|
|
main(argc, argv)
|
|
int argc; /* number of command line args */
|
|
char *argv[]; /* command line args */
|
|
{
|
|
long stat; /* status return from calls */
|
|
char portText[10];
|
|
int sigNum;
|
|
int gooseOutput=0;
|
|
|
|
setbuf(stdout, NULL);
|
|
pglZzzCtx = &glZzzCtx;
|
|
pglZzzCxCmd = &glZzzCxCmd;
|
|
|
|
stat = zzzInitAtStartup(pglZzzCtx, pglZzzCxCmd);
|
|
assertAlways(stat == OK);
|
|
|
|
if (argc > 2) {
|
|
printf("Usage: %s [server or hostName]\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
else if (argc == 2) {
|
|
if (envGetConfigParam(&EPICS_CMD_PROTO_PORT, 10, portText) == NULL) {
|
|
printf("error getting %s\n", EPICS_CMD_PROTO_PORT.name);
|
|
return ERROR;
|
|
}
|
|
sscanf(portText, "%d", &glZzzIPPort);
|
|
if (strcmp(argv[1], "server") != 0) {
|
|
execlp("cmdClient", "cmdClient", argv[1], portText, (char *)0);
|
|
(void)printf("couldn't exec to cmdClient\n");
|
|
return ERROR;
|
|
}
|
|
serverMode = 1;
|
|
pglZzzCtx->inUseName = argv[1];
|
|
/*-----------------------------------------------------------------------------
|
|
* establish a socket to use to listen for potential clients to connect
|
|
*----------------------------------------------------------------------------*/
|
|
if (ezsCreateListenSocket(&pglZzzCtx->listenSock, glZzzIPPort) != 0) {
|
|
perror("create listen socket");
|
|
exit(1);
|
|
}
|
|
}
|
|
else
|
|
pglZzzCtx->inUseName = "shell";
|
|
|
|
#if 1
|
|
stat = ca_task_initialize();
|
|
assert(stat == ECA_NORMAL);
|
|
#endif
|
|
|
|
genSigInit(zzzSigHandler);
|
|
if ((sigNum = setjmp(pglZzzCtx->sigEnv)) != 0) {
|
|
(void)printf("zzzTask signal received: %d\n", sigNum);
|
|
goto zzzTaskWrapup;
|
|
}
|
|
|
|
while (!pglZzzCtx->stop) {
|
|
if (serverMode)
|
|
zzzListenCheck(&pglZzzCxCmd);
|
|
if (!serverMode || pglZzzCtx->inUse) {
|
|
#if 0
|
|
if (gooseOutput == 0)
|
|
gooseOutput = 30 * pglZzzCxCmd->promptFlag;
|
|
#endif
|
|
if (cmdRead(&pglZzzCxCmd) != NULL) {
|
|
if (serverMode && pglZzzCxCmd->inputEOF) {
|
|
pglZzzCxCmd->pCommand = "close";
|
|
pglZzzCxCmd->inputEOF = 0;
|
|
}
|
|
zzzCmdCrunch(&pglZzzCxCmd, pglZzzCtx);
|
|
}
|
|
#if 0
|
|
if (gooseOutput && serverMode && pglZzzCtx->inUse) {
|
|
if (gooseOutput % 2 == 0) {
|
|
(void)printf("\001");
|
|
}
|
|
gooseOutput--;
|
|
}
|
|
#endif
|
|
}
|
|
#if 1
|
|
stat = ca_pend_event(0.001);
|
|
assert(stat != ECA_EVDISALLOW);
|
|
#endif
|
|
fflush(stdout); /* flushes needed for socket I/O */
|
|
fflush(stderr);
|
|
fflush(pglZzzCxCmd->dataOut);
|
|
sleep(1);
|
|
/* MDA - usleep isn't POSIX
|
|
usleep(100000); /* sleep .1 second */
|
|
*/
|
|
}
|
|
|
|
return OK;
|
|
|
|
zzzTaskWrapup:
|
|
if (pglZzzCtx->reopenIO) {
|
|
(void)zzzFreopenAllStd(pglZzzCtx,pglZzzCtx->reopenIOoption);
|
|
pglZzzCtx->reopenIO = 0;
|
|
}
|
|
zzzListenQuit(&pglZzzCxCmd);
|
|
|
|
/* put code for cleanup here */
|
|
|
|
#if 1
|
|
stat = ca_task_exit();
|
|
if (stat != ECA_NORMAL)
|
|
(void)printf("zzz: ca_task_exit error: %s\n", ca_message(stat));
|
|
#endif
|
|
|
|
if (pglZzzCxCmd->dataOutRedir)
|
|
fclose(pglZzzCxCmd->dataOut);
|
|
|
|
fflush(stdout); /* flushes needed for sockets */
|
|
fflush(stderr);
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzSigHandler - signal handling
|
|
*
|
|
* DESCRIPTION
|
|
* These routines set up for the signals to be caught for zzzTask
|
|
* and handle the signals when they occur.
|
|
*
|
|
* RETURNS
|
|
* void
|
|
*
|
|
* BUGS
|
|
* o not all signals are caught
|
|
* o it's not clear how useful it is to catch the signals which originate
|
|
* from the keyboard
|
|
*
|
|
*-*/
|
|
static void
|
|
zzzSigHandler(signo)
|
|
int signo;
|
|
{
|
|
if (signo == SIGPIPE) {
|
|
if (serverMode)
|
|
zzzListenClose(&pglZzzCxCmd);
|
|
return;
|
|
}
|
|
printf("entered zzzSigHandler for signal:%d\n", signo);
|
|
signal(signo, SIG_DFL);
|
|
longjmp(pglZzzCtx->sigEnv, signo);
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzCmdCrunch - process a command line
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* RETURNS
|
|
* void
|
|
*
|
|
* BUGS
|
|
* o text
|
|
*
|
|
*-*/
|
|
static void
|
|
zzzCmdCrunch(ppCxCmd, pZzzCtx)
|
|
CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */
|
|
ZZZ_CTX *pZzzCtx; /* IO pointer to zzz context */
|
|
{
|
|
CX_CMD *pCxCmd; /* local copy of pointer, for convenience */
|
|
|
|
pCxCmd = *ppCxCmd;
|
|
if (strcmp(pCxCmd->pCommand, "assert0") == 0) {
|
|
/*-----------------------------------------------------------------------------
|
|
* under SunOS, this generates SIGABRT
|
|
*----------------------------------------------------------------------------*/
|
|
assertAlways(0);
|
|
}
|
|
else if (strcmp(pCxCmd->pCommand, "quit") == 0) {
|
|
pZzzCtx->stop = 1;
|
|
if (serverMode)
|
|
zzzListenClose(ppCxCmd);
|
|
}
|
|
else if (strcmp(pCxCmd->pCommand, "trap") == 0) {
|
|
int *j;
|
|
j = (int *)(-1);
|
|
j = (int *)(*j);
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "close") == 0) {
|
|
if (*ppCxCmd != (*ppCxCmd)->pCxCmdRoot)
|
|
(void)printf("close illegal in source'd file\n");
|
|
else if (strcmp(pglZzzCtx->inUseName, "shell") == 0)
|
|
(void)printf("close illegal--zzz not running as server\n");
|
|
else if (serverMode) {
|
|
zzzListenClose(ppCxCmd);
|
|
}
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "dataOut") == 0) {
|
|
if ((*ppCxCmd)->dataOutRedir)
|
|
fclose ((*ppCxCmd)->dataOut);
|
|
(*ppCxCmd)->dataOutRedir = 0;
|
|
if (nextNonSpaceField( &(*ppCxCmd)->pLine, &(*ppCxCmd)->pField,
|
|
&(*ppCxCmd)->delim) < 1)
|
|
(*ppCxCmd)->dataOut = stdout;
|
|
else {
|
|
(*ppCxCmd)->dataOut = fopen((*ppCxCmd)->pField, "a");
|
|
if ((*ppCxCmd)->dataOut == NULL) {
|
|
(void)printf("couldn't open %s\n", (*ppCxCmd)->pField);
|
|
(*ppCxCmd)->dataOut = stdout;
|
|
(*ppCxCmd)->dataOutRedir = 0;
|
|
}
|
|
else
|
|
(*ppCxCmd)->dataOutRedir = 1;
|
|
}
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "search") == 0) {
|
|
ca_search("jjj", &pCh);
|
|
ca_pend_io(.001);
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "clear") == 0) {
|
|
ca_clear_channel(pCh);
|
|
ca_pend_io(.001);
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "server") == 0) {
|
|
(void)printf( "\"server\" legal only on command line\n");
|
|
(void)printf( "use help usage for more information\n");
|
|
}
|
|
else if (strcmp((*ppCxCmd)->pCommand, "source") == 0) {
|
|
cmdSource(ppCxCmd);
|
|
}
|
|
else {
|
|
/*----------------------------------------------------------------------------
|
|
* help (or illegal command)
|
|
*----------------------------------------------------------------------------*/
|
|
(void)nextNonSpaceField(&pCxCmd->pLine, &pCxCmd->pField,
|
|
&pCxCmd->delim);
|
|
helpIllegalCommand(stdout, &pCxCmd->helpList, pCxCmd->pCommand,
|
|
pCxCmd->pField);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzListenCheck - listen for client connections
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* RETURNS
|
|
*
|
|
* BUGS
|
|
* o cleanup of listen socket isn't dealt with. Under SunOS, the system
|
|
* seems to clean up OK.
|
|
*
|
|
* In fact, under SunOS, shutdown and/or close seem to inhibit proper
|
|
* cleanup by the system.
|
|
*
|
|
* SEE ALSO
|
|
*
|
|
* EXAMPLE
|
|
*
|
|
*-*/
|
|
void
|
|
zzzListenCheck(ppCxCmd)
|
|
CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */
|
|
{
|
|
long stat;
|
|
char clientName[30];
|
|
static char sockPrompt[80];
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* check to see if a connect attempt has been made. If the server is
|
|
* already in use by a client, connect attempts will be rejected.
|
|
*----------------------------------------------------------------------------*/
|
|
if (ezsListenExclusiveUse(&pglZzzCtx->clientSock,
|
|
&pglZzzCtx->listenSock, &pglZzzCtx->inUse,
|
|
clientName, glZzzId) < 0) {
|
|
perror("check for connect attempt");
|
|
exit(1);
|
|
}
|
|
|
|
if (pglZzzCtx->inUse && pglZzzCtx->inUseInit == 0) {
|
|
/*-----------------------------------------------------------------------------
|
|
* When a connect attempt succeeds, change stdin and stdout to use
|
|
* the socket, so that server I/O (i.e., I/O for this program) will
|
|
* automatically be re-directed to the client. This must be done
|
|
* for all tasks in this program.
|
|
*
|
|
* The prompt is changed to a special prompt which ends witn \n. This
|
|
* is done to avoid buffer flushing problems on some operating systems.
|
|
* A special prefix of "#p#r#" is added to the normal prompt so that
|
|
* the client knows that some massaging needs to be done before
|
|
* displaying the prompt to the user.
|
|
*----------------------------------------------------------------------------*/
|
|
(void)printf("%s connected\n", clientName);
|
|
if (zzzFreopenAllStd(pglZzzCtx, 1) != OK)
|
|
assertAlways(0);
|
|
pglZzzCtx->oldPrompt = (*ppCxCmd)->prompt;
|
|
assert(strlen(pglZzzCtx->oldPrompt) < 72);
|
|
(void)sprintf(sockPrompt, "#p#r#%s\n", pglZzzCtx->oldPrompt);
|
|
(*ppCxCmd)->prompt = sockPrompt;
|
|
zzzListenSetReopen(2); /* reopen all other tasks to socket */
|
|
pglZzzCtx->inUseInit = 1;
|
|
(*ppCxCmd)->promptFlag = 1;
|
|
#if 0
|
|
stat = zzzStartup(serverMode, clientName);
|
|
assertAlways(stat == OK);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzListenClose
|
|
*
|
|
*-*/
|
|
long
|
|
zzzListenClose(ppCxCmd)
|
|
CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */
|
|
{
|
|
if (pglZzzCtx->inUse) {
|
|
#if 0
|
|
shutdown(pglZzzCtx->clientSock, 2);
|
|
#endif
|
|
close(pglZzzCtx->clientSock);
|
|
pglZzzCtx->clientSock = -1;
|
|
pglZzzCtx->inUse = 0;
|
|
pglZzzCtx->inUseInit = 0;
|
|
if (zzzFreopenAllStd(pglZzzCtx, 0) != OK)
|
|
assertAlways(0);
|
|
(void)printf("%s disconnected\n", pglZzzCtx->inUseName);
|
|
(*ppCxCmd)->prompt = pglZzzCtx->oldPrompt;
|
|
zzzListenSetReopen(0); /* reopen all other tasks to std... */
|
|
}
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzListenQuit
|
|
*
|
|
*-*/
|
|
long
|
|
zzzListenQuit(ppCxCmd)
|
|
CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */
|
|
{
|
|
if (pglZzzCtx->clientSock != -1) {
|
|
shutdown(pglZzzCtx->clientSock, 2);
|
|
close(pglZzzCtx->clientSock);
|
|
}
|
|
#if 0
|
|
shutdown(pglZzzCtx->listenSock, 2);
|
|
close(pglZzzCtx->listenSock);
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzListenSetReopen
|
|
*
|
|
*-*/
|
|
void
|
|
zzzListenSetReopen(option)
|
|
int option; /* I option for reopen for use with zzzFreopenAllStd */
|
|
{
|
|
pglZzzCtx->reopenIOoption = option;
|
|
pglZzzCtx->reopenIO = 1;
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzFreopenAllStd - reopen stdin, stdout, and stderr
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* RETURNS
|
|
* OK, or
|
|
* ERROR (an error message has been printed)
|
|
*
|
|
* BUGS
|
|
* o text
|
|
*
|
|
*-*/
|
|
static int
|
|
zzzFreopenAllStd(pCtx, option)
|
|
ZZZ_CTX *pCtx; /* IO pointer to zzz context */
|
|
int option; /* I activity selector:
|
|
0 set back to use old fd's
|
|
1 use client socket, save old fd's
|
|
2 use client socket, don't save old fd's */
|
|
{
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
if (option == 0) {
|
|
if (ezsFreopenToFd(stdin, &pCtx->oldStdinFd, NULL) < 0) {
|
|
perror("restore stdin");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stdout, &pCtx->oldStdoutFd, NULL) < 0) {
|
|
perror("restore stdout");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stderr, &pCtx->oldStderrFd, NULL) < 0) {
|
|
perror("restore stderr");
|
|
return ERROR;
|
|
}
|
|
}
|
|
else if (option == 1) {
|
|
if (ezsFreopenToFd(stdin, &pCtx->clientSock, &pCtx->oldStdinFd) < 0) {
|
|
perror("reopen stdin");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stdout, &pCtx->clientSock, &pCtx->oldStdoutFd) < 0) {
|
|
perror("reopen stdout");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stderr, &pCtx->clientSock, &pCtx->oldStderrFd) < 0) {
|
|
perror("reopen stderr");
|
|
return ERROR;
|
|
}
|
|
}
|
|
else if (option == 2) {
|
|
if (ezsFreopenToFd(stdin, &pCtx->clientSock, NULL) < 0) {
|
|
perror("reopen stdin");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stdout, &pCtx->clientSock, NULL) < 0) {
|
|
perror("reopen stdout");
|
|
return ERROR;
|
|
}
|
|
if (ezsFreopenToFd(stderr, &pCtx->clientSock, NULL) < 0) {
|
|
perror("reopen stderr");
|
|
return ERROR;
|
|
}
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
/*+/subr**********************************************************************
|
|
* NAME zzzInitAtStartup - initialization for zzz
|
|
*
|
|
* DESCRIPTION
|
|
* Perform several initialization duties:
|
|
* o initialize the zzz context
|
|
* o initialize the command context block
|
|
* o initialize the help information
|
|
*
|
|
* RETURNS
|
|
* OK, or
|
|
* ERROR
|
|
*
|
|
* BUGS
|
|
* o text
|
|
*
|
|
*-*/
|
|
static long
|
|
zzzInitAtStartup(pZzzCtx, pCxCmd)
|
|
ZZZ_CTX *pZzzCtx;
|
|
CX_CMD *pCxCmd;
|
|
{
|
|
/* code to initialize zzz context here */
|
|
pZzzCtx->inUse = 0;
|
|
pZzzCtx->listenSock = -1;
|
|
pZzzCtx->clientSock = -1;
|
|
pZzzCtx->oldStdinFd = -1;
|
|
pZzzCtx->oldStdoutFd = -1;
|
|
pZzzCtx->oldStderrFd = -1;
|
|
|
|
cmdInitContext(pCxCmd, " zzz: ");
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
* help information initialization
|
|
*----------------------------------------------------------------------------*/
|
|
helpInit(&pCxCmd->helpList);
|
|
/*-----------------------------------------------------------------------------
|
|
* help info--generic commands
|
|
*----------------------------------------------------------------------------*/
|
|
helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpCmds, "commands", "\n\
|
|
Generic commands are:\n\
|
|
bg\n\
|
|
close (use help usage for more info)\n\
|
|
dataOut [filePath] (default is to normal output)\n\
|
|
help [topic]\n\
|
|
quit (or ^D) (use help usage for more info)\n\
|
|
source filePath\n\
|
|
");
|
|
/*-----------------------------------------------------------------------------
|
|
* help info--bg command
|
|
*----------------------------------------------------------------------------*/
|
|
helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpBg, "bg", "\n\
|
|
Under SunOS, no bg command is directly available. Instead, if zzz\n\
|
|
is being run from the C shell (csh), it can be put in the background\n\
|
|
using several steps from the keyboard (with the first % on each line being\n\
|
|
the prompt from csh):\n\
|
|
type ^Z, then type\n\
|
|
% bg %zzz\n\
|
|
\n\
|
|
To move zzz to the foreground, type\n\
|
|
% fg %zzz\n\
|
|
");
|
|
/*-----------------------------------------------------------------------------
|
|
* help info--zzz-specific commands
|
|
*----------------------------------------------------------------------------*/
|
|
helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpCmdsSpec, "commands", "\n\
|
|
zzz-specific commands are:\n\
|
|
abcdef\n\
|
|
\n\
|
|
Output from commands flagged with * can be routed to a file by using the\n\
|
|
\"dataOut filePath\" command. The present contents of the file are\n\
|
|
preserved, with new output being written at the end.\n\
|
|
");
|
|
/*-----------------------------------------------------------------------------
|
|
* help info--zzz usage information
|
|
*----------------------------------------------------------------------------*/
|
|
helpTopicAdd(&pCxCmd->helpList, &pCxCmd->helpUsage, "usage", "\n\
|
|
zzz performs some functions. This is a skeleton program.\n\
|
|
\n\
|
|
For information on moving zzz into the background, use the \"help bg\"\n\
|
|
command.\n\
|
|
\n\
|
|
zzz can be run as a server by:\n\
|
|
under SunOS: % zzz server\n\
|
|
\n\
|
|
zzz can be run as a client connecting to a zzz server on \"hostName\" by:\n\
|
|
under SunOS: % zzz hostName\n\
|
|
\n\
|
|
When running zzz as a client, two commands are of special interest:\n\
|
|
\n\
|
|
close shuts down the client but leaves the server running\n\
|
|
quit shuts down both the client and the server\n\
|
|
");
|
|
return OK;
|
|
}
|