#include "mysccs.h"
SCCSID("@(#)filexfer.c   %E%   SAP   %I%")

static char * this_File GNU_UNUSED = __FILE__;

/************************************************************************/
/* $Id: //tools/src/freeware/gsstest/filexfer.c#3 $
 ************************************************************************
 *
 * Copyright (c) 1998-2000  SAP AG.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by SAP AG"
 *
 * 4. The name "SAP AG" must not be used to endorse or promote products
 *    derived from this software without prior written permission.
 *    For written permission, please contact www.press@sap.com
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by SAP AG"
 *
 * THIS SOFTWARE IS PROVIDED BY SAP AG ``AS IS'' AND ANY EXPRESSED
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. SAP AG SHALL BE LIABLE FOR ANY DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE ONLY IF CAUSED BY SAP AG'S
 * INTENT OR GROSS NEGLIGENCE. IN CASE SAP AG IS LIABLE UNDER THIS
 * AGREEMENT FOR DAMAGES CAUSED BY SAP AG'S GROSS NEGLIGENCE SAP AG
 * FURTHER SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT, AND SHALL NOT BE LIABLE IN EXCESS OF THE AMOUNT OF
 * DAMAGES TYPICALLY FORESEEABLE FOR SAP AG, WHICH SHALL IN NO EVENT
 * EXCEED US$ 500.000.- 
 *
 ************************************************************************/


#include "gsstest.h"

#ifdef HAVE_SYS_WAIT_H
#  include <sys/types.h>
#  include <sys/wait.h>
#endif


/*
 * export_to_file()
 *
 *
 */
int
export_to_file( int                 p_trclevel,  char	           * p_filename,
	        struct tmsg_job   * p_joblist,	 int		     p_rci,
		struct ctx_desc	 ** pp_ini,	 struct ctx_desc  ** pp_acc,
		int	          * pp_success )
{
   char	       * this_Call GNU_UNUSED = "export_to_file";
   FILE	       * fp	      = NULL;
   gss_name_t    ini_name1    = GSS_C_NO_NAME;
   gss_name_t    ini_name2    = GSS_C_NO_NAME;
   gss_name_t	 acc_name1    = GSS_C_NO_NAME;
   gss_name_t	 acc_name2    = GSS_C_NO_NAME;
   gss_OID       mech_oid1    = GSS_C_NO_OID;
   gss_OID	 mech_oid2    = GSS_C_NO_OID;
   OM_uint32     lifetime1;
   OM_uint32	 lifetime2;
   OM_uint32	 flags1;
   OM_uint32	 flags2;
   OM_uint32	 maj_stat;
   OM_uint32	 xp_rci;
   size_t	 i;
   int	         open1;
   int		 open2;
   int		 local_ini1;
   int	         local_ini2;
   int		 success      = FALSE;
   int		 rc	      = 0;
   int	         result;

   (*pp_success) = success;

   fp = fopen( p_filename, "wb");

   if ( fp==NULL ) {
      rc++;
      XVEB((V_ERR, "%s(): failed to open \"%s\" for writing: %s\n",
		   this_Call, p_filename, strerror(errno) ));
      return(rc);
   }

   rc += inquire_context( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->ctx,
			  &ini_name1, &acc_name1, &lifetime1, &mech_oid1,
			  &flags1, &local_ini1, &open1, &maj_stat );
   if ( maj_stat!=GSS_S_COMPLETE ) {

      XVEB((V_ERR, "%s(): error with initiator context -- aborting file export!\n", this_Call));

   } else {

      rc += inquire_context( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->ctx,
			     &ini_name2, &acc_name2, &lifetime2, &mech_oid2,
			     &flags2, &local_ini2, &open2, &maj_stat );
      if ( maj_stat!=GSS_S_COMPLETE ) {

	 XVEB((V_ERR, "%s(): error with acceptor context -- aborting file export!\n", this_Call));

      } else {

	 rc += verify_same_aclkey( p_trclevel, "comparing initiator names",
				   "from initiator context", FALSE, (*pp_ini)->gssfp, ini_name1,
				   "from acceptor  context", FALSE, (*pp_acc)->gssfp, ini_name2 );
      
	 rc += verify_same_aclkey( p_trclevel, "comparing target    names",
				   "from initiator context", FALSE, (*pp_ini)->gssfp, acc_name1,
				   "from acceptor  context", FALSE, (*pp_acc)->gssfp, acc_name2 );

	 if ( compare_oid(mech_oid1, ((*pp_ini)->gssfp)->mech)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mechanism of initiator context mutated:\n", this_Call));
	    print_oid( 2, "original         ", ((*pp_ini)->gssfp)->mech );
	    print_oid( 2, "inquire_context()", mech_oid1     );
	 }

	 if ( compare_oid(mech_oid2, ((*pp_acc)->gssfp)->mech)!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mechanism of acceptor context mutated:\n", this_Call));
	    print_oid( 2, "original         ", ((*pp_acc)->gssfp)->mech );
	    print_oid( 2, "inquire_context()", mech_oid2     );
	 }

	 if ( lifetime1 != lifetime2 ) {
	    OM_uint32  diff;

	    diff = (lifetime1 > lifetime2 )
		   ? (lifetime1 - lifetime2) : (lifetime2 - lifetime1);
	    if ( diff > MAX_CTX_LIFETIME_SKEW ) {
	       /* we tolerate a 20 seconds difference in lifetime between        */
	       /* initiator and acceptor -- busy machine and slow smartcards :(  */
	       rc++;
	       XVEB((V_ERR, "%s(): Acceptor and Initiator context lifetimes differ\n"
		            "  after security context transfer:\n", this_Call));
	       print_lifetime( 2, "initiator context lifetime", lifetime1 );
	       print_lifetime( 2, "acceptor context lifetime",  lifetime2 );
	    }

	 }

	 if ( ((*pp_ini)->flags) != flags1 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mutating initiator context attributes!\n", this_Call));
	    print_ctx_flags( 2, "inquire_context", "  established ini context", &((*pp_ini)->flags) );
	    print_ctx_flags( 2, "inquire_context", "  current     ini context", &flags1 );
	 }
	 if ( ((*pp_acc)->flags) != flags2 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mutating acceptor context attributes!\n", this_Call));
	    print_ctx_flags( 2, "inquire_context", "  established acc context", &((*pp_acc)->flags) );
	    print_ctx_flags( 2, "inquire_context", "  current     acc context", &flags2 );
	 }

	 if ( (local_ini1==0 && local_ini2==0)
	      || (local_ini1!=0 && local_ini2!=0) ) {
	    rc++;
	    XVEB((V_ERR, "%s(): inconsistent \"local_ini\" from inquire_context %d,%d !\n",
			 this_Call, (int)local_ini1, (int)local_ini2 ));
	 }

	 if ( (open1!=0 && open2==0) || (open1==0 && open2!=0) ) {
	    rc++;
	    XVEB((V_ERR, "%s(): \"open\" from inquire_context mutated %d->%d !\n",
			    this_Call, (int)open1, (int)open2 ));
	 }

	 if ( p_joblist!=NULL ) {
	    for ( i=0 ; p_joblist[i].action!=0 ; i++ );
	    result = write_object( p_trclevel, fp, XP_TMSG_JOBLIST, p_joblist, sizeof(struct tmsg_job) * (i+1) );
	    if ( result==0 ) {
	       success = TRUE;
	    } else {
	       rc++;
	       success = FALSE;
	    }
	 }

	 if ( p_joblist!=NULL && success==FALSE ) {

	    XVEB((V_ERR, "%s(): export of joblist failed!\n", this_Call));

	 } else {

	    rc += write_to_file( p_trclevel, fp, local_ini1, *pp_ini, &success );

	    if ( success==FALSE ) {

	       XVEB((V_ERR, "%s(): export of initiator context failed!\n", this_Call));

	    } else {

	       rc += write_to_file( p_trclevel, fp, local_ini2, *pp_acc, &success );

	       if ( success==FALSE ) {

		  XVEB((V_ERR, "%s(): export of acceptor context failed!\n", this_Call));

	       } else {

		  xp_rci = rc + p_rci;
		  result = write_object( p_trclevel, fp, XP_RCI, &xp_rci, 0 );
		  if ( result!=0 ) {
		     success = FALSE;
		  }

	       }

	    }

	 }

      } /* inquire_context(acceptor_context) succeeded */

   } /* inquire_context(initiator_context) succeeded */

   if ( fp!=NULL ) {
      if ( fclose(fp)==0 ) {
	 (*pp_success) = success;
      } else {
	 rc++;
	 XVEB((V_ERR, "%s(): fclose(\"%s\") failed: %s\n",
		      this_Call, p_filename, strerror(errno) ));
      }
   }

   rc += release_name( (*pp_ini)->gssfp, &ini_name1 );
   rc += release_name( (*pp_acc)->gssfp, &ini_name2 );
   rc += release_name( (*pp_ini)->gssfp, &acc_name1 );
   rc += release_name( (*pp_acc)->gssfp, &acc_name2 );

   if ( success!=FALSE ) {
      XVEB((V_OK, verbose_level-2, "  %s():   export of security contexts succeeded.\n", this_Call));
   }

   rc += release_ctx_desc( pp_ini );
   rc += release_ctx_desc( pp_acc );

   return(rc);

} /* export_to_file() */




/*
 * import_from_file()
 *
 *
 */
int
import_from_file( int		     p_trclevel,   char		     * p_filename,
		  int		     p_copy_names,
		  struct tmsg_job ** pp_joblist,
		  DLL_GSSFP_T      * p_gssfp_ini,  DLL_GSSFP_T	     * p_gssfp_acc,
		  struct ctx_desc ** pp_ini,	   struct ctx_desc  ** pp_acc,
		  int		   * pp_rci,	   int		     * pp_success )
{
   char			* this_Call GNU_UNUSED = "import_from_file";
   FILE			* fp         = NULL;
   gss_name_t		  ini_name1  = NULL;
   gss_name_t		  ini_name2  = NULL;
   gss_name_t		  acc_name1  = NULL;
   gss_name_t		  acc_name2  = NULL;
   gss_OID		  mech_oid1  = GSS_C_NO_OID;
   gss_OID		  mech_oid2  = GSS_C_NO_OID;
   struct xfer_ctx_s	* ctx1	     = NULL;
   struct xfer_ctx_s	* ctx2	     = NULL;
   struct xfer_ctx_s	* ctx3	     = NULL;
   struct tmsg_job      * joblist    = NULL;
   OM_uint32		  lifetime1;
   OM_uint32		  lifetime2;
   OM_uint32		  flags1;
   OM_uint32		  flags2;
   OM_uint32		  maj_stat;
   OM_uint32		  xp_rci     = 0;
   int			  open1;
   int			  open2;
   int			  local_ini1;
   int			  local_ini2;
   int			  success    = FALSE;
   int		          rc         = 0;


   (*pp_ini)	  = NULL;
   (*pp_acc)	  = NULL;

   fp = fopen(p_filename, "rb");
   if ( fp==NULL ) {
      rc++;
      XVEB((V_ERR, "%s() failed to open file \"%s\" for reading: %s\n",
		   this_Call, p_filename, strerror(errno) ));
      goto error;
   }

   rc += read_from_file( p_trclevel, fp, p_filename, &ctx1, &joblist );

   if ( ctx1!=NULL ) {

      rc += read_from_file( p_trclevel, fp, p_filename, &ctx2, NULL  );

      if ( ctx2!=NULL ) {

	 rc += read_from_file( p_trclevel, fp, p_filename, &ctx3, NULL );
	 if ( ctx1==NULL ) {
	    rc++;
	    XVEB((V_ERR, "%s() couldn't read RC value from context file!\n", this_Call));
	 } else {
	    xp_rci = ctx3->rci;
	 }
	    
	 if ( compare_oid( &(ctx1->ctx_mech), p_gssfp_ini->mech )!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mismatch of mechanism for initiating context:\n", this_Call));
	    print_oid( 2, "transferred mech",       &(ctx1->ctx_mech) );
	    print_oid( 2, "initiator library mec ", p_gssfp_ini->mech );
	 }

	 if ( compare_oid( &(ctx2->ctx_mech), p_gssfp_acc->mech )!=0 ) {
	    rc++;
	    XVEB((V_ERR, "%s(): mismatch of mechanism for accepting context:\n", this_Call));
	    print_oid( 2, "transferred mech",      &(ctx2->ctx_mech) );
	    print_oid( 2, "acceptor library mec ", p_gssfp_acc->mech );
	 }

	 /* reading of the exported security context looks successful so far,   */
	 /* now let's reimport both context ends and do some consistency checks */
	 (*pp_ini) = create_ctx_desc( p_gssfp_ini, "initiator",
				      ctx1->ini_prname.value, ctx1->ini_prname.length, &(ctx1->can_nt),
				      ctx1->acc_prname.value, ctx1->acc_prname.length, &(ctx1->can_nt) );

	 (*pp_ini)->flags	 = ctx1->flags;
	 (*pp_ini)->flags_req	 = ctx1->flags_req;
	 (*pp_ini)->lifetime	 = ctx1->lifetime;
	 (*pp_ini)->lifetime_req = ctx1->lifetime_req;
	 (*pp_ini)->qop		 = ctx1->qop;
	 (*pp_ini)->enter	 = ctx1->enter;
	 (*pp_ini)->cred_type	 = ctx1->cred_type;
	 (*pp_ini)->type         = CTX_INITIATOR;


	 (*pp_acc) = create_ctx_desc( p_gssfp_acc, "acceptor",
				      ctx2->ini_prname.value, ctx2->ini_prname.length, &(ctx2->can_nt),
				      ctx2->acc_prname.value, ctx2->acc_prname.length, &(ctx2->can_nt) );

	 (*pp_acc)->flags	 = ctx2->flags;
	 (*pp_acc)->flags_req	 = ctx2->flags_req;
	 (*pp_acc)->lifetime	 = ctx2->lifetime;
	 (*pp_acc)->lifetime_req = ctx2->lifetime_req;
	 (*pp_acc)->qop		 = ctx2->qop;
	 (*pp_acc)->enter	 = ctx2->enter;
	 (*pp_acc)->cred_type	 = ctx2->cred_type;
	 (*pp_acc)->type         = CTX_ACCEPTOR;


	 if ( (*pp_ini)==NULL  ||  (*pp_acc)==NULL ) {

	    XVEB((V_ERR, "%s(): creation of context structures failed, aborting!\n", this_Call ));

	 } else {

	    rc += import_sec_context( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->type,
				      &(ctx1->exp_ctx), &((*pp_ini)->ctx), &maj_stat );
	    if ( (*pp_ini)->ctx==GSS_C_NO_CONTEXT  ||  maj_stat!=GSS_S_COMPLETE ) {

	       XVEB((V_ERR, "%s(): import of initiating security context failed, aborting!\n",
			    this_Call));
	    
	    } else {

	       rc += import_sec_context( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->type,
				         &(ctx2->exp_ctx), &((*pp_acc)->ctx), &maj_stat );

	       if ( (*pp_acc)->ctx==GSS_C_NO_CONTEXT  ||  maj_stat!=GSS_S_COMPLETE ) {

		  XVEB((V_ERR, "%s(): import of accepting security context failed, aborting!\n",
			       this_Call ));
	       
	       } else {

		  success = TRUE;

		  rc += inquire_context( verbose_level, (*pp_ini)->gssfp, (*pp_ini)->ctx,
				         &ini_name1, &acc_name1, &lifetime1,
					 &mech_oid1, &flags1, &local_ini1, &open1, &maj_stat );

		  rc += verify_same_name( p_trclevel, (*pp_ini)->gssfp, "verifying initiator name from initiating context",
					  "separately transferred  ", ini_name1,
					  "from transferred context", (*pp_ini)->ini_name );

		  rc += verify_same_name( p_trclevel, (*pp_ini)->gssfp, "verifying acceptor name from initiating context",
				          "separately transferred  ", acc_name1,
					  "from transferred context", (*pp_ini)->acc_name );

		  if ( compare_oid( &(ctx1->ctx_mech), mech_oid1)!=0 ) {
		     rc++;
		     XVEB((V_ERR, "%s(): mechanism of initiating context mutated:\n", this_Call));
		     print_oid( 2, "separately transferred mech", mech_oid1      );
		     print_oid( 2, "mech from imported context ", &(ctx1->ctx_mech) );
		  }

		  rc += inquire_context( verbose_level, (*pp_acc)->gssfp, (*pp_acc)->ctx,
					 &ini_name2, &acc_name2, &lifetime2,
					 &mech_oid2, &flags2, &local_ini2, &open2, &maj_stat );

		  rc += verify_same_name( p_trclevel, (*pp_acc)->gssfp, "verifying initiator name from accepting context",
					  "separately transferred  ", ini_name2,
					  "from transferred context", (*pp_acc)->ini_name );
      
		  rc += verify_same_name( p_trclevel, (*pp_acc)->gssfp, "verifying acceptor name from accepting context",
					  "separately transferred  ", acc_name2,
					  "from transferred context", (*pp_acc)->acc_name );

		  if ( compare_oid( &(ctx2->ctx_mech), mech_oid1)!=0 ) {
		     rc++;
		     XVEB((V_ERR, "%s(): mechanism of accepting context mutated:\n", this_Call));
		     print_oid( 2, "separately transferred mech", mech_oid2      );
		     print_oid( 2, "mech from imported context ", &(ctx1->ctx_mech) );
		  }
		  
		  if ( flags1!=(*pp_ini)->flags ) {
		     rc++;
		     XVEB((V_ERR, "%s(): initiator context attributes mutated!\n", this_Call));
		     print_ctx_flags( 2, "inquire_context", "  established    ini context", &((*pp_ini)->flags) );
		     print_ctx_flags( 2, "inquire_context", "  newly imported ini context", &flags1 );
		  }

		  if ( flags2!=(*pp_acc)->flags ) {
		     rc++;
		     XVEB((V_ERR, "%s(): acceptor context attributes mutated!\n", this_Call));
		     print_ctx_flags( 2, "inquire_context", "  established    acc context", &((*pp_acc)->flags)  );
		     print_ctx_flags( 2, "inquire_context", "  newly imported acc context", &flags2 );
		  }

		  if ( (local_ini1==0 && local_ini2==0)
		       || ( local_ini1!=0 && local_ini2!=0) ) {
		     rc++;
		     XVEB((V_ERR, "%s(): inconsistent \"local_ini\" from inquire_context %d,%d !\n",
				  this_Call, (int)local_ini1, (int)local_ini2 ));
		  }

		  if ( (open1!=0 && open2==0) || (open1==0 && open2!=0) ) {
		     rc++;
		     XVEB((V_ERR, "%s(): \"open\" from inquire_context mutated %d->%d !\n",
				  this_Call, (int)open1, (int)open2 ));
		  }

		  /* when transferring a security context to a child process, */
		  /* we may want to copy these names into the p_gssfp struct  */
		  if ( p_copy_names!=0 ) {
		     if ( ctx1->can_nt.elements!=NULL  &&  ((*pp_ini)->gssfp)->can_nametype==GSS_C_NO_OID ) {
			set_static_nt_oid( (*pp_ini)->gssfp, nt_ini_oid, &(ctx1->can_nt) );
		     }
		     if ( ctx1->ini_prname.length>0  &&  ((*pp_ini)->gssfp)->initiator==NULL ) {
			VERBOSE_DUPMEM( ctx1->ini_prname.value, ctx1->ini_prname.length,
				        ((*pp_ini)->gssfp)->initiator, ((*pp_ini)->gssfp)->initiator_len );
		     }
		     if ( ctx1->acc_prname.length>0  &&  ((*pp_ini)->gssfp)->acceptor==NULL ) {
			VERBOSE_DUPMEM( ctx1->acc_prname.value, ctx1->acc_prname.length,
				        ((*pp_ini)->gssfp)->acceptor, ((*pp_ini)->gssfp)->acceptor_len );
		     }

		     if ( ctx2->can_nt.elements!=NULL  &&  ((*pp_acc)->gssfp)->can_nametype==GSS_C_NO_OID ) {
			set_static_nt_oid( (*pp_acc)->gssfp, nt_acc_oid, &(ctx2->can_nt) );
		     }
		     if ( ctx2->ini_prname.length>0  &&  ((*pp_acc)->gssfp)->initiator==NULL ) {
			VERBOSE_DUPMEM( ctx2->ini_prname.value, ctx2->ini_prname.length,
				        ((*pp_acc)->gssfp)->initiator, ((*pp_acc)->gssfp)->initiator_len );
		     }
		     if ( ctx2->acc_prname.length>0  &&  ((*pp_acc)->gssfp)->acceptor==NULL ) {
			VERBOSE_DUPMEM( ctx2->acc_prname.value, ctx2->acc_prname.length,
				        ((*pp_acc)->gssfp)->acceptor, ((*pp_acc)->gssfp)->acceptor_len );
		     }
		  
		  } /* p_copy_names!=0 */
	       
	       } /*  (*pp_acc)->ctx!=NULL   gssapi-import of accepting security context succeeded */

	    } /* (*pp_ini)->ctx!=NULL   gssapi-import of initiating security context succeded */

	 } /* (*pp_ini)!=NULL  &&  (*pp_acc)!=NULL  */

      } /* ctx2!=NULL     file-import of 2nd context endpoint succeeded */

   } /* ctx1!=NULL	  file-import of 1st context endpoint succeeded */


   if ( success==FALSE ) {
error:
      success = FALSE; /* just for safety when using goto error; ...   */
      rc += release_ctx_desc( pp_ini );
      rc += release_ctx_desc( pp_acc );

      if ( joblist!=NULL ) {
	 free( joblist );
	 joblist = NULL;
      }

   } else {

      XVEB((V_OK, verbose_level-2, "  %s(): import of security contexts succeeded.\n", this_Call));

   }

   if ( fp!=NULL ) {
      fclose(fp);  /* read-only -- ignoring errors from fclose() ... */
   }

   free_xfer_ctx( &ctx1 );
   free_xfer_ctx( &ctx2 );
   free_xfer_ctx( &ctx3 ); /* this is only for the rci value transfer */

   rc += release_name( p_gssfp_ini, &ini_name1  );
   rc += release_name( p_gssfp_acc, &ini_name2  );
   rc += release_name( p_gssfp_ini, &acc_name1  );
   rc += release_name( p_gssfp_acc, &acc_name2  );

   (*pp_joblist) = joblist;

   if ( pp_success!=NULL ) { (*pp_success) = success;      }
   if ( pp_rci!=NULL )     { (*pp_rci)     = (int) xp_rci; }

   return(rc);

} /* import_from_file() */




/*
 * write_to_file()
 *
 *
 */
int
write_to_file( int     p_trclevel,	FILE	          * p_fp,
	       int     p_initiator,	struct ctx_desc   * ctx,
	       int   * pp_success )
{
   char		     * this_Call GNU_UNUSED = "write_to_file";
   gss_buffer_desc     exp_ctx;
   gss_buffer_desc     name_buf_ini;
   gss_buffer_desc     name_buf_acc;
   xp_object_et	       type;
   OM_uint32	       maj_stat;
   int		       rc        = 0;
   int		       result;

   (*pp_success)  = FALSE;

   exp_ctx.value  = NULL;
   exp_ctx.length = 0;
   name_buf_ini.value  = NULL;
   name_buf_ini.length = 0;
   name_buf_acc.value  = NULL;
   name_buf_acc.length = 0;

   if ( ctx->ctx==NULL ) {
      XVEB((V_BUG, "%s(): need a context handle!\n", this_Call ));
      return(rc);
   }

   rc += export_sec_context( verbose_level, ctx->gssfp, ctx->type, &(ctx->ctx), &exp_ctx, &maj_stat );
   if ( maj_stat!=GSS_S_COMPLETE )
      goto error;

   /* accumulate statistics on the interprocess token size */
   (ctx->gssfp)->ctx_exported_count[ctx->type]++;
   if ( (ctx->gssfp)->ctx_exported_min[ctx->type]==0
        ||  (ctx->gssfp)->ctx_exported_min[ctx->type] > exp_ctx.length ) {
      (ctx->gssfp)->ctx_exported_min[ctx->type] = exp_ctx.length;
   }
   if ( (ctx->gssfp)->ctx_exported_max[ctx->type] < exp_ctx.length ) {
      (ctx->gssfp)->ctx_exported_max[ctx->type] = exp_ctx.length;
   }


   type   = (p_initiator) ? XP_CONTEXT_INI : XP_CONTEXT_ACC;
   result = write_object( p_trclevel, p_fp, type, exp_ctx.value, exp_ctx.length );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_OID_MECH, (ctx->gssfp)->mech, 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_FLAGS, &(ctx->flags), 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_FLAGS_REQ, &(ctx->flags_req), 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_LIFETIME, &(ctx->lifetime), 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_LIFETIME_REQ, &(ctx->lifetime_req), 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_TIME, &(ctx->enter), 0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_QOP, &(ctx->qop),  0 );
   if ( result!=0 ) goto error;

   result = write_object( p_trclevel, p_fp, XP_CRED_TYPE, &(ctx->cred_type), 0 );
   if ( result!=0 ) goto error;

   if ( (ctx->gssfp)->canonical_names_calls!=FALSE  &&  ctx->ini_name!=GSS_C_NO_NAME ) {
      rc += export_name( verbose_level, ctx->gssfp, ctx->ini_name, &name_buf_ini, &maj_stat );
      if ( name_buf_ini.length==0 || name_buf_ini.value==NULL )
	 goto error;

      result = write_object( p_trclevel, p_fp, XP_XPNAME_INI,
			     name_buf_ini.value, name_buf_ini.length );
      if ( result!=0 ) goto error;
   } /* canonical_names!=FALSE */

   if ( ctx->ini_prname.value!=NULL  &&  ctx->ini_prname.length ) {
      result = write_object( p_trclevel, p_fp, XP_PRNAME_INI,
			     ctx->ini_prname.value, ctx->ini_prname.length );
      if ( result!=0 ) goto error;
   }

   if ( (ctx->gssfp)->canonical_names_calls!=FALSE  &&  ctx->acc_name!=GSS_C_NO_NAME ) {
      rc += export_name( verbose_level, ctx->gssfp, ctx->acc_name, &name_buf_acc, &maj_stat );
      if ( name_buf_acc.length==0 || name_buf_acc.value==NULL )
	 goto error;

      result = write_object( p_trclevel, p_fp, XP_XPNAME_ACC,
			     name_buf_acc.value, name_buf_acc.length );
      if ( result!=0 ) goto error;
   } /* canonical_names!=FALSE */

   if ( ctx->acc_prname.value!=NULL  &&  ctx->acc_prname.length ) {
      result = write_object( p_trclevel, p_fp, XP_PRNAME_ACC,
			     ctx->acc_prname.value, ctx->acc_prname.length );
      if ( result!=0 ) goto error;
   }

   if ( (ctx->gssfp)->can_nametype!=GSS_C_NO_OID ) {
      result = write_object( p_trclevel, p_fp, XP_OID_NT, (ctx->gssfp)->can_nametype, 0 );
      if ( result!=0 ) goto error;
   }

   result = write_object( p_trclevel, p_fp, XP_TERMINATOR, NULL, 0 );
   if ( result!=0 ) goto error;

   (*pp_success) = TRUE;

   if ( 0 ) {
error:
      rc++;
   }

   rc += release_buffer( ctx->gssfp, &exp_ctx      );
   rc += release_buffer( ctx->gssfp, &name_buf_ini );
   rc += release_buffer( ctx->gssfp, &name_buf_acc );

   return(rc);

} /* write_to_file() */




/*
 * write_object()
 *
 *
 */
int
write_object( int             p_trclevel,     FILE   * p_fp,
	      xp_object_et    p_object_type,  void   * p_object,
	      size_t	      p_object_len )
{
   char	          * this_Call GNU_UNUSED = "write_object";
   unsigned char  * ptr;
   unsigned char  * extra_ptr = NULL;
   gss_OID	    oid;
   size_t	    extra_len = 0;
   size_t	    len;
   OM_uint32	    val;
   unsigned char    prefix[32];
   unsigned char    extra[128];

   prefix[0] = (Uchar) p_object_type;
   ptr = &prefix[1];
   
   switch( p_object_type ) {

      case XP_XPNAME_INI:
      case XP_XPNAME_ACC:
      case XP_PRNAME_INI:
      case XP_PRNAME_ACC:
      case XP_CONTEXT_INI:
      case XP_CONTEXT_ACC:
			extra_ptr = p_object;
			extra_len = p_object_len;
			break;


      case XP_RCI:
      case XP_QOP:
      case XP_LIFETIME:
      case XP_LIFETIME_REQ:
      case XP_TIME:	
      case XP_FLAGS:
      case XP_FLAGS_REQ: val = *( (OM_uint32 *)p_object);
			STORE_UINT32_MSB(val, extra);
			extra_ptr    = extra;
			extra_len    = sizeof(OM_uint32);
			break;


      case XP_CRED_TYPE:
			val = (OM_uint32) *( (sc_variants_et *)p_object);
			STORE_UINT32_MSB(val, extra);
			extra_ptr    = extra;
			extra_len    = sizeof(OM_uint32);
			break;


      case XP_OID_MECH:
      case XP_OID_NT:
			
			oid	     = (gss_OID) p_object;
			extra_ptr    = oid->elements;
			extra_len    = oid->length;
			break;


      case XP_TERMINATOR:
			extra[0]     = 0xff;
			extra_ptr    = &extra[0];
			extra_len    = 1;
			break;


      case XP_TMSG_JOBLIST:
			extra_ptr    = p_object;
			extra_len    = p_object_len;
			break;


      default:		XVEB((V_BUG, "%s(): Unknown object type %u for file export!\n",
				     this_Call, (Uint) p_object_type ));
			return(1);

   } /* switch( p_object_type ) */


   STORE_UINT32_MSB(extra_len, ptr);
   ptr += 4;

   len = 5;
   if ( fwrite( prefix, 1, len, p_fp )!=len ) {
write_error:
      XVEB((V_ERR, "%s(): fwrite(len=%u) failed: %s\n",
		   this_Call, (Uint) len, strerror(errno) ));
      return(1);
   }

   len = extra_len;
   if ( fwrite( extra_ptr, 1, len, p_fp )!=len )
      goto write_error;

   return(0);

} /* write_object() */




/*
 * read_from_file()
 *
 *
 */
int
read_from_file( int		      p_trclevel,
		FILE		    * p_fp,	      char		* p_filename,
		struct xfer_ctx_s  ** pp_xfer_ctx,    struct tmsg_job  ** pp_joblist )
{
   char              * this_Call GNU_UNUSED = "read_from_file";
   struct xfer_ctx_s * xc	    = NULL;
   char		     * ptr	    = NULL;
   char		     * hptr;
   size_t	       len;
   char		       header[32];
   xp_object_et	       object_type;
   int		       rc           = 0;
   int		       more         = TRUE;

   (*pp_xfer_ctx) = NULL;
   if ( pp_joblist!=NULL ) { (*pp_joblist)  = NULL; }

   len = sizeof( *xc );
   xc = calloc( 1, len );

   if ( xc==NULL ) {
mem_error:
      XVEB((V_ERR, "%s() malloc(%lu) failed!\n", this_Call, (Ulong) len ));
      goto error;
   }

   while( feof(p_fp)==0  &&  ferror(p_fp)==0  &&  rc==0  &&  more!=FALSE ) {
   
      len = 5;

      if ( fread(header, 1, len, p_fp)!=len ) {

	 if ( len>0  ||  feof(p_fp)==0  ) {
	    rc++;
io_error:
	    XVEB((V_ERR, "%s(): error while reading file \"%s\": %s\n",
			 this_Call, p_filename, strerror(errno) ));
	 }
	 break;

      } else { 

	 object_type = (xp_object_et) header[0];
	 hptr = &header[1];
	 len = LOAD_UINT32_MSB( hptr );
	 ptr = malloc(len+1); /* provide room for a terminating NUL char */
	 if ( ptr==NULL )
	    goto mem_error;

	 if ( fread(ptr, 1, len, p_fp)!=len ) {
	    goto io_error;
	 }

	 ptr[len] = '\0'; /* NUL-terminate printable string */

	 switch( object_type ) {

	    case XP_CONTEXT_INI:
	    case XP_CONTEXT_ACC:
		     if ( xc->exp_ctx.value!=NULL ) {
			XVEB((V_ERR, "%s(): duplicate context?!\n", this_Call));
			rc++;
		     } else {
			xc->exp_ctx.value   = ptr;
			xc->exp_ctx.length  = len;
			ptr                 = NULL;
			xc->initiator = (object_type==XP_CONTEXT_INI) ? TRUE : FALSE;
		     }
		     break;


            case XP_XPNAME_INI:
		     if ( xc->ini_xpname.value!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate exported initiator name!\n", this_Call));
		     } else {
			xc->ini_xpname.value  = ptr;
			xc->ini_xpname.length = len;
			ptr                 = NULL;
		     }
		     break;


	    case XP_PRNAME_INI:
		     if ( xc->ini_prname.value!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate printable initiator name!\n", this_Call));
		     } else {
			xc->ini_prname.value  = ptr;
			xc->ini_prname.length = len;
			ptr                 = NULL;
		     }
		     break;


	    case XP_XPNAME_ACC:
		     if ( xc->acc_xpname.value!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate exported acceptor name!\n", this_Call));
		     } else {
			xc->acc_xpname.value  = ptr;
			xc->acc_xpname.length = len;
			ptr		      = NULL;
		     }
		     break;


	    case XP_PRNAME_ACC:
		     if ( xc->acc_prname.value!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate printable acceptor name!\n", this_Call));
		     } else {
			xc->acc_prname.value  = ptr;
			xc->acc_prname.length = len;
			ptr		      = NULL;
		     }
		     break;


	    case XP_OID_MECH:
		     if ( xc->ctx_mech.elements!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate mechanism OID for context!\n", this_Call));
		     } else {
			xc->ctx_mech.elements = ptr;
			xc->ctx_mech.length   = (OM_uint32)len;
			ptr		      = NULL;
		     }
		     break;


	    case XP_OID_NT:
		     if ( xc->can_nt.elements!=NULL ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate canonial printable nametype OID!\n", this_Call));
		     } else {
			xc->can_nt.elements  = ptr;
			xc->can_nt.length    = (OM_uint32)len;
			ptr                  = NULL;
		     }
		     break;


	     case XP_LIFETIME:
		     if ( xc->lifetime>0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate context lifetime!\n", this_Call));
		     } else {
			xc->lifetime = LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_LIFETIME_REQ:
		     if ( xc->lifetime_req>0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate context lifetime_req!\n", this_Call));
		     } else {
			xc->lifetime_req = LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_TIME:
		     if ( xc->enter>0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate enter time!\n", this_Call));
		     } else {
			xc->enter = LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_FLAGS_REQ:
		     if ( xc->flags_req>0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate flags_req!\n", this_Call));
		     } else {
			xc->flags_req = LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_FLAGS:
		     if ( xc->flags>0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate flags!\n", this_Call));
		     } else {
			xc->flags = LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_CRED_TYPE:
		     if ( xc->cred_type!=0 ) {
			rc++;
			XVEB((V_ERR, "%s(): duplicate credentials type!\n", this_Call));
		     } else {
			xc->cred_type = (sc_variants_et) LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_QOP:
		     if ( xc->qop!=0 ) {  /* we won't detect multiple default qops ... */
			rc++;
			XVEB((V_ERR, "%s(): duplicate qop value!\n", this_Call));
		     } else {
			xc->qop = (OM_uint32) LOAD_UINT32_MSB(ptr);
		     }
		     break;


	     case XP_TERMINATOR:
		     more = FALSE;
		     break;


	     case XP_TMSG_JOBLIST:
		     if ( pp_joblist!=NULL ) {
			(*pp_joblist) = (struct tmsg_job *) ptr;
			ptr           = NULL;
		     } else {
			XVEB((V_BUG, "%s(): skipping XP_TMSG_JOBLIST while reading exported contexts!\n", this_Call));
		     }
		     break;


	     case XP_RCI:
		     xc->rci = (OM_uint32) LOAD_UINT32_MSB(ptr);
		     more = FALSE;
		     break;


	     default:
		     rc++;
		     XVEB((V_ERR, "%s(): unknown exported object id: %lu!\n",
				  this_Call, (Ulong) object_type ));
		     break;
	 }

	 if ( ptr!=NULL ) {
	    /* if nobody claimed this memory, then free() it */
	    free( ptr );
	 }

      } /* endif fread()==success */


   } /* end while() */


   if ( ferror(p_fp)!=0 ) {
      rc++;
      XVEB((V_ERR, "%s(): error reading context from file: %s\n",
		   this_Call, strerror(errno) ));
      goto error;
   }

   if (rc==0) {

      (*pp_xfer_ctx) = xc;
      xc	     = NULL;

   } else {

error:
      if ( xc!=NULL ) 
	 free_xfer_ctx( &xc );

   }

   return(rc);

} /* read_from_file() */




/*
 * free_xfer_ctx()
 *
 *
 */
void
free_xfer_ctx( struct xfer_ctx_s ** pp_xc )
{
   if ( *pp_xc!=NULL ) {
      if ( ((*pp_xc)->exp_ctx.value)    !=NULL )  { free( (*pp_xc)->exp_ctx.value     ); }
      if ( ((*pp_xc)->ctx_mech.elements)!=NULL )  { free( (*pp_xc)->ctx_mech.elements ); }
      if ( ((*pp_xc)->can_nt.elements)  !=NULL )  { free( (*pp_xc)->can_nt.elements   ); }
      if ( ((*pp_xc)->ini_xpname.value) !=NULL )  { free( (*pp_xc)->ini_xpname.value  ); }
      if ( ((*pp_xc)->ini_prname.value) !=NULL )  { free( (*pp_xc)->ini_prname.value  ); }
      if ( ((*pp_xc)->acc_xpname.value) !=NULL )  { free( (*pp_xc)->acc_xpname.value  ); }
      if ( ((*pp_xc)->acc_prname.value) !=NULL )  { free( (*pp_xc)->acc_prname.value  ); }
      memset( *pp_xc, 0, sizeof( **pp_xc ) );
      free( *pp_xc );
      *pp_xc = NULL;
   }

   return;

} /* free_xfer_ctx() */





/*
 * transfer_via_file()
 *
 *
 */
int
transfer_via_file( int                p_trclevel,  char		    * p_filename,
		   struct tmsg_job  * p_joblist,
		   struct ctx_desc ** pp_ini,	   struct ctx_desc ** pp_acc,
		   int	            * pp_success )
{
   char		     * this_Call GNU_UNUSED = "transfer_via_file";
   DLL_GSSFP_T	     * gssfp_ini = (*pp_ini)->gssfp;
   DLL_GSSFP_T	     * gssfp_acc = (*pp_acc)->gssfp;
   struct ctx_desc   * ini       = NULL;
   struct ctx_desc   * acc       = NULL;
   struct tmsg_job   * joblist   = NULL;
   int		       success   = FALSE;
   int		       rc        = 0;
   int		       rci;

   rc += export_to_file( p_trclevel, p_filename, p_joblist, 0,
			 pp_ini, pp_acc, &success );

   if ( success!=FALSE ) {

      rc += import_from_file( p_trclevel, p_filename, FALSE, &joblist,
			      gssfp_ini, gssfp_acc, &ini, &acc, &rci, &success );

      if ( success!=FALSE ) {

	 gssfp_ini->context_xferred++;
	 if ( gssfp_ini != gssfp_acc ) {
	    gssfp_acc->context_xferred++;
	 }

	 if ( joblist!=NULL ) {
	    rc += test_message_exchange( p_trclevel, "file-transferred job",
					 options.show_timing_data,
					 joblist, pp_ini, pp_acc, NULL, NULL, &success );
	 
	 }

      }

   }

   remove( p_filename );

   if ( joblist!=NULL ) { free( joblist ); joblist = NULL; }

   rc += release_ctx_desc( pp_ini );
   rc += release_ctx_desc( pp_acc );

   (*pp_ini) = ini;
   (*pp_acc) = acc;

   if ( pp_success!=NULL ) { (*pp_success) = success; }

   return(rc);

} /* transfer_via_file() */




/*
 * transfer_via_child()
 *
 *
 */
int
transfer_via_child( int		       p_trclevel,  struct tmsg_job  * p_joblist,
		    struct ctx_desc ** pp_ini,	    struct ctx_desc ** pp_acc,
		    int		     * pp_success )
{
   char		     * this_Call GNU_UNUSED = "transfer_via_child";
   DLL_GSSFP_T	     * gssfp_ini = (*pp_ini)->gssfp;
   DLL_GSSFP_T	     * gssfp_acc = (*pp_acc)->gssfp;
   struct ctx_desc   * ini	 = NULL;
   struct ctx_desc   * acc	 = NULL;
   struct tmsg_job   * joblist	 = NULL;
   int		       success   = FALSE;
   int		       rci       = 0;
   int		       rc        = 0;
   int		       sysret    = 0;
   int		       xerrno    = 0;
   char		       cmdline[1024];

   remove( PARENT_CTX_FILE );
   rc += export_to_file( p_trclevel, PARENT_CTX_FILE, p_joblist, 0,
			 pp_ini, pp_acc, &success );

   if ( success!=FALSE ) {
      remove( CHILD_CTX_FILE );
#if defined(__OS400__)
      sprintf( cmdline, "CALL %s parm('-c')", prog_exe_name );
#else  /* !__OS400__ */
      if ( NULL!=strchr(prog_exe_name, ' ') || NULL!=strchr(prog_exe_name, '\t') ) {
          sprintf( cmdline, "\"%s\" -c", prog_exe_name );
      } else {
          sprintf( cmdline, "%s -c", prog_exe_name );
      }
#endif /* !__OS400__ */
      fflush(vfp); /* safety: flush all pending output, so client can append */
      errno  = 0;
      sysret = system( cmdline );
      xerrno = errno;
      tail_logfile( logfp ); /* move to end of file -- client may have been writing */

#if defined(HAVE_POSIX_SYSTEM)
      if ( WEXITSTATUS(sysret)!=0 ) {
	 XVEB((V_ERR, "%s(): system(\"%s\")==%d\n", this_Call, cmdline,
	       (int)WEXITSTATUS(sysret) ));
	 if ( xerrno!=0 ) {
	    XVEB((V_ERR, "        errno = %d \"%s\"\n",
	       (int)xerrno, strerror(xerrno) ));
	 }
	 rc++;
      }
#elif defined(HAVE_DOSWIN_SYSTEM)
      if ( sysret!=0 ) {
	 XVEB((V_ERR, "%s(): system(\"%s\")==%d %s\n", this_Call, cmdline,
	       (int)sysret, strerror(xerrno) ));
      }
#endif
      rc += import_from_file( p_trclevel, CHILD_CTX_FILE, FALSE, &joblist,
			      gssfp_ini, gssfp_acc, &ini, &acc, &rci, &success );
      if ( rci>0 ) {
	 XVEB((V_ERR, "%s(): Child returned accumulated error code rc=%u\n",
		      this_Call, (Uint) rci));
      }
      rc += rci;

      if ( success!=FALSE ) {
	 gssfp_ini->context_xferred++;
	 gssfp_ini->context_xferred_child++;
	 if ( gssfp_ini != gssfp_acc ) {
	    gssfp_acc->context_xferred++;
	    gssfp_acc->context_xferred_child++;
	 }
      }

   }

   remove( CHILD_CTX_FILE  );
   remove( PARENT_CTX_FILE );

   if ( joblist!=NULL ) { free(joblist);  joblist = NULL; }

   if ( ini==NULL || acc==NULL ) {
      release_ctx_desc( &ini );
      release_ctx_desc( &acc );

   } else {

      rc += release_ctx_desc( pp_ini );
      rc += release_ctx_desc( pp_acc );

      (*pp_ini) = ini;
      (*pp_acc) = acc;
   }

   if ( pp_success!=NULL ) { (*pp_success) = success; }

   return(rc);

} /* transfer_via_child() */




#define PROG_OPT_TYPE_TERMINATOR   0
#define PROG_OPT_TYPE_UINT	   1
#define PROG_OPT_TYPE_UINT32	   2
#define PROG_OPT_TYPE_STRING	   3

typedef enum prog_options_e {
   PROG_OPT_NONE	      =  0,
   PROG_OPT_BOGUS_CHECK	      =  1,
   PROG_OPT_SAP_CONSTRAINTS   =  2,
   PROG_OPT_TRY_MSGPROT	      =  3,
   PROG_OPT_SHOW_TIMING_DATA  =  4,
   PROG_OPT_CROSS_PROC_XFER   =	 5,
   PROG_OPT_VERBOSE_LEVEL     =  6,
   PROG_OPT_NUM_PAR_CTX	      =	 7,
   PROG_OPT_MECH_TO_USE	      =  8,
   PROG_OPT_DLL_NAME_1	      =  9,
   PROG_OPT_TARGET_NAME	      = 10,
   PROG_OPT_FORCE_XFER	      = 11,
   PROG_OPT_ZAP_TRAILING_NULS = 12,
   PROG_OPT_WRAP_RANGE_LEVEL  = 13,
   PROG_OPT_TEST_ERRORS       = 14,
   PROG_OPT_ERROR_LOCATION    = 15,
   PROG_OPT_LOG_FILENAME      = 16,
   PROG_OPT_LOG_TEE	      = 17,
   PROG_OPT_DLL_NAME_2	      = 18,
   PROG_OPT_TRY_USER_ERRORS   = 19
} prog_options_et;




/*
 * write_single_option()
 *
 *
 * Return value:
 *   == 0       success
 *   <> 0       failure (fatal)
 */
int
write_single_option( FILE      * p_fp,		char	      * p_filename,
		     int         p_opt_type,	int	        p_option,
		     void      * p_opt_val,	size_t	        p_opt_len )
{
   char	       * this_Call GNU_UNUSED = "write_single_option";
   OM_uint32     optuint;
   OM_uint32     len;
   size_t	 wlen;
   char	         prefix[16];
   char		 value[16];
   char	       * ptr;
   int	         result    = 0;

   prefix[0] = (char)p_opt_type;
   prefix[1] = (char)p_option;
   switch( p_opt_type ) {

      case PROG_OPT_TYPE_UINT:
	       optuint = *((Uint *)p_opt_val);
	       STORE_UINT32_MSB( optuint, value );
	       p_opt_val = &value[0];
	       p_opt_len = sizeof(OM_uint32);
	       break;


      case PROG_OPT_TYPE_UINT32:
	       optuint = *((OM_uint32 *)p_opt_val);
	       STORE_UINT32_MSB( optuint, value );
	       p_opt_val = &value[0];
	       p_opt_len = sizeof(OM_uint32);
	       break;


      case PROG_OPT_TYPE_STRING:
	       break;


      case PROG_OPT_TYPE_TERMINATOR:
	       p_opt_val = NULL;
	       p_opt_len = 0;
	       break;

      default:
	       XVEB((V_BUG, "%s(): invalid option type %d!\n", this_Call, (int)p_opt_type ));
	       result = 1;
	       goto error;
   }

   len = (OM_uint32) p_opt_len;
   ptr = &prefix[2];
   STORE_UINT32_MSB( len, ptr );

   wlen = 6;
   if ( wlen!=fwrite( prefix, 1, wlen, p_fp ) ) {
      goto io_error;

   } else {

      if ( len>0 ) {
	 wlen = len;
	 if ( wlen!=fwrite( p_opt_val, 1, wlen, p_fp ) ) {
io_error:
	    result = 1;
	    XVEB((V_ERR, "%s(): fwrite(file=\"%.150s\",len=%u) failed: %s\n",
			 this_Call, p_filename, (Uint) wlen, strerror(errno) ));
	 }
      }
   }

error:
   return(result);

} /* write_single_option() */




/*
 * write_options()
 *
 *
 * Return value:
 *   == 0       success
 *   <> 0       failure (fatal)
 */
int
write_options( FILE   * p_fp,  char   * p_filename )
{
   char      * this_Call GNU_UNUSED = "write_options";
   int	       result    = 0;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_BOGUS_CHECK, &(options.bogus_check), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_SAP_CONSTRAINTS, &(options.sap_constraints), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_SHOW_TIMING_DATA, &(options.show_timing_data), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_TRY_MSGPROT, &(options.try_msgprot_anyway), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_CROSS_PROC_XFER, &(options.cross_process_xfer), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_FORCE_XFER, &(options.force_xfer), 0 );
   if (result!=0) goto error;

   /* options.child_process is not transferred/inherited */

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_ZAP_TRAILING_NULS, &(options.zap_trailing_nuls), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_TEST_ERRORS, &(options.test_errors), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_ERROR_LOCATION, &(options.error_location), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_LOG_TEE, &(options.log_tee), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_TRY_USER_ERRORS, &(options.try_user_errors), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_VERBOSE_LEVEL, &(options.verbose_level), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_NUM_PAR_CTX, &(options.num_parallel_contexts), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_WRAP_RANGE_LEVEL, &(options.wrap_range_level), 0 );
   if (result!=0) goto error;

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_UINT,
			  PROG_OPT_MECH_TO_USE, &(options.mech_to_use), 0 );
   if (result!=0) goto error;

   if ( options.dll_name_1!=NULL ) {
      result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_STRING,
			  PROG_OPT_DLL_NAME_1, options.dll_name_1, strlen(options.dll_name_1) );
      if (result!=0) goto error;
   }

   if ( options.dll_name_2!=NULL ) {
      result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_STRING,
			  PROG_OPT_DLL_NAME_2, options.dll_name_2, strlen(options.dll_name_2) );
      if (result!=0) goto error;
   }

   if ( options.target!=NULL ) {
      result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_STRING,
			  PROG_OPT_TARGET_NAME, options.target, strlen(options.target) );
      if (result!=0) goto error;
   }

   if ( options.logfile!=NULL ) {
      result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_STRING,
			  PROG_OPT_LOG_FILENAME, options.logfile, strlen(options.logfile) );
      if (result!=0) goto error;
   }

   result = write_single_option( p_fp, p_filename, PROG_OPT_TYPE_TERMINATOR,
			  PROG_OPT_NONE, NULL, 0 );

error:
   return(result);

} /* write_options() */




/*
 * write_options_to_file()
 *
 *
 */
int
write_options_to_file( char    * p_filename )
{
   char	       * this_Call GNU_UNUSED = "write_options_to_file";
   FILE	       * fp          = NULL;
   int	         result      = 0;

   fp = fopen( p_filename, "wb");
   if ( fp==NULL ) {
      result = 1;
      XVEB((V_ERR, "%s(): fopen(\"%.150s\", \"wb\") failed: %s\n",
	           this_Call, p_filename, strerror(errno) ));
   } else {

      result = write_options( fp, p_filename );

   }

   if ( fp!=NULL ) {
      if ( fclose(fp)!=0  &&  result==0 ) {
	 result = 1;
	 XVEB((V_ERR, "%s(): fclose(\"%.150s\") failed: %s\n",
		      this_Call, p_filename, strerror(errno) ));
      }
   }

   return(result);

} /* write_options_to_file() */




/*
 * read_single_option()
 *
 *
 */
int
read_single_option( FILE	 * p_fp,	  char	      * p_filename,
		    int		 * pp_opt,	  int	      * pp_opt_type,
		    void	** pp_opt_val,	  size_t      * pp_opt_len )
{
   char        * this_Call GNU_UNUSED = "read_single_option";
   char          prefix[16];
   char	         value[16];
   char        * tmpbuf	   = NULL;
   char	       * ptr;
   size_t        rlen, erlen;
   size_t	 opt_len;
   int	         result    = 0;

   (*pp_opt_type)    = PROG_OPT_TYPE_TERMINATOR;
   (*pp_opt)	     = PROG_OPT_NONE;
   (*pp_opt_val)     = NULL;
   (*pp_opt_len)     = 0;

   erlen = 6;
   rlen  = fread( &prefix[0], 1, erlen, p_fp );
   if ( rlen!=erlen ) {

      goto io_error;

   } else {

      (*pp_opt_type) = (int) prefix[0];
      (*pp_opt)	     = (int) prefix[1];
      ptr	     = &prefix[2];
      opt_len	     = (size_t) (LOAD_UINT32_MSB( ptr ));

      if ( (*pp_opt_type)==PROG_OPT_TYPE_UINT
	   ||  (*pp_opt_type)==PROG_OPT_TYPE_UINT32 ) {
	 erlen = 4;
	 rlen  = fread( &value[0], 1, erlen, p_fp );
	 if ( rlen!=erlen ) {
	    goto io_error;
	 }
      }

      switch( (*pp_opt_type) ) {

	 case PROG_OPT_TYPE_TERMINATOR:
	       break;


	 case PROG_OPT_TYPE_UINT:
	 case PROG_OPT_TYPE_UINT32:
	       (*pp_opt_len) = (size_t) (LOAD_UINT32_MSB(value));
	       break;


	 case PROG_OPT_TYPE_STRING:
	       tmpbuf = malloc( opt_len + 1 ); /* provide room for terminating NUL char */
	       if ( tmpbuf==NULL ) {

		  XVEB((V_ERR, "%s(): malloc(%lu) failed!\n", this_Call, (Ulong) opt_len ));
		  result = 1;

	       } else {

		  erlen		  = opt_len;
		  rlen		  = fread( tmpbuf, 1, erlen, p_fp );
		  tmpbuf[opt_len] = '\0'; /* NUL-terminate printable string */
		  (*pp_opt_val)   = tmpbuf;
		  (*pp_opt_len)   = opt_len;
		  tmpbuf          = NULL;

	       }
	       break;


	 default:
	       XVEB((V_BUG, "%s(): invalid option type %d in file \"%s\"!\n",
			    this_Call, (int)*pp_opt_type, p_filename ));
	       result = 1;
	       break;

      }

   }
      
   if ( ferror(p_fp)!=0 ) {
io_error:
      result = 1;
      XVEB((V_ERR, "%s(): fread(file=\"%.150s\", len=%u) failed: %s\n",
		   this_Call, p_filename, (Uint)erlen, strerror(errno) ));
   }

   if ( tmpbuf!=NULL ) { free(tmpbuf); tmpbuf = NULL; }

   return(result);

} /* read_single_option() */




/*
 * read_options()
 *
 *
 */
int
read_options( FILE   * p_fp,    char    * p_filename )
{
   char          * this_Call GNU_UNUSED = "read_options";
   int		   opt;
   int		   opt_type;
   void	 	 * opt_val;
   size_t	   opt_len;
   int		   result    = 0;

   for (;;) {

      result = read_single_option( p_fp, p_filename, &opt, &opt_type, &opt_val, &opt_len );
      
      if ( result!=0 )
	 break;

      if ( opt_type==PROG_OPT_TYPE_TERMINATOR )
	 break;

      switch( opt ) {
	 case PROG_OPT_BOGUS_CHECK:
	    (options.bogus_check)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_SAP_CONSTRAINTS:
	    (options.sap_constraints)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_SHOW_TIMING_DATA:
	    (options.show_timing_data)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_TRY_MSGPROT:
	    (options.try_msgprot_anyway)     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_CROSS_PROC_XFER:
	    (options.cross_process_xfer)     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_FORCE_XFER:
	    (options.force_xfer)	     = (bool_opt) opt_len;
	    break;

	 /* child process indicator is a local issue */

	 case PROG_OPT_ZAP_TRAILING_NULS:
	    (options.zap_trailing_nuls)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_TEST_ERRORS:
	    (options.test_errors)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_ERROR_LOCATION:
	    (options.error_location)	     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_VERBOSE_LEVEL:
	    (options.verbose_level)	     = (Uint) opt_len;
	    break;

	 case PROG_OPT_NUM_PAR_CTX:
	    (options.num_parallel_contexts)  = (Uint) opt_len;
	    break;

	 case PROG_OPT_WRAP_RANGE_LEVEL:
	    (options.wrap_range_level)	     = (Uint) opt_len;
	    break;

	 case PROG_OPT_MECH_TO_USE:
	    (options.mech_to_use)	     = (Uint) opt_len;
	    break;

	 case PROG_OPT_DLL_NAME_1:
	    (options.dll_name_1)	     = (char *) opt_val;
	    opt_val			     = NULL;
	    break;

	 case PROG_OPT_DLL_NAME_2:
	    (options.dll_name_2)	     = (char *) opt_val;
	    opt_val			     = NULL;
	    break;

	 case PROG_OPT_TARGET_NAME:
	    (options.target)		     = (char *) opt_val;
	    opt_val			     = NULL;
	    break;

	 case PROG_OPT_LOG_FILENAME:
	    (options.logfile)		     = (char *) opt_val;
	    opt_val			     = NULL;
	    break;

	 case PROG_OPT_LOG_TEE:
	    (options.log_tee)		     = (bool_opt) opt_len;
	    break;

	 case PROG_OPT_TRY_USER_ERRORS:
	    (options.try_user_errors)	     = (bool_opt) opt_len;
	    break;

	 default:
	    XVEB((V_BUG, "%s(): found invalid option %d in file \"%s\"\n",
			this_Call, (int)opt, p_filename ));
	    result = 1;
	    break;

      }

      if ( opt_val!=NULL ) {
	 free( opt_val );
	 opt_val  = NULL;
      }

   }

   return( result );

} /* read_options() */




/*
 * read_options_from_file()
 *
 *
 */
int
read_options_from_file( char    * p_filename )
{
   char       * this_Call GNU_UNUSED = "read_options_from_file";
   FILE	      * fp        = NULL;
   int	        result    = 0;

   fp = fopen( p_filename, "rb");
   if ( fp==NULL ) {
      result = 1;
      XVEB((V_ERR, "%s(): fopen(\"%.150s\", \"rb\") failed: %s\n",
	           this_Call, p_filename, strerror(errno) ));

   } else {

      clean_options();
      result = read_options( fp, p_filename );

   }

   if ( fp!=NULL ) {
      fclose(fp); /* ignore errors here -- we were only reading! */
      fp = NULL;
   }

   return(result);

} /* read_options_from_file() */


