home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
TELECOM
/
stg_v4.lzh
/
smenu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-11-11
|
20KB
|
1,227 lines
/*
* smenu
*
* Copyright (c) 1993 by Scott Griepentrog
*
*/
#include <time.h>
#include "stgnet.h"
#include "trm.h"
#include "pwd.h"
char *malloc();
extern char **environ;
#define MAX_SIZE 8192 /* max .mnu file size */
char acBuffer[BUF_SIZE];
struct passwd *pwd;
char *pcUser;
int iTest=0; /* test mode */
char *apcHigh[]=
{
"so","se",
"us","ue",
"mb","mr",
"md","me",
0
};
char acHigh[32]; /* highlight start */
char acHend[32]; /* highlight end */
struct sSel
{
struct sSel *psNext; /* linked list */
char *pcKey; /* key label */
char *pcCmd; /* command string */
char cKey; /* key to press */
char cOff; /* offset in pcKey to cKey */
};
struct sMenu
{
struct sMenu *psNext; /* linked list */
char acFile[32]; /* menu name */
char *pcText; /* entire menu file */
int iSize; /* length of text */
struct sSel *psSel; /* link list of selections */
char *pcPrompt; /* prompt string */
char cInUse; /* recusrion check */
} *psMenuList=0;
char *
strsav(s)
char *s;
{
char *p;
p=malloc(strlen(s)+1);
if (!p)
{
syserr("malloc: %m");
exit(errno);
}
strcpy(p,s);
return(p);
}
char *
strnsav(s,n)
char *s;
int n;
{
char *p;
p=malloc(n+1);
if (!p)
{
syserr("malloc: %m");
exit(errno);
}
strncpy(p,s,n);
p[n]=0;
return(p);
}
/* reload menus in use - termcap has changed */
void
ReLoad()
{
struct sMenu *psMenu;
psMenu=psMenuList;
while (psMenu)
{
Prompt(psMenu);
psMenu=psMenu->psNext;
}
}
void
PickHigh()
{
char *hi,*he;
char **ppcCode;
ppcCode=apcHigh;
while (*ppcCode)
{
hi=_TrmString(*ppcCode++);
he=_TrmString(*ppcCode++);
if (!hi || !he)
continue;
strcpy(acHigh,_TrmConvert(hi));
strcpy(acHend,_TrmConvert(he));
return;
}
*acHigh=*acHend=0;
}
Fork(pcCmd)
char *pcCmd;
{
int iPid;
int iWait;
char **ppcArgs;
ppcArgs=arglist(pcCmd,0);
if (*pcCmd=='-')
pcCmd++;
if (!stricmp(ppcArgs[0],"unsetenv"))
{
if (!ppcArgs[1])
{
syserr("unsetenv take one argument\n");
return(ERR);
}
set_env(ppcArgs[1],0);
return(0);
}
if (!stricmp(ppcArgs[0],"setenv"))
{
if (!ppcArgs[1] || !ppcArgs[2])
{
syserr("setenv takes two arguments\n");
return(ERR);
}
if (!strcmp(ppcArgs[1],"TERM"))
{
if (_Trm(ppcArgs[2])==ERR)
{
wstringf(2,"smenu: unknown terminal type '%s'\n",ppcArgs[2]);
/* reload from var */
if (_Trm(getenv("TERM"))!=ERR)
PickHigh();
return(ERR);
}
else
{
PickHigh();
ReLoad();
}
}
set_env(ppcArgs[1],ppcArgs[2]);
return(0);
}
iPid=_fork(pcCmd,ppcArgs,environ);
if (iPid==ERR)
{
syserr("cant fork %s: %m",*ppcArgs);
return(ERR);
}
/* for unix: ignore ^C while waiting */
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
while (1)
{
iWait=wait(&errno);
if (iWait==ERR)
{
syserr("wait %s %m",*ppcArgs);
goto fork_err;
}
if (iWait==iPid)
break;
}
if (errno)
{
#ifdef _OS9
syserr("'%s' exited: %m",pcCmd);
#else
if (errno&0x7F)
syserr("'%s' exit signal %d",pcCmd,errno&0x7F);
else
syserr("'%s' exit code %d",pcCmd,errno>>8);
#endif
goto fork_err;
}
return(0);
fork_err:
signal(SIGINT,SIG_DFL);
signal(SIGQUIT,SIG_DFL);
return(ERR);
}
Run(pcCmd,ppcArgs)
char *pcCmd;
char **ppcArgs;
{
int iMenu=0;
int iExit=0;
char *pcBuf;
char *pcTemp;
char **ppcTemp;
int iTemp;
char acInput[256];
char acTemp[256];
pcBuf=acBuffer;
*acInput=0;
switch (*pcCmd)
{
case '-':
iMenu++;
pcCmd++;
break;
case '+':
iMenu++;
case '#':
iExit++;
pcCmd++;
break;
case '=':
pcCmd++;
default:
break;
}
while (*pcCmd)
{
if (*pcCmd=='\\')
{
/* pass next character regardless */
*pcCmd++;
*pcBuf++=*pcCmd++;
continue;
}
if (*pcCmd=='%')
{
if (!*++pcCmd)
break;
switch(*pcCmd)
{
case '(':
pcCmd++;
pcTemp=acTemp;
while (*pcCmd && *pcCmd!=')')
*pcTemp++=*pcCmd++;
if (*pcCmd)
pcCmd++;
*pcTemp=0;
pcTemp=getenv(acTemp);
if (pcTemp) while (*pcTemp)
*pcBuf++=*pcTemp++;
break;
case '\'':
pcCmd++;
pcTemp=acTemp;
while (*pcCmd && *pcCmd!='\'')
*pcTemp++=*pcCmd++;
if (*pcCmd)
pcCmd++;
*pcTemp=0;
writeln(2,acTemp,strlen(acTemp));
if (get_line(acInput,128,0)==ERR)
return(ERR);
pcTemp=acInput;
while (*pcTemp)
*pcBuf++=*pcTemp++;
break;
case '@':
pcTemp=acInput;
while (*pcTemp)
*pcBuf++=*pcTemp++;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (ppcArgs)
{
ppcTemp=ppcArgs;
iTemp=*pcCmd++-'0';
while (iTemp-- && *ppcTemp)
ppcTemp++;
if (*ppcTemp)
{
pcTemp=*ppcTemp;
while (*pcTemp)
*pcBuf++=*pcTemp++;
}
}
break;
case '^':
pcCmd++;
if (!*acInput)
return(0);
break;
default:
syserr("unknown code %%%c",*pcCmd);
return(ERR);
}
continue;
}
/* don't need quotes with \ escape
if (*pcCmd==34)
{
if (*++pcCmd==34)
{
*pcBuf++=34;
pcCmd++;
continue;
}
while (*pcCmd && *pcCmd!=34)
*pcBuf++=*pcCmd++;
if (*pcCmd)
pcCmd++;
continue;
}
*/
if (*pcCmd==';')
{
*pcBuf=0;
if (Fork(acBuffer)==ERR)
return(0);
pcCmd++;
pcBuf=acBuffer;
continue;
}
*pcBuf++=*pcCmd++;
}
*pcBuf=0;
if (iMenu)
Menu(acBuffer);
else
Fork(acBuffer);
if (iExit)
exit(0);
return(0);
}
Prompt(psMenu)
struct sMenu *psMenu;
{
struct sSel *psSel;
char acPrompt[256];
char *pcPrompt;
char *pcTemp;
char *pcHigh;
int iPrompt;
int iWidth;
int iFirst;
iWidth=_TrmNumber("co");
if (!iWidth)
iWidth=80;
pcPrompt=acPrompt;
iPrompt=0;
iFirst=1;
/* have to put menu name in prompt string for later width adjustment */
pcTemp=psMenu->acFile;
while (*pcTemp)
{
if (!iPrompt)
*pcPrompt++=toupper(*pcTemp++);
else
*pcPrompt++=*pcTemp++;
iPrompt++;
}
*pcPrompt++=':';
*pcPrompt++=' ';
iPrompt+=2;
/* loop on selection list */
psSel=psMenu->psSel;
while (psSel)
{
/* if no pcCmd, selection is not allowed */
if (!psSel->pcCmd)
{
psSel=psSel->psNext;
continue;
}
/* is there enough space for this entry? */
if (iPrompt>iWidth/2 && iPrompt+strlen(psSel->pcKey)+3+10>iWidth)
{
/* no, go to next line */
*pcPrompt++='\n';
*pcPrompt++='\r';
iPrompt=0;
iFirst=1;
pcTemp=psMenu->acFile;
while (*pcTemp++)
{
*pcPrompt++=' ';
iPrompt++;
}
*pcPrompt++=' ';
*pcPrompt++=' ';
iPrompt+=2;
}
/* add to prompt */
if (!iFirst)
{
*pcPrompt++=',';
*pcPrompt++=' ';
iPrompt+=2;
}
else
iFirst=0;
pcTemp=psSel->pcKey;
while (*pcTemp)
{
if (pcTemp-psSel->pcKey==psSel->cOff)
{
pcHigh=acHigh;
if (!*pcHigh)
{
*pcPrompt++='[';
iPrompt++;
}
else
while (*pcHigh)
*pcPrompt++=*pcHigh++;
*pcPrompt++=toupper(*pcTemp++);
iPrompt++;
pcHigh=acHend;
if (!*pcHigh)
{
*pcPrompt++=']';
iPrompt++;
}
else
while (*pcHigh)
*pcPrompt++=*pcHigh++;
}
else if (pcTemp-psSel->pcKey<psSel->cOff)
{
*pcPrompt++=tolower(*pcTemp++);
iPrompt++;
}
else
{
*pcPrompt++=*pcTemp++;
iPrompt++;
}
}
psSel=psSel->psNext;
}
*pcPrompt++=' ';
*pcPrompt++=':';
*pcPrompt=0;
if (psMenu->pcPrompt)
free(psMenu->pcPrompt);
psMenu->pcPrompt=strsav(acPrompt);
return(0);
}
Parse(psMenu)
struct sMenu *psMenu;
{
char *pcText;
char *pcEnd;
char *pcLine;
char *pcTemp;
char *pcLast;
char cLast;
struct sSel *psSel;
struct sSel *psSeek;
char acKeyUsed[128];
/* clear key map */
memset(acKeyUsed,0,128);
pcLast="";
/* skip display section */
pcText=psMenu->pcText;
pcEnd=pcText+psMenu->iSize;
while (pcText<pcEnd)
{
if (*pcText=='~' && pcText[1]=='\n')
break;
while (pcText<pcEnd && *pcText!='\n')
pcText++;
if (pcText<pcEnd)
pcText++;
}
if (pcText==pcEnd)
{
syserr("unexpected EOF in %s",
psMenu->acFile);
return(ERR);
}
/* terminate display section */
*pcText++=0;
*pcText++=0;
/* loop through remainder */
while (pcText<pcEnd)
{
pcLine=pcText;
/* find end of line */
while (pcText<pcEnd && *pcText!='\n')
pcText++;
if (pcText==pcEnd)
{
syserr("unexpected EOF in %s",
psMenu->acFile);
return(ERR);
}
*pcText++=0;
/* ignore empty or single char line */
if (!*pcLine || !pcLine[1])
continue;
/* add line to selection list */
psSel=(struct sSel *)malloc(sizeof(*psSel));
if (!psSel)
{
syserr("malloc: %m");
return(ERR);
}
memset(psSel,0,sizeof(*psSel));
/* add to END of list */
if (!psMenu->psSel)
psMenu->psSel=psSel;
else
{
psSeek=psMenu->psSel;
while (psSeek && psSeek->psNext)
psSeek=psSeek->psNext;
psSeek->psNext=psSel;
}
/* now parse selection line */
pcTemp=pcLine;
/* first, hunt separator code */
pcTemp++; /* first char at least is not a char */
while (*pcTemp)
{
if (*pcTemp=='\\')
{
pcTemp++;
if (*pcTemp)
pcTemp++;
continue;
}
if (*pcTemp==' ' || *pcTemp=='/')
{
pcTemp++;
continue;
}
if (*pcTemp<'@')
break;
pcTemp++;
}
if (!*pcTemp)
{
syserr("syntax error at '%s' in %s",
pcLine,psMenu->acFile);
return(ERR);
}
/* save key label */
psSel->pcKey=strnsav(pcLine,pcTemp-pcLine);
pcLine=pcTemp;
/* and remove \'s */
pcTemp=psSel->pcKey;
while (*pcTemp)
{
if (*pcTemp=='\\')
{
strcpy(pcTemp,pcTemp+1);
if (*pcTemp)
pcTemp++;
}
else
pcTemp++;
}
/* and select key code */
pcTemp=psSel->pcKey;
while (*pcTemp)
{
if (*pcTemp=='~')
{
strcpy(pcTemp,pcTemp+1);
break;
}
pcTemp++;
}
if (!*pcTemp)
{
pcTemp=psSel->pcKey;
if (!strcmp(pcTemp,pcLast))
pcTemp=psSel->pcKey+cLast;
else
while (*pcTemp)
{
if (!acKeyUsed[toupper(*pcTemp)])
break;
pcTemp++;
}
}
if (!*pcTemp)
{
syserr("cant set key for %s in %s",
psSel->pcKey,psMenu->acFile);
return(ERR);
}
psSel->cKey=toupper(*pcTemp);
acKeyUsed[psSel->cKey]++;
psSel->cOff=pcTemp-psSel->pcKey;
pcLast=psSel->pcKey;
cLast=psSel->cOff;
next_code:
switch (*pcLine)
{
case '-':
case '=':
case '#':
psSel->pcCmd=pcLine;
break;
case '?':
case '!':
pcTemp=pcLine+1;
while (*pcTemp && *pcTemp>'@')
pcTemp++;
strncpy(b,pcLine+1,pcTemp-pcLine);
b[pcTemp-pcLine-1]=0;
if (*pcLine=='?')
{
if (!isgrpmem(b,pcUser))
continue;
}
else
{
if (isgrpmem(b,pcUser))
continue;
}
pcLine=pcTemp;
goto next_code;
default:
strcpy(b,pcLine);
if (strlen(b)>10)
strcpy(b+10,"...");
syserr("unknown code '%s' in %s",
b,psMenu->acFile);
return(ERR);
}
}
/* set prompt from selection list */
if (Prompt(psMenu))
return(ERR);
return(0);
}
struct sMenu *
Load(pcFile)
char *pcFile;
{
struct sMenu *psMenu;
int hFile;
long lSize;
stringf(b,"%s/%s.mnu",MENU_DIR,pcFile);
hFile=open(b,O_RDONLY);
if (hFile==ERR)
{
syserr("cant open %s: %m",b);
return(0);
}
lSize=_gs_size(hFile);
if (lSize>MAX_SIZE)
{
syserr("menu %s is too large",pcFile);
close(hFile);
return(0);
}
psMenu=(struct sMenu *)malloc(sizeof(*psMenu));
if (!psMenu)
{
syserr("malloc: %m");
close(hFile);
return(0);
}
memset(psMenu,0,sizeof(*psMenu));
psMenu->iSize=(int)lSize;
psMenu->pcText=malloc(psMenu->iSize);
if (!psMenu->pcText)
{
syserr("malloc: %m");
close(hFile);
free(psMenu);
return(0);
}
if (read(hFile,psMenu->pcText,psMenu->iSize)==ERR)
{
syserr("read %s: %m",pcFile);
close(hFile);
free(psMenu->pcText);
free(psMenu);
return(0);
}
close(hFile);
strcpy(psMenu->acFile,pcFile);
if (Parse(psMenu)==ERR)
{
free(psMenu->pcText);
free(psMenu);
return(0);
}
return(psMenu);
}
Display(psMenu,iMode,ppcArgs)
struct sMenu *psMenu;
int iMode;
char **ppcArgs;
{
char *pcText;
char *pcOut;
char *pcEnd;
char *pcTemp;
int x,y;
int iTerm; /* next terminator is not for ~& */
if (iTest)
return;
iTerm=0;
pcOut=acBuffer;
pcEnd=pcOut+BUF_SIZE;
pcText=psMenu->pcText;
/* skip to first ~& for minimal display */
if (iMode>1)
{
while (*pcText)
{
if (*pcText=='~' && pcText[1]=='&')
{
pcText+=2;
break;
}
pcText++;
}
}
while (*pcText) switch(*pcText)
{
case '~':
switch (*++pcText)
{
case '\n':
case '&':
pcText++;
continue;
case ':':
pcText++;
if (iMode>1)
{
if (iTerm)
{
/* that terminator for ~? or ~! */
iTerm=0;
continue;
}
while (*pcText)
{
if (*pcText=='~' && pcText[1]=='&')
{
pcText+=2;
break;
}
pcText++;
}
}
continue;
case '$':
pcText++;
x=0;
while (*pcText && *pcText>='0' && *pcText<='9')
{
x*=10;
x+=*pcText++-'0';
}
if (!*pcText)
continue;
while (x--)
*pcOut++=*pcText;
pcText++;
continue;
case '=':
write(1,acBuffer,pcOut-acBuffer);
pcOut=acBuffer;
pcTemp=b;
while (*pcText && *pcText!='\n')
{
*pcTemp++=*pcText++;
}
if (*pcText)
pcText++;
*pcTemp=0;
Run(b,ppcArgs);
continue;
case '?':
pcText++;
if (!(b[0]=*pcText++)) break;
if (b[0]=='^')
{
if (iMode)
goto skip;
iTerm=1;
continue;
}
if (!(b[1]=*pcText++)) break;
b[2]=0;
pcTemp=_TrmString(b);
if (!pcTemp)
goto skip;
iTerm=1;
continue;
case '!':
pcText++;
if (!(b[0]=*pcText++)) break;
if (b[0]=='^')
{
if (!iMode)
goto skip;
iTerm=1;
continue;
}
if (!(b[1]=*pcText++)) break;
b[2]=0;
pcTemp=_TrmString(b);
if (pcTemp)
goto skip;
iTerm=1;
continue;
skip:
while (*pcText)
{
if (pcText[0]=='~' && pcText[1]==':')
{
pcText+=2;
break;
}
pcText++;
}
continue;
case '@':
if (!*++pcText) break;
pcTemp=acHigh;
while (*pcTemp)
*pcOut++=*pcTemp++;
*pcOut++=*pcText++;
pcTemp=acHend;
while (*pcTemp)
*pcOut++=*pcTemp++;
continue;
case '(':
pcText++;
x=atoi(pcText);
while (*pcText && *pcText!=',' && *pcText!=')')
pcText++;
y=0;
if (*pcText==',')
y=atoi(++pcText);
while (*pcText && *pcText!=')')
pcText++;
if (*pcText)
pcText++;
pcTemp=_TrmGoXY(x,y);
if (pcTemp)
pcTemp=_TrmConvert(pcTemp);
if (pcTemp) while (*pcTemp)
*pcOut++=*pcTemp++;
continue;
default:
if (!(b[0]=*pcText++)) break;
if (!(b[1]=*pcText++)) break;
b[2]=0;
pcTemp=_TrmString(b);
pcTemp=_TrmConvert(pcTemp);
if (pcTemp) while (*pcTemp)
*pcOut++=*pcTemp++;
continue;
}
case '\n':
*pcOut++=*pcText++;
*pcOut++='\r';
break;
default:
*pcOut++=*pcText++;
/* dump output if getting too big */
if (pcOut-acBuffer>BUF_SIZE/2)
{
write(1,acBuffer,pcOut-acBuffer);
pcOut=acBuffer;
}
break;
}
if (pcOut-acBuffer)
write(1,acBuffer,pcOut-acBuffer);
}
Menu(pcCmd)
char *pcCmd;
{
struct sMenu *psMenu;
struct sSel *psSel;
char cKey;
char **ppcMenu;
char acCmd[128];
int iTemp;
if (strlen(pcCmd)>127)
pcCmd[127]=0;
strcpy(acCmd,pcCmd);
ppcMenu=arglist(acCmd,0);
if (iTest)
{
iTemp=iTest-1;
while (iTemp--)
write(1," ",2);
wstringf(1,"%s\n",acCmd);
}
psMenu=psMenuList;
while (psMenu)
{
if (!stricmp(*ppcMenu,psMenu->acFile))
break;
psMenu=psMenu->psNext;
}
if (!psMenu)
{
psMenu=Load(*ppcMenu);
if (!psMenu)
return(ERR);
psMenu->psNext=psMenuList;
psMenuList=psMenu;
if (!iTest && _gs_rdy(2)==ERR)
Display(psMenu,0,ppcMenu);
}
else
{
if (!iTest && _gs_rdy(2)==ERR)
Display(psMenu,2,ppcMenu);
}
if (psMenu->cInUse)
{
syserr("recursion to menu %s",*ppcMenu);
return(ERR);
}
psMenu->cInUse=1;
if (iTest)
{
iTemp=0;
psSel=psMenu->psSel;
while (psSel)
{
if (*psSel->pcCmd=='-')
{
if (psSel->pcCmd[1])
{
iTest++;
Menu(psSel->pcCmd+1);
iTest--;
}
else
iTemp++;
}
if (*psSel->pcCmd=='#')
iTemp++;
psSel=psSel->psNext;
}
if (!iTemp && iTest>1)
wstringf(2,"ERROR: no exit (- or #) from menu!\n");
psMenu->cInUse=0;
return(0);
}
prompt:
if (_gs_rdy(2)==ERR)
write(1,psMenu->pcPrompt,strlen(psMenu->pcPrompt));
getkey:
cKey=get_key();
if (cKey==ERR)
exit(228);
if (cKey=='\n')
{
writeln(1,"\n",1);
Display(psMenu,1,ppcMenu);
goto prompt;
}
if (!cKey)
goto getkey;
psSel=psMenu->psSel;
while (psSel)
{
if (psSel->pcCmd)
if (psSel->cKey==toupper(cKey))
break;
psSel=psSel->psNext;
}
if (!psSel)
{
writeln(1,"\7",1);
goto getkey;
}
wstringf(2,"%s\n",psSel->pcKey);
if (!strcmp(psSel->pcCmd,"-"))
{
psMenu->cInUse=0;
return(0);
}
Run(psSel->pcCmd,ppcMenu);
Display(psMenu,2,ppcMenu);
goto prompt;
}
void
Startup()
{
int hFile;
int iTemp;
stringf(b,"%s/startup",MENU_DIR);
hFile=open(b,O_RDONLY);
if (hFile==ERR)
return;
while ((iTemp=readln(hFile,b,BUF_SIZE))>0)
{
*(b+iTemp-1)=0;
if (*b!='*' && *b!='#' && *b!=';')
Fork(b);
}
close(hFile);
}
main(argc,argv)
char **argv;
{
int iLoad=1;
int iStartup=1;
char start;
start=tolower(**argv);
openerr("smenu",0,LOG_STGNET);
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
/* if (**argv!='-')
iLoad=0;
*/
dash(argv)
{
case '#':
wstringf(2,"smenu: %s\n",STG_VER);
exit(0);
case '?':
wstringf(2,"smenu {-e} {-t} {menu-file}\n");
wstringf(2," -e - do not load environ.dat\n");
wstringf(2," -s - do not run startup\n");
wstringf(2," -t - test sub-menu arrangment\n");
STGVER;
exit(0);
case 'e':
iLoad=0;
break;
case 's':
iStartup=0;
break;
case 't':
iTest=1;
break;
default:
wstringf(2,"invalid option: %s\n",--*argv);
exit(1);
}
/* who are we? */
/* pcUser=getlogin();
if (!pcUser)
exit(214);
*/
pwd=getpwuid(getuid());
if (!pwd)
exit(214);
pcUser=pwd->pw_name;
/* load environment */
if (iLoad)
load_env();
/* run menu/startup file */
if (iStartup)
Startup();
/* load termcap (optional) */
if (_Trm(getenv("TERM"))!=ERR)
{
PickHigh();
}
/* execute first menu */
if (*argv)
Menu(*argv);
else
if (start=='u')
Menu(pcUser);
else
Menu("main");
}