/* ------------------------------------------------------------------------ */ /* */ /* [ctx.c] Program Context */ /* */ /* Copyright (c) 1993 by D\olle, Manns */ /* ------------------------------------------------------------------------ */ #include "ctx_app.h" #include "sink.h" #include "literal.h" #include "pathes.h" #include "binimg.h" #include "ctx_imp.h" /*!
This module [ctx] provides functions for the definition of a program context and the verification of the current commandline against the current program context.
The definition interface will be used by the CMD Compiler which parses and evaluates a command decription file [.cmd], constructs a program context out of it and finally makes it persistent.
The application interface will be used by the applications and the generated
program specific context modules.
*/
/* ------------------------- Globals & Constants -------------------------- */
#define CONTEXT CTX_T_IMP(CTX_ctx_val())
#define EXTENSION ".cim"
#define Num_MIN "-2147483648" /* min value of a "long int" */
#define Num_MAX "2147483647" /* max value of a "long int" */
static int errcnt = 0;
/*I------------------------ Definition interface -------------------------- */
CTX_T CTX_new(short cnt, string name)
/* make new context for program 'name' and 'cnt' arguments */
{ CTX_T_Imp ctx; int i;
BUG_NULL(name);
ctx = New(CTX_T_Imp);
ctx->cnt = cnt;
ctx->prg = StrCopy(name);
ctx->tbl = (CTX_L_Imp)NewMem(cnt * SizeOf(CTX_L_Imp));
for( i=0; i < cnt; ++i )
ctx->tbl[i].asg = False;
return( ctx );
}
void CTX_free(CTX_T ctx)
/* drop context 'ctx' */
{ int cnt, i;
BUG_NULL(ctx);
cnt = CTX_T_IMP(ctx)->cnt;
FreeMem(CTX_T_IMP(ctx)->prg);
if( cnt &&
CTX_T_IMP(ctx)->tbl[cnt-1].asg &&
! strcmp(CTX_T_IMP(ctx)->tbl[cnt-1].ide,CTX_VARG) )
{ StdCPtr* vargs = (StdCPtr*)CTX_T_IMP(ctx)->tbl[cnt-1].val;
if( CTX_T_IMP(ctx)->tbl[cnt-1].typ == CTX_PATH ||
CTX_T_IMP(ctx)->tbl[cnt-1].typ == CTX_NAME ||
CTX_T_IMP(ctx)->tbl[cnt-1].typ == CTX_STRING )
for( i=1; i <= intCAST(vargs[0]); ++i ) FreeMem(vargs[i]);
FreeMem(CTX_T_IMP(ctx)->tbl[cnt-1].ide);
FreeMem(vargs);
--cnt;
}
for( i=0; i < cnt; ++i )
{
if( CTX_T_IMP(ctx)->tbl[i].asg &&
( CTX_T_IMP(ctx)->tbl[i].typ == CTX_PATH ||
CTX_T_IMP(ctx)->tbl[i].typ == CTX_NAME ||
CTX_T_IMP(ctx)->tbl[i].typ == CTX_STRING ) )
FreeMem(CTX_T_IMP(ctx)->tbl[i].val);
FreeMem(CTX_T_IMP(ctx)->tbl[i].ide);
if( CTX_T_IMP(ctx)->tbl[i].cat == CTX_OPT ||
CTX_T_IMP(ctx)->tbl[i].cat == CTX_ENV )
FreeMem(CTX_T_IMP(ctx)->tbl[i].dft);
}
FreeMem(CTX_T_IMP(ctx)->tbl);
FreeMem(ctx);
}
void CTX_set
(
CTX_T ctx, short idx, string ide,
byte cat, byte typ, string dft
)
/* assign argument 'ide' of category 'cat', with type 'typ' and
default value 'dft' to context entry ctx[idx]
*/
{
BUG_NULL(ctx); BUG_NULL(ide);
BUG_RNG0(idx,CTX_T_IMP(ctx)->cnt);
CTX_T_IMP(ctx)->tbl[idx].ide = StrCopy(ide);
CTX_T_IMP(ctx)->tbl[idx].cat = cat;
CTX_T_IMP(ctx)->tbl[idx].typ = typ;
if( CTX_T_IMP(ctx)->tbl[idx].cat == CTX_OPT ||
CTX_T_IMP(ctx)->tbl[idx].cat == CTX_ENV )
CTX_T_IMP(ctx)->tbl[idx].dft = dft;
}
void CTX_put(string env, CTX_T ctx)
/* put binary image of context 'ctx' */
{ string hd; int i;
BUG_NULL(env); BUG_NULL(ctx);
putBgn(env,CTX_T_IMP(ctx)->prg,EXTENSION);
hd = Str_printf
(
"[%s%s] Binary Context Image for %s\n",
CTX_T_IMP(ctx)->prg,EXTENSION,CTX_T_IMP(ctx)->prg
);
putHeader(hd,"cim",1,0);
putWord(CTX_T_IMP(ctx)->cnt);
putString(CTX_T_IMP(ctx)->prg);
for( i=0; i < CTX_T_IMP(ctx)->cnt; ++i )
{
putString(CTX_T_IMP(ctx)->tbl[i].ide);
putByte(CTX_T_IMP(ctx)->tbl[i].cat);
putByte(CTX_T_IMP(ctx)->tbl[i].typ);
if( CTX_T_IMP(ctx)->tbl[i].cat == CTX_OPT ||
CTX_T_IMP(ctx)->tbl[i].cat == CTX_ENV )
putString(CTX_T_IMP(ctx)->tbl[i].dft);
}
FreeMem(hd);
putEnd();
}
CTX_T CTX_get(string env, string name)
/* get context from binary image [$'env'/'name'.cim] */
{ CTX_T_Imp ctx; short cnt; int i;
BUG_NULL(env); BUG_NULL(name);
getBgn(env,name,EXTENSION);
getHeader("cim",1,0);
getWord(&cnt);
ctx = New(CTX_T_Imp);
ctx->cnt = cnt;
getString(&ctx->prg);
ctx->tbl = (CTX_L_Imp)NewMem(cnt * SizeOf(CTX_L_Imp));
for( i=0; i < ctx->cnt; ++i )
{
getString(&ctx->tbl[i].ide);
getByte(&ctx->tbl[i].cat);
getByte(&ctx->tbl[i].typ);
if( ctx->tbl[i].cat == CTX_OPT ||
ctx->tbl[i].cat == CTX_ENV )
getString(&ctx->tbl[i].dft);
ctx->tbl[i].asg = False;
}
getEnd();
return ctx;
}
void CTX_usage(CTX_T ctx)
/* print usage for context 'ctx' */
{ CTX_T_Imp ctx_imp = CTX_T_IMP(ctx); int i;
fprintf(STDERR,"Usage: %s", ctx_imp->prg);
for (i = 0; i < ctx_imp->cnt; i++)
if (ctx_imp->tbl[i].cat == CTX_OPT)
{
fprintf(STDERR," [-%s",ctx_imp->tbl[i].ide);
switch( ctx_imp->tbl[i].typ )
{
case CTX_FLAG : break;
case CTX_PATH : fprintf(STDERR,"=path"); break;
case CTX_STRING : fprintf(STDERR,"=string"); break;
case CTX_NAME : fprintf(STDERR,"=name"); break;
case CTX_INT : fprintf(STDERR,"=int"); break;
default : fprintf(STDERR,"???(%d)",ctx_imp->tbl[i].cat); break;
}
fprintf(STDERR,"]");
}
for (i = 0; i < ctx_imp->cnt; i++)
if(ctx_imp->tbl[i].cat == CTX_ARG)
fprintf(STDERR," %s",ctx_imp->tbl[i].ide);
FNL(STDERR);
STD_ERREXIT;
}
void CTX_C_Modul(string Environ, CTX_T Ctx, c_bool shortform)
/* compiles context 'ctx' to [$'Environ'/'Name'_cim.c] */
{ FILE *tf; CTX_T_Imp ctx = CTX_T_IMP(Ctx);
c_string fid; int maxlen=0, i;
if( shortform )
fid = Str_printf("%.4s_cim",ctx->prg);
else
fid = Str_printf("%s_cim",ctx->prg);
tf = OpenPath(Environ,fid,".c","wt");
fprintf(tf,"/* [%s.c] Context table for '%s' */\n\n", fid, ctx->prg);
fprintf(tf,"#include \"ctx.h\"\n\n");
fprintf(tf,"void CTX_init_%s(int argc, c_string argv[])\n",ctx->prg);
fprintf(tf,"{ CTX_T ctx;\n");
fprintf(tf," ctx = CTX_new(%d,\"%s\");\n",ctx->cnt,ctx->prg);
for (i = 0; i < ctx->cnt; i++)
maxlen = MAX(strlen(ctx->tbl[i].ide),maxlen);
for (i = 0; i < ctx->cnt; i++)
{
fprintf(tf," CTX_set(ctx,%2d,",i);
fprintf(tf,"\"%s\",%*s",ctx->tbl[i].ide,maxlen-strlen(ctx->tbl[i].ide),"");
switch( ctx->tbl[i].cat )
{
case CTX_ARG : fprintf(tf,"CTX_ARG,"); break;
case CTX_OPT : fprintf(tf,"CTX_OPT,"); break;
case CTX_ENV : fprintf(tf,"CTX_ENV,"); break;
default : fprintf(tf,"%7d,",ctx->tbl[i].cat); break;
}
switch( ctx->tbl[i].typ )
{
case CTX_FLAG : fprintf(tf,"CTX_FLAG, "); break;
case CTX_PATH : fprintf(tf,"CTX_PATH, "); break;
case CTX_STRING : fprintf(tf,"CTX_STRING,"); break;
case CTX_NAME : fprintf(tf,"CTX_STRING,"); break;
case CTX_INT : fprintf(tf,"CTX_INT, "); break;
default : fprintf(tf,"%10d,",ctx->tbl[i].cat); break;
}
if( ctx->tbl[i].cat == CTX_OPT ||
ctx->tbl[i].cat == CTX_ENV )
{ string s = LIT_c_str_lit(ctx->tbl[i].dft);
fprintf(tf,"StrCopy(%s)",s); FreeMem(s);
}
else
fprintf(tf,"\"\"");
fprintf(tf,");\n");
}
fprintf(tf," CTX_ctx_set(ctx);\n");
fprintf(tf," CTX_interprete(argc, argv);\n");
fprintf(tf,"}\n");
fclose(tf);
FreeMem(fid);
}
static void CTX_prVal(int typ, Abs_T val, bool shell)
{
switch( typ )
{
case CTX_FLAG :
fprintf(STDOUT,"%s",ABS_CAST(bool,val)?"true":"false");
break;
case CTX_PATH :
if( shell ) fprintf(STDOUT,"%s",(string)val);
else fprintf(STDOUT,"<%s>",(string)val);
break;
case CTX_STRING :
if( shell ) fprintf(STDOUT,"'%s'",(string)val);
else fprintf(STDOUT,"\"%s\"",(string)val);
break;
case CTX_NAME :
fprintf(STDOUT,"%s",(string)val);
break;
case CTX_INT :
fprintf(STDOUT,"%ld",(long)val);
break;
default : C_BUG;
}
}
void CTX_sh_list(void)
/* print shell context */
{ int i;
for (i = 0; i < CONTEXT->cnt; i++)
{
fprintf(STDOUT,"%s=",CONTEXT->tbl[i].ide);
if (CONTEXT->tbl[i].asg)
{
if( i == CONTEXT->cnt - 1 && ! strcmp(CONTEXT->tbl[i].ide,CTX_VARG) )
fprintf(STDOUT,"%d",intCAST(((Abs_T*)CONTEXT->tbl[i].val)[0]));
else CTX_prVal(CONTEXT->tbl[i].typ,CONTEXT->tbl[i].val,True);
}
NL;
}
}
void CTX_list(CTX_T ctx)
/* DEBUG; print context 'ctx' */
{ CTX_T_Imp ctx_imp = CTX_T_IMP(ctx); int maxlen=0,i;
fprintf(STDOUT,"Command %s\n", ctx_imp->prg);
for (i = 0; i < ctx_imp->cnt; i++)
maxlen = MAX(strlen(ctx_imp->tbl[i].ide),maxlen);
for (i = 0; i < ctx_imp->cnt; i++)
{
fprintf(STDOUT,"-- ");
switch( ctx_imp->tbl[i].cat )
{
case CTX_ARG : fprintf(STDOUT,"Arg"); break;
case CTX_OPT : fprintf(STDOUT,"Opt"); break;
case CTX_ENV : fprintf(STDOUT,"Env"); break;
default : fprintf(STDOUT,"???(%d)",ctx_imp->tbl[i].cat); break;
}
fprintf(STDOUT," %-*s : ",maxlen,ctx_imp->tbl[i].ide);
switch( ctx_imp->tbl[i].typ )
{
case CTX_FLAG : fprintf(STDOUT,"flag "); break;
case CTX_PATH : fprintf(STDOUT,"path "); break;
case CTX_STRING : fprintf(STDOUT,"string"); break;
case CTX_NAME : fprintf(STDOUT,"name "); break;
case CTX_INT : fprintf(STDOUT,"int "); break;
default : fprintf(STDOUT,"???(%d)",ctx_imp->tbl[i].cat); break;
}
fprintf(STDOUT," = ");
if (ctx_imp->tbl[i].asg)
{
if( i == ctx_imp->cnt - 1 && ! strcmp(ctx_imp->tbl[i].ide,CTX_VARG) )
{ int cnt = intCAST(((Abs_T*)ctx_imp->tbl[i].val)[0]), j;
fprintf(STDOUT,"%d",cnt);
for( j=1; j <= cnt; ++j )
{
fprintf(STDOUT,"\n %s(%3d) = ",CTX_VARG,j);
CTX_prVal(ctx_imp->tbl[i].typ,((Abs_T*)ctx_imp->tbl[i].val)[j],False);
}
}
else CTX_prVal(ctx_imp->tbl[i].typ,ctx_imp->tbl[i].val,False);
}
else
fprintf(STDOUT,"[UNDEFINED]");
NL;
}
}
static long Num_error(string val, string msg)
{
fprintf(STDOUT,"[Usage] : %s : %s\n",msg,val);
errcnt += 1; return 0;
}
static bool Num_less(bool sa, string va, bool sb, string vb)
/* whether to strings containing numbers are less w.r.t. numeric order */
/* sa, sb are the signs, true means negative */
/* va, vb are the digits without leading 0s */
{
if (sa != sb) return sa && !sb;
if (sa) return Num_less(0,vb,0,va);
if (strlen(va) != strlen(vb)) return strlen(va) < strlen(vb);
return strcmp(va,vb) < 0;
}
static long Num_conv(string val)
/* converts a string to an integer */
/* this routine is very general and not restricted to short, int or long */
{ bool sign = (val[0] == '-'); int slen = strlen(val), i, j; long res;
/* skip leading 0s */
if (val[sign] == 0)
return Num_error(val,"Zahl erwarted");
for (i = sign; i < slen-1; i++) if (val[i] != '0') break;
/* check digits */
for (j = i; j < slen; j++)
if ('0' > val[j] || val[j] > '9')
return Num_error(val,"Zahl erwarted");
/* check value range */
if (Num_less(sign,val+i,Num_MIN[0]=='-',Num_MIN+(Num_MIN[0]=='-')))
return Num_error(val,"zu kleine Zahl");
if (Num_less(Num_MAX[0]=='-',Num_MAX+(Num_MAX[0]=='-'),sign,val+i))
return Num_error(val,"zu grosse Zahl");
/* actual conversion */
res = 0;
for (j = i; j < slen; j++)
res = 10 * res + (val[j]-'0');
return sign ? -res : res;
}
static Abs_T CTX_convert(int typ, string val)
/* converts ctx-typed value val */
{
switch( typ )
{
case CTX_FLAG :
if (strcmp(val,"true" ) == 0) return (Abs_T) 1;
if (strcmp(val,"false") == 0) return (Abs_T) 0;
fprintf(STDERR,"[Usage]: '%s' is not a boolean value",val);
errcnt += 1; return (Abs_T) 0;
case CTX_PATH :
return (Abs_T) StrCopy(val);
case CTX_NAME :
return (Abs_T) StrCopy(val);
case CTX_STRING :
return (Abs_T) StrCopy(val);
case CTX_INT :
return (Abs_T) Num_conv(val);
default:
C_BUG; return (Abs_T) 0;
}
}
static void CTX_assing(int idx, string val)
{
if (idx < 0) return;
if (CONTEXT->tbl[idx].cat != CTX_OPT
&& CONTEXT->tbl[idx].cat != CTX_ARG
&& CONTEXT->tbl[idx].cat != CTX_ENV )
{
fprintf(STDERR,"[Usage]: cannot assign to option '%s'\n",
CONTEXT->tbl[idx].ide);
errcnt += 1; return;
}
if (CONTEXT->tbl[idx].asg)
{
fprintf(STDERR,"[Usage]: option '%s' assigned more than once\n",
CONTEXT->tbl[idx].ide);
errcnt += 1; return;
}
CONTEXT->tbl[idx].asg = True;
CONTEXT->tbl[idx].val = CTX_convert(CONTEXT->tbl[idx].typ,val);
}
static void CTX_varg_asgn(int vargc, string vargv[])
{ Abs_T* vargs = (Abs_T*)NewMem((vargc + 1) * sizeof(Abs_T)); int i;
vargs[0] = ABS_CAST(Abs_T,vargc);
for( i=0; i < vargc; ++i )
vargs[i+1] = CTX_convert(CONTEXT->tbl[CONTEXT->cnt-1].typ,vargv[i]);
CONTEXT->tbl[CONTEXT->cnt-1].asg = True;
CONTEXT->tbl[CONTEXT->cnt-1].val = (StdCPtr)vargs;
}
static void CTX_option_flag(string flg)
{ int idx = intCAST(CTX_eval(flg,True));
if (idx < 0)
{
fprintf(STDERR,"[Usage]: invalid option '%s'\n",flg);
errcnt += 1;
return;
}
if ((CONTEXT->tbl[idx].cat != CTX_OPT &&
CONTEXT->tbl[idx].cat != CTX_ENV ) ||
CONTEXT->tbl[idx].typ != CTX_FLAG )
{
fprintf(STDERR,"[Usage]: Option '%s' is not a flag\n",flg);
errcnt += 1;
}
CTX_assing(idx,strcmp(CONTEXT->tbl[idx].dft,"true")?"true":"false");
}
static void CTX_option_asgn(string var, string val)
/* NOTE: var must be freed */
{ int idx = intCAST(CTX_eval(var,True));
if (idx < 0)
{
fprintf(STDERR,"[Usage]: invalid option '%s'\n",var);
errcnt += 1;
}
else
CTX_assing(idx,val);
FreeMem(var);
}
static int CTX_args(void)
/* number of static arguments in current context */
{ int cnt = 0, i;
for (i = 0; i < CONTEXT->cnt; i++)
cnt += ( CONTEXT->tbl[i].cat == CTX_ARG &&
! CONTEXT->tbl[i].asg &&
strcmp(CONTEXT->tbl[i].ide,CTX_VARG) );
return cnt;
}
static void CTX_argument(string arg)
{ int i;
for (i = 0; i < CONTEXT->cnt; i++)
if (CONTEXT->tbl[i].cat == CTX_ARG && !CONTEXT->tbl[i].asg)
break;
if (i >= CONTEXT->cnt || ! strcmp(CONTEXT->tbl[i].ide,CTX_VARG))
{
fprintf(STDERR,"[Usage]: unexpected argument '%s'\n",arg);
errcnt += 1; return;
}
CTX_assing(i,arg);
}
static string loc_dirname; /* the path of the actual program executable */
string CTX_dirname(void)
/* path of the current program */
{
return loc_dirname;
}
static void prep_loc_dirname(string argv0)
/* prepares 'loc_dirname' */
{ int i,len = strlen(argv0);
loc_dirname = StrCopy(argv0);
for
(
i = len;
i>0 && loc_dirname[i-1] != '\\' && loc_dirname[i-1] != '/';
i--
); /* noop */
if (i>0) loc_dirname[i-1] = 0;
for (; i>=0; i--)
if (loc_dirname[i] == '\\')
loc_dirname[i] = '/';
}
void CTX_interprete(int argc, string argv[])
/* process current program context */
{ int first_varg = argc, i;
prep_loc_dirname(argv[0]);
if (argc == 2 && !strcmp(argv[1],"-?" )) { CTX_usage(CONTEXT); STD_ERREXIT; }
if (argc == 2 && !strcmp(argv[1],"-??")) { CTX_list (CONTEXT); STD_ERREXIT; }
if( CONTEXT->cnt && ! strcmp(CONTEXT->tbl[CONTEXT->cnt-1].ide,CTX_VARG) )
{ int cnt = CTX_args(), idx = 1;
for (i = 1; i < argc; i++)
if (argv[i][0] == '-') idx = i + 1;
else
{
if( cnt > 0 )
{
idx = i + 1; --cnt;
}
}
first_varg = idx;
}
/* process options */
for (i = 1; i < MIN(argc,first_varg); i++)
{
if (argv[i][0] == '-')
{ string opt = argv[i]; int sl = strlen(opt), j;
for( j = 0; j < sl; j++) if (opt[j] == '=') break;
if (j >= sl)
CTX_option_flag(argv[i]+1);
else
CTX_option_asgn(SubStrCopy(argv[i]+1,j-1),argv[i]+j+1);
}
}
/* process arguments */
for (i = 1; i < MIN(argc,first_varg); i++)
{
if (argv[i][0] != '-') CTX_argument(argv[i]);
}
/* process variable arguments */
if( CONTEXT->cnt && ! strcmp(CONTEXT->tbl[CONTEXT->cnt-1].ide,CTX_VARG) )
{
if( first_varg < argc )
CTX_varg_asgn(argc-first_varg,&argv[first_varg]);
else
CTX_varg_asgn(0,(string*)NULL);
}
/* process defaults */
for (i = 0; i < CONTEXT->cnt; i++)
if (!CONTEXT->tbl[i].asg)
{
switch( CONTEXT->tbl[i].cat )
{
case CTX_ARG :
fprintf(STDERR,"[Usage]: paramenter '%s' expected\n",
CONTEXT->tbl[i].ide);
errcnt += 1; break;
case CTX_OPT :
CTX_assing(i,CONTEXT->tbl[i].dft);
break;
case CTX_ENV :
if( !STR_EMPTY(getenv(CONTEXT->tbl[i].ide)) )
CTX_assing(i,getenv(CONTEXT->tbl[i].ide));
else
if( *(CONTEXT->tbl[i].dft) != '\0' )
CTX_assing(i,CONTEXT->tbl[i].dft);
else
{
fprintf(STDERR,"[Usage]: shell variable '%s' expected\n",
CONTEXT->tbl[i].ide);
errcnt += 1;
}
break;
default : C_BUG;
}
}
if (errcnt > 0)
CTX_usage(CONTEXT);
}
/*I------------------------ Application interface ------------------------- */
void CTX_init(int argc, string argv[])
/* initialize and process current program context */
{ CTX_T ctx; string stdnam;
assert0(argc>0,"interner Fehler bei der Uebergabe der Kommandozeile");
/* MSDOS produces d:\Path...\ProgId.EXE, the full pathname in argv[0] */
/* UNIX produces ProgId or Path/ProgId, in dependance of the call */
/* thus we remove prefixes and suffixes to get the program name */
stdnam = BaseFile(argv[0]);
CTX_ctx_set(CTX_new(1,"[CTX_init]"));
CTX_set(CONTEXT,0,"PATH",CTX_ENV,CTX_PATH,"");
CTX_interprete(1, argv);
ctx = CTX_get("PATH",stdnam);
CTX_free(CONTEXT);
CTX_ctx_set(ctx);
FreeMem(loc_dirname);
CTX_interprete(argc,argv);
FreeMem(stdnam);
}
void CTX_quit(void)
/* drop current program context */
{
CTX_free(CONTEXT);
FreeMem(loc_dirname);
}