#!/usr/bin/perl -w
{
#  timecnv.pl				WJS  Jun 97 (mods Apr & Jun 99)
#  Uses defgb to translate julian date <--> gregorian date and/or do
#    time zone conversions.
#  Input is a set of environment variables (see below).  Output is
#    from an defgb command appropriate to do the desired conversions
#  Set environment variable $debug_env_var (currently TIMECNV_DEBUG) 
#    to a file name (stdout or stderr are allowed) to print debugging info
#    to that file.  Setting this also sets debug mode for
#    pid_file_cleanup so that temp files are not cleaned up.  This cleanup
#    switch can also be set independently - see pid_file_cleanup.

$version = "timecnv.pl  version 2.2  25 Aug 2005";
#  25 Aug 2005  v 2.2  WJS
#     Julian_day_since_1_Jan_1970
#           [Begin 2.2]
#  20 Mar 2005  v 2.1  WJS
#     Try to guess at which variable is default for various input menus
#     Bug fix: no such time template as N.
#     Bug fix: wrong identifying string sent to an error message
#           [Begin 2.1]
#   5 Aug 2004	v 2.0  WJS
#	    [Needs pid_file_cleanup package]
#	    [Needs path_info_routines package]
#	    [Needs wjs_web_perl_utilities package]
#	    [Needs cgi-lib.pl.  Got from internet via Chris' pointer]
#	    [Begin 2.0]

  require ("cgi-lib.pl");
  require ("pid_file_cleanup.pl");
  require ("wjs_web_perl_utilities.pl");


#    Following variable names come from Bob's thesaurus
#    (Note 1999 date in comment.  Probably approx dates these
#    values.  WJS Mar 05)
  $official_UTC_suffix = "_UTC";
  $official_local_suffix = "_local";
  $official_year_var = "year";
  $official_month_var = "month";
  $official_day_var = "day";
  $official_time_var = "time";
  $official_yrday_var = "yrday";
  $official_yrday0_var = "yrday0";
#    Next 3 don't... asked for names for first 2; got 3 suggested 
#    sets of names - picked longest (fewest width probs) (9 Sep 99)
#    So, when I got to name #3, I didn't ask (25 Aug 05)
  $official_julyr_var = "julian_year"; 
  $official_julyr0_var = "julian_year0"; 
  $official_cumjulyr0_var = "days_since_1970"; 

  $this_programs_file_spec = $0;
  $debug_env_var = "TIMECNV_DEBUG";
  $pid_file_cleanup_debug_env_var = "PID_FILE_CLEANUP_DEBUG";

  (defined($debug = $ENV{$debug_env_var})) &&
    (  (open (DEBUG_FILE,">$debug")) || &quit ("Bad open/write $debug: $!")  );

  $date = localtime();

#    Set up environment.  Assume .pl routine is in our directory
  $build_opt_env = "./build-opt-env.pl";

#     Hard to tell if do command works.  It's doc'ed to return the last
#     evaluated line of the "done" file.  However, what if file isn't
#     there, or there are compilation errs, etc?  Some testing seems to
#     show that $! is set to "No such file" even if do works.  However,
#     if $! is NOT "No such file", it seems to indicate an appropriate
#     error; eg, "Permission denied".  Wouldn't expect $? to be set
#     and it's not.  $@ had a chance, but isn't set either...
#     Decided to test for read access; otherwise let chips fall...
#	-r does NOT set $! for "Permission denied", so set it up
#     ourselves.  Seems to be set for "file not found"...  Fun w/
#     string/numeric context trying to set $!, too.
#     (Since we're perl "do"-ing, -r is needed rather than -x... I think)
#	Not sure if above "do" comments also refer to "require".  Think so
  $! = 0;
  if ( ! -r $build_opt_env) {
    $msg = ($! == 0) ? "Permission denied" : $!;
    &quit ("Cannot read $build_opt_env: $msg");
  }

  require ($build_opt_env);

#    Directories needing / chomping.  Might be used in either
#    print_form or process_form, or both
  $save_RS = $/;	# RS = record separator; another name for $/
  $/ = "/";	# Set up chomp to remove trailing slashes, if any
  chomp($tmpdir_root = $ENV{"JGTEMP"});
  chomp($tmpdir = $ENV{"USETEMPDIR"});
  chomp($jgofs_root = $ENV{"OPTHOME"});
  chomp($jgdefn = $ENV{"JGSCRIPTDIR"});
  $/ = $save_RS;

#    Directories used by both print_form and process_form
  $bin_dir = "$jgofs_root/bin";		# Where listvar is
  
#    Programs used by both print_form and process_form
  $listvar = "$bin_dir/listvar";
  &check_x_access($listvar);

#    Form action routine
  $form_action_file = $this_programs_file_spec;
#    Assume jg is appropriately defined to OO's httpd server.  In any
#    case, this defn is relative to httpd server's root, not $jgofs_root
  ($form_action_name) = ($form_action_file =~ /.*\/(.+)/);
  $form_action_name || ($form_action_name = $form_action_file); 
  $form_action = $jgdefn ? "$jgdefn/$form_action_name" : $form_action_name;

#    Get input info
#	Form includes lots of stuff including
#      Variable name "on form"		Value
#	path_info		"Original" PATH_INFO (w/html escapes removed)
#	query_string		"Original" QUERY_STRING (w/html escapes removed)
#      See also "File locations" and "Form input fields"
  &ReadParse(*form_info);

#     Main purpose of putting form action routine on form as a hidden
#   variable is to distinguish between use of this program in the
#   "put the form up" and "process the form results" modes.  
#   That's because ReadParse looks at QUERY_STRING for input as well as 
#   at the form.
#     The consistency check is just on general principles - anyone who de-
#   liberately mis-routes things here can change the hidden value as well.
  $after_submit = &get_form_var("form_action","OPT","FILESPEC");
  if ($after_submit) {
      ($after_submit eq $form_action) ||
	 &quit ("Submit action routine should be same as form value"   ,
		"Action = $form_action; Form value = $after_submit"     );
  }

  if ($after_submit) {
     &process_form;
  } else {
     &print_form;
  }

  exit;
}

sub print_form
{

#   Next value must NOT be in "common" section.  PID will change
#   when we are re-entered to process form - whole purpose of $unique
#   is to tie our entrances together
  $unique = $$;

  if ($debug) {
#####
#####	These values well out of date as of v 2.0
#####
    $test_path_info = "//globec.whoi.edu:8081/globec/gb/test/test.html0";
    $test_server = "wjs_server.whoi.edu";
    $test_port = "32767";
    $test_remote_host = "wjs_remote_host.whoi.edu";
    $ENV{"SERVER_NAME"} = $test_server;
    $ENV{"SERVER_PORT"} = $test_port;
    $ENV{"PATH_INFO"} = $test_path_info;
    $ENV{"REMOTE_HOST"} = $test_remote_host;

#     Set up build-opt-env
    $ENV{"OPTHOME"} = "/users/develop_OO";
    $ENV{"MYADDR"} = "$test_server:$test_port";
    $ENV{"DEFAULT_PORT"} = $test_port;
    $ENV{"JGTEMP"} = "/tmp/$test_port";
    $build_opt_env = "/usr/people/wsass/wjs_build-opt-env.pl";

    &printheader;
    print "<pre>";
    foreach $key (sort keys %ENV) {
      print $key, '=', $ENV{$key}, "\n";
    }
    print "</pre>";
  }

#   See form field descriptions and "official variable names"
#   in process_form.
  %official_varname =	( 
			   "in_g_year",		$official_year_var,
			   "in_j_year",		$official_year_var,
			   "in_month",		$official_month_var,
			   "in_day",		$official_day_var,
			   "in_t_hour",		$official_time_var,
			   "in_t_minutes",	$official_time_var,
			   "in_t_hourmin",	$official_time_var,
			   "in_t_hrminsecs",	$official_time_var,
			   "in_j_name",		$official_yrday_var 
			);
  %unofficial_varname = ( 
			   "in_j_name",		$official_julyr_var, 
			   "in_t_hour",		"hour",
			   "in_t_minutes",	"minute",
			   "in_t_seconds_combo","second",
			   "in_t_seconds_sep",	"second"
			);

  ($obj = $ENV{"OBJECT"}) || &quit ("No OBJECT info reached this routine");
  ($dispobj = $ENV{"DISPOBJ"}) ||
			&quit ("No DISPOBJ info reached this routine");

#   Send along PATH_INFO and QUERY_STRING.  Values from $build_opt_env
#   not always exactly what we want.  Also, we may eventually want
#   extended URL stuff.  If we don't have a PATH_INFO, odds are excellent
#   that we are dead.  However, for all we know, $build_opt_env is smarter
#   than we are (as of Jul 99, it needs PATH_INFO, so this is moot), so
#   struggle a little bit
  $path_info = $ENV{"PATH_INFO"};
  $path_info || ($path_info = $ENV{"OBJEXT"});
  $path_info || ($path_info = $ENV{"OBJECT"});
  $path_info || &quit ("No object information reached this program");
  (defined ($query_string = $ENV{"QUERY_STRING"})) || ($query_string = "");

  ($remote_host) = ($tmpdir =~ m"^$tmpdir_root/(.+)$");
  $remote_host  || ($remote_host = "?");

#    Directories used by print_form
  $shtml_dir = ".";			# Where timecnv.shtml lives

#    Data files used by print_form
  $html_file = "$shtml_dir/timecnv.shtml";
#	Don't confuse $listvar_file (output from listvar; input to process_
#    form) with $varlist_file (output from process_form; varlist optional
#    file input to defgb) or with $listvar (the program itself)
  $listvar_file = "$tmpdir/timecnv_$unique.listvar"; 

#    Program used "from" form
#      (x access check not ENTIRELY superfluous - we could be in print_form
#      via a "perl timecnv.pl" command instead of a "./timecnv.pl" command)
  &check_x_access($form_action_file);

#    #include values in timecnv.shtml that we will deal with.  Anything
#    not on this list makes us issue an error
  $listvar_key = "timecnv.varlist";
  $object_key = "timecnv.objectname";
  $hidden_key = "timecnv.hidden";
###

#    Presumably build-opt-env.pl took care of temp dir stuff.  However,
#    leave it here  1) in case we want to change location from that in
#    build-opt-env  2) build-opt-env doesn't check that dir was created OK, etc
  if (-e $tmpdir) {
    (-d $tmpdir) || &quit ("$tmpdir is not a directory");
    (-w $tmpdir) || &quit ("Cannot create files in directory $tmpdir");
  } else {
    mkdir ($tmpdir,0755) || &quit("Cannot create directory $tmpdir : $!");
  }

#    Get list of variables into temp file.  This list will also be used
#    by timecnv.pl, so don't alter format w/o changing that program
#      -a switch requires listvar 1.2.  Putting it last allows the
#    command to work w/old versions of listvar (except of course that
#    you don't get attributes...)
#	Next 2 lines assume we don't need PATH_INFO any more.  We could
#	save it and restore it, or use the commented-out code below which
#	never fiddles w/it at all...
  delete ($ENV{"PATH_INFO"});
  $listvar_commands = "$listvar $obj -a";
#		  $listvar_commands = "/bin/csh -c " .
#				      "\"(" .
#					"unsetenv PATH_INFO" .
#				      ";" .
#					$listvar . " " . $obj . " -a" .
#				      ")\"";

  open (FILE,"$listvar_commands | ") ||
	&quit ("Bad pipe open/read $listvar_commands : $!\n");
  open (OUTFILE,"+> $listvar_file") ||
	&quit ("Cannot write $listvar_file : $!\n");
  while (<FILE>) {
    (defined($debug)) && print "$_";
#      If record leads off with &x, we have a listvar error, so die
    if (/^&x/) {
      close FILE;
      close OUTFILE;
      &quit ("listvar died: $_");
    }
    print OUTFILE $_;
  }
#   If tempted to do error checking on close (since that gives status
#   of pipe command), note that listvar seems to exit w/29 (Illegal seek)
#   after what seemed to be a good listvar...
  close FILE;

###
  &printheader;
  print "<!-- This form prepared from $html_file by $version --><br>\n";

#    Copy .shtml file to stdout, detecting and acting on #include
#    directives and inserting proper form action routine
  open (FILE,$html_file) || &quit("Cannot read $html_file : $!\n");

  $need_to_print_form_action=1;
  $default1 = $default2 = "";

  while (<FILE>) {
#     Look for <select name="menuname">. allowing embedded blanks
    ($menu_name) = /< *select +name *= *"(.*)"/;
    if ($menu_name) {
      $default1 = $official_varname{$menu_name};
      $default2 = $unofficial_varname{$menu_name};
    }

#     Look for <!--#include file="filename", allowing embedded blanks
    if (/<!-- *#include +file *= *"(.*)"/) {


      if ($1 eq $listvar_key) {
#	  Put in copy of object's variable list preceded by "<option>"
#	  Remember that listvar output has leading blanks (important to
#	    timecnv.pl, which is why we didn't remove them here)
#         If we know what menu we're displaying and we find an
#           appropriate "official" variable name, make that variable the
#           selected one.  Depend on timecnv.shtml to have already printed
#           out a "none" option, which will be selected by virtue of its
#           position if we don't label any other ones
#         (Also depend on timecnv.shtml to have appropriate "select"
#           preceding the #include!  I think this is required...)
	seek (OUTFILE,0,0) || &quit("Bad rewind on $listvar_file\n");
        $default_varname_selected = 0;
	while (  defined($rec = <OUTFILE>)  )   {
#	    Varname might be preceded by blanks and followed by [attr_list]
	  $rec =~ s/^ *//;
	  ($rec) = split(/\[/,$rec);
          print "<option";
#           Accept any varname that starts w/default string.  Looking
#           at "start" covers year, year_gmt, year_UTC, etc
          if ( ! $default_varname_selected) {
            (
                ($default1  &&  (index ($rec,$default1) == 0))
                                ||
                ($default2  &&  (index ($rec,$default2) == 0))
            )
                                &&
            (print " selected")  && ($default_varname_selected = 1);
          }
          print ">$rec";
	}

      } elsif ($1 eq $hidden_key) {
#	  Pass essential parameters via hidden variables in form
#	  process_form will both default all of these and accept more of
#	  these - be careful.  For example, $unique cannot be defaulted
#	  correctly no matter what.
	$h = "input type=\"hidden\"";
	print "<$h name=\"path_info\" value=\"$path_info\">\n";
	print "<$h name=\"query_string\" value=\"$query_string\">\n";
	print "<$h name=\"unique\" value=\"$unique\">\n";
	print "<$h name=\"form_action\" value=\"$form_action\">\n";
	print "<$h name=\"listvar_file \" value=\"$listvar_file \">\n";

      } elsif ($1 eq $object_key) {
	print $dispobj;

      } else {
	&quit("Cannot do requested #include\n","	$_");
      }


#     Look for <form method="post", allowing embedded blanks.  If found,
#     replace with action routine from this file.
    } elsif (/< *form +method *= *\"post\"/) {
#	Ignore commented out form action lines
      if ( ! /^< *!/ ) {
	$need_to_print_form_action || 
	  &quit(">1 uncommented form action line in $html_file");
        print "<form method=\"post\" action=$form_action>\n";
        $need_to_print_form_action = 0;
      }

    } else {
      print $_;
    }

  }

#    Wonder if we ever get here...  Checked - yes
  close OUTFILE;
  close FILE;

  exit;
}

####################################################3
####################################################3
####################################################3

sub process_form
{
  if ($debug) {
    print &printheader;
    print "<pre>";
    foreach $key (sort keys %ENV) {
      print $key, '=', $ENV{$key}, "\n";
    }
    foreach $key (sort keys %form_info) {
      print $key, '=', $form_info{$key}, "\n";
    }
    print "</pre>";
  }

  $path_info_from_form = &get_form_var("path_info","REQ","NOCHECK");

###
#    File locations, etc.  If not passed along, default them (hence seeming
#      duplication of pieces of file information).
#    Communication here is w/&print_form, not the user
#      Variable name "on form"		Value
#	unique			 Some string unique to this timecnv job
#	pid_file_cleanup_debug_output_file
#				 File spec of where to put pid_file_cleanup 
#				 debug output.  This spec is used if timecnv's
#				 debug env var is set but pid_file_cleanup's
#				 debug env var is NOT set

  $unique = &get_form_var("unique","REQ");

#	Directories used by process_form
  $util_dir = $bin_dir;		# Where parse_path_info stuff is
  $serv_dir = ".";		# Where serv lives (use ENV{RUNDIR}?)
  $defgbmethod_objsubdir = "";	# Object subdir where defgbmethod is defined

#	Programs used by process_form
  $make_path_info = "$util_dir/make_path_info_string";
  $parse_path_info = "$util_dir/parse_path_info";
  $serv = "$serv_dir/serv";
  &check_x_access($make_path_info);
  &check_x_access($parse_path_info);
  &check_x_access($serv);

#	Next defn is an object spec, not a file spec, so we can't just define
#	a default directory of . (directory is subdirectory of 
#	jgofs_root/objects containing .objects file containing 
#	defgbmethod defn).
#	  dctsearch wants an initial slash (at least if no subdir is spec'd),
#	so remove all leading & trailing slashes, and put in exactly
#	what we (think) we want.  Look at comments in dct.c for what
#	things w/ & w/o slashes, etc mean...
  if ($defgbmethod_objsubdir) {
    ($defgb_object) = ($defgbmethod_objsubdir =~ m"^/*(.*)/*$");
    $defgb_object = "/$defgb_object/defgbmethod";
  } else {
    $defgb_object = "/defgbmethod";
  }

#	Data files used by process_form
#    _$unique is indicator to cleanup routine that ALL files that look like
#    file_root except for the $unique are eligible for cleanup.  Ergo,
#    file_root must be unique to timecnv work.  See pid_file_cleanup.pl
  $listvar_file = &get_form_var("listvar_file","OPT","FILESPEC");
  $pid_file_cleanup_debug_output_file = 
		&get_form_var("pid_file_cleanup_debug","OPT","FILESPEC");
  ($pid_file_cleanup_debug_output_file)
	||
  ( $pid_file_cleanup_debug_output_file = 
				"$tmpdir/pid_file_output_$unique.debug");

  $obj_name = "timecnv_$unique";
  $ind_file = "$tmpdir/$obj_name.ind";
  $relative_varlist_file = "$obj_name.varlist";
  $varlist_file = "$tmpdir/$relative_varlist_file";
  $relative_tdparams_file = "$obj_name.tdparams";
  $tdparams_file = "$tmpdir/$relative_tdparams_file";
#    Logically temporary for this program, so save list for cleanup
  @temp_files = ($varlist_file,$ind_file,$tdparams_file,
					$pid_file_cleanup_debug_output_file);
  $listvar_file && push(@temp_files,$listvar_file);

  $JGOFS_maxlevel = 9;

###
#  Form input fields
#			Descriptions: permissible vals separated by slashes
#			"" means absence is significant beyond just "not specd"
#			  (note that in general, can't get "" from a form)
# in_location  			# In time "utc"/"local"
						# Kinds of time disp data
# in_timedisp_varname  	      	# Variable name/""
# in_timedisp_zone  	      	# Time zone
# in_timedisp_zonename		# Zone name abbrev
# in_timedisp_disp_hours	# Hours
# in_timedisp_disp_min		# Minutes
# in_timedisp_type"		# "name"/"zone"/"disp"/""

# in_g_year			# Year data (a varname)/""
# in_g_century			# Century (19 or 20)/""
# in_g_decade			# Decade (0-9)/""
# in_g_yr			# Year within decade (0-9)/""

# in_j_year			# Year data (a varname)/""
# in_j_century			# Century (19 or 20)/""
# in_j_decade			# Decade (0-9)/""
# in_j_yr			# Year within decade (0-9)/""

# in_month			# Month data (a varname)
# in_month_fmt			# "integer"/"alpha" (11 or Nov)

# in_day			# Day data (a varname)

# in_time			# "separate"/"combo-secs"/"combined"/"julian"

# in_t_hour			# Hour data (a varname)
# in_t_minutes			# Minute data (a varname)
# in_t_seconds_sep		# Second data for t_hour, t_min time (a varname)
# in_t_hourmin			# Combined hour/min data (a varname)
# in_t_seconds_combo		# Second data for t_hourmin time (a varname)
# in_t_hrminfmt			# "HHMM"/"HHMM.MMMM"
# in_t_hrminsecs		# Combined hour/min/sec data (a varname)
# in_t_hrminsecfmt		# "HH:MM:SS.SSSS"/"HH:MM:SS"/"HH.HHHH"

# in_j_jan1			# "day_0"/"day_1"
# in_j_365yr			# "normal"/"fixed" (normal/ignore leap)
# in_j_dayyr			# "jday"/"fracyear"
# in_j_name			# Julian data (a varname)

# out_location			# Out time "utc"/"local"
# out_calendar			# Out time "julian"/"gregorian"
# out_jan1			# "day_0"/"day_1"
# out_j_fmt			# "Julian_day"/"Julian_year"/
				#	"Julian_day_since_1_Jan_1970"

###
#    Be sure all vars are defined

  $timedisp = &get_form_var("in_timedisp_varname","OPT");
  $timedispzone = &get_form_var("in_timedisp_zone","OPT","NUMBER");
  $timedispzoneabbrv = &get_form_var("in_timedisp_zonename","OPT");
  $timedisphrs = &get_form_var("in_timedisp_disp_hours","OPT","NUMBER");
  $timedispmin = &get_form_var("in_timedisp_disp_min","OPT","NUMBER");
  $timedispformat = &get_form_var("in_timedisp_type","OPT");

  $inutcflag = &get_form_var("in_location","OPT");

  $jyear = &get_form_var("in_j_year","OPT");
  $jcentury = &get_form_var("in_j_century","OPT","NUMBER");
  $jdecade = &get_form_var("in_j_decade","OPT","NUMBER");
  $jdecade_year = &get_form_var("in_j_yr","OPT","NUMBER");

  $gyear = &get_form_var("in_g_year","OPT");
  $gcentury = &get_form_var("in_g_century","OPT","NUMBER");
  $gdecade = &get_form_var("in_g_decade","OPT","NUMBER");
  $gdecade_year = &get_form_var("in_g_yr","OPT","NUMBER");

  $month = &get_form_var("in_month","OPT");
  $monthformat = &get_form_var("in_month_fmt","OPT");
  
  $day = &get_form_var("in_day","OPT");

  $timepiecesflag = &get_form_var("in_time","OPT");

  $hour = &get_form_var("in_t_hour","OPT");
#    Form does not ask for next variable.  Derived in code below
  $hourformat = "";
  
  $minute = &get_form_var("in_t_minutes","OPT");
#    Form does not ask for next variable.  Defaulted in code below
  $minuteformat = "";
  
  $second_sep = &get_form_var("in_t_seconds_sep","OPT");
  $second_combo = &get_form_var("in_t_seconds_combo","OPT");
#    Form does not ask for next variable.  Defaulted in code below
  $secondformat = "";
  
  $hourmin = &get_form_var("in_t_hourmin","OPT");
  $hourminformat = &get_form_var("in_t_hrminfmt","OPT");
  $hourminsec = &get_form_var("in_t_hrminsecs","OPT");
  $hourminsecformat = &get_form_var("in_t_hrminsecfmt","OPT","NOCHECK");
  ($hourminsecformat =~ /[HMS\.\:]+/) ||
	&bad_form_var("in_t_hrminsecfmt",$hourminsecformat,
					"defgb hrminsec format",'HMS.:');

  $julian = &get_form_var("in_j_name","OPT");
  $injulianflag = &get_form_var("in_j_dayyr","OPT");
  $injulday1jan = &get_form_var("in_j_jan1","OPT");
  $injulmaxdaysinyear = &get_form_var("in_j_365yr","OPT");

  $oututcflag = &get_form_var("out_location","OPT");
  $outjulgreg = &get_form_var("out_calendar","OPT");
  $outjulday1jan = &get_form_var("out_jan1","OPT");
  $outjulianflag = &get_form_var("out_j_fmt","OPT");
#    Form does not ask for next variable at the moment...
#    If it does, we'll need new "official" names for output variables w/
#	that kind of time in them...
  $outjulmaxdaysinyear="normal";

  if ($debug) {
    print DEBUG_FILE "\n",
      "	... Running $version	at $date.  Job id = $unique\n",
      "defgb_object = $defgb_object\n",
      "listvar = $listvar\n",
      "make_path_info = $make_path_info\n",
      "parse_path_info = $parse_path_info\n",
      "temp directory = $tmpdir\n",
      "indirect temp file = $ind_file\n",
      "varlist temp file = $varlist_file\n",
      "timedateparams temp file = $tdparams_file\n",
      "Input variable names/values:\n",
      "	in_location/$inutcflag\n",
      "	in_timedisp_varname/$timedisp\n",
      "	in_timedisp_zone/$timedispzone\n",
      "	in_timedisp_zonename/$timedispzoneabbrv\n",
      "	in_timedisp_disp_hours/$timedisphrs\n",
      "	in_timedisp_disp_min/$timedispmin\n",
      "	in_timedisp_type/$timedispformat\n",
      "	in_g_year/$gyear\n",
      "	in_g_century/$gcentury\n",
      "	in_g_decade/$gdecade\n",
      "	in_g_yr/$gdecade_year\n",
      "	in_j_year/$jyear\n",
      "	in_j_century/$jcentury\n",
      "	in_j_decade/$jdecade\n",
      "	in_j_yr/$jdecade_year\n",
      "	in_month/$month\n",
      "	in_month_fmt/$monthformat\n",
      "	in_day/$day\n",
      "	in_time/$timepiecesflag\n",
      "	in_t_hour/$hour\n",
      "	in_t_minutes/$minute\n",
      "	in_t_seconds_sep/$second_sep\n",
      "	in_t_seconds_combo/$second_combo\n",
      "	in_t_hourmin/$hourmin\n",
      "	in_t_hrminfmt/$hourminformat\n",
      "	in_t_hrminsecs/$hourminsec\n",
      "	in_t_hrminsecfmt/$hourminsecformat\n",
      "	in_j_jan1/$injulday1jan\n",
      "	in_j_365yr/$injulmaxdaysinyear\n",
      "	in_j_dayyr/$injulianflag\n",
      "	in_j_name/$julian\n",
      "	out_location/$oututcflag\n",
      "	out_calendar/$outjulgreg\n",
      "	out_jan1/$outjulday1jan\n",
      "	out_j_fmt/$outjulianflag\n",
      "\n";
  }

###
#    Check or create temporary directory
  if (-e $tmpdir) {
    (-d $tmpdir) || &quit ("$tmpdir is not a directory");
    (-w $tmpdir) || &quit ("Cannot create files in directory $tmpdir");
  } else {
    mkdir ($tmpdir,0775) || &quit("Cannot create directory $tmpdir : $!");
  }

###
#     Arrange to relocate files so that they do NOT
#     appear in temp_files (we still want temp_files to clean up any previous
#     temp files).  Also, define a couple more files
#   Next defines location for defgb's .ind, etc files for this object
#   The directory is a "temp" directory in some sense, and its cleanup
#   parameters obviously determine "persistence"
  $persistent_object_dir = "$tmpdir/persistent_objects";
  if (-e $persistent_object_dir) {
     (-d $persistent_object_dir) ||
			&quit ("$persistent_object_dir is not a directory");
     (-w $persistent_object_dir) ||
	&quit ("Cannot create files in directory $persistent_object_dir");
  } else {
     mkdir ($persistent_object_dir,0775) ||
		&quit("Cannot create directory $persistent_object_dir : $!");
  }

  $file_root = "$persistent_object_dir/$obj_name";
  $ind_file = "$file_root.ind";
  $varlist_file = "$persistent_object_dir/$relative_varlist_file";
  $tdparams_file = "$persistent_object_dir/$relative_tdparams_file";

###
#    Process input params into various lists, etc
#    We check a lot, but we don't check a lot.  Eg, if a variable is
#	spec'd without a corresponding format, we probably skip it.
#	Another example: we don't check that there's any transformation
#	required at all; we don't even check if there's any input to transform.
#	defgb is probably the best diagnostic, but we try to catch stuff
#	here; esp. violations of interface between env vars and this routine.

  $ninput_vars = 0;

#    List of variables that must be in input data (according to env vars)
  $needed_input_vars = " ";

#    List of variables to hold output.  Usually we must generate these,
#    but they might be in the object
  $needed_output_vars = " ";

#    List of required input that is NOT in object.  Presently this is
#    confined to year and time displacement info.  Determined by flag env
#    var that says data env var is either varname or data.  If flag says
#    "data", we generate a level 0 variable to hold it.  Note that this
#    variable name must NOT be in object
  $generated_input_vars = " ";
  $lev0_data = "";

#    String of form
#	intimeconvention = UTC/local
#	outtimeconvention = UTC/local
#	invar1name = var1name; invar1format = fmt1_string
#	invar2name = var2name; invar2format = fmt2_string
#		.
#		.
#	invarNname = varNname; invarNformat = fmtN_string
#	outmonthvar = varname
#		.
#		.
#    etc.  See defgb doc for format of this string, fmt_strings, etc
#    This stuff is the guts of the whole matter.
  $tdparams_string = "";

#				Input time convention
  if ($inutcflag eq "") {
    $in_suffix = "";
  } else {
    if ($inutcflag eq "utc") { $in_suffix = $official_UTC_suffix }
    elsif ($inutcflag eq "local") { $in_suffix = $official_local_suffix }
    else { &quit ("Unrecognized input utc flag.  Value = $inutcflag") }

    $tdparams_string .= "intimeconvention = $inutcflag\n";
  }

#				Input time displacement stuff
#				This is difference between local and UTC
#				    regardless of whether times are local or UTC
  if ($timedispformat ne "") { 
    if ($timedisp eq "")  {
#				data from form; generate var name & format
#	  $generated_input_vars .= (some name depending on format.  See thesaurus) . " ";
      $timedisp = "timedispvar";
      $generated_input_vars .= " timedispvar ";

      if ($timedispformat eq "name") {
#	  At least 3 letters/digits.  Only 3 defined at moment, but...
	if ($timedispzoneabbrv =~ /^\w{3,}$/) { $lev0_data .= $timedispzoneabbrv }
	else { &quit (	"Unrecognized time zone name format.",
			"Value = $timedispzoneabbrv") }
      } elsif ($timedispformat eq "zone") {
#	  0, or +- followed by 1 or 2 digits
	if ($timedispzone =~ /^(0|[+-]\d{1,2})$/) { $lev0_data .= $timedispzone }
	else { &quit (	"Unrecognized time zone format.",
			"Value = $timedispzone") }
      } elsif ($timedispformat eq "disp") {
#	  00, or +- followed by 2 digits
	if ($timedisphrs =~ /^(00|[+-]\d\d)$/) { $lev0_data .= $timedisphrs }
	else { &quit (	"Unrecognized time displacement hours format.",
			"Value = $timedisphrs") }
        if ($timedispmin =~ /^\d\d$/) { $lev0_data .= $timedispmin }
	else { &quit (	"Unrecognized time displacement minute format.",
			"Value = $timedispmin") }
      }
      $lev0_data .= " ";

    } else {
#				data in object
      &add_needed_var ($timedisp);
    }

    $tdparams_string .= "invar" . ++$ninput_vars . "name = $timedisp; " .
    			"invar" . $ninput_vars   . "template = ";
    if ($timedispformat eq "name") { $tdparams_string .= "ZZZZZZ\n" }
    elsif ($timedispformat eq "zone") { $tdparams_string .= "zzz\n" }
    elsif ($timedispformat eq "disp") { $tdparams_string .= "RRRRR\n" }
    else { &quit (  "Unrecognized time displacement format.",
		    "Value = $timedispformat") }
  }

#		Date

#    Julian dates must have a julian variable.  Gregorian dates must
#    have a year.  Validate.
  $gregorian = (  ($gyear ne "")   || ($gcentury ne "") || 
  		  ($gdecade ne "") || ($gdecade_year ne "")  );

  ($julian eq "") && ( ! $gregorian) &&
      &quit("Please select a variable in the Julian date section...",
	    "  or a year in the Gregorian date section");
  ($julian ne "") && $gregorian &&
      &quit("Cannot select both a variable in the Julian date section...",
            "  and a year in the Gregorian date section");


  if ($gregorian) {
#				Input date is Gregorian
    $year = $gyear;
    $century = $gcentury;
    $decade = $gdecade;
    $decade_year = $gdecade_year;
#					Input month
    if ($month ne "") {
      $monthformat ||  &quit ("Month data but no corresponding format");
      &add_needed_var ($month);
      $tdparams_string .= "invar" . ++$ninput_vars . "name = $month; " .
			  "invar" . $ninput_vars . "template = ";
      if ($monthformat eq "integer") { $tdparams_string .= "m\n" }
      elsif ($monthformat eq "alpha") { $tdparams_string .= "bbb\n" }
      else { &quit ("Unrecognized month format.","Value = $monthformat") }
    }

#					Input day
    if ($day ne "") {
      &add_needed_var ($day);
      $tdparams_string .= "invar" . ++$ninput_vars . "name = $day; " .
			  "invar" . $ninput_vars . "template = d\n";
    }

  } else {

#				Input date is Julian
    $year = $jyear;
    $century = $jcentury;
    $decade = $jdecade;
    $decade_year = $jdecade_year;

    &add_needed_var ($julian);
#      Julian info is asked in 2 parts of the form; date and time
#      Save its timedateparams info separately, then add to tdparams_string
#      when all is done.
    $julian_tdparams_string = "invar" . ++$ninput_vars . "name = $julian; " .
			       "invar" . $ninput_vars . "template = ";

    ($injulianflag =~ /^(fracyear|jday)$/) ||
		&quit ("Unrecognized input julian days/years flag.",
		       "Value = $injulianflag");
    ($injulmaxdaysinyear =~ /^(fixed|normal)$/) ||
		&quit ("Unrecognized value for input julian max days in year.",
		       "Value = $injulmaxdaysinyear");
    ($injulday1jan =~ /^day_(0|1)$/) ||
		&quit ("Unrecognized julian value for input date of 1 Jan.",
		       "Value = $injulday1jan");

#	Start by selecting julian day/year input.  Assume Jan 1 = 0 and 
#	leap days are included in years.
#	Modify each assumption as appropriate.  See defgb doc for "magic
#	letter" defn.
    $template_char = ($injulianflag eq "jday") ? "v" : "K";
    ($injulday1jan eq "day_1") && ($template_char =~ tr/vK/jg/);
    ($injulmaxdaysinyear eq "fixed") && ($template_char =~ tr/vKjg/VPJG/);
    $julian_tdparams_string .= $template_char;

  }
#				Input year
  if (($century ne "") && ($decade ne "") && ($decade_year ne "")) {
    ($year eq "") || &quit ("Year specified as variable and also as value");
#				data in input vars; generate var name
    $tdparams_string .= "invar" . ++$ninput_vars . "name = ";
    $generated_input_vars .= " " . $official_year_var . $in_suffix . " ";
    $tdparams_string .= $official_year_var . $in_suffix . "; ";
    $lev0_data .= $century.$decade.$decade_year." ";
    $tdparams_string .= "invar" . $ninput_vars . "template = y\n";
  } else {
    (($century ne "") || ($decade ne "") || ($decade_year ne ""))
		&&  &quit ("Year data not completely specified");
    if ($year ne "") {
#				data in object
      $tdparams_string .= "invar" . ++$ninput_vars . "name = ";
      &add_needed_var ($year);
      $tdparams_string .= "$year; ";
      $tdparams_string .= "invar" . $ninput_vars . "template = y\n";
    }
  }

#				Input time
#     Recode "combined" time that only specs decimal hours to "separate"
#     time (this is way originally coded & allows for future decimal minutes)
  ($timepiecesflag eq "combined") && ($hourminsecformat eq "H")
    && ($timepiecesflag = "separate") && ($hourformat = "H");

  if ($timepiecesflag eq "") {
    (  ($hour ne "")  || ($minute ne "")  || ($second_sep ne "") ||
       ($hourmin ne "") || ($hourminsec ne "")  || ($second_combo ne "")   )
      &&  &quit("Time pieces specified but flag to interpret them not spec'd\n",
		"Hour/minute/second1/hourmin/hourminsec/second2 pieces are: ",
		"$hour/$minute/$second_sep/$hourmin/$hourminsec/$second_combo");
#						Time in w/julian date
  } elsif ($timepiecesflag eq "julian") {
    $gregorian  &&
	&quit("Time spec'd as part of Julian date but input is Gregorian");
    $julian_tdparams_string .= ".o";
#						Hours, mins, and secs
  } elsif ($timepiecesflag eq "separate") {
#							Hours (required)
    ($hour eq "")  &&  &quit("Time pieces flag of separate requires hour data");
#     If/when form asks for hourformat, next line is ... && die.
    ($hourformat eq "") && ($hourformat = "hh");
    &add_needed_var ($hour);
    $tdparams_string .= "invar" . ++$ninput_vars . "name = $hour; " .
			"invar" . $ninput_vars . "template = ";
    if ($hourformat eq "hh") { $tdparams_string .= "HH\n" }
    elsif ($hourformat eq "hh.hh") { $tdparams_string .= "H.\n" }
    else { &quit ("Unrecognized hour format.","Value = $hourformat") }
#							Minutes (optional)
    if ($minute ne "") {
      ($hourformat eq "hh.hh") &&
			&quit ("Cannot have minutes and decimal hours");
#	If/when form asks for minuteformat, next line is ... && die.
      ($minuteformat eq "") && ($minuteformat = "mm");
      &add_needed_var ($minute);
      $tdparams_string .= "invar" . ++$ninput_vars . "name = $minute; " .
			  "invar" . $ninput_vars . "template = ";
      if ($minuteformat eq "mm") { $tdparams_string .= "MM\n" }
      elsif ($minuteformat eq "mm.mm") { $tdparams_string .= "M.\n" }
      else { &quit ("Unrecognized minute format.","Value = $minuteformat") }
    }
#							Seconds (optional)
    if ($second_sep ne "") {
      ($minuteformat eq "mm.mm") &&
			&quit ("Cannot have seconds and decimal minutes");
#	If/when form asks for secondformat, next line is ... && die.
      ($secondformat eq "") && ($secondformat = "ss.ss");
      &add_needed_var ($second_sep);
      $tdparams_string .= "invar" . ++$ninput_vars . "name = $second_sep; " .
			  "invar" . $ninput_vars . "template = ";
      if ($secondformat eq "ss") { $tdparams_string .= "SS\n" }
      elsif ($secondformat eq "ss.ss") { $tdparams_string .= "S.\n" }
      else { &quit ("Unrecognized second format.","Value = $secondformat") }
    }
#						HHMM hours & mins, and secs
  } elsif ($timepiecesflag eq "combo-secs") {
#							24 hr time (required)
    (  ($hourminformat eq "") || ($hourmin eq "") )  &&
      &quit ("Time pieces flag of HRMIN requires 24 hour time format and data");
    &add_needed_var ($hourmin);
    $tdparams_string .= "invar" . ++$ninput_vars . "name = $hourmin; " .
			"invar" . $ninput_vars . "template = ";
#     No N. widthless input - widthless is only for fields w/single input time
#     type, and this has 2.  Hopefully 6 q's are enough (and shorter fields
#     get terminated properly!)
    if ($hourminformat eq "HHMM.MMMM") { $tdparams_string .= "NNNN.qqqqqq\n" }
    elsif ($hourminformat eq "HHMM") { $tdparams_string .= "NNNN\n" }
    else { &quit ("Unrecognized hourmin format.","Value = $hourminformat") }
#							Seconds (optional)
    if ($second_combo ne "") {
      ($hourminformat eq "HHMM.MMMM") &&
			&quit ("Cannot have seconds and decimal minutes");
#	If/when form asks for secondformat, next line is ... && die.
      ($secondformat eq "") && ($secondformat = "ss.ss");
      &add_needed_var ($second_combo);
      $tdparams_string .= "invar" . ++$ninput_vars . "name = $second_combo; " .
			  "invar" . $ninput_vars . "template = ";
      if ($secondformat eq "ss") { $tdparams_string .= "SS\n" }
      elsif ($secondformat eq "ss.ss") { $tdparams_string .= "S.\n" }
      else { &quit ("Unrecognized second format.","Value = $secondformat") }
      }
#						HH:MM:SS
  } elsif ($timepiecesflag eq "combined") {
    ($hourminsec eq "") && &quit ("Time pieces flag of combined requires data");
    &add_needed_var ($hourminsec);
#     4 S's arbitrary to match form; 6 Q's arbitrary and presumably will cause
#     trouble if actual precision is > 6 digs
    $hourminsecformat =~ s/\.SSSS/\.QQQQQQ/;
    $tdparams_string .= "invar" . ++$ninput_vars . "name = $hourminsec; " .
			"invar" . $ninput_vars . "template = " . $hourminsecformat .
			"\n";

  } else {
    &quit ("Unrecognized time pieces flag.","Value = $timepiecesflag");
  }


#				Output time convention
  if ($oututcflag eq "") {
    $out_suffix = "";
  } else {
    if ($oututcflag eq "utc") { $out_suffix = $official_UTC_suffix }
    elsif ($oututcflag eq "local") { $out_suffix = $official_local_suffix }
    else { &quit ("Unrecognized output UTC/local flag.","Value = $oututcflag") }

    $tdparams_string .= "outtimeconvention = $oututcflag\n";
  }

  if ($outjulgreg eq "julian") {
    ($outjulianflag =~ /^Julian_(year|day|day_since_1_Jan_1970)$/) ||
		&quit ("Unrecognized output julian days/years flag.",
		       "Value = $outjulianflag");
    ($outjulmaxdaysinyear =~ /^(fixed|normal)$/) ||
		&quit ("Unrecognized value for output julian max days in year.",
		       "Value = $outjulmaxdaysinyear");
    ($outjulday1jan =~ /^day_(0|1)$/) ||
		&quit ("Unrecognized julian value for output date of 1 Jan.",
		       "Value = $outjulday1jan");
#	Start by selecting julian day/year output.  Assume Jan 1 = 0 and 
#	leap days are included in years.
#	Modify each assumption as appropriate.  See defgb doc for "magic
#	letter" defn.
    if ($outjulianflag eq "Julian_day") { $template_char = "v"; }
    elsif ($outjulianflag eq "Julian_year") { $template_char = "K"; }
    else { $template_char = "s"; }
    ($outjulday1jan eq "day_1") && ($template_char =~ tr/vKs/jgX/);
    ($template_char eq "X") &&
	  &quit ("Julian_day_since_1_Jan_1970 only works with Jan 1 = day 0");
#	Note that if we actually allow $outjulmaxdaysinyear to be user-spec'd
#	the next 4-way if-elsif becomes an 8-way, and moves to after
#	the tr/vKjg/VPJG/ line (or maybe we make an 8-entry hash keyed
#	by vKjgVPJG whose values are the correct variable names...)
    if ($template_char eq "v") { $tmp = $official_yrday0_var; }
    elsif ($template_char eq "j") { $tmp = $official_yrday_var; }
    elsif ($template_char eq "K") { $tmp = $official_julyr0_var; }
    elsif ($template_char eq "g") { $tmp = $official_julyr_var; }
    elsif ($template_char eq "s") { $tmp = $official_cumjulyr0_var; }
    ($outjulmaxdaysinyear eq "fixed") && ($template_char =~ tr/vKjg/VPJG/);
    $tdparams_string .= "outjulianformat = $template_char; ";
    $tmp .= $out_suffix;
    $needed_output_vars .= " $tmp ";
    $tdparams_string .= "outjulianname = $tmp\n";
  } elsif ($outjulgreg eq "gregorian") {
    $tmp = $official_month_var . $out_suffix;
    $needed_output_vars .= " $tmp ";
    $tdparams_string .= "outmonthname = $tmp\n";
    $tmp = $official_day_var . $out_suffix;
    $needed_output_vars .= " $tmp ";
    $tdparams_string .= "outdayname = $tmp\n";
    $tmp = $official_time_var . $out_suffix;
    $needed_output_vars .= " $tmp ";
    $tdparams_string .= "outtimename = $tmp\n";
  } else {
    &quit ("Unrecognized output Julian/Gregorian flag.","Value = $outjulgreg");
  }

  if ($debug) {
    print DEBUG_FILE "\n",
      "Needed input variables = $needed_input_vars\n",
      "Needed output variables = $needed_output_vars\n",
      "Generating input variables = $generated_input_vars\n",
      "\n";
  }

###
#    Get PATH_INFO pieces.  Need to force methods to output jgofs for
#    listvar, then restore protocol for final defgb output.  Also,
#    need to be sure that final defgb output includes enough levels.
#    Forcing done where needed; input done here.
  $tmp = `$parse_path_info -no $path_info_from_form`;
  $? && &quit ("$parse_path_info exited w/code $?","Result string: $tmp");
  %path_info = split(/[,=]/,$tmp);
  if ($debug) {
    print DEBUG_FILE "\nPieces of PATH_INFO follow:\n";
    while (  ($key,$value) = each(%path_info)  ) {
      print DEBUG_FILE "$key<->$value\n";
    }
    print DEBUG_FILE "\n";
  }
  ($obj = $path_info{"object"}) ||
			&quit("No object information reached this program");

###
#    Get variable list from object.  Convert it into
#	a b c >
#	d e f g
#    format while checking that all variables we're supposed to have
#    are actually in object

#    This initialization along with perl -w will protect against
#    too many levels in input object.  Would let defgb do this
#    except that time_cnv might add the "bad" level, and I'd rather
#    diagnose that in here.  Also, this initialization protects against
#    variation in $[ - we want to define the range of subscripts
  foreach (0..$JGOFS_maxlevel) { $levlist[$_] = "" }

  $maxlevel = 0;
  $smallest_time_level = 0;

#    If we already have a file created by listvar, don't go over the
#    net to get it again
  if ($listvar_file) {
    (open (FILE,$listvar_file)) || &quit ("Cannot read $listvar_file: $!");
  } else {
    $listvar_commands = "/bin/csh -c " .
		        "\"(" .
			  "unsetenv PATH_INFO" .
		        ";" .
			  $listvar . " " . $path_info{"object"} . " -a " .
		        ")\"";
    $debug && (print DEBUG_FILE "$listvar_commands\n");
    (open (FILE,"$listvar_commands | ")) ||
			&quit ("Bad pipe open/read $listvar_commands: $!");
  }

  while (<FILE>) {
    $debug && (print DEBUG_FILE "$_");
#      If record leads off with &x, we have a listvar error, so die
    if (/^&x/) {
      close FILE;
      &quit ("listvar died: $_");
    }
    chomp;

#      listvar puts out 2 leading blanks per level.  Count the # of
#      (presumably) leading pairs as we remove them.  Add a blank at
#      end for convenience
    $lev = s/(  )//g;
    ($lev > $maxlevel) && ($maxlevel = $lev);
    $_ .= " ";
#	get varname w/o width
    ($varname) = split (/\[/);
    $varname = " $varname ";
    $levlist[$lev] .= $_;
    
#      Check for conflicting var names.
#      Update list of variables we still need, and record level if
#      we found any such variables.  We assume that variables in
#      object appear from "largest" to "smallest" time value.  (If
#      not, there will be trouble doing time calcs anyway)
#      Similarly, update list of output variables we might need to add.
    ($generated_input_vars =~ /$varname/) &&
	    &quit ("Cannot generate variable $varname for year/timezone data" .
		   "since it is already in object");
    ($needed_input_vars =~ s/$varname//) && ($smallest_time_level = $lev);
    $needed_output_vars =~ s/$varname//;
  }
  (close FILE) || &quit("Problem closing $listvar_file: $!");
  $listvar_commands   &&   $?   &&
		&quit("Problem w/listvar process $listvar_commands: $?");

#    Shouldn't be anything but (possibly) white space left in list of
#    needed variables.
  ($needed_input_vars =~ /^\s*$/) ||
			&quit ("Need missing variables $needed_input_vars");

#    Add closing > to all levels but last.  Adding carriage return
#    is for convenience in future print command (which must supply return
#    for last level itself)
  foreach (0..$maxlevel-1) {
    $levlist[$_] .= ">\n";
  }

################################################

#  Build object
#      1) Build files for defgb.  We'll need indirect & timedateparams files.
#    Most likely we will also need to create a varlist file, inserting
#    output variables at the level of the "smallest" input time.  If 
#    year or time zone need to be inserted, the varlist file will become
#    the new level 0 file.
#      2) Add entry to .objects file for temporary objects pointing to
#    defgb(@indirect_file)
#      3) Put this object in PATH_INFO for further use

#   See if we have new variables
  $new_level_0 = ($generated_input_vars !~ /^\s*$/);
  if ($new_level_0) {
    ($maxlevel < $JGOFS_maxlevel) ||
	&quit ("Cannot add year and/or timezone info - too many levels");
  }
  $new_varlist = (  $new_level_0  || ($needed_output_vars !~ /^\s*$/)  );

  ($id = $ENV{"REMOTE_HOST"}) || ($id = "eff. UID $>");
#    In next line, # is defgb comment indicator
  $defgb_comment = "#  Written for $id $date  ";

###
#    Indirect file
  (open (IND_FILE,">$ind_file")) || &quit ("Bad open/write $ind_file: $!");
#    In next line {}s are defgb "where did this comment come from" delims
  print IND_FILE $defgb_comment,"{$version}\n";
  if ($new_level_0) {
    print IND_FILE "datafile = $relative_varlist_file\n";
  } else {
#     In next line, {}s are defgb delims indicating object input
    print IND_FILE "datafile = {$obj}\n";
    $new_varlist && (print IND_FILE "varlist = $relative_varlist_file\n");
  }
  print IND_FILE "timedateparams = $relative_tdparams_file\n";
  (close IND_FILE) || &quit ("Problem closing $ind_file: $!");
  
###
#    Timedateparams file
  (open (TDPARAMS_FILE,">$tdparams_file")) ||
				&quit ("Bad open/write $tdparams_file: $!");
  print TDPARAMS_FILE "$defgb_comment\n";
  print TDPARAMS_FILE $tdparams_string;
  (defined($julian_tdparams_string)) &&
			(print TDPARAMS_FILE "$julian_tdparams_string\n");
  (close TDPARAMS_FILE) || &quit ("Problem closing $tdparams_file: $!");

###
#    Varlist file
  if ($new_varlist) {
    (open (VARLIST_FILE,">$varlist_file")) ||
				&quit ("Bad open/write $varlist_file: $!");
    print VARLIST_FILE "$defgb_comment\n";

#      In next line, > is def not-last-level indicator
    $new_level_0 && (print VARLIST_FILE "$generated_input_vars>\n");

#      Print variable list, inserting needed variables on the same level
#      as the smallest input time.  All levlist entries except last have
#      concluding ">\n" sequence
    foreach ( 0..$smallest_time_level-1 ) {
      print VARLIST_FILE $levlist[$_];
    }
    print VARLIST_FILE $needed_output_vars,$levlist[$smallest_time_level];
    foreach ( $smallest_time_level+1..$maxlevel ) {
      print VARLIST_FILE $levlist[$_];
    }
    print VARLIST_FILE "\n";

#    If this is a new level 0 file, put in the level 0 data and pointer to
#    level 1.  {}s are defgb delims indicating object input
#    Adjust levels to reflect new object structure
    if ($new_level_0) {
      print VARLIST_FILE $lev0_data," {$obj}\n";
      $smallest_time_level++;
      $maxlevel++;
    }

    (close VARLIST_FILE) || &quit ("Problem closing $varlist_file: $!");
  }

  $new_path_info_data = "object=$defgb_object" .
			",protocol=" . $path_info{"protocol"} .
			",level=";

#   Make sure at least $smallest_time_level levels are asked for (since
#   that's where we're putting our output!).  Empty level string here should
#   mean "no level in PATH_INFO", which should mean all levels.  Must go
#   through "defined", etc contortions because a level of 0 is legit value
#   but has same "true/false" value as undef or ""
  $level = (defined($path_info{"level"})) ? $path_info{"level"} : "";
  ($level eq "") && ($level = $JGOFS_maxlevel);
  ($level < $smallest_time_level) && ($level = $smallest_time_level);
  $new_path_info_data .= $level;

#   Add any {}ed options.  {}s are JGOFS delimiters of the PATH_INFO
#   "options" string.
  $ext_url = "";
  while ( ($key,$value) = each %path_info ) {
    ($key =~ /^(object|level|protocol)$/) && next;
    $ext_url .= "$key=$value,";
  }
  chop $ext_url;
  ($ext_url eq "") || ($new_path_info_data .= "{$ext_url}");
    
  $new_path_info = `$make_path_info -no $new_path_info_data`;
  $?  &&  &quit ("$make_path_info exited w/code $?",
					"Result string: $new_path_info");    
###
#    Do it!

  $ENV{"PATH_INFO"} = $new_path_info;

  $tmp = &get_form_var("query_string","OPT","NOCHECK");
  $new_query_string = '@' . $ind_file;
  $tmp && ($new_query_string .= ",$tmp");
  $ENV{"QUERY_STRING"} = $new_query_string ;

  $debug && 
	(print DEBUG_FILE "PATH_INFO=$new_path_info\nQUERY_STRING=$new_query_string\n");

#   Not sure if next line is needed, since we include extended URL in 
#   PATH_INFO, which presumably would allow outer to decide if button
#   should be displayed.
  $ENV{"DIRLINK"} = ($path_info{"DIR"}) ? "TRUE" : "FALSE";

  $! = $? = 0;
  system ($serv);
#   Deliberately leave temp files around if bad serv...
  ($! || $?) &&
	&quit  ("$serv died. \$! = $!; \$? = $?\n",
		"PATH_INFO=$new_path_info\n",
		"QUERY_STRING=$new_query_string\n");

#   Should really save var and restore it afterwards, but we're leaving
#   this program directly...
  $debug  &&
($ENV{$pid_file_cleanup_debug_env_var} = $pid_file_cleanup_debug_output_file);

  &pid_file_cleanup($unique,@temp_files);

  exit;
}

##################################


sub add_needed_var
{
  my ($var) = @_;
  
  $var = " $var ";
  ($needed_input_vars =~ /$var/) &&
  	&quit("Cannot have 2 pieces of input coming from same variable",
  	      "Variable = $var");
  $needed_input_vars .= $var;
}

#########################

# Below is an attempt to cram as much as possible on the command line
# to try to get rid of indirect files

#    Build defgb command that will do work.  There is a problem with
#    special characters defgb wants that shell fools with (and maybe
#    that the characters used to protect things from the shell are mean-
#    ingful to perl!).  In particular, 1) perl does things with @
#    2) both shell and perl do things with \  3) shell does things with ; and
#    {}  4) not sure if perl does things with '
#    Args should look to defgb like
#		@ind_file {object} varlist_file \timedate_opt1;_opt2;_opt3
#      a) ind_file must be a defgb indirect file.
#      b) The timedate_options parameter is a defgb timedate optional file 
#    with its "records" all on one line.  At some point, the ind_file and
#    varlist_file will be able to be expressed this way, eliminating the need
#    for any files
#  $defgb_cmd = "$defgb @" . $timecnv_otheropt_indirect .
#		" '{" . $obj . "}' " . 
#		$varlist_file .
#		" \\\\invar1name=$year\\;invar1template=yyyy\\;" .
#		"invar2name=$julian\\;invar2template=jjj.ooooooo\\;" .
#		"outmonthname=mo\\;outdayname=da\\;outtimename=time_greg";

#########################

#   Below is some old routine related to debugging.  Probably useless;
#   esp as of v 2.0
# sub redefine_files
# {
#	Things defined in program (v 1.0)
#  $/ = "/";	# Set up chomp to remove trailing slashes, if any
#  chomp($tmpdir_root = $ENV{"JGTEMP"});
#  chomp($tmpdir = $ENV{"USETEMPDIR"});
#  chomp($jgofs_root = $ENV{"OPTHOME"});
#  $/ = $save_RS;
#
#  $bin_dir = "$jgofs_root/bin";	# Where listvar is
#  $util_dir = $bin_dir;		# Where parse_path_info stuff is
#  $shtml_dir = ".";			# Where timecnv.shtml lives
#######  $method_dir = "$jgofs_root/methods";	# Where defgb lives
#  $object_root = "$jgofs_root/objects";	# Where objects are
#  $persistent_object_root = "$object_root/persistent_object";
#
#  $form_action = "/jg/add_persistent.pl";
#
#  $html_file = "$shtml_dir/add_persistent.shtml";
#
#	Testing from my home directory on globec
#  $object_root = "/data5/globec/objects/globec/gb/test";
#  $persistent_object_root = "$object_root/persistent_object";
#  $form_action = "add_persistent.pl";
#######  $defgb = "/data5/globec/methods/defgb";
#}
