
	/* this is the program that takes a user request file, 
	          1. checks it to see that there are no inconsistencies 
	                    or invalid parameters specified, 
	          2. adds some information about the instrument if it's
	                    not already there, 

          *** I have removed these two functions that it used to do: ***
	        (   3. moves the (new) file into the target directory          )
	        (   4. appends the file name to the NEW file in the target dir )
	      the old code can be restored by #define MOVE_VALID.  MWR 12/9/91.

	   if the request file is OK, the program exits with code 0 and
	   prints the new request file's name to stdout; otherwise,
	   it exits with a non-zero code.  The original request file
	   is NOT CHANGED; it is the caller's responsibility to delete it,
	   or move it somewhere.

	   error messages get printed to stderr, non-error diagnostics go
	   to stdout. The diagnostics can be suppressed by compiling without
	   the VERBOSE flag, but if suppressed, scripts that depend on the
	   output will break!

	   many, many messages will be printed to stdout as program progresses
	   if compiled with the DEBUG flag.
		3/26/92 - added pointerr to accepted procedures - RRT
	        4/4/92  - added JDSTART keyword (and fixed a bug) - MWR
	*/

    /*
     *   added NUMPERNI keyword, default value: 1     2/26/93 MWR 
     */

#include <stdio.h>
#include <time.h>
#include <string.h>
#include "bait.h"
#include "pf.h"

#undef MOVE_VALID			/* if defined, valid request files are moved */
                  			/*   to RQS_DIR automatically.  I don't want */
                 			/*   to do this as of 12/9/91. MWR  */

#define KEY_LEN         20	/* max length of keyword */
#define OPTIONAL         0	/* this one is optional */
#define REQUIRED         1	/* this line is required in input */
#define EXACT            2	/* if specified, value must match exactly */
#define FORBIDDEN        3	/* user must NOT specify this! */
#define CONFIG           1	/* take value from config file */
#define DEFAULT          0	/* don't take default from config file */

struct value_pair {
	char keyword[KEY_LEN];	/* the keyword (all capital letters) */
	int  type;				/* PF_BOOLEAN, INT, PF_DOUBLE, PF_STRING */
	int  required;			/* tells if the line MUST appear in input to */
	              			/*   this program, and if value must match */
	int  source;			/* if CONFIG, get value from configuration file */
	int  first_only;		/* if 1, then only take value from first header */
	char value[PF_LEN+1];	/* the default value, expressed AS A PF_STRING! */
};

	/* note that some of the default values are to be found in the
	   configuration file, rather than explicitly listed here. */

static struct value_pair parameter[] =  {
	{ "OBSERVAT", PF_STRING   , EXACT    , CONFIG   , 1, ""           },
	{ "INSTRUME", PF_STRING   , EXACT    , CONFIG   , 1, ""           },
	{ "OBJECT"  , PF_STRING   , REQUIRED , DEFAULT  , 0, ""           },
	{ "OBSERVER", PF_STRING   , REQUIRED , DEFAULT  , 1, ""           },
	{ "SENDMAIL", PF_BOOLEAN  , OPTIONAL , DEFAULT  , 1, "F"          },
	{ "MAILADDR", PF_STRING   , OPTIONAL , DEFAULT  , 1, ""           },
	{ "EXPTIME" , PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "IMAGETYP", PF_STRING   , OPTIONAL , DEFAULT  , 0, "'object'"   },

	/*
	 * this line should go in when everything is working 
	{ "PROCEDUR", PF_STRING   , FORBIDDEN, DEFAULT  , 0, "'photo_proc'"},
	 * the following is just for test purposes 
	 */

	{ "PROCEDUR", PF_STRING   , OPTIONAL,  DEFAULT  , 0, "'photo_proc'"},
	{ "RA"      , PF_STRING   , REQUIRED , DEFAULT  , 0, ""           },
	{ "DEC"     , PF_STRING   , REQUIRED , DEFAULT  , 0, ""           },
	{ "EPOCH"   , PF_DOUBLE   , REQUIRED , DEFAULT  , 0, ""           },
	{ "MAGNITUD", PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "SIGTONOI", PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "FILTERS" , PF_STRING   , REQUIRED , DEFAULT  , 0, ""           },

	/* added these two 3/25/92 MWR */
	{ "SEEING"  , PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "TWINKLE" , PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },

	{ "ZEROCOR" , PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "DARKCOR" , PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "FIXPIX"  , PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "CRFLAG"  , PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "FLATCOR" , PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "FRINGCOR", PF_INTEGER  , OPTIONAL , DEFAULT  , 0, "0"          },
	{ "CCDSEC"  , PF_STRING   , OPTIONAL , CONFIG   , 0, ""           },
	{ "DAYSTART", PF_STRING   , REQUIRED , DEFAULT  , 1, ""           },
	{ "DAYEND"  , PF_STRING   , REQUIRED , DEFAULT  , 1, ""           },
	{ "UT-START", PF_STRING   , OPTIONAL , DEFAULT  , 1, ""           },
	{ "JDSTART" , PF_DOUBLE   , OPTIONAL , DEFAULT  , 1, ""           },
	{ "LSTSTART", PF_STRING   , OPTIONAL , DEFAULT  , 1, ""           },
	{ "LSTEND"  , PF_STRING   , OPTIONAL , DEFAULT  , 1, ""           },
	{ "NUM-OBS" , PF_INTEGER  , OPTIONAL , DEFAULT  , 1, "1"          },
	{ "NUMPERNI", PF_INTEGER  , OPTIONAL , DEFAULT  , 1, "1"          },
	{ "PRIORITY", PF_INTEGER  , OPTIONAL , DEFAULT  , 1, "10"         },
	{ "GURA"    , PF_STRING   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "GUDEC"   , PF_STRING   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "GUEPOCH" , PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "GUMAG"   , PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, ""           },
	{ "GUIDEMOD", PF_STRING   , OPTIONAL , DEFAULT  , 0, "'guide'"    },
	{ "MOONPHAS", PF_DOUBLE   , OPTIONAL , DEFAULT  , 1, "1.0"        },
	{ "MOONDIST", PF_DOUBLE   , OPTIONAL , DEFAULT  , 1, "15.0"       },
	{ "MAXAIRMA", PF_DOUBLE   , OPTIONAL , DEFAULT  , 0, "3.0"        },

	/* this entry simply marks the end of the list of structures */
	{ "END"                                  }    
};

extern void exit( /* int status */ );

	/* private functions */
static int mkname( /* char *s */ );
static int mkid( /* char *s */ );
static int insert_reqid( /* pf_handle pf_out, int num_hdr, char *rqsfile */ );
static process_header( /* pf_handle pf_in, pf_handle pf_out */ );
static check( /* char *keyword, char *value, pf_handle pf_config */ );
static equal_val( /* char *user_val, char *comp_val, int type */ );
static equal_type( /* int type1, int type2 */ );
static int check_times( /* int num_pairs */ );
static check_dates( /* int num_pairs */ );
static int check_mail( /* int num_pairs */ );
static double assign_prob( /* int num_pairs */ );

static int daystart, dayend, ut_start, num_obs, priority;
char *progname = "accepter";
static char rqsfile[80];

	/* usage:             
	 *               accepter filename 
	 *   or
	 *               accepter help
	 *
	 * exits with code 0 if all went according to plan
	 *            code -1 if there was an error 
	 */


main(argc, argv)
int argc;
char *argv[];
{
	int i, num_hdr;
	pf_handle pf_in, pf_out;
	char fname[80], object[80], last_object[80];
	FILE *fp;

	strcpy(last_object, "");

	if (argc != 2) {
		fprintf(stderr, "usage: %s filename \n", progname);
		exit(-1);
	}
	if (strcmp(argv[1], "help") == 0) {
		fprintf(stderr, "accepter filename - checks user request, puts in %s if valid\n", 
				TARGET_DIR);
		exit(0);
	}

	if ((num_hdr = pf_headers(argv[1])) < 1) {
		fprintf(stderr, "%s: file %s is not a pseudo-FITS header file\n",
				progname, argv[1]);
		exit(-1);
	}
	if (mkname(rqsfile) < 0) {
		fprintf(stderr, "%s: can't create unique file name \n", progname);
		exit(-1);
	}
	for (i = 1; i <= num_hdr; i++) {
		if ((pf_in = pf_open(argv[1], i, PF_EXIST)) < 0) {
			fprintf(stderr, "%s: error opening header %d in file %s\n",
					progname, i, argv[1]);
		}
		if ((pf_out = pf_open(rqsfile, i, PF_CREATE)) < 0) {
			fprintf(stderr, "%s: can't open output file %s\n", 
						progname, rqsfile);
			exit(-1);
		}
		if (pf_getval(pf_in, "OBJECT", object) == NULL)
			strcpy(object, last_object);
		else
			strcpy(last_object, object);
		fprintf(stdout, "%s: checking file %-15s, object ..%s..\n", 
					progname, argv[1], object);
		if (process_header(i, pf_in, pf_out) < 0) {
			if (pf_close(pf_in) < 0) {
				fprintf(stderr, "%s: error closing input file %s\n", 
						progname, argv[1]);
			} 
			else if (pf_close(pf_out) < 0) {
				fprintf(stderr, "%s: error closing output file %s\n", 
						progname, rqsfile);
			}
			else if (unlink(rqsfile) < 0) {
				fprintf(stderr, "%s: error unlinking output file %s\n", 
						progname, rqsfile);
			}
			else {
				fprintf(stdout, "%s: error in input file %s, header %d. No output file created.\n", progname, argv[1], i);
			}
			exit(-1);
		}
		if (pf_close(pf_in) < 0) {
			fprintf(stderr, "error closing input file %s\n", argv[1]);
			exit(-1);
		}
		else if (pf_close(pf_out) < 0) {
			fprintf(stderr, "error closing output file %s\n", rqsfile);
			exit(-1);
		}
	}
	
#ifdef MOVE_VALID
	/* now move the verified request file into the target directory */
	sprintf(fname, "%s%s", TARGET_DIR, rqsfile);
	if (link(rqsfile, fname) < 0) {
		fprintf(stderr, "error linking %s to %s \n", rqsfile, fname);
		exit(-1);
	}
	if (unlink(argv[1]) < 0) {
		fprintf(stderr, "error unlinking %s \n", rqsfile);
		exit(-1);
	}

	/* and finally, append the name of this new file to the list of
	   new targets for the list generator to use */
	sprintf(fname, "%s%s", TARGET_DIR, NEWREQ_FILE);
	if ((fp = fopen(fname, "a")) == NULL) {
		fprintf(stderr, "can't append file name %s to file %s\n",
				rqsfile, fname);
		exit(-1);
	} 
	fprintf(fp, "%s\n", rqsfile);
	fclose(fp);
#endif

	fprintf(stdout, "%s:          file %-15s, object ..%s.. is OK\n", 
			progname, argv[1], object);
    printf("%s\n", rqsfile); 
	exit(0);
}

	/* this function inserts a line that looks like:
	              REQID     =  'FebS4s342'
	   which will be used to identify this particular header/observation
	   request in all other programs. */

static int
insert_reqid(pf_out, num_hdr)
pf_handle pf_out;
int num_hdr;
{
	char buf[80], idstr[PF_LEN + 1], *p;
	int len;

	if (num_hdr == 1) {
		/* strip off the trailing ".rqs" from the filename */
/***
		if ((p = strchr(rqsfile, '.')) != NULL)
			len = p - rqsfile;
		else
			len = strlen(rqsfile);
****/
		strncpy(buf, rqsfile, strlen(rqsfile) - 4);
		buf[strlen(rqsfile) - 4] = '\0';
		pf_strtoval(buf, idstr);
	}
	else {
		if (mkid(buf) < 0) {
			fprintf(stderr, "%s: can't create unique observation ID string\n",
						progname);
			return(-1);
		}
		pf_strtoval(buf, idstr);
	}
	if (pf_putval(pf_out, "REQID", idstr) == NULL) {
		fprintf(stderr, "%s: can't put ID string into output file %s\n",
				progname, rqsfile);
		return(-1);
	}
	return(0);
}

	/* create a unique file name of the form
	              Decxxxx.rqs
	   where the first three letters are the current month,
	   and the x's are replaced by some characters.  
	   return 0 if OK, or -1 if something goes wrong. */

static int
mkname(s)
char *s;
{
	if (mkid(s) < 0)
		return(-1);
	strcat(s, RQS_EXTENSION);
	return(0);
}

	/* create a unique name of the form
	              Decxxxx
	   where the first three letters are the current month,
	   and the x's are replaced by some characters.  
	   Specifically, 
	      the first letter corresponds to the current day-of-month
	      the second                                  hour
	      the third                                   minute
	      the fourth                                  second

	   return 0 if OK, or -1 if something goes wrong. */

static int
mkid(s)
char *s;
{
	int i;
	char buf[80], *mktemp();
	long ltime, time();
	struct tm *tm;

	ltime = time((long *) 0);
	strcpy(buf, ctime(&ltime));
	s[0] = buf[4];
	s[1] = buf[5];
	s[2] = buf[6];
	tm = gmtime(&ltime);
	if (tm->tm_mday <= 26)
		s[3] = 'a' + tm->tm_mday - 1;
	else
		s[3] = 'A' + tm->tm_mday - 27;
	s[4] = 'a' + tm->tm_hour;
	if (tm->tm_min < 26)
		s[5] = 'a' + tm->tm_min;
	else if (tm->tm_min < 52)
		s[5] = 'A' + tm->tm_min - 26;
	else
		s[5] = '0' + tm->tm_min - 52; 
	if (tm->tm_sec < 26)
		s[6] = 'a' + tm->tm_sec;
	else if (tm->tm_sec < 52)
		s[6] = 'A' + tm->tm_sec - 26;
	else
		s[6] = '0' + tm->tm_sec - 52; 
	s[7] = '\0';
	return(0);
}

		
	/* go through the input header, making sure that all required keywords
	   are present, that the values supplied are valid, and place all
	   the appropriate information (as well as all comments) into the
	   output header.

	   returns 0 if the input header is valid, or -1 if there is 
	   invalid information anywhere. If an error occurs because of information
	   the user has placed in the header, an error message of the form
			"  error: blah blah blah"
	   is printed to stdout; this can be sent back to the user in an 
	   E-mail message so that he knows how to fix the problem.  But if
	   an error occurs in some internal routine, like 'pf_putval' or
	   trying to open the CONFIG_FILE, the message is sent to stderr.
	   The user shouldn't know about these, only the telescope admin. */

static
process_header(num_hdr, pf_in, pf_out)
int num_hdr;
pf_handle pf_in, pf_out;
{
	pf_handle pf_config;
	int i, num_pairs;
	char comp_val[PF_LEN + 1], user_val[PF_LEN + 1], buf[PF_LEN + 1], *vp;
	double prob;

	if ((pf_config = pf_open(CONFIG_FILE, 1, PF_EXIST)) < 0) {
		fprintf(stderr, "process_header: can't open config file ..%s..\n", 
				CONFIG_FILE);
		return(-1);
	}

	if (insert_reqid(pf_out, num_hdr) < 0) {
		fprintf(stderr, "process_header: can't insert_reqid \n");
		return(-1);
	}

	for (num_pairs = 0; strncmp(parameter[num_pairs].keyword, "END", 3) != 0;
										num_pairs++)
		;

	for (i = 0; i < num_pairs; i++) {

#ifdef DEBUG
		printf("working on keyword %s \n", parameter[i].keyword);
#endif

		if (parameter[i].required == FORBIDDEN) {
			if (pf_getval(pf_in, parameter[i].keyword, user_val) != NULL) {
#ifdef VERBOSE
				printf("  error: FORBIDDEN keyword '%s' was specified\n",
						parameter[i].keyword);
#endif
				return(-1);
			}
			if (pf_putval(pf_out, parameter[i].keyword, parameter[i].value) == NULL) {
				fprintf(stderr, "process_header: couldn't pf_putval for %s\n",
							parameter[i].keyword);
				return(-1);
			}
			continue;
		}
			
		if (parameter[i].source == CONFIG) {
			if (pf_getval(pf_config, parameter[i].keyword, comp_val) == NULL) {
				fprintf(stderr, "process_header: config file missing keyword ..%s..\n",
						parameter[i].keyword);
				pf_close(pf_config);
			 	return(-1);
			}
		}
		else
			strcpy(comp_val, parameter[i].value);

		/* get the user value for this field - if this is not the first 
		   header, though, use the first header's value if appropriate. */
		if ((num_hdr > 1) && (parameter[i].first_only == 1))
			strcpy(user_val, parameter[i].value);
		else if (pf_getval(pf_in, parameter[i].keyword, user_val) == NULL) {
			if ((num_hdr == 1) && (parameter[i].required == REQUIRED)) {
#ifdef VERBOSE
				printf("  error: required parameter ..%s.. not supplied\n",
						parameter[i].keyword);
#endif
				pf_close(pf_config);
				return(-1);
			}
			if (num_hdr == 1) 
				strcpy(user_val, "");
			else
				strcpy(user_val, parameter[i].value);
		}
		else {
			if (equal_type(pf_valtype(user_val), parameter[i].type) < 0) {
#ifdef VERBOSE
				printf("  error: user val for ..%s.. of wrong type\n",
						parameter[i].keyword);
#endif
				pf_close(pf_config);
				return(-1);
			}
		}

#ifdef DEBUG
		printf("  user value ..%s..  comp value ..%s..\n", user_val, comp_val);
#endif
		/* now that we have the user's value for the keyword and/or the
		   computer value (from config file or init structure), we can
		   figure out what to do, depending on the parameter[i].required */
		if (parameter[i].required == EXACT) {
			if (strcmp(user_val, "") == 0) {
				vp = comp_val;
			}
			else if (equal_val(user_val, comp_val, parameter[i].type) != 0) {
#ifdef VERBOSE
				printf("  error: user value ..%s.. doesn't exactly match ..%s..\n",
							user_val, comp_val);
#endif
				pf_close(pf_config);
				return(-1);
			}
			vp = user_val;
		}
		else if (parameter[i].required == REQUIRED) {
			if (strcmp(user_val, "") == 0) {
#ifdef VERBOSE
				printf("  error: required parameter ..%s.. not supplied\n",
						parameter[i].keyword);
#endif
				pf_close(pf_config);
				return(-1);
			}
			vp = user_val;
		}
		else if (parameter[i].required == OPTIONAL) {
			if (strcmp(user_val, "") == 0)
				vp = comp_val;
			else
				vp = user_val;
		}
		strcpy(parameter[i].value, vp);

		/* okay, now, if the string pointed to by 'vp' is not empty
		   (possible for optional keywords the user didn't specify)
		   check to make sure that the type of the value is correct,
		   then put the (keyword, value) pair into the output header */
		if (strcmp(vp, "") == 0)
			continue;
		if (equal_type(pf_valtype(vp), parameter[i].type) < 0) {
#ifdef VERBOSE
			printf("  error: type mismatch for parameter ..%s..\n",
					parameter[i].keyword);
#endif
			pf_close(pf_config);
			return(-1);
		}
		if (check(parameter[i].keyword, vp, pf_config) < 0) {
#ifdef VERBOSE
			printf("  error: check routine rejects value ..%s.. for parameter %s\n",
					vp, parameter[i].keyword);
#endif
			pf_close(pf_config);
			return(-1);
		}
		if (pf_putval(pf_out, parameter[i].keyword, vp) == NULL) {
			fprintf(stderr, "process_header: pf_putval() fails on keyword ..%s..\n",
					parameter[i].keyword);
			pf_close(pf_config);
			return(-1);
		}
	}

	/* check to see if user said to send mail, but gave no E-mail address */
	if (check_mail(num_pairs) != 0) {
#ifdef VERBOSE
			printf("  error: no E-mail address given, but SENDMAIL=T\n");
#endif
			pf_close(pf_config);
			return(-1);
	}

	/* make sure that either EXPTIME or MAGNITUD was specified */
	if (check_times(num_pairs) != 0) {
#ifdef VERBOSE
		printf("  error: neither EXPTIME nor MAGNITUD given \n");
#endif
		return(-1);
	}

	/* check to see that the DAYSTART and DAYEND values make sense,
	   and that DAYEND comes after DAYSTART. Also make sure that
	   if either LSTSTART or LSTEND appears, so does the other. */
	if (check_dates(num_pairs) != 0) {
#ifdef VERBOSE
		printf("  error: invalid DAYSTART/END or LSTSTART/END values\n");
#endif
		pf_close(pf_config);
		return(-1);
	}

	/* assign a probability PER NIGHT for the observation, based on the 
	   number of images needed and the range of UT dates. */
	if ((prob = assign_prob(num_pairs)) < 0) {
#ifdef VERBOSE
		printf("  error: can't assign PROBABIL; error in dates?\n");
#endif
		pf_close(pf_config);
		return(-1);
	}
	pf_doubletoval(prob, buf);
	if (pf_putval(pf_out, "PROBABIL", buf) == NULL) {
		fprintf(stderr, "process_header: pf_putval on PROBABIL yields error\n");
		return(-1);
	}

	/* copy any comments from the original header */	
	while (pf_getcomment(pf_in, user_val) != NULL)
		if (pf_putcomment(pf_out, user_val) == NULL) {
			fprintf(stderr, "process_header: can't put comment ..%s..\n", 
					user_val);
			return(-1);
		}
 
	if (pf_close(pf_config) < 0) {
		fprintf(stderr, "process_header: can't close config file %s\n", 
				CONFIG_FILE);
		return(-1);
	}
	return(0);
}

	/* well, you just can't get around having some "manual" checking
	   of values, to make sure that dates are valid dates, times are
	   valid times, etc. Sigh. There's no graceful way I can think 
	   to do this.... 

	   Be careful not to screw up the passed string parameter 'value'
	   in here....

	   print diagnostic messages to stdout.
	
	   return 0 if the value is OK, or -1 if it's invalid. */

static
check(keyword, value, pf_config)
char *keyword, *value;
pf_handle pf_config;
{
	int inum, cnum;
	double dnum, d1, d2, d3;
	char string[PF_LEN + 1], buf[PF_LEN + 1];

	if (strcmp(keyword, "EXPTIME") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for EXPTIME ..%s..\n", value);
			return(-1);
		}
		if (dnum < 0) {
			printf("bad value for EXPTIME ..%lf..\n", dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "IMAGETYP") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for IMAGETYP ..%s..\n", value);
			return(-1);
		}
		str_to_lower(string);
		if ((strcmp(string, "object") != 0) && (strcmp(string, "zero") != 0) &&
		    (strcmp(string, "dark")   != 0) && (strcmp(string, "flat") != 0) &&
		    (strcmp(string, "comparison") != 0)) {
			printf("invalid value for IMAGETYP ..%s..\n", string);
			return(-1);
		}
	}
	else if (strcmp(keyword, "PROCEDUR") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for IMAGETYP ..%s..\n", value);
			return(-1);
		}
		str_to_lower(string);
		if ((strcmp(string, "photo_proc") != 0) && 
		    (strcmp(string, "photo") != 0) && 
			(strcmp(string, "photoc") != 0) && 
			(strcmp(string, "pointerr") != 0) && 
		    (strcmp(string, "photo_all") != 0)) {
			printf("invalid value for PROCEDUR ..%s..\n", string);
			return(-1);
		}
	}
	else if (strcmp(keyword, "RA") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for RA ..%s..\n", value);
			return(-1);
		}
		if (is_ra(string) != 0) {
			printf("bad format for RA ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	/* check to see that Dec is valid format, and that it is within the 
	   telescope Dec limits */
	else if (strcmp(keyword, "DEC") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for DEC ..%s..\n", value);
			return(-1);
		}
		if (is_dec(string) != 0) {
			printf("bad format for DEC ..%s..\n", string);
			return(-1);
		}
		if (val_dec2deg(value, &dnum) < 0) {
			printf("bad format for DEC ..%s..\n", string);
			return(-1);
		}
		if (pf_getval(pf_config, "TELLIMN", buf) < 0) {
			printf("can't get value for TELLIMN from config file\n");
			return(-1);
		}
		if (pf_valtodouble(buf, &d1) < 0) {
			printf("bad value for TELLIMN from config file ..%s..\n", buf);
			return(-1);
		}
		if (pf_getval(pf_config, "TELLIMS", buf) < 0) {
			printf("can't get value for TELLIMS from config file\n");
			return(-1);
		}
		if (pf_valtodouble(buf, &d2) < 0) {
			printf("bad value for TELLIMS from config file ..%s..\n", buf);
			return(-1);
		}
		if ((dnum > d1) || (dnum < d2)) {
#ifdef DEBUG
			fprintf(stderr, "%s.check: Dec %lf out of range (%.1lf, %.1lf)\n",
					progname, dnum, d1, d2);
#endif
			return(-1);
		}
	}
	else if (strcmp(keyword, "EPOCH") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for EPOCH ..%s..\n", value);
			return(-1);
		}
		if (dnum < 0) {
			printf("invalid value for EPOCH ..%lf..\n", dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "MAGNITUD") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for MAGNITUD ..%s..\n", value);
			return(-1);
		}
	}	
	else if (strcmp(keyword, "SIGTONOI") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for SIGTONOI ..%s..\n", value);
			return(-1);
		}
		if (dnum <= 0) {
			printf("invalid value for SIGTONOI ..%lf..\n", dnum);
			return(-1);
		}
	}	
	else if (strcmp(keyword, "FILTERS") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for FILTERS ..%s..\n", value);
			return(-1);
		}
		if (is_filter(string) != 0) {
			printf("invalid value for FILTERS ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "SEEING") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for SEEING ..%s..\n", value);
			return(-1);
		}
		if (dnum < 0) {
			printf("invalid value for SEEING ..%lf..\n", dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "TWINKLE") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for TWINKLE ..%s..\n", value);
			return(-1);
		}
		if (dnum < 0) {
			printf("invalid value for TWINKLE ..%lf..\n", dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "CCDSEC") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for CCDSEC ..%s..\n", value);
			return(-1);
		}
		if (is_ccdsec(string) != 0) {
			printf("invalid value for CCDSEC ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}			
	else if (strcmp(keyword, "DAYSTART") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for DAYSTART ..%s..\n", value);
			return(-1);
		}
		if (is_date(string) != 0) {	
			printf("invalid value for DAYSTART ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "DAYEND") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for DAYEND ..%s..\n", value);
			return(-1);
		}
		if (is_date(string) != 0) {	
			printf("invalid value for DAYEND ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "JDSTART") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for JDSTART ..%s..\n", value);
			return(-1);
		}
		if (dnum < 0.0) {	
			printf("invalid value for JDSTART ..%lf..\n", dnum);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "UT-START") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for UT-START ..%s..\n", value);
			return(-1);
		}
		if (is_time(string) != 0) {	
			printf("invalid value for UT-START ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "LSTSTART") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for LSTSTART ..%s..\n", value);
			return(-1);
		}
		if (is_time(string) != 0) {	
			printf("invalid value for LSTSTART ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "LSTEND") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for LSTSTART ..%s..\n", value);
			return(-1);
		}
		if (is_time(string) != 0) {	
			printf("invalid value for LSTEND ..%s..\n", string);
			return(-1);
		}
		else
			return(0);
	}
	else if (strcmp(keyword, "NUM-OBS") == 0) {
		if (pf_valtoint(value, &inum) < 0) {
			printf("bad value for NUM-OBS ..%s..\n", value);
			return(-1);
		}
		if (pf_getval(pf_config, "MAXOBS", string) < 0) {
			printf("can't get value for MAXOBS from config file \n");
			return(-1);
		}
		if (pf_valtoint(string, &cnum) < 0) {
			printf("invalid value for MAXOBS from config file ..%s..\n", string);
			return(-1);
		}
		if ((inum <= 0) || (inum > cnum)) {
			printf("invalid value for NUM-OBS ..%d.., not in range %d - %d\n", 
					inum, 1, cnum);
			return(-1);
		}
	}
	/* note that the MINPRIOR value is the largest number that the priority
	   field is allowed to have, and the MAXPRIOR is the smallest; this 
	   sounds wrong, but works because the most important things have
	   priorities which are small (like -1). */
	else if (strcmp(keyword, "PRIORITY") == 0) {
		if (pf_valtoint(value, &inum) < 0) {
			printf("bad value for PRIORITY ..%s..\n", value);
			return(-1);
		}
		if (pf_getval(pf_config, "MINPRIOR", string) < 0) {
			printf("can't get value for MINPRIOR from config file \n");
			return(-1);
		}
		if (pf_valtoint(string, &cnum) < 0) {
			printf("invalid value for MINPRIOR from config file ..%s..\n", 
					string);
			return(-1);
		}
		if (inum > cnum) {
			printf("PRIORITY value %d is > max value %d\n", inum, cnum);
			return(-1);
		}
		if (pf_getval(pf_config, "MAXPRIOR", string) < 0) {
			printf("can't get value for MAXPRIOR from config file \n");
			return(-1);
		}
		if (pf_valtoint(string, &cnum) < 0) {
			printf("invalid value for MAXPRIOR from config file ..%s..\n", 
					string);
			return(-1);
		}
		if (inum < cnum) {
			printf("PRIORITY value %d is < min value %d\n", inum, cnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "GUIDEMOD") == 0) {
		if (pf_valtostr(value, string) < 0) {
			printf("bad value for GUIDEMOD ..%s..\n", value);
			return(-1);
		}
		str_to_lower(string);
		if ((strcmp(string, "noguide") != 0) && (strcmp(string, "guide") != 0) &&
		    (strcmp(string, "tryguide") != 0)) {
			printf("invalid value for GUIDEMOD ..%s..\n", string);
			return(-1);
		}
	}
	else if (strcmp(keyword, "MOONPHAS") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for MOONPHAS ..%s..\n", value);
			return(-1);
		}
		if ((dnum < 0.0) || (dnum > 1.0)) {
			printf("invalid value for MOONPHAS ..%lf.. not in range 0.0 - 1.0\n", 	
					dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "MOONDIST") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for MOONDIST ..%s..\n", value);
			return(-1);
		}
		if ((dnum < 0.0) || (dnum > 180.0)) {
			printf("invalid value for MOONDIST ..%lf.. not in range 0.0 - 180.0\n", 	
					dnum);
			return(-1);
		}
	}
	else if (strcmp(keyword, "MAXAIRMA") == 0) {
		if (pf_valtodouble(value, &dnum) < 0) {
			printf("bad value for MAXAIRMA ..%s..\n", value);
			return(-1);
		}
		if (dnum < 1.0) {
			printf("invalid value for MAXAIRMA ..%ld..\n", dnum);
			return(-1);
		}
	}
	return(0);
}

	/* return 0 if the values of the two passed strings, when interpreted 
	   as the appropriate type, are the same. Otherwise, return -1. */

static
equal_val(val1, val2, type)
char *val1, *val2;
int type;
{
	char str1[PF_LEN + 1], str2[PF_LEN + 1];
	int inum1, inum2;
	double dnum1, dnum2;
	int bool1, bool2;

	switch (type) {
	case PF_BOOLEAN: 
		if (pf_valtobool(val1, &bool1) < 0)
			return(-1);
		if (pf_valtobool(val2, &bool2) < 0)
			return(-1);
		if (bool1 != bool2)
			return(-1);
		break;
	case PF_INTEGER:
		if (pf_valtoint(val1, &inum1) < 0)
			return(-1);
		if (pf_valtoint(val2, &inum2) < 0)
			return(-1);
		if (inum1 != inum2)
			return(-1);
		break;
	case PF_DOUBLE:
		if (pf_valtodouble(val1, &dnum1) < 0)
			return(-1);
		if (pf_valtodouble(val2, &dnum2) < 0)
			return(-1);
		if (dnum1 != dnum2)
			return(-1);
		break;
	case PF_STRING:
		if (pf_valtostr(val1, str1) < 0)
			return(-1);
		if (pf_valtostr(val2, str2) < 0)
			return(-1);
		if (strcmp(str1, str2) != 0)
			return(-1);
		break;
	default:
		return(-1);
	}
	return(0);
} 	
		
	/* try to match the first data type to the second - return 0 if the 
	   two types are 'equivalent' - meaning 
	          1. that they are either exactly the same, 
	    or
	          2. the first is PF_INTEGER and the second is PF_DOUBLE.

	   Note that if first is PF_DOUBLE and second is PF_INTEGER, the two
	   are NOT considered a good match.

	   return -1 if not a good match. */

static
equal_type(type1, type2)
int type1, type2;
{
	if (type1 == type2)
		return(0);
	if ((type1 == PF_INTEGER) && (type2 == PF_DOUBLE))
		return(0);
	return(-1);
}


	/* this routine checks to see that
	       a. the DAYEND value comes on or after DAYSTART
	       b. the DAYEND value is on or after today (Greenwich time)
	   and
	       c. if either LSTSTART or LSTEND is given, then both are given
	       d. if JDSTART is given, it lies within the DAYSTART/DAYEND range

	   if any of these conditions are not met, it returns -1 and prints
	   some diagnostic to stdout.  otherwise, it returns 0.  */


static
check_dates(num_pairs)
int num_pairs;
{
	int i;
	int start_day, start_month, start_year;
	int end_day, end_month, end_year;
	int cur_day, cur_month, cur_year;
	int lst_s_flag, lst_e_flag, jd_flag;
	double start_jd, end_jd, given_jd, ut;

	lst_s_flag = 0;
	lst_e_flag = 0;
	jd_flag = 0;

	for (i = 0; i < num_pairs; i++) {
		if (strcmp(parameter[i].keyword, "DAYSTART") == 0) {
			if (read_date(parameter[i].value, &start_day, &start_month,
					&start_year) != 0) {
				printf("  invalid date for DAYSTART ..%s..\n", parameter[i].value);
				return(-1);
			}
		}
		if (strcmp(parameter[i].keyword, "DAYEND") == 0) {
			if (read_date(parameter[i].value, &end_day, &end_month, 
					&end_year) != 0) {
				printf("  invalid date for DAYEND ..%s..\n", parameter[i].value);
				return(-1);
			}
		}
		if (strcmp(parameter[i].keyword, "LSTSTART") == 0) {
			if (strcmp(parameter[i].value, "") != 0) 
				lst_s_flag = 1;
		}
		if (strcmp(parameter[i].keyword, "LSTEND") == 0) {
			if (strcmp(parameter[i].value, "") != 0) 
				lst_e_flag = 1;
		}
		if (strcmp(parameter[i].keyword, "JDSTART") == 0) {
			if (strcmp(parameter[i].value, "") != 0) {
				if (pf_valtodouble(parameter[i].value, &given_jd) < 0) {
					printf("  invalid value for JDSTART ..%s..\n", parameter[i].value);
					return(-1);
				}
				jd_flag = 1;
			}
		}
	}

	/* make sure that both of LSTSTART and LSTEND appear, or neither appears */
	if (lst_e_flag + lst_s_flag == 1) {
		printf("  either LSTSTART or LSTEND was specified alone \n");
		return(-1);
	}

	/* make sure that the start date comes before (or is same as) end date */
	if (start_year > end_year) {
		printf("  DAYSTART year %d after DAYEND year %d\n", start_year, end_year);
		return(-1);
	}
	if (start_year == end_year) {
		if (start_month > end_month) {
			printf("  DAYSTART month %d after DAYEND month %d\n", start_month,
						end_month);
			return(-1);
		}
		else if (start_month == end_month) {
			if (start_day > end_day) {
				printf("  DAYSTART day %d after DAYEND day %d\n", start_day,
						end_day);
				return(-1);
			}
		}
	}

	/* now make sure that the end date comes after (or is same as) 
	   the current date in Greenwich time */

	get_today(&cur_day, &cur_month, &cur_year);
	if (cur_year > end_year) {
		printf("  DAYEND year %d before current year %d\n", end_year, cur_year);
		return(-1);
	}
	if (cur_year == end_year) {
		if (cur_month > end_month) {
			printf("  DAYEND month %d before current month %d\n", end_month,
					cur_month);
			return(-1);
		}
		else if (cur_month == end_month) {
			if (cur_day > end_day) {
				printf("  DAYEND day %d before current day %d\n", end_day,
						cur_day);
				return(-1);
			}
		}
	}

	/* make sure that a given JDSTART falls within the DAYSTART/DAYEND 
	   range. */
	if (jd_flag == 1) {
		ut = 0.0;
		get_jd(start_day, start_month, start_year, ut, &start_jd);
		get_jd(end_day, end_month, end_year, ut, &end_jd);
		end_jd += 1.0;
		if (given_jd < start_jd) {
			printf("  JDSTART %.4lf before DAYSTART value %.4lf\n",
					given_jd, start_jd);
			return(-1);
		}
		else if (given_jd > end_jd) {
			printf("  JDSTART %.4lf after (DAYEND + 1) value %.4lf\n",
					given_jd, end_jd);
			return(-1);
		}
	}

	return(0);
} 
	


	/* make sure that if the user specified that E-mail is to be sent to her,
	   that she gave an E-mail address. return 0 if OK, or -1 if an error. */

static int 
check_mail(num_pairs)
int num_pairs;
{
	int i, bool;
	char address[PF_LEN + 1];
	
	bool = FALSE;
	strcpy(address, "");
	for (i = 0; i < num_pairs; i++) {
		if (strcmp(parameter[i].keyword, "SENDMAIL") == 0) {
			if (pf_valtobool(parameter[i].value, &bool) < 0) {
				printf("bad value for SENDMAIL ..%s..\n", 
							parameter[i].value);
				return(-1);
			}
		}
		if (strcmp(parameter[i].keyword, "MAILADDR") == 0) {
			if ((strcmp(parameter[i].value, "") != 0) &&
			    (pf_valtostr(parameter[i].value, address) < 0)) {
				printf("bad value for MAILADDR ..%s..\n", 
							parameter[i].value);
				return(-1);
			}
		}
	}
	if ((bool == TRUE) && (strcmp(address, "") == 0)) {
		printf("SENDMAIL specified, but no MAILADDR given \n");
		return(-1);
	}

	return(0);
}

	/* assign a probability PER NIGHT to the observation - that is, for
	   each night within the UT window specified by the user, the
	   observation will only be made if a random number is less than or
	   equal to the probability. 

	   for now, this routine uses a simple formula:
	                     num of observations
	           prob = --------------------------
	                    num of nights in window

	   but this is probably going to change (especially when we add the
	   multi-image per night capability). */

static double
assign_prob(num_pairs)
int num_pairs;
{
	int num_obs, st_day, st_month, st_year, en_day, en_month, en_year;
	int i, num_days;
	int st_flag, en_flag, num_flag;
	double prob;

	st_flag = en_flag = num_flag = 0;
	for (i = 0; i < num_pairs; i++) {
		if (strcmp(parameter[i].keyword, "DAYSTART") == 0) {
			st_flag = 1;
			if (read_date(parameter[i].value, &st_day, &st_month, &st_year) < 0)
				return(-1);
		}
		if (strcmp(parameter[i].keyword, "DAYEND") == 0) {
			en_flag = 1;
			if (read_date(parameter[i].value, &en_day, &en_month, &en_year) < 0)
				return(-1);
		}
		if (strcmp(parameter[i].keyword, "NUM-OBS") == 0) {
			num_flag = 1;
			if (pf_valtoint(parameter[i].value, &num_obs) < 0)
				return(-1);
		}
	}
	if ((!st_flag) || (!en_flag) || (!num_flag)) {
		printf("either DAYSTART, DAYEND or NUM-OBS not specified\n");
		return(-1);
	}

	if ((num_days = since_1950(en_day, en_month, en_year) - 
				since_1950(st_day, st_month, st_year)) < 0) {
		printf("DAYEND comes before DAYSTART \n");
		return(-1);
	}
	num_days++;					/* since dates are inclusive... */
	if (num_days <= num_obs)
		prob = 1.0;
	else
		prob = ((double)num_obs) / ((double)num_days);
	
	return(prob);
}

	/* return 0 if either MAGNITUD or EXPTIME was given, or -1 otherwise */

static int
check_times(num_pairs)
int num_pairs;
{
	int i, flag;	

	flag = 0;
	for (i = 0; i < num_pairs; i++) {
		if (strcmp(parameter[i].keyword, "EXPTIME") == 0) {
			return(0);
		}
		if (strcmp(parameter[i].keyword, "MAGNITUD") == 0) {
			return(0);
		}
	}
	printf("neither EXPTIME nor MAGNITUD was specified\n");
	return(1);
}
