/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. */ #define import_spp #define import_libc #define import_stdio #define import_ctype #include #include "config.h" #include "operand.h" #include "param.h" #include "grammar.h" #include "mem.h" #include "task.h" #include "errs.h" #include "clmodes.h" #include "construct.h" #include "proto.h" /* * PARAM -- Operations upon parameters. */ extern int cldebug; extern char *undefval; extern char *nullstr; extern char *eofstr; extern char *indefstr; extern char *indeflc; XINT parhead; /* dict index of first pfile */ #define INDEX_OFFSET 0 /* Offsets using index list. */ #define DIRECT_OFFSET 1 /* Offsets put on stack directly. */ int mode_offset = INDEX_OFFSET; char *loc_field = "Attempt to access undefined field in local variable %s.\n"; /* PARAMFIND -- Search for a parameter with the given name off pfile *pfp. * If name is null, then search for one in n'th pos, counting from 0. * not counting M_HIDDEN params. * Return NULL if cannot find one with given name or at given position * or ERR if allowing abbreviations and pname is ambiguous. * Never return ERR if looking for a positional arg; some callers of paramfind() * Depend on this and don't check for ERR; beware if change it. */ struct param * paramfind (struct pfile *pfp, char *pname, int pos, int exact) { register char first_char; register struct param *pp; struct ltask *ltp; if (pfp == NULL) return (NULL); if (cldebug) { eprintf ("paramfind() looking down pfile `%s'/%x for ", (ltp = pfp->pf_ltp) ? ltp->lt_lname : "", pfp); if (pname != NULL && *pname != '\0') eprintf ("`%s'\n", pname); else eprintf ("position %d\n", pos); } /* Check for both ways "name may be null" */ if (pname == NULL || *pname == '\0') { for (pp = pfp->pf_pp; pp != NULL; pp = pp->p_np) if (!(pp->p_mode & M_HIDDEN) && pos-- == 0) return (pp); } else if (abbrev() && !exact) { /* Settle for abbreviation of name */ struct param *candidate; int n; candidate = NULL; n = strlen (pname); first_char = pname[0]; for (pp = pfp->pf_pp; pp != NULL; pp = pp->p_np) { if (*pp->p_name == first_char) if (!strncmp (pp->p_name, pname, n)) { if (pp->p_name[n] == '\0') return (pp); /* exact hit */ if (candidate == NULL) candidate = pp; else candidate = (struct param *) ERR; } } return (candidate); } else { /* Name must be exact. */ for (pp = pfp->pf_pp; pp != NULL; pp = pp->p_np) { if (!strcmp (pp->p_name, pname)) return (pp); } } return (NULL); } /* PARAMSET -- Pop top operand and assign to given field of param *pp, * with possible type conversion via opcast() to pp->p_type. * Be darn sure to pop an operand in all cases! * All preallocated string storage ends with null; take care to preserve this * by never copying into full length. assigning into the name of a * list-structured param closes the file if it's open and clears EOF. * We don't check if the popped op is undefined. * * Parameter indirection complicates setting the p_value, p_min, and p_max * fields (the only fields for which indirection is permitted). When one * of these fields is indirect it is a string valued operand containing * as value a string of the form ")indirparam". Hence, the value, min, or * max field may be of type string while the parameter itself (p_type) is * of some other datatype. Indirection will be overriden if the operand * to be set is a data value rather than an indirect reference string. * If the operand is a data value the parameter field may change its datatype. * If the operand is an indirect reference the field must already be of type * string with sufficient string storage allocated for the new string. * String storage must be allocated when the pfile is loaded. * * Enumerated types are implemented as a string of | separated fields * stored in the p_min field. The p_min field must have been set to some * string value when the pfile was loaded or storage will not have been * allocated. While the enumerated type is supported only for string valued * params, integers may be stored as strings in a string valued parameter * to permit enumerating the legal values of an integer parameter, e.g.: * * order of interpolator (3|5|7) (5): */ void paramset (register struct param *pp, int field) { struct operand o; int bastype; /* OT_BASIC portion of p_type */ int valtype; /* OT_BASIC type of current value */ int optype; /* OT_BASIC type of operand */ int arrflag; /* Array indicator. */ int list; /* set if p->p_type & PT_LIST */ int len; /* max length of storage, if in-line */ o = popop(); list = pp->p_type & PT_LIST; arrflag = pp->p_type & PT_ARRAY; bastype = pp->p_type & OT_BASIC; valtype = pp->p_valo.o_type & OT_BASIC; optype = o.o_type & OT_BASIC; /* Check if unauthorized access to local variable. */ if (pp->p_mode&M_LOCAL && field != FN_VALUE && field != FN_NULL) cl_error (E_UERR, loc_field, pp->p_name); /* If a CL parameter, value may need parsing to set some internal * variables (logging, eparam, etc.). Take care of this before * changing the value of the parameter, in case the new value is * illegal. */ if (pp->p_flags & P_CL) parse_clmodes (pp, &o); switch (field) { case FN_NAME: cl_error (E_UERR, "may not change name of parameter `%s'", pp->p_name); case FN_TYPE: cl_error (E_UERR, "may not change type of parameter `%s'", pp->p_name); case FN_MODE: if (optype != OT_STRING) cl_error (E_UERR, "modes are strings"); if (opindef (&o)) cl_error (E_UERR, "tried to set mode of `%s' to %s", pp->p_name, indefstr); o.o_type = pp->p_mode; /* reuse briefly as a temp */ if ((pp->p_mode = scanmode (o.o_val.v_s)) == ERR) { pp->p_mode = o.o_type; /* restore from temp */ cl_error (E_UERR, "bad mode string `%s'", o.o_val.v_s); } break; case FN_NULL: case FN_VALUE: /* Assigning into a list param closes an existing file, * changes the name of the list file, and clears P_LEOF. */ if (list) { closelist (pp); pp->p_flags &= ~P_LEOF; } /* If parameter indirection is in effect the datatype of the value * field will be string, while the parameter type itself may be * any datatype. If we are overriding redirection with a real * value for the parameter then the datatype of p_valo may change. */ if (!list && bastype != OT_STRING && (valtype != OT_STRING || optype != OT_STRING)) { /* Set nonstring datatype. */ if (optype != bastype) { pushop (&o); opcast (bastype); o = popop(); } if (!arrflag) pp->p_valo = o; else { /* We must generate reference to appropriate value. */ int offset; int *p_i; double *p_r; offset = getoffset (pp); if (bastype == OT_BOOL || bastype == OT_INT) { p_i = pp->p_aval.a_i + offset; *p_i = o.o_val.v_i; } else if (bastype == OT_REAL) { p_r = pp->p_aval.a_r + offset; *p_r = o.o_val.v_r; } } break; /* break from switch */ } len = pp->p_lenval; if (optype != OT_STRING) { pushop (&o); opcast (bastype); o = popop(); } if (bastype == OT_STRING && arrflag) { char **p_s; int offset; offset = getoffset (pp); p_s = pp->p_aval.a_s + offset; strncpy (*p_s, o.o_val.v_s, len-1); break /* out of switch */; } pp->p_valo.o_type = o.o_type; if (!opindef (&o)) strncpy (pp->p_val.v_s, o.o_val.v_s, len-1); break; case FN_MIN: /* minimum */ if (bastype == OT_BOOL || pp->p_type & (PT_STRUCT|PT_GCUR|PT_IMCUR|PT_UKEY|PT_PSET)) cl_error (E_UERR, e_nominmax); /* If string type and no values were enumerated in the pfile, * no storage will have been allocated in the min field for the * enumeration list and we must abort. Otherwise space is avail * for PF_SZMINSTR chars. */ if (bastype == OT_STRING && pp->p_flags & P_UMIN) cl_error (E_UERR, "string storage not allocated for p_min"); if (bastype == OT_STRING || (valtype == OT_STRING && optype == OT_STRING)) { if (optype != OT_STRING) { pushop (&o); opcast (OT_STRING); o = popop(); } if (opindef (&o)) pp->p_flags |= P_IMIN; else { strncpy (pp->p_min.v_s, o.o_val.v_s, PF_SZMINSTR-1); pp->p_flags &= ~(P_IMIN|P_UMIN); pp->p_mino.o_type = o.o_type; } } else { if (optype != bastype) { pushop (&o); opcast (bastype); o = popop(); } pp->p_mino = o; if (opindef (&o)) pp->p_flags |= P_IMIN; else { pp->p_min = o.o_val; pp->p_flags &= ~(P_IMIN|P_UMIN); } } break; case FN_MAX: /* maximum */ if (bastype == OT_BOOL || (bastype == OT_STRING && !(pp->p_type & PT_FILNAM))) { cl_error (E_UERR, e_nominmax); } if (pp->p_type & PT_FILNAM) { pushop (&o); opcast (OT_STRING); o = popop(); if (opindef (&o)) pp->p_flags |= P_IMAX; else { strncpy (pp->p_max.v_s, o.o_val.v_s, PF_SZMAXSTR-1); pp->p_flags &= ~(P_IMAX|P_UMAX); pp->p_maxo.o_type = o.o_type; } } else { pushop (&o); opcast (bastype); o = popop(); pp->p_maxo = o; if (opindef (&o)) pp->p_flags |= P_IMAX; else { pp->p_max = o.o_val; pp->p_flags &= ~(P_IMAX|P_UMAX); } } break; case FN_PROMPT: /* the prompt string; length <= before */ pushop (&o); opcast (OT_STRING); o = popop(); if (opindef (&o)) *pp->p_prompt = '\0'; else { len = strlen (pp->p_prompt); strncpy (pp->p_prompt, o.o_val.v_s, len - 1); } break; default: cl_error (E_IERR, e_badsw, field, "paramset()"); } } /* VALIDPARAMGET -- Push given field of param onto stack. Read next entry * in file if list-structured. If getting FN_NULL, query if in query mode * or if pp is out of range. Call error if return value would be undefined. */ void validparamget (register struct param *pp, int field) { struct operand o; paramget (pp, field); o = popop(); if (opundef(&o)) cl_error (E_UERR, "The requested field of parameter `%s' is undefined", pp->p_name); if (field == FN_NULL && pp->p_flags & P_LEOF) cl_error (E_UERR, "EOF from list param `%s' in expression", pp->p_name); pushop (&o); } /* PARAMGET -- Push given field of param onto stack. Read next entry in file * if list-structured. If getting FN_NULL, query if in query mode or if pp * is out of range. Value returned may be undefined. */ void paramget (register struct param *pp, int field) { char mode[5]; /* used to turn bits into string */ struct operand result; char buf[20]; /* to stuff the expanded type in */ char *bp; int bastype; int arrflag; bastype = pp->p_type & OT_BASIC; arrflag = pp->p_type & PT_ARRAY; /* Check if unauthorized access to local variable. */ if (pp->p_mode&M_LOCAL && field != FN_VALUE && field != FN_NULL) cl_error (E_UERR, loc_field, pp->p_name); switch (field) { case FN_NAME: result.o_type = OT_STRING; result.o_val.v_s = pp->p_name; break; case FN_TYPE: result.o_type = OT_STRING; switch (pp->p_type & OT_BASIC) { case OT_STRING: result.o_val.v_s = "s"; break; case OT_INT: result.o_val.v_s = "i"; break; case OT_REAL: result.o_val.v_s = "r"; break; case OT_BOOL: result.o_val.v_s = "b"; break; default: result.o_val.v_s = "?"; break; } break; case FN_XTYPE: result.o_type = OT_STRING; bp = buf; if (pp->p_type & PT_LIST) *bp++ = '*'; else if (arrflag) *bp++ = 'a'; switch (bastype) { case OT_BOOL: *bp++ = 'b'; break; case OT_INT: *bp++ = 'i'; break; case OT_REAL: *bp++ = 'r'; break; case OT_STRING: *bp++ = 's'; break; } /* Overwrite the string descriptor that appears with PT_FILNAM, * PT_STRUCT and the cursors. */ if (pp->p_type & PT_FILNAM) { *--bp = 'f'; if (pp->p_type & PT_FBIN) *++bp = 'b'; if (pp->p_type & PT_FNOE) *++bp = 'n'; if (pp->p_type & PT_FER) *++bp = 'r'; if (pp->p_type & PT_FTXT) *++bp = 't'; if (pp->p_type & PT_FEW) *++bp = 'w'; *++bp = '\0'; } else if (pp->p_type & PT_STRUCT) { strcpy (--bp, "struct"); } else if (pp->p_type & PT_GCUR) { strcpy (--bp, "gcur"); } else if (pp->p_type & PT_IMCUR) { strcpy (--bp, "imcur"); } else if (pp->p_type & PT_UKEY) { strcpy (--bp, "ukey"); } else if (pp->p_type & PT_PSET) { strcpy (--bp, "pset"); } else *bp = '\0'; *bp = '\0'; result.o_val.v_s = buf; break; case FN_MODE: makemode (pp, mode); result.o_type = OT_STRING; result.o_val.v_s = mode; break; case FN_NULL: /* Without an explicit field we give the meaningful "worth" * of the param, which is not necessarilly the 4th param field. * If PT_LIST, read entry from list. */ if (effmode (pp) & M_QUERY) { /* Just query to get result. */ query (pp); result = popop(); } else { /* Use pp to get result; query if not in range. */ if (pp->p_type & PT_LIST) { result = readlist (pp); /* may set P_LEOF */ } else if (arrflag) { /* If an array get appropriate value. */ int offset; offset = getoffset(pp); result.o_type = bastype; if (bastype == OT_BOOL || bastype == OT_INT) result.o_val.v_i = *(pp->p_aval.a_i + offset); else if (bastype == OT_REAL) result.o_val.v_r = *(pp->p_aval.a_r + offset); else if (bastype == OT_STRING) result.o_val.v_s = *(pp->p_aval.a_s + offset); } else result = pp->p_valo; /* Do not range check if we have an indirect reference. */ if (!((result.o_type & OT_BASIC) == OT_STRING && *result.o_val.v_s == PF_INDIRECT)) if (!(pp->p_flags & P_LEOF) && !inrange (pp, &result)) { query (pp); result = popop(); } } break; case FN_VALUE: /* Explicit reference to the "value" field means return the * value, or if indirect, the file name for the indirection. */ if (arrflag) { int offset; offset = getoffset(pp); result.o_type = bastype; if (bastype == OT_BOOL || bastype == OT_INT) result.o_val.v_i = *(pp->p_aval.a_i + offset); else if (bastype == OT_REAL) result.o_val.v_r = *(pp->p_aval.a_r + offset); else if (bastype == OT_STRING) result.o_val.v_s = *(pp->p_aval.a_s + offset); } else result = pp->p_valo; break; case FN_LENGTH: result.o_type = OT_INT; result.o_val.v_i = pp->p_lenval; break; case FN_MIN: if (pp->p_flags & P_UMIN) setopundef (&result); else if (pp->p_flags & P_IMIN) setopindef (&result); else result = pp->p_mino; break; case FN_MAX: if (pp->p_flags & P_UMAX) setopundef (&result); else if (pp->p_flags & P_IMAX) setopindef (&result); else result = pp->p_maxo; break; case FN_PROMPT: result.o_type = OT_STRING; result.o_val.v_s = pp->p_prompt; break; default: cl_error (E_IERR, e_badsw, field, "paramget()"); } /* Parameter indirection. If the value of the parameter is given as * ")paramspec" use the value of the referenced parameter. Multiple * levels of indirection are permitted. */ if ((result.o_type & OT_BASIC) == OT_STRING && *result.o_val.v_s == PF_INDIRECT) { char redir[SZ_FNAME]; struct param *np; char *pk, *t, *p, *f; strncpy (redir, &result.o_val.v_s[1], SZ_FNAME-1); redir[SZ_FNAME-1] = EOS; breakout (redir, &pk, &t, &p, &f); /* Task "_" is shorthand for the name of the current package. */ if (((t == NULL || *t == EOS) && *redir == '.') || strcmp (t, "_") == 0) t = pp->p_pfp->pf_ltp->lt_pkp->pk_name; np = paramsrch (pk, t, p); if (np == pp) cl_error (E_UERR, "self referential indirection on param `%s'", pp->p_name); paramget (np, *f); } else { /* Check for indefinite values. */ if (arrflag && (field == FN_VALUE || field == FN_NULL)) { if ((result.o_type == OT_BOOL || result.o_type == OT_INT) && result.o_val.v_i == INDEFL) { setopindef (&result); } else if (result.o_type == OT_REAL && result.o_val.v_r == INDEFR) { setopindef (&result); } } else if ((result.o_type & OT_BASIC) == OT_STRING) { char *ip = result.o_val.v_s; /* Check for an escaped string indirection, e.g. "\)param". ** If we have one, remove the escape char from the result ** string. */ if (*ip == '\\' && *(ip+1) == PF_INDIRECT) strcpy (ip, &result.o_val.v_s[1]); } pushop (&result); } } /* MAKEMODE -- Fill in characters of string s according to which mode bits * are on in param pp. S should be at least 5 characters long, in the * (impossible) worse case. */ void makemode (struct param *pp, char *s) { register int m = pp->p_mode; if (m & M_AUTO) *s++ = PF_AUTO; if (m & M_QUERY) *s++ = PF_QUERY; if (m & M_HIDDEN) *s++ = PF_HIDDEN; if (m & M_LEARN) *s++ = PF_LEARN; *s = '\0'; } /* NEWPARAM -- Allocate a new, empty, param on the dictionary and link in * at end of list of params off pfile *pfp. Put the new entry at the end of * the list and update pfp->pf_lastpp. * This is so as to preserve the order in which the params were added to allow * positional argument matching. * Null out all unused fields except the three union values. */ struct param * newparam (struct pfile *pfp) { register struct param *newpp; newpp = (struct param *) memneed (PARAMSIZ); if (pfp->pf_pp == NULL) pfp->pf_lastpp = pfp->pf_pp = newpp; else { pfp->pf_lastpp->p_np = newpp; pfp->pf_lastpp = newpp; } newpp->p_pfp = pfp; newpp->p_flags = newpp->p_type = newpp->p_mode = 0; newpp->p_valo.o_type = newpp->p_mino.o_type = newpp->p_maxo.o_type = 0; newpp->p_name = newpp->p_prompt = nullstr; newpp->p_listval = NULL; newpp->p_listfp = NULL; newpp->p_lenval = 0; newpp->p_np = NULL; return (newpp); } /* PARAMSRCH -- Hunt for and return pointer to param in given package and ltask. * If no ltask specified, use standard search path, ie, check the params for * the current ltask, then the current package, then the cl. * Else find pfile for the given ltask, reading it in if it's not in core. * do not accept the ltask name if it's not defined. * If the param is list-structured, open the list file if it isn't already * and P_LEOF is not set; thus, paramget() should close the list file * and set P_LEOF when it sees EOF and leave it set so we can't open * it again. Do done of this if we just want the .value field. * If dealing with a task that has no param file, try to satisfy the request * from positional args. If that fails, make one that will query. * Positional args were made named $n by posargset, or the like, and are * accessed by name. A named reference returns the next (as counted in * pf_n) positional arg so two references by the same name will not return * the same value. However, if there are no more positional args, then * one is made and will cause a query to the same param on each reference. * Call error() and do not return if cannot find it. */ struct param * paramsrch (char *pkname, char *ltname, char *pname) { register struct param *pp; struct pfile *pfp; struct param *lookup_param(); /* First search for a regular parameter. If this fails then we * handle the case when currentask has no pfile. */ pp = lookup_param (pkname, ltname, pname); if (currentask->t_pfp->pf_flags & PF_FAKE) { if (((XINT)pp == ERR || pp == NULL) && *pname != '$') { /* If dealing with a task that has no param file, try to * satisfy the request from positional args. If that fails, * make one that will query. */ pfp = currentask->t_pfp; pp = paramfind (pfp, (char *)NULL, pfp->pf_n++, NO); if (pp == NULL) { pp = newfakeparam (pfp, pname, 0, OT_STRING, SZ_FNAME); pp->p_mode |= M_QUERY; /* If, instead, we query and set P_OK, a prompt will not * be generated again if the same param is rereferenced. * That's great but problem is that satisfying from * positional args cannot work like this since the name * isn't saved. query (pp); popop(); pp->p_flags |= P_OK; */ } } } if ((XINT)pp == ERR) cl_error (E_UERR, e_nopfile, ltname); if (pp == NULL) cl_error (E_UERR, e_pnonexist, pname); return (pp); } /* DEFPAR -- Determine if the named parameter exists. Name may include * package, task and param names, task and param names, or just the param name, * with appropriate searching as necessary. False is returned if either the * task has no param file or the param does not exist. */ int defpar (char *param_spec) { char sbuf[SZ_LINE]; char *pkname, *ltname, *pname, *junk; strcpy (sbuf, param_spec); breakout (sbuf, &pkname, <name, &pname, &junk); switch ((XINT) lookup_param (pkname, ltname, pname)) { case NULL: case ERR: return (NO); default: return (YES); } } /* DEFVAR -- Determine if the named environment variable exists. */ int defvar (char *envvar) { char sbuf[SZ_LINE]; if (c_envfind (envvar, sbuf, SZ_LINE) <= 0) return (NO); else return (YES); } /* LOOKUP_PARAM -- Hunt for and return pointer to param in given package * and ltask. If task does not have param file, NULL is returned. If pfile * exists but is not loaded, it is loaded before searching for parameter. * Returns valid pp if sucessful; NULL if param file exists but contains no * such param, and ERR if there is no param file. * All other problems (package, task unknown or ambiguous) result in an abort. * Called by PARAMSRCH and by DEFPAR. */ struct param * lookup_param (char *pkname, char *ltname, char *pname) { register struct param *pp; register struct package *pkp; register struct ltask *ltp; struct pfile *pfp; struct pfile *pfiles[64]; struct param *candidate; int ambig, npfiles, i; pp = NULL; if (*ltname == '\0') { /* No ltask or package given so check standard places. If the * current task is cl the search order is curpack,cl. Otherwise, * the search order is curtask,package,cl, where `package' is * the package to which the current task belongs, NOT the current * package. The current task is the task which is currently * executing; while a task is executing, any psets referenced * by the main task pfile are loaded and linked into a list off * the main pfile. Note that this also hold for the pkg pfile, * since the pkg-task is always executing while any tasks therein * are executing (unless the pkg script exits with a keep()). */ npfiles = 0; if (currentask->t_ltp == firstask->t_ltp) { /* The current task is the cl() task. */ pfiles[npfiles++] = NULL; pfiles[npfiles++] = curpack->pk_pfp; } else { /* The current task is a normal compiled or script task. * Search the main pfile for the task, any pset-files * referenced by the main pfile, and lastly the package pfile * and any pset-files referenced by the package pfile. */ struct pfile *pfp_head[2]; int i; pfp_head[0] = currentask->t_pfp; pfp_head[1] = currentask->t_ltp->lt_pkp->pk_pfp; for (i=0; i <= 1; i++) if ((pfp = pfp_head[i]) != NULL) { pfiles[npfiles++] = pfp; if (pfp->pf_flags & PF_PSETREF) { while ( (pfp = pfp->pf_npset) ) { pfiles[npfiles++] = pfp; if (npfiles >= 62) cl_error (E_IERR, "lookup_param: too many pfiles"); } } } } pfiles[npfiles++] = firstask->t_pfp; /* firstask == cl */ /* Search for the named parameter in all the pfiles in the search * path. If an exact match is found in any pfile we are done. * If abbreviations are enabled and a non-unique abbreviation is * indicated, keep searching pfiles and abort only if an exact * match is not found in some other pfile. */ candidate = NULL; ambig = 0; for (i=0; i < npfiles; i++) { pfp = pfiles[i]; if (pfp != NULL && (pp = paramfind(pfp, pname,0,NO)) != NULL) { if ((XINT)pp == -1) { ambig++; } else if (!strcmp (pp->p_name, pname)) { ambig = 0; break; /* exact match */ } else if (candidate != NULL && candidate != pp) { ambig++; } else { candidate = pp; } } } if (ambig) cl_error (E_UERR, e_pambig, pname, ""); else if (pp == NULL) pp = candidate; } else { if (*pkname != '\0') { /* If the package name is given, search only that package. */ pkp = pacfind (pkname); if ((XINT)pkp == ERR) cl_error (E_UERR, e_pckambig, pkname); if (pkp == NULL) cl_error (E_UERR, e_pcknonexist, pkname); /* Search for ltask; it must exist and the given name must * be an unambiguous abbreviation. */ ltp = ltaskfind (pkp, ltname, 1); if (ltp == NULL) cl_error (E_UERR, e_tnonexist, ltname); if ((XINT)ltp == ERR) cl_error (E_UERR, e_tambig, ltname); } else { /* Ltask name given but not package name. Do circular search * for ltask; abort if not found or ambiguous. */ ltp = ltasksrch ("", ltname); } /* Get param file pointer and find parameter. Return ERR if no * pfile. */ if ((pfp = pfilefind (ltp)) == NULL) { if (ltp->lt_flags & LT_PFILE) pfp = pfileload (ltp); else /* no pfile */ return ((struct param *)ERR); } pp = paramfind (pfp, pname, 0, NO); if ((XINT)pp == ERR) cl_error (E_UERR, e_pambig, pname, ltp->lt_lname); } return (pp); } /* PRINTPARAM -- Convert the info in param pp to text and print it on * file fp. Return ERR if have a write error, else OK. * Don't write M_FAKE params unless we are writing to stderr. * Put quotes around strings; convert escape chars into escape sequences. * Don't call error() so caller can have a chance to close the file. */ int printparam (struct param *pp, register FILE *fp) { register int type, bastype; register char *bp; char *index(); char buf[20]; int arrflag; int size_arr=0; int i; /* a misc variable. */ if ((pp->p_mode & M_FAKE) && fp != stderr) return (OK); type = pp->p_type; bastype = type & OT_BASIC; arrflag = type & PT_ARRAY; /* NAME */ fputs (pp->p_name, fp); fputc (PF_DELIM, fp); /* TYPE */ bp = buf; if (type & PT_LIST) *bp++ = '*'; else if (arrflag) *bp++ = 'a'; switch (bastype) { case OT_BOOL: *bp++ = 'b'; break; case OT_INT: *bp++ = 'i'; break; case OT_REAL: *bp++ = 'r'; break; case OT_STRING: *bp++ = 's'; break; } /* Overwrite the string descriptor that appears with PT_FILNAM, * PT_STRUCT and the cursors. */ if (type & PT_FILNAM) { *--bp = 'f'; if (type & PT_FBIN) *++bp = 'b'; if (type & PT_FNOE) *++bp = 'n'; if (type & PT_FER) *++bp = 'r'; if (type & PT_FTXT) *++bp = 't'; if (type & PT_FEW) *++bp = 'w'; *++bp = '\0'; } else if (type & PT_STRUCT) { strcpy (--bp, "struct"); } else if (type & PT_GCUR) { strcpy (--bp, "gcur"); } else if (type & PT_IMCUR) { strcpy (--bp, "imcur"); } else if (type & PT_UKEY) { strcpy (--bp, "ukey"); } else if (type & PT_PSET) { strcpy (--bp, "pset"); } else *bp = '\0'; fputs (buf, fp); fputc (PF_DELIM, fp); /* MODE */ makemode (pp, buf); fputs (buf, fp); fputc (PF_DELIM, fp); /* VALUE. * Set i if pp is a struct or cursor. * Print the max length of structs or cursors even if they are not * defined. */ i = type & (PT_STRUCT|PT_GCUR|PT_IMCUR|PT_UKEY); if (opindef(&pp->p_valo) && !i) { fputs (indefstr, fp); } else if (opundef(&pp->p_valo) && !i) { ; } else if (type & (PT_LIST|PT_FILNAM|PT_PSET)) { /* Put quotes around string, may contain special chars */ qputs (pp->p_val.v_s, fp); } else if (bastype == OT_STRING && !arrflag) { if (i) /* -1 to allow for +1 added for \0 in addparam(). */ fprintf (fp, "%d", pp->p_lenval - 1); else { /* Quote string, may contain special chars */ qputs (pp->p_val.v_s, fp); } } else if (arrflag) { /* Print array descriptor info, and get size of array for * printing values later. */ int dim, d; short *lenoff; size_arr = 1; dim = pp->p_val.v_a->a_dim; lenoff = & (pp->p_val.v_a->a_len) ; fprintf (fp,"%d,", dim); for (d=0; d<2*dim; d++) { if (d%2 == 0) size_arr *= *lenoff; fprintf(fp, "%d,", *lenoff++); } /* Terminate the line. */ fprintf(fp, "\\\n"); } else fprop (fp, &pp->p_valo); if (!arrflag) fputc (PF_DELIM, fp); /* MINIMUM. * Set i if this param has a min/max field. reuse in max printing. */ i = (bastype != OT_BOOL && !(type & (PT_STRUCT|PT_GCUR|PT_IMCUR|PT_UKEY|PT_PSET))); if (pp->p_flags & P_IMIN) fputs (indefstr, fp); else if (pp->p_flags & P_UMIN) ; else if (i) fprop (fp, &pp->p_mino); fputc (PF_DELIM, fp); /* MAXIMUM */ if (pp->p_flags & P_IMAX) fputs (indefstr, fp); else if (pp->p_flags & P_UMAX) ; else if (i) fprop (fp, &pp->p_maxo); fputc (PF_DELIM, fp); /* PROMPT. */ if (*pp->p_prompt != '\0') qputs (pp->p_prompt, fp); if (!arrflag) fputc ('\n', fp); else fprintf (fp, ",\\\n"); /* Structs and cursors get printed on their own line. */ if (!(type & PT_LIST) && (type&(PT_STRUCT|PT_GCUR|PT_IMCUR|PT_UKEY))) { if (opindef (&pp->p_valo)) fputs (indefstr, fp); else if (opundef (&pp->p_valo)) ; else fputs (pp->p_val.v_s, fp); fputc ('\n', fp); } if (arrflag) { /* For a first approximation use a fixed number of * values per line. */ int count=0, lcount=0, n_per=0, *p_i= (int *) NULL; double *p_r= (double *) NULL; char **p_s = NULL; if (bastype == OT_BOOL) { n_per = 20; p_i = pp->p_aval.a_i; } else if (bastype == OT_INT) { n_per = 10; p_i = pp->p_aval.a_i; } else if (bastype == OT_REAL) { n_per = 4; p_r = pp->p_aval.a_r; } else if (bastype == OT_STRING) { n_per = 2; p_s = pp->p_aval.a_s; } count = 0; lcount = 0; for (; count n_per) { fprintf(fp, "\\\n"); lcount = 0; } if (bastype == OT_BOOL) { if (*p_i != INDEFL) { if (*p_i++) fprintf (fp, "yes"); else fprintf (fp, "no"); } else p_i++; } else if (bastype == OT_INT) { if (*p_i == INDEFL) p_i++; else fprintf (fp, "%d", *p_i++); } else if (bastype == OT_REAL) { if (*p_r == INDEFR) p_r++; else fprintf (fp, "%g", *p_r++); } else if (bastype == OT_STRING) { /* The undefined string is the null string, so * we needn't check for it. */ qputs (*p_s++, fp); } if (count < size_arr-1) fprintf (fp, ","); else fprintf (fp, "\n"); } } if (ferror (fp)) return (ERR); return (OK); } /* QPUTS -- Print a string on the output stream, converting all recognized * control characters (newline, tab, and string delimiters) into escape * sequences, so that they can later be read back in unmodified. */ void qputs (register char *str, register FILE *fp) { register char ch; fputc ('"', fp); while ((ch = *str++) != '\0') { switch (ch) { case '\n': fputs ("\n", fp); /* avoid super long lines */ break; case '\t': fputs ("\\t", fp); break; case '\r': fputs ("\\r", fp); break; case '\f': fputs ("\\f", fp); break; case '\\': fputc ('\\', fp); ch = *str++; fputc (ch, fp); break; case '\'': fputs ("\\'", fp); break; case '"': fputs ("\\\"", fp); break; default: fputc (ch, fp); } } fputc ('"', fp); } /* PVALDEFINED -- Decide whether string s is indefinite (one of indefstr or * indeflc) or undefined (s == undefval), and set pp->p_type bits accordingly. * Return YES if neither of these conditions exist, else NO. Note that * the null string a null string per se does not qualify as an undefined * value. */ int pvaldefined (struct param *pp, char *s) { int val; val = NO; if (s == NULL || s == undefval) setopundef (&pp->p_valo); else if (!strcmp (s, indefstr) || !strcmp (s, indeflc)) setopindef (&pp->p_valo); else val = YES; return (val); } /* NEWFAKEPARAM -- Make a fake parameter off pfp. Use newparam to actually * allocate space. If name is NULL, name the parameter $pos, else name it * name. Add one to pos because users see names as one-indexed. * Type of param is type; if OT_STRING allocation is for SZ_FNAME characters. * Check for pos > 99 as we only allowing room for 2 digits in $name for. * Check for both kinds of null strings, just in case. */ struct param * newfakeparam ( struct pfile *pfp, char *name, int pos, int type, int string_len /* if new param is type string, size of string */ ) { register struct param *pp; pp = newparam (pfp); if (name == NULL || *name == '\0') { if (++pos > 99) cl_error (E_UERR, "too many fake positional params"); pp->p_name = memneed (btoi(4)); /* need room for "$nn\0" */ sprintf (pp->p_name, "$%d", pos); } else pp->p_name = comdstr (name); if (cldebug) eprintf ("adding fake param `%s', type code %d\n", pp->p_name, type); type &= OT_BASIC; pp->p_valo.o_type = type; pp->p_mino.o_type = type; pp->p_maxo.o_type = type; if (type == OT_STRING) { /* Allocate specified amount of space, add the eos and init * max length. Other types need no initialization. */ pp->p_val.v_s = memneed (btoi(string_len+1)); pp->p_val.v_s[string_len] = '\0'; /* the permanent eos. */ pp->p_lenval = string_len+1; } pp->p_type = type; pp->p_valo.o_type = OT_UNDEF; pp->p_mode = M_FAKE; pp->p_flags = (P_UMIN|P_UMAX); return (pp); } /* GETOFFSET -- Getoffset returns the offset from the beginning of the array * for using the index values stored on the stack. */ int getoffset (struct param *pp) { int dim, offset, index; short *plen, *poff, len, off; if (mode_offset == DIRECT_OFFSET) { n_indexes--; if (n_indexes < 0) cl_error(E_UERR, e_indexunf); offset = pop() ; mode_offset = INDEX_OFFSET; } else { dim = pp->p_val.v_a->a_dim; plen = &(pp->p_val.v_a->a_len) ; poff = plen + 1; offset = 0; while (dim-- > 0) { len = *(plen + 2*dim); off = *(poff + 2*dim); if (offset > 0) offset *= len; n_indexes--; if (n_indexes < 0) cl_error(E_UERR, e_indexunf); index = pop(); if (index < off || index > off+len-1) cl_error(E_UERR, "Array subscript error. Index %d is %d.", dim+1, index); offset += index-off; } } return (offset); } /* OFFSETMODE -- Offsetmode() permits the user to choose whether to calculate * the offsets using an index list, or to push the offset onto the stack * directly. */ void offsetmode (int mode) { if (mode) mode_offset = DIRECT_OFFSET; else mode_offset = INDEX_OFFSET; } /* SIZE_ARRAY -- Get the number of elements in an array. */ int size_array (struct param *pp) { int dim, d, size; short *len; size = 1; if (pp->p_type & PT_ARRAY ) { dim = pp->p_val.v_a->a_dim; len = &(pp->p_val.v_a->a_len) ; for (d=0; d < dim; d++) size *= *(len+2*d); } return (size); }