diff options
author | Joe Hunkeler <jhunkeler@gmail.com> | 2015-08-11 16:51:37 -0400 |
---|---|---|
committer | Joe Hunkeler <jhunkeler@gmail.com> | 2015-08-11 16:51:37 -0400 |
commit | 40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch) | |
tree | 4464880c571602d54f6ae114729bf62a89518057 /pkg/cl/modes.c | |
download | iraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz |
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'pkg/cl/modes.c')
-rw-r--r-- | pkg/cl/modes.c | 1279 |
1 files changed, 1279 insertions, 0 deletions
diff --git a/pkg/cl/modes.c b/pkg/cl/modes.c new file mode 100644 index 00000000..1258b032 --- /dev/null +++ b/pkg/cl/modes.c @@ -0,0 +1,1279 @@ +/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + */ + +#define import_spp +#define import_libc +#define import_stdio +#define import_ctype +#include <iraf.h> + +#include "config.h" +#include "clmodes.h" +#include "construct.h" +#include "operand.h" +#include "param.h" +#include "grammar.h" +#include "mem.h" +#include "task.h" +#include "errs.h" +#include "proto.h" + + +/* + * MODES -- Handle the parameter mode operations, such as determining effective + * mode, checking if in range and queries. + * Also handle the global modes of the cl, such as abbreviations, menus, and + * logging. Macro defns for all but abbreviations are in clmodes.h; it is + * involved enough to be a real function in this file. + */ + +#define INIT_DELAY 3 /* sleep params, bkg_query() */ +#define DELAY_MULT 1.4 +#define MAXDELAY (60*5) /* sleep at most 5 minutes */ +#define BKQ_TIMEOUT (60*60*3) /* time out after 3 hours */ +#define SZ_PROMPTBUF SZ_LINE /* avoid string overflow */ + +extern int cldebug; +extern char *eofstr; +extern int bkgno; /* our job number, if background */ +extern int ppid; /* parent's pid, if background */ + +/* These are set, by setclmodes(), right after the cl's pfile is read. there + * is one for each special-function cl parameter. + * Once set, they are used by the macros in clmodes.h to efficiently determine + * the various function settings yet allow them to remain normal parameters. + */ +struct param *clabbrev; /* allow abbreviations? */ +struct param *clmenus; /* display tasks in curpack with prompt?*/ +struct param *clshowtype; /* display task type in menus */ +struct param *clkeeplog; /* keep all input in logfile? */ +struct param *cllexmodes; /* enable lexical mode switching */ +struct param *cllogfile; /* name of the logfile */ +struct param *clnotify; /* notify parent when bkg task is done */ +struct param *clecho; /* echo commands from scripts on stderr */ +int cllogmode = LOG_COMMANDS; /* Logging control flag */ + + +/* Calculate the effective mode for the given parameter, considering + * its own mode and the modes for the current task and the cl. + * Inhibit query mode if set on the command line or hidden but + * enable it if the param is not in range. The range test cannot be done + * here for list params because we'd have to read the list to do it. + * Return a bit-mapped code (built up of M_XXX bits) of the result. + * Since learn mode is not defined at the parameter level, pp == NULL + * is used to indicate we are just interested in M_LEARN info. + * Local variables cannot be prompted for so it is an error if their + * values are undefined. + */ +int +effmode ( + struct param *pp +) +{ + static char *localerr = + "Attempt to access undefined local variable `%s'.\n"; + + register int mode, modebits; + struct operand o; + int clmode, ltmode, pkmode, offset; + int interactive; + + /* Check if param is a local variable. If it is undefined + * this is an ERR, if defined just return mode 0 to defeat + * querying. + */ + if (pp != NULL) + if (pp->p_mode & M_LOCAL) { + if (opundef (&(pp->p_valo))) + cl_error (E_UERR, localerr, pp->p_name); + return (0); + } + + /* Determine whether or not the current task was called interactively. + * Menu mode is only permitted for tasks called interactively. + */ + interactive = 0; + if (prevtask) + interactive = (prevtask->t_flags & (T_INTERACTIVE|T_BATCH)); + if (interactive) + modebits = (M_QUERY|M_HIDDEN|M_MENU); + else + modebits = (M_QUERY|M_HIDDEN); + + clmode = scanmode (firstask->t_modep->p_val.v_s); + ltmode = scanmode (currentask->t_modep->p_val.v_s); + pkmode = -1; + + mode = 0; + if (pp != NULL) { + /* In determining the effective mode we go up the hierarchy of + * parameter, task, package, cl. The mode is taken from the first + * of these which is not automatic. + */ + if ((mode = (pp->p_mode & modebits))) + ; + else if ((mode = (ltmode & modebits))) + ; + else { + /* Check the mode of the package to which the ltask belongs, + * which need not be the "current" package. + */ + struct pfile *pfp; + + if ((pfp = currentask->t_ltp->lt_pkp->pk_pfp)) { + struct param *ppx; + ppx = paramfind (pfp, "mode", 0, YES); + if ((ppx != NULL) && (ppx != (struct param *)ERR)) + pkmode = scanmode (ppx->p_val.v_s); + } + + if (pkmode > 0 && (mode = (pkmode & modebits))) + ; + else if ((mode = (clmode & modebits))) + ; + else + mode = M_AUTO; + } + + /* Defeat query mode if param set on command line or it's a + * hidden param or if menu mode is in effect. + */ + if ((pp->p_flags & P_CLSET) || (pp->p_mode & M_HIDDEN) || + (mode & M_MENU)) + mode &= ~M_QUERY; + + /* Query unconditionally if param is out of range or undefined. + */ + if (!(mode & M_QUERY) && !(pp->p_type & PT_LIST)) { + + /* To check whether an array element is in range we + * must get the appropriate element of the array. However + * the stack must be reset so that the element can be accessed + * again by the calling routine. + */ + if (pp->p_type & PT_ARRAY) { + offset = getoffset(pp); + + poffset (offset); + paramget(pp, FN_VALUE); + + poffset (offset); + + o = popop(); + if (!inrange (pp, &o)) + mode |= M_QUERY; + + } else { + /* Use temporary scratch variable for range checking in + * this case; sometimes the value of an enumerated + * parameter would get trashed in the process. There is + * probably some deeper, darker bug lurking down there, + * but haven't found it yet, so this will suffice for now. + */ + o = pp->p_valo; + if (!inrange (pp, &o)) + mode |= M_QUERY; + } + } + } + + /* Enable learn mode only for tasks called interactively - don't bother + * to learn parameters if the task is called from a script or in batch + * mode. + */ + if (interactive) + mode |= (clmode & M_LEARN) | (ltmode & M_LEARN); + + return (mode); +} + + +/* TASKMODE -- Determine the effective mode for a task. + */ +int +taskmode ( + register struct task *tp +) +{ + register int modebits, mode; + struct pfile *pfp; + int clmode, pkmode, ltmode; + int interactive, learn; + + /* Determine whether or not the task was called interactively. + * Menu mode is only permitted for tasks called interactively. + */ + interactive = 0; + if (next_task(tp)) + interactive = (next_task(tp)->t_flags & (T_INTERACTIVE|T_BATCH)); + if (interactive) + modebits = (M_QUERY|M_HIDDEN|M_MENU); + else + modebits = (M_QUERY|M_HIDDEN); + + ltmode = scanmode (tp->t_modep->p_val.v_s); + clmode = scanmode (firstask->t_modep->p_val.v_s); + learn = ((ltmode|clmode) & M_LEARN); + + /* If the mode of the task is anything but AUTO we are done. + */ + if ((mode = (ltmode & modebits))) + if (interactive || !(mode & M_MENU)) + return (mode|learn); + + /* If the package to which the task belongs has a pfile and the mode + * of the package is anything but AUTO, we are done. + */ + if ((pfp = tp->t_ltp->lt_pkp->pk_pfp)) { + struct param *ppx; + + pkmode = ERR; + ppx = paramfind (pfp, "mode", 0, YES); + if ((ppx != NULL) && (ppx != (struct param *)ERR)) + pkmode = scanmode (ppx->p_val.v_s); + + if (pkmode != ERR && (mode = (pkmode & modebits))) + if (interactive || !(mode & M_MENU)) + return (mode|learn|(pkmode&M_LEARN)); + } + + /* Return the CL mode (menu mode not permitted at the CL level). + */ + return (clmode); +} + + +/* QUERY -- Query the user for the value of a parameter. Prompt with the + * current value if any. Keep this up until we can push a reasonable value. + * Also, store the new value in the parameter (except for list params, where, + * since the values are not kept, all that may change is P_LEOF if seen). + * Give prompt, or name if none, current value and range if int, real or + * filename. Accept CR to leave value unchanged, else take the string + * entered to be the new value. Repeat until parameter value is in range. + * We mean to talk straight to the user here; thus, interact with the real + * stdio, not the effective t_stdio, so that redirections do not get in + * the way. In batch mode, a forced query is handled by writing a + * message on the terminal of the parent cl (the original stderr), and + * leaving some info describing the query in a file in uparm (if there is + * no uparm, we abort). We then loop, waiting for the user to run "service" + * in the interactive cl to service the query, leaving the answer in a + * another file which we read and then delete. If we wait a long time and + * get no response, we timeout. + */ +void +query ( + struct param *pp +) +{ + static char *oormsg = + "ERROR: Parameter value is out of range; try again"; + register char *ip; + char buf[SZ_PROMPTBUF+1]; + struct operand o; + int bastype, batch, arrflag, offset, n_ele, max_ele, fd; + char *index(), *nlp, *nextstr(); + char *bkg_query(), *query_status; + char *abuf; + + bastype = pp->p_type & OT_BASIC; + batch = firstask->t_flags & T_BATCH; + arrflag = pp->p_type & PT_ARRAY; + + if (arrflag) { /* We may access the array many */ + offset = getoffset (pp); /* times, so save the offset and */ + /* push it when necessary. */ + poffset (offset); + max_ele = size_array (pp) - offset; + } else + max_ele = 1; + + + forever { + if (batch) { + /* Query from a background job. + */ + query_status = bkg_query (buf, SZ_PROMPTBUF, pp); + + } else if (pp->p_type & (PT_GCUR|PT_IMCUR)) { + /* Read a graphics cursor. + */ + char source[33]; + int cursor; + + /* Determine the source of graphics cursor input, chosen from + * either the graphics or image cursor or the terminal. + */ + if (pp->p_type & PT_GCUR) { + if (c_envfind ("stdgcur", source, 32) <= 0) + strcpy (source, "stdgraph"); + } else { + if (c_envfind ("stdimcur", source, 32) <= 0) + strcpy (source, "stdimage"); + } + + if (strcmp (source, "stdgraph") == 0) + cursor = STDGRAPH; + else if (strcmp (source, "stdimage") == 0) + cursor = STDIMAGE; + else + goto text_query; /* get value from terminal */ + + /* Read a physical graphics cursor. + */ + pp->p_flags &= ~P_LEOF; + if (cursor == STDIMAGE) { + /* The following is a kludge used to temporarily implement + * the logical image cursor read. In the future this will + * be eliminated, and the c_rcursor call below (cursor + * mode) will be used for stdimage as well as for stdgraph. + * The present code (IMDRCUR) goes directly to the display + * server to get the cursor value, bypassing cursor mode + * and the (currently nonexistent) stdimage kernel. + */ + char str[SZ_LINE+1], keystr[10]; + int wcs, key; + float x, y; + + if (c_imdrcur ("stdimage", + &x,&y,&wcs,&key,str,SZ_LINE, 1, 1) == EOF) { + query_status = NULL; + + } else { + if (isprint(key) && !isspace(key)) + sprintf (keystr, "%c", key); + else + sprintf (keystr, "\\%03o", key); + sprintf (buf, "%.3f %.3f %d %s %s\n", + x, y, wcs, keystr, str); + query_status = (char *) ((XINT)strlen(buf)); + } + + } else if (c_rcursor (cursor, buf, SZ_PROMPTBUF) == EOF) { + query_status = NULL; + } else + query_status = (char *) ((XINT)strlen(buf)); + + } else if (pp->p_type & PT_UKEY) { + /* Read a user keystroke command from the terminal. + */ + pp->p_flags &= ~P_LEOF; + if (c_rdukey (buf, SZ_PROMPTBUF) == EOF) + query_status = NULL; + else + query_status = (char *) ((XINT)strlen(buf)); + + } else { +text_query: fd = spf_open (buf, SZ_PROMPTBUF); + pquery (pp, fdopen(fd,"a")); + spf_close (fd); + + c_stgputline ((XINT)STDOUT, buf); + if (c_stggetline ((XINT)STDIN, buf, SZ_PROMPTBUF) > 0) + query_status = (char *) ((XINT) strlen(buf)); + else + query_status = NULL; + } + + ip = buf; + + /* Set o to the current value of the parameter. Beware that some + * of the logical branches which follow assume that struct o has + * been initialized to the current value of the parameter. + */ + if (pp->p_type & PT_LIST) + setopundef (&o); + else if (arrflag) { + paramget(pp, FN_VALUE); + poffset (offset); + o = popop(); + } else + o = pp->p_valo; + + /* Handle eof, a null-length line (lone carriage return), + * and line with more than SZ_LINE chars. Ignore leading whitespace + * if basic type is not string. + */ + if (query_status == NULL) { + /* Typing eof will use current value (as will a lone + * newline) but if param is a list, it is a meaningful + * answer. + */ + if (pp->p_type & PT_LIST) { + closelist (pp); /* close an existing file */ + pp->p_flags |= P_LEOF; + o = makeop (eofstr, OT_STRING); + break; + } + goto testval; + } + + /* Ignore leading whitespace if it is not significant for this + * datatype. Do this before testing for empty line, so that a + * return such as " \n" is equivalent to "\n". I.e., do not + * penalize the user if they type the space bar by accident before + * typing return to accept the default value. + */ + if (bastype != OT_STRING || (pp->p_type & (PT_FILNAM|PT_PSET))) + while (*ip == ' ' || *ip == '\t') + ip++; + + if (*ip == '\n') { + /* Blank lines usually just accept the current value + * but if the param is a string and is undefined, + * it sets the string to a (defined) nullstring. + */ + *ip = '\0'; + if (bastype == OT_STRING && opundef (&o)) + o = makeop (ip, bastype); + else + goto testval; + } + + if ((nlp = index (ip, '\n')) != NULL) + *nlp = '\0'; /* cancel the newline */ + else + goto testval; + + /* Finally, we have handled the pathological cases... + */ + if ((pp->p_type & PT_LIST) && + (!strcmp (ip,eofstr) || !strcmp (ip,"eof"))) { + + closelist (pp); + pp->p_flags |= P_LEOF; + o = makeop (eofstr, OT_STRING); + break; + + } else { + if (arrflag) { + /* In querying for arrays we may set more than one + * element of the array in a single query. However + * we must set the first element. So we will pretend + * to be a scalar until that first element is set + * and then enter a loop where we may set other + * elements. + */ + abuf = ip; + ip = nextstr(&abuf, stdin); + if (ip == NULL || ip == (char *) ERR || ip == undefval) + goto testval; + } + + o = makeop (ip, bastype); + } + +testval: + /* If parameter value is in range, we are done. If it is out of + * range and we are a batch job or an interactive terminal job, + * print an error message and request that the user enter a legal + * value. If the CL is being run taking input from a file, abort, + * else we will go into a loop reading illegal values from the + * input file and printing out lots of error messages. + */ + if (inrange (pp, &o)) + break; + else if (batch) + eprintf ("\n[%d] %s", bkgno, oormsg); + else if (isatty (fileno (stdin))) + eprintf ("%s\n", oormsg); + else + cl_error (E_UERR, oormsg); + } + + if (!(pp->p_type & PT_LIST)) { + /* update param with new value. + */ + if (cldebug) { + eprintf ("changing `%s.p_val' to ", pp->p_name); + fprop (stderr, &o); + eprintf ("\n"); + } + + pushop (&o); + paramset (pp, FN_VALUE); + pp->p_flags |= P_QUERY; + } + + pushop (&o); + + if (arrflag && query_status != NULL && *ip != '\0') { + /* If we have an array assign values until something + * is used up or until we hit any error. + */ + n_ele = 1; + forever { + if (n_ele >= max_ele) /* End of array. */ + break; + ip = nextstr(&abuf, stdin); + + if (ip == NULL) /* End of query line. */ + break; + + if (ip == (char *) ERR) { /* Error on query line. */ + eprintf("Error loading array value.\n"); + break; + } + + if (ip != undefval) { + o = makeop (ip, bastype); + if ( ! inrange (pp, &o) ) { /* Not in range. */ + eprintf("Array value outside range.\n"); + break; + } + + offset++; /* Next element in array. */ + poffset (offset); + + pushop (&o); + paramset (pp, FN_VALUE); + } else + offset++; + + n_ele++; + } + } + +} + + +/* NEXTSTR -- Get the next string in a prompt. + */ +char * +nextstr ( + char **pbuf, + FILE *fp +) +{ + char *p, *nxtchr(); + static char tbuf[SZ_LINE]; + char quote; + int cnt; + + p = *pbuf; + + /* Skip white space. */ + while ( *p == ' ' || *p == '\t' || *p =='\n') + p = nxtchr(p, fp); + + /* Reached end? */ + if (*p == '\0') { + *pbuf = p; + return (NULL); + } + + quote = '\0'; + cnt = 0; + + /* Quoted string. */ + if (*p == '\'' || *p == '"') { + quote = *p; + p = nxtchr (p, fp); + + while (*p != quote) { + + if (p == '\0' || cnt >= SZ_LINE) + return ( (char *) ERR); + + else { + tbuf[cnt++] = *p; + p = nxtchr(p, fp); + } + } + /* Skip quote. */ + p = nxtchr (p, fp); + + } else { + /* Unquoted string. */ + while (*p != ' ' && *p != '\t' && *p != '\n' && + *p != '\0' && *p != ',') { + + if (cnt >= SZ_LINE) + return ( (char *) ERR ); + + tbuf[cnt++] = *p; + p = nxtchr (p, fp); + } + } + tbuf[cnt] = '\0'; + + /* Skip any white-space following. */ + while (*p == ' ' || *p == '\t' || *p == '\n') + p = nxtchr(p, fp); + + if (*p != ',' && *p != '\0') + return ( (char *) ERR); + + /* Skip delimiter. */ + if (*p == ',') + p = nxtchr(p, fp); + + *pbuf = p; + if (cnt == 0) { + /* Return a quoted null string, otherwise the field was skipped. */ + if (quote != '\0') + return (tbuf); + else + return (undefval); + } else + return (tbuf); +} + + +/* NXTCHR -- Get a pointer to the next char, reading the next line if necessary. + */ +char * +nxtchr ( + char *p, + FILE *fp +) +{ + /* P may point to within readbuf on return, so it had better be + * static. + */ + static char readbuf[SZ_LINE]; + + if (*p) + p++; +start: + if (*p == '\\') { + if (*(p+1) == '\n') { + if (fgets (readbuf, SZ_LINE, fp) == NULL) + /* We assume that the newline is always followed by a + * null in return from fgets. + */ + return (p+2); + else { + p = readbuf; + goto start; + } + } + } + + return (p); +} + + +/* PQUERY -- Print the query message. + */ +void +pquery ( + register struct param *pp, + FILE *fp +) +{ + struct operand o; + int offset, arrflag; + + arrflag = pp->p_type & PT_ARRAY; + + fprintf (fp, *pp->p_prompt == '\0' ? pp->p_name : pp->p_prompt); + + /* Show the ranges if they are defined and this is a parameter + * type that has ranges. + */ + if (range_check (pp)) { + fprintf (fp, " ("); + if (!(pp->p_flags & (P_IMIN|P_UMIN))) { + paramget (pp, FN_MIN); + o = popop(); + fprop (fp, &o); + } + if ((pp->p_type & OT_BASIC) != OT_STRING) + fprintf (fp, ":"); + if (!(pp->p_flags & (P_IMAX|P_UMAX))) { + paramget (pp, FN_MAX); + o = popop(); + fprop (fp, &o); + } + fputc (')', fp); + } + + /* Print the array indices. We get the offset and convert back + * to the indices. This works regardless of the offset mode. + */ + if (arrflag) { + int dim, d, rem, temp; + short *len, *off; + + offset = getoffset (pp); + poffset (offset); /* Restore stack for later reference */ + + dim = pp->p_val.v_a->a_dim; + len = &(pp->p_val.v_a->a_len) ; + off = &(pp->p_val.v_a->a_off) ; + + fputc ('[', fp); + temp = offset; + for (d=0; d<dim; d++) { + + if (d>0) + fputc (',', fp); + + rem = (temp % *len) + *off; + fprintf (fp, "%d",rem); + temp = temp / *len; + len = len + 2; + off = off + 2; + } + fputc (']', fp); + } + + /* Set o to the current value of the parameter. List files do + * not keep a value in core, however, and we certainly do not want + * to read the list to get one. + */ + if (pp->p_type & PT_LIST) + setopundef (&o); + else { + paramget (pp, FN_VALUE); + o = popop(); + + /* Restore offset on stack if array. */ + if (arrflag) { + poffset (offset); + } + } + + /* Print current value if not undefined. Ok if just indefinite. + */ + if (!opundef (&o)) { + if ((o.o_type & OT_BASIC) != OT_STRING || *(o.o_val.v_s) != '\0') { + fprintf (fp, " ("); + fprop (fp, &o); + fputc (')', fp); + } + } + fprintf (fp, ": "); + fflush (fp); +} + + +/* BKG_QUERY -- Send the "waiting for parameter input" to the user terminal, + * and loop until the background query response file is readable. + * This happens when the user responds to the query by executing "service". + * Check frequently in the beginning, gradually lengthening the sleep periods + * so that we do not hog the machine if the user is out to lunch. Timeout + * after a suitable interval if no response. + */ +char * +bkg_query ( + char *obuf, /* same calling sequence as 'fgets' */ + int maxch, + register struct param *pp +) +{ + char bqfile[SZ_PATHNAME], qrfile[SZ_PATHNAME]; + int waitime, delay; + char *envget(), *fgets_status; + FILE *fp, *in; + + if (notify()) + eprintf ("\n[%d] stopped waiting for parameter input\n", bkgno); + get_bkgqfiles (bkgno, ppid, bqfile, qrfile); + + /* Get names of the query and query response files and open the query + * file to receive the query. Post query request on the user terminal. + * If an old query response file happens to be lying about, delete it. + */ + c_delete (bqfile); + if ((fp = fopen (bqfile, "w")) == NULL) + cl_error (E_UERR, "Cannot create file `%s' for query", bqfile); + c_delete (qrfile); + + /* Print the query prompt into the background query request file. + */ + pquery (pp, fp); + fclose (fp); + + waitime = 0; + delay = INIT_DELAY; + + /* Loop until the query response file is readable. Sleep for + * progressively longer intervals if no response, then timeout. + */ + do { + if (waitime > BKQ_TIMEOUT) { + c_delete (bqfile); + cl_error (E_UERR, "Timeout on query"); + } else { + delay = (delay *= DELAY_MULT) > MAXDELAY ? MAXDELAY : delay; + c_tsleep (delay); + waitime += delay; + } + } while (c_access (qrfile,0,0) == NO); + + if ((in = fopen (qrfile, "r")) == NULL) + cl_error (E_UERR, "cannot open query response file"); + + fgets_status = fgets (obuf, maxch, in); + fclose (in); + c_delete (qrfile); + + return (fgets_status); +} + + +/* SERVICE_BKGQUERY -- Called by the user to service a background query. + * We must open the background query file for the indicated task and type + * out the prompt therein for the user. The user's response in then placed + * in the query response file, we delete the original query file, and we + * are done. When the bkg job wakes up it will read the response file and + * (assuming there are no errors) continue on. + */ +void +service_bkgquery ( + int bkgno /* ordinal of job requiring service */ +) +{ + register int ch; + char bqfile[SZ_PATHNAME], qrfile[SZ_PATHNAME]; + char qrtemp[SZ_PATHNAME]; + char response[SZ_LINE+1]; + FILE *fp; + + if (bkg_jobactive (bkgno) == NO) + cl_error (E_UERR, "No such job"); + else + get_bkgqfiles (bkgno, c_getpid(), bqfile, qrfile); + c_mktemp ("uparm$QR", qrtemp, SZ_PATHNAME); + + if ((fp = fopen (bqfile, "r")) == NULL) + cl_error (E_UERR, "No query is pending for bkg job [%d]", bkgno); + + /* Copy query file verbatim to the user's terminal. The last line + * will not have a newline, but that is ok here. + */ + while ((ch = fgetc(fp)) != EOF) + putchar (ch); + fflush (stdout); + + /* Get user's response and write into query response file. + * We write the response first into a temp file and then rename the + * temp file to eliminate the chance that the bkg job will try to + * open and read the response file before the data has all been + * written into it (happens on systems that do not lock files + * opened by another process for writing). + */ + c_delete (qrtemp); + fgets (response, SZ_LINE, stdin); + if ((fp = fopen (qrtemp, "w")) == NULL) + cl_error (E_UERR, "Cannot open `%s' to respond to query", qrtemp); + fputs (response, fp); + fclose (fp); + c_rename (qrtemp, qrfile); + + /* Do not delete the query file until we successfully respond to + * the query (in case of an abort). + */ + c_delete (bqfile); +} + + +/* GET_BKGQFILES -- Get the name of a background query file. This routine + * aborts if the directory uparm$ is not defined. Since we have two processes + * communicating via files, we must have a fixed directory both processes + * expect to find the files. We assume that the user does not start a bkg + * job and then change uparm$ in the foreground cl. + */ +void +get_bkgqfiles ( + int bkgno, + int pid, + char *bkg_query_file, + char *query_response_file +) +{ + int filecode; + char *envget(); + + if (envget (UPARM) == NULL) + cl_error (E_UERR, + "Logical directory 'uparm$' not defined, cannot query"); + + filecode = bkgno * 10000 + (pid % 10000); + sprintf (bkg_query_file, "%sBQF%d", envget(UPARM), filecode); + sprintf (query_response_file, "%sBQR%d", envget(UPARM), filecode); +} + + +/* INRANGE -- Check whether operand *op is in range, that is, that its o_val + * field is within the limits defined by the p_min/max fields in param *pp. + * Return YES if it is in range, else NO. In the case of filenames, also + * check that the PT_FXX access attributes are true. Also, filenames are + * considered out of range is they are indefinite (unlike other types; see + * below). + * The basic types for the operand and the parameter must agree. + * Always return YES for types that do not have ranges (only ints, reals, + * and filenames have ranges), when min > max, or when op is INDEF. + * Always return NO if op is UNDEFined. + * This routine uses binexp() and thus the operand stack. + */ +int +inrange ( + register struct param *pp, + register struct operand *op +) +{ + register int fulltype, bastype; + struct operand omin, test; + + fulltype = pp->p_type; + bastype = fulltype & OT_BASIC; + + /* If the operand is undefined, it is out of range. Indefinite is + * inrange for int and real type params. + */ + if (opundef (op)) + return (NO); + if (opindef (op) && bastype & (OT_INT|OT_REAL)) + return (YES); + + /* If range checking is disabled, and the parameter value is defined, + * it is in range. + */ + if (range_check (pp) == 0) + return (YES); + + if (fulltype & PT_FILNAM) { + /* check any access attributes given. + */ + char *filnam = op->o_val.v_s; + if (opindef (op)) + return (NO); + + if ((fulltype & PT_FER) && c_access (filnam, READ_ONLY, 0) == NO) + cl_error (E_UERR, "File `%s' is not readable", filnam); + if ((fulltype & PT_FEW) && c_access (filnam, WRITE_ONLY, 0) == NO) + cl_error (E_UERR, "File `%s' is not writable", filnam); + if ((fulltype & PT_FNOE) && c_access (filnam,0,0) == YES) + cl_error (E_UERR, "File `%s' exists", filnam); + + if ((fulltype & PT_FTXT) && c_access (filnam, 0, TEXT_FILE) == NO) + cl_error (E_UERR, "File `%s' is not a text file", filnam); + if ((fulltype & PT_FBIN) && c_access (filnam, 0, TEXT_FILE) == YES) + cl_error (E_UERR, "File `%s' is not a binary file", filnam); + } + + /* If the param is string valued and the legal values are enumerated, + * any minimum match abbreviation is considered in range. Return the + * FULL string in the operand structure. The legal values of an + * enumerated string type parameter are given in the min field as a + * string of the form "val|val|val". Embedded whitespace is not + * permitted. + */ + if (bastype == OT_STRING && !(pp->p_flags & P_UMIN)) { + char *s, *delim, *match; + char *val, *index(); + int n; + + paramget (pp, FN_MIN); + omin = popop(); + if (omin.o_type != OT_STRING || op->o_type != OT_STRING) + return (NO); + + val = op->o_val.v_s; + n = strlen (val); + match = NULL; + + for (delim = s = omin.o_val.v_s; delim && *s; s=delim+1) { + delim = index (s, '|'); + if (delim) + *delim = '\0'; + if (strncmp (s, val, n) == 0) { + if (match) { + eprintf ("ambiguous abbreviation '%s'\n", val); + return (NO); + } else + match = s; + } + } + + if (match != NULL) + op->o_val.v_s = comdstr (match); + return (match != NULL); + } + + /* Check the minimum value, if one is given. + */ + if (!(pp->p_flags & (P_IMIN|P_UMIN))) { + pushop (op); + paramget (pp, FN_MIN); + binexp (OP_GE); /* op >= p_min? */ + test = popop(); + if (!test.o_val.v_i) /* if (false) op out of range */ + return (NO); + } + + /* Check the maximum value, if one is given. + */ + if (!(pp->p_flags & (P_IMAX|P_UMAX))) { + pushop (op); + paramget (pp, FN_MAX); + binexp (OP_LE); /* op <= p_max? */ + test = popop(); + if (!test.o_val.v_i) /* if (false) op out of range */ + return (NO); + } + return (YES); +} + + +/* RANGE_CHECK -- Determine if range checking is in effect. Range checking + * is only employed for int, real, string (enumerated) and filename params. + * If both the min and max fields are set, but max is less than min, checking + * is disabled. + */ +int +range_check ( + struct param *pp +) +{ + int fulltype, bastype; + struct operand test, omin, omax; + + fulltype = pp->p_type; + bastype = fulltype & OT_BASIC; + + /* No range checking for bools, or when range values are undefined + * or indefinite. + */ + if (bastype == OT_BOOL || + fulltype & (PT_STRUCT|PT_GCUR|PT_IMCUR|PT_UKEY|PT_PSET)) + return (NO); + if (pp->p_flags & (P_IMIN|P_UMIN) && pp->p_flags & (P_IMAX|P_UMAX)) + return (NO); + + /* Range checking is disabled if the max value is set lower than + * the min value. + */ + if (!(pp->p_flags & (P_UMIN|P_IMIN|P_UMAX|P_IMAX))) { + omax.o_type = omin.o_type = bastype; + omin.o_val = pp->p_min; + omax.o_val = pp->p_max; + pushop (&omin); + pushop (&omax); + binexp (OP_GT); /* p_min > p_max? */ + test = popop(); + if (test.o_val.v_i) /* if (true) artificially pass */ + return (NO); + } + + return (YES); /* should range check */ +} + + +/* SETCLMODES -- Set up the cl mode reference pointers to point to their + * special-function params. tp is firstask. Set the pointers to NULL if the + * parameter is not found. Called once by login() after the cl's pfile has + * been read in. + */ +void +setclmodes ( + struct task *tp +) +{ + register struct param *pp; + register char *name; + int bastype; + + clabbrev = clmenus = clshowtype = clkeeplog = cllexmodes = cllogfile = + clnotify = clecho = NULL; + + for (pp = tp->t_pfp->pf_pp; pp != NULL; pp = pp->p_np) { + + /* Set "CL parameter" bit to aid checking in paramset(). + * Also, parse any parameters that need it. (This is necessary + * to get the current values of `logmode' when running in bkg.) + */ + pp->p_flags |= P_CL; + parse_clmodes (pp, &pp->p_valo); + + /* Limit the strcmp's to only those params with the right + * basic time to speed this up a bit. Be careful when adding + * new entries that they go into the right type. + * For now, at least, ignore all list params. + */ + if (pp->p_type & PT_LIST) + continue; + + bastype = pp->p_type & OT_BASIC; + name = pp->p_name; + if (bastype == OT_STRING) { + if (!strcmp (name, "mode")) + firstask->t_modep = pp; + else if (!strcmp (name, "logfile")) + cllogfile = pp; + } else if (bastype == OT_BOOL) { + if (!strcmp (name, "menus")) + clmenus = pp; + else if (!strcmp (name, "showtype")) + clshowtype = pp; + else if (!strcmp (name, "keeplog")) + clkeeplog = pp; + else if (!strcmp (name, "lexmodes")) + cllexmodes = pp; + else if (!strcmp (name, "abbreviate")) + clabbrev = pp; + else if (!strcmp (name, "notify")) + clnotify = pp; + else if (!strcmp (name, "echo")) + clecho = pp; + } + } +} + + +#define NEXT_TOKEN while (*ip == ' ' || *ip == '\t' || *ip == '\n') ip++; \ + if (!*ip) break; +#define NEXT_WHITE while (*ip != ' ' && *ip != '\t' && *ip != '\0') ip++; + +/* PARSE_CLMODES -- Called whenever a CL parameter is set at runtime. A + * few of the CL parameters need to be parsed and internal variables set + * appropriately. Tokens in the parameter strings are white-space + * delimited. + */ +void +parse_clmodes ( + struct param *pp, + struct operand *newval +) +{ + register char *name, *ip; + + name = pp->p_name; + + if (!strcmp (name, "logmode")) { + ip = newval->o_val.v_s; + while (*ip) { + NEXT_TOKEN; + + /* Check the next token; only a few matching characters + * are needed. Default values are set elsewhere, so we + * check for all possibilities here. + */ + if (strncmp (ip, "commands", 5) == 0) + cllogmode |= LOG_COMMANDS; + else if (strncmp (ip, "nocommands", 5) == 0) + cllogmode &= ~LOG_COMMANDS; + + else if (strncmp (ip, "background", 5) == 0) + cllogmode |= LOG_BACKGROUND; + else if (strncmp (ip, "nobackground", 5) == 0) + cllogmode &= ~LOG_BACKGROUND; + + else if (strncmp (ip, "errors", 5) == 0) + cllogmode |= LOG_ERRORS; + else if (strncmp (ip, "noerrors", 5) == 0) + cllogmode &= ~LOG_ERRORS; + + else if (strncmp (ip, "trace", 5) == 0) + cllogmode |= LOG_TRACE; + else if (strncmp (ip, "notrace", 5) == 0) + cllogmode &= ~LOG_TRACE; + + else if (*ip != '\0') + eprintf ("unrecognized logging set-option `%s'\n", ip); + + NEXT_WHITE; + } + + } else if (!strcmp (name, "logfile")) { + reset_logfile(); + + } else if (!strcmp (name, "epinit")) { + ip = newval->o_val.v_s; + while (*ip) { + NEXT_TOKEN; + + if (strncmp (ip, "standout", 5) == 0) + ep_standout = YES; + else if (strncmp (ip, "nostandout", 5) == 0) + ep_standout = NO; + else if (strncmp (ip, "showall", 5) == 0) + ep_showall = YES; + else if (strncmp (ip, "noshowall", 5) == 0) + ep_showall = NO; + else if (*ip != '\0') + eprintf ("unrecognized eparam set-option `%s'\n", ip); + + NEXT_WHITE; + } + + } else if (!strcmp (name, "ehinit")) { + ip = newval->o_val.v_s; + while (*ip) { + NEXT_TOKEN; + + if (strncmp (ip, "verify", 5) == 0) + eh_verify = YES; + else if (strncmp (ip, "noverify", 5) == 0) + eh_verify = NO; + else if (strncmp (ip, "standout", 5) == 0) + eh_standout = YES; + else if (strncmp (ip, "nostandout", 5) == 0) + eh_standout = NO; + else if (strncmp (ip, "bol", 3) == 0) + eh_bol = YES; + else if (strncmp (ip, "eol", 3) == 0) + eh_bol = NO; + else if (*ip != '\0') + eprintf ("unrecognized ehistory set-option `%s'\n", ip); + + NEXT_WHITE; + } + + } else if (!strcmp (name, "szprcache")) { + /* Change the size of the process cache. + */ + pr_setcache (newval->o_val.v_i); + + } else if (!strcmp (name, "mode")) { + /* Menu mode is not permitted at the CL level. + */ + char *index(); + + if (index (newval->o_val.v_s, 'm') != NULL) + cl_error (E_UERR, + "menu mode is permitted only for packages and tasks"); + } +} + + +/* ABBREV -- Determine if abbreviations are allowed. Abbreviations are + * only allowed if the currentask is interactive (or batch), or if the + * currentask is a builtin and the previous task is interactive (or batch), + * regardless of value of clabbrev parameter. + */ +int +abbrev (void) +{ + /* Enable abbreviations everywhere for now. + int cflags = currentask->t_flags; + int pflags = prevtask->t_flags; + + if (clabbrev == NULL) + return (NO); + if ((clabbrev->p_valo.o_type & (OT_UNDEF|OT_INDEF)) || + !clabbrev->p_valo.o_val.v_i) + return (NO); + + if (cflags & (T_INTERACTIVE|T_BATCH)) + return (YES); + if ((cflags & T_BUILTIN) && (pflags & (T_INTERACTIVE|T_BATCH))) + return (YES); + + return (NO); + */ + + return (YES); +} + +/* POFFSET--push an offset in an array for a later reference. + */ +void +poffset (int off) +{ + n_indexes++; + push (off); + offsetmode(1); +} |