static char sccsid[] = "@(#)rfcexec  	20.35.4.1	SAP	96/07/10";

/*
 *   R  F  C  E  X  E  C
 *
 *   rfc server program offering function modules
 *   for file and pipe access.
 *
 */

#undef __EXTENSIONS__     /* no Sun extensions */
#define _XOPEN_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>


/*
 * include RFC API
 */

/* SAP flag for OS/2 */
#if defined(OS2)
#define SAPonOS2_2x
#endif

/* SAP flag for Windows NT or 95 */
#ifdef _WIN32
#  define SAPonNT
#endif

/* SAP flag for Windows 3.xx */
#ifdef _WINDOWS
#  ifndef _WIN32
#    define SAPonWINDOWS
#  endif
#endif

#if !defined(SAPonUNIX) && !defined(SAPonNT) && !defined(SAPonWINDOWS) && !defined(SAPonOS2_2x) && !defined(SAPonOS400)
#include "saptype.h"
/*--------------------------------------------------------------------*/
/* The include file saptype.h is only for SAP internal make.          */
/*                                                                    */
/* If you want to make this program on your environment you must      */
/* compile and link with some flags depending on your operating       */
/* system.                                                            */
/*                                                                    */
/* Following flags are possible:                                      */
/*                                                                    */
/* UNIX:  #define SAPonUNIX                                           */
/* OS/2, Windows 3.x, NT or 95: already defined in this file          */
/*                                                                    */
/*--------------------------------------------------------------------*/
#endif

#include "saprfc.h"
#include "sapitab.h"

#ifdef WIN32
#define popen _popen
#define pclose _pclose
#endif

#ifdef OS2
#include "os2pipe.h"
#endif

#ifdef VMS
#include "vmspipe.h"
char *vmsstrerror (int error_code);
#endif

#ifdef SAPonWINDOWS
#include "windows.h"
#endif

#ifdef SAPonAS400
#include <qp0z1170.h>
#include "o4port.h"
#endif

#ifdef SIGCHLD
#ifndef SAPonVMS
#include <sys/wait.h>
#endif
#endif

/*
 * local prototypes & declarations
 */
static RFC_RC run        ( RFC_HANDLE handle, char * command,
		           unsigned comsize, ITAB_H itab_h, int mode,
                           RFC_INT *status );
static RFC_RC file       ( RFC_HANDLE handle, char * filename,
		           unsigned size, ITAB_H itab_h, int mode );
static RFC_RC pipe_itab  ( RFC_HANDLE handle,
		           char * command,
		           ITAB_H itab_h,
		           int    mode,
                           RFC_INT *status );

#ifdef SAPonWINDOWS
/* _loadds in protoype is necessary for QuickWin programs */

static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_pipe( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_file( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_exec( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds mail       ( RFC_HANDLE handle );
#else

static RFC_RC DLL_CALL_BACK_FUNCTION remote_pipe( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION remote_file( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION remote_exec( RFC_HANDLE handle );
static RFC_RC DLL_CALL_BACK_FUNCTION mail       ( RFC_HANDLE handle );
#endif

static RFC_RC file_access( RFC_HANDLE,
			   char * filename,
			   ITAB_H itab_h,
			   int    open_mode );
static RFC_RC install    ( RFC_HANDLE handle );

static int itab2file     ( FILE * fil, ITAB_H itab_h );
static int file2itab     ( ITAB_H itab_h, FILE * fil );

static char * remote_exec_docu( void );
static char * mail_docu       ( void );
static char * remote_file_docu( void );
static char * remote_pipe_docu( void );

#define RUN_FREE    0
#define RUN_WAIT    1
#define RUN_READ    2

#define FILE_READ   0
#define FILE_WRITE  1

static const unsigned table_size = 80;
static int            errorline;
static void help(void);

/*
 * error handler : ignore all errors and exit.
 * static functions cannot be exported in Windows,
 * so dont make myErrorhandler static
 */
void DLL_CALL_BACK_FUNCTION myErrorhandler ( char * errorId )
{
   exit( 1 );
}


/*
 *  main function for an RFC server program
 */

/*ARGSUSED*/
main( int argc, char ** argv )
{
   /* initialized data */
   static RFC_ENV    env;

   RFC_HANDLE handle;
   RFC_RC     rc;

   if (argc == 1)
   {
     help();
     return 0;
   }

   /*
    * install error handler
    */
   env.errorhandler = myErrorhandler;
   RfcEnvironment( &env );

   /*
    * accept connection
    *
    * (command line argv must be passed to RfcAccept)
    */
   handle = RfcAccept( argv );


   /*
    * static function to install offered function modules
    * ( see below )
    */
   rc = install(handle);
   if( rc != RFC_OK )
   {
     /*
      * if error occured,
      * close connection with error message and exit
      */
     RfcAbort( handle, "Initialization error" );
     exit(1);
   }


   /*
    * enter main loop
    */
   do
   {
     rc = RfcDispatch( handle );

   } while( rc == RFC_OK );


   /*
    * connection was closed by the client :
    * also close connection and terminate
    */

   RfcClose( handle );
   return 0;
} /* main */


/*ARGSUSED*/
static RFC_RC install( RFC_HANDLE handle )
{
   RFC_RC rc;

   /*
    * install the function modules offered
    *
    * the documentation texts are placed in static memory
    * within some static functions to keep things readable.
    */

   /*
    * Mail interface (only as example, not for official use)
    */
#ifdef SAPonWINDOWS
   rc = RfcInstallFunctionExt(handle, "RFC_MAIL",
			    (RFC_ONCALL)mail,
			    mail_docu() );
#else
   rc = RfcInstallFunction("RFC_MAIL",
			    mail,
			    mail_docu() );
#endif /* SAPonWindows */
   if( rc != RFC_OK ) return rc;

   /*
    * Pipe interface
    */
#ifdef SAPonWINDOWS
   rc = RfcInstallFunctionExt(handle, "RFC_REMOTE_PIPE",
			    (RFC_ONCALL)remote_pipe,
			    remote_pipe_docu() );
#else
   rc = RfcInstallFunction("RFC_REMOTE_PIPE",
			    remote_pipe,
			    remote_pipe_docu() );
#endif /* SAPonWindows */
   if( rc != RFC_OK ) return rc;

   /*
    * Text file interface
    */
#ifdef SAPonWINDOWS
   rc = RfcInstallFunctionExt(handle, "RFC_REMOTE_FILE",
			    (RFC_ONCALL)remote_file,
			    remote_file_docu() );
#else
   rc = RfcInstallFunction("RFC_REMOTE_FILE",
			    remote_file,
			    remote_file_docu() );
#endif /* SAPonWindows */
   if( rc != RFC_OK ) return rc;

   /*
    * Asynchronous pipe interface
    */
#ifdef SAPonWINDOWS
   rc = RfcInstallFunctionExt(handle, "RFC_REMOTE_EXEC",
			    (RFC_ONCALL)remote_exec,
			    remote_exec_docu() );
#else
   rc = RfcInstallFunction("RFC_REMOTE_EXEC",
			    remote_exec,
			    remote_exec_docu() );
#endif /* SAPonWindows */
   if( rc != RFC_OK ) return rc;

   return RFC_OK;
} /* install */

/*
 *
 * code representing the offered function modules
 *
 */

/*
 * function RFC_REMOTE_FILE
 *
 * _loadds in protoype is necessary for QuickWin programs
 *
 */
#ifdef SAPonWINDOWS
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_file(  RFC_HANDLE handle )
#else
static RFC_RC DLL_CALL_BACK_FUNCTION remote_file(  RFC_HANDLE handle )
#endif
{
    char          filename[256];
    RFC_PARAMETER parameter[4];
    RFC_TABLE     table[2];
    RFC_RC        rc;
    RFC_CHAR      write_flag;
    int           mode;

    /*
     * prolog
     *
     * receive parameters and tables
     */

    /* defaults */
    memset( filename, 0, sizeof( filename ) );
    write_flag = 0;

    /* 1st parameter */
    parameter[0].name = "FILE";
    parameter[0].nlen = 4;
    parameter[0].addr = (void *) filename;
    parameter[0].leng = sizeof(filename);
    parameter[0].type = TYPC;

    /* 2nd parameter */
    parameter[1].name = "WRITE";
    parameter[1].nlen = 5;
    parameter[1].addr = (void *) &write_flag;
    parameter[1].leng = sizeof(write_flag);
    parameter[1].type = TYPC;

    /* terminate array */
    parameter[2].name = NULL;

    /* table */
    table[0].name = "FILEDATA";
    table[0].nlen = 8;
    table[0].type = TYPC;
    table[0].leng = table_size;
    table[0].itmode = RFC_ITMODE_BYREFERENCE; /* pass by reference */

    /* terminate array */
    table[1].name = NULL;

    /* receive data */
    rc = RfcGetData( handle, parameter, table );

    if( rc != RFC_OK ) return rc;

    if( write_flag != 'X' )
      mode = FILE_READ;
    else
      mode = FILE_WRITE;

    /*
     * do the work
     */
    rc = file( handle, filename, sizeof(filename),
		      table[0].ithandle, mode );

    if( rc != RFC_OK ) return rc;

    /*
     * epilog
     *
     * return parameters and tables
     *
     * 'table' must be passed from RfcGetData without changes
     */

    /* no parameters */
    parameter[0].name = NULL;

    /* return to the caller */
    rc = RfcSendData( handle, parameter, table );

    return rc;
} /* remote_file */


/*
 * Function RFC_REMOTE_PIPE
 *
 * _loadds in protoype is necessary for QuickWin programs
 *
 */
#ifdef SAPonWINDOWS
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_pipe(  RFC_HANDLE handle )
#else
static RFC_RC DLL_CALL_BACK_FUNCTION remote_pipe(  RFC_HANDLE handle )
#endif
{
    char          command[256];
    RFC_PARAMETER parameter[4];
    RFC_TABLE     table[2];
    RFC_RC        rc;
    RFC_CHAR      read_flag = 0;
    int           mode;

    memset( command, 0, sizeof( command ) );

    parameter[0].name = "COMMAND";
    parameter[0].nlen = 7;
    parameter[0].addr = (void *) command;
    parameter[0].leng = sizeof(command);
    parameter[0].type = TYPC;
    parameter[1].name = "READ";
    parameter[1].nlen = 4;
    parameter[1].addr = (void *) &read_flag;
    parameter[1].leng = sizeof(read_flag);
    parameter[1].type = TYPC;
    parameter[2].name = NULL;

    table[0].name = "PIPEDATA";
    table[0].nlen = 8;
    table[0].type = TYPC;
    table[0].leng = table_size;
    table[0].itmode = RFC_ITMODE_BYREFERENCE;

    table[1].name = NULL;

    rc = RfcGetData( handle, parameter, table );
    if( rc != RFC_OK ) return rc;

    if( read_flag != 'X' )
      mode = RUN_WAIT;
    else
      mode = RUN_READ;

    rc = run( handle, command, sizeof(command),
		      table[0].ithandle, mode, (RFC_INT *)0 );

    if( rc != RFC_OK ) return rc;

    parameter[0].name = NULL;
    rc = RfcSendData( handle, parameter, table );

    return rc;
} /* remote_pipe */


/*
 * Function RFC_REMOTE_EXEC
 *
 * _loadds in protoype is necessary for QuickWin programs
 *
 */
#ifdef SAPonWINDOWS
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds remote_exec(  RFC_HANDLE handle )
#else
static RFC_RC DLL_CALL_BACK_FUNCTION remote_exec(  RFC_HANDLE handle )
#endif
{
    char          command[256];
    RFC_PARAMETER parameter[2];
    RFC_TABLE     table[2];
    RFC_RC        rc;

    memset( command, 0, sizeof( command ) );

    parameter[0].name = "COMMAND";
    parameter[0].nlen = 7;
    parameter[0].addr = (void *) command;
    parameter[0].leng = sizeof(command);
    parameter[0].type = TYPC;
    parameter[1].name = NULL;

    table[0].name = "PIPEDATA";
    table[0].nlen = 8;
    table[0].type = TYPC;
    table[0].leng = table_size;
    table[0].itmode = RFC_ITMODE_BYREFERENCE;

    table[1].name = NULL;

    rc = RfcGetData( handle, parameter, table );
    if( rc != RFC_OK ) return rc;

    rc = run( handle, command, sizeof(command),
		      table[0].ithandle, RUN_FREE, (RFC_INT *)0 );

    if( rc != RFC_OK ) return rc;

    parameter[0].name = NULL;
    rc = RfcSendData( handle, parameter, table );

    return rc;
} /* remote_exec */


/*
 * Function RFC_MAIL (only as example, not for official use)
 *
 * _loadds in protoype is necessary for QuickWin programs
 *
 */
#ifdef SAPonWINDOWS
static RFC_RC DLL_CALL_BACK_FUNCTION _loadds mail(  RFC_HANDLE handle )
#else
static RFC_RC DLL_CALL_BACK_FUNCTION mail(  RFC_HANDLE handle )
#endif
{
    char          user[256];
    char          command[sizeof(user) + 32];
    RFC_PARAMETER parameter[2];
    RFC_TABLE     table[2];
    RFC_RC        rc;

    memset( user, 0, sizeof( user ) );

    parameter[0].name = "USER";
    parameter[0].nlen = 4;
    parameter[0].addr = (void *) user;
    parameter[0].leng = sizeof(user);
    parameter[0].type = TYPC;
    parameter[1].name = NULL;

    table[0].name = "MAIL";
    table[0].nlen = 4;
    table[0].type = TYPC;
    table[0].leng = table_size;
    table[0].itmode = RFC_ITMODE_BYREFERENCE;

    table[1].name = NULL;

    rc = RfcGetData( handle, parameter, table );
    if( rc != RFC_OK ) return rc;

    sprintf( command, "mail %.*s", sizeof(user), user );

#if defined(SAPonNT) || defined(SAPonWINDOWS)
    RfcAbort(handle, "Function RFC_MAIL is not supported on Windows");
    exit(1);
#endif

    rc = run( handle, command, strlen( command ),
		      table[0].ithandle, RUN_WAIT, (RFC_INT *)0 );

    if( rc != RFC_OK ) return rc;

    parameter[0].name = NULL;
    rc = RfcSendData( handle, parameter, table );

    return rc;
} /* mail */


/*
 * static functions which do the work
 */

static RFC_RC run( RFC_HANDLE handle, char * command,
		   unsigned comsize, ITAB_H itab_h, int mode,
                   RFC_INT * status)
{
    int    i;
    RFC_RC rc;

    for( i = comsize - 1;
	 i > 0 && command[i] == ' ';
	 i-- )
    {
       command[i] = 0;
    }
    if( command[0] == 0 )
    {
      RfcAbort( handle, "don't know what to do" );
      exit(1);
    }
    rc = pipe_itab( handle, command, itab_h, mode, status );
    return rc;
} /* run */


static RFC_RC file( RFC_HANDLE handle, char * filename,
		   unsigned size, ITAB_H itab_h, int mode )
{
    int    i;
    RFC_RC rc;

    for( i = size - 1;
	 i > 0 && filename[i] == ' ';
	 i-- )
    {
       filename[i] = 0;
    }
    if( filename[0] == 0 )
    {
      RfcAbort( handle, "no filename given" );
      exit(1);
    }
    rc = file_access( handle, filename, itab_h, mode );
    return rc;
} /* file */


static RFC_RC file_access( RFC_HANDLE handle,
			   char * filename,
			   ITAB_H itab_h,
			   int    open_mode )
{
   FILE * fil;
   char * open_string;
   int    rc;

   if( open_mode == FILE_READ ) open_string = "r";
   else                         open_string = "w";

   fil = fopen( filename, open_string );
   if( fil == NULL )
   {
      char * ptr = strerror( errno );
      char   buffer[256];

      errorline = __LINE__;
      sprintf( buffer, "%s (%d)", ptr, errorline );
      RfcAbort( handle, buffer );
      exit(1);
   }

   if( itab_h != NULL )
   {
     switch( open_mode )
     {
       case FILE_READ :
	rc = file2itab( itab_h, fil );
	break;
       case FILE_WRITE :
	rc = itab2file( fil, itab_h );
	break;
     }
     if( rc )
     {
	char * ptr;
	char   buffer[256];
        if( rc == 2 ) ptr = "not enough memory";
	else          ptr = strerror( errno );


	sprintf( buffer, "%s (%d)", ptr, errorline );
	RfcAbort( handle, buffer );
        exit(1);
     }
   }
   fclose( fil );
   return RFC_OK;
} /* file_access */


static int itab2file( FILE * fil, ITAB_H itab_h )
{
   int    l = ItLeng( itab_h );
   char   buffer[256];
   int    rc;
   int    i;

   for( i = 1; ; i++ )
   {
     int    j;
     char * ptr = (char *) ItGetLine( itab_h, i );
     if( ptr == NULL ) break;

     j = l - 1;
     while( j >= 0 && ptr[j] == ' ' ) j--;
     j++;

     sprintf( buffer, "%.*s\n", j, ptr );
     rc = fputs( buffer, fil );
     if( rc == EOF )
     {
       errorline = __LINE__;
       return 1;
     }
   }
   return 0;
} /* itab2file */


static int file2itab( ITAB_H itab_h, FILE * fil )
{
   int    l = ItLeng( itab_h );
   char   buffer[256];

   for(;;)
   {
     char * p;
     int    read_l;

     p = fgets( buffer, sizeof(buffer), fil );
     if( p == NULL )
     {
	if( ferror( fil ) )
	{
	   errorline = __LINE__;
	   return 1;
	}
	else
	{
	   return 0;
	}
     }
     read_l = strlen(p);
     if( p[read_l - 1] == '\n' ) read_l--;
     if( read_l > l ) read_l = l;
     p = (char *) ItAppLine( itab_h );
     if( p == NULL )
     {
	errorline = __LINE__;
	return  2;
     }
     if( read_l < l )
     {
       memcpy( p, buffer, read_l );
       memset( p + read_l, ' ', l - read_l );
     }
     else
     {
       memcpy( p, buffer, l );
     }
   }
} /* file2itab */


static RFC_RC pipe_itab( RFC_HANDLE handle,
		      char * command,
		      ITAB_H itab_h,
		      int    mode,
                      RFC_INT *status )
{
    FILE * fil;
    int    rc;
    char * open_string;

#ifdef SAPonWINDOWS
    /* there are no Pipes for MS-Windows, so just start the new process
       and no communication between parent and child process available
     */
    {
      UINT rc;

      rc = WinExec(command,1);   /* Start child and show normal */

      if(rc < 32)
      {
        char   buffer[256];
	sprintf(buffer,
	  "Error %d by executing with WinExec (DOS command not allowed!)", rc);
	RfcAbort( handle, buffer );
	exit(1);
      }
      else
      {
        if ( status != (RFC_INT *)0 )
          *status = (RFC_INT)RFC_OK;
        return RFC_OK;
      }
    }
#else /* ! SAPonWINDOWS */
#ifdef SAPonAS400
    pid_t pid;
    char *pathvar;
    char *argv[20];
    char *ptr;
    char *cmd;
    int argc=0;
    int cmdlen, i;

    cmdlen = strlen(command)+1;
    if ((cmd = ptr = (char *) malloc (cmdlen)) == NULL) {
       char* p = strerror(errno);
       char buffer[256];
       sprintf(buffer, "malloc: %s (%d)", ptr, errorline);
       RfcAbort(handle, buffer);
       return(RFC_FAILURE);
    }

    memcpy((void *)ptr, (void *)command, cmdlen);

    i=0;
    argc=0;

    while ((i<cmdlen) && (argc<19)) {
       argv[argc++] = ptr+i;
       for (; ((i<cmdlen) && (ptr[i] != ' ') && (ptr[i] != 0)); i++)
           ;
       if (i<cmdlen) {
          ptr[i]=0;
          i++;
       }
    }
    argv[argc] = NULL;

    if ((pathvar = getenv("PATH")) == NULL)
       putenv("PATH=%LIBL%:");

    pid = as4_spawn(cmd, (char **)(&argv), NULL, AS4_SPAWN_NOWAIT);

    if (pid == -1) {
       char* p = strerror(errno);
       char buffer[256];
       free(cmd);
       sprintf(buffer, "spawn: %s (%d)", ptr, errorline);
       RfcAbort(handle, buffer);
       return(RFC_FAILURE);
    }

    free(cmd);
    return(RFC_OK);

    /* end of SAPonAS400 */
#else
    if( mode == RUN_READ ) open_string = "r";
    else                   open_string = "w";

#ifdef SIGCHLD
#ifndef SAPonVMS

    if( mode == RUN_FREE )
    {
      signal( SIGCHLD, SIG_IGN );
    }
    else
    {
      signal( SIGCHLD, SIG_DFL );
    }

    /*
     * eat up zombies
     *
     * (wait for some childs, but don't wait)
     *
     */
    while( waitpid (-1, (int *)0, WNOHANG ) > 0 )
    {
      ;
    }

#endif
#endif
#endif

    fil = popen( command, open_string );
    if( fil == NULL )
    {
      char * ptr = strerror( errno );
      char   buffer[256];

      errorline = __LINE__;
      sprintf( buffer, "%s (%d)", ptr, errorline );
      RfcAbort( handle, buffer );
      exit(1);
    }

    if( itab_h != NULL )
    {
      if( mode == RUN_READ )
      {
	rc = file2itab( itab_h, fil );
      }
      else
      {
	rc = itab2file( fil, itab_h );
      }
      if( rc != 0 )
      {
	char * ptr;
	char   buffer[256];
        if( rc == 2 ) ptr = "not enough memory";
	else          ptr = strerror( errno );

	sprintf( buffer, "%s (%d)", ptr, errorline );
	RfcAbort( handle, buffer );
	exit(1);
      }
    }
    if( mode == RUN_FREE )
    {
      fclose( fil );

      if ( status != (RFC_INT *)0 )
          *status = (RFC_INT)0;

    }
    else
    {
      rc = pclose( fil );

      if( rc > 0 )
      {
	 char buffer[512];
         if ( status != (RFC_INT *)0 )
             *status = (RFC_INT)rc;

	 sprintf( buffer, "%s terminated with exit code %d.",
		  command, rc );
         RfcAbort( handle, buffer );
         exit(1);
      }
    }

    if ( status != (RFC_INT *)0 )
        *status = (RFC_INT)RFC_OK;

    return RFC_OK;
#endif /* ! SAPonWindows */
} /* pipe_itab */


/*
 *
 * function module documentation
 *
 */

/*
 * insert newline characters to start a new line
 */
#undef  NL
#define NL "\n"

static char * remote_pipe_docu( void )
{
   static char docu[] =
 "allows to start programs and to pipe an internal table into "      NL
 "stdin. The caller waits until the program ends."                   NL
 ""                                                                  NL
 "IMPORTING"                                                         NL
 "  COMMAND        C(256)"                                           NL
 "    program name"                                                  NL
 "  READ           C(1)                 (Default SPACE)"             NL
 "    if READ is 'X' the internal table is filled from stdout"       NL
 "    instead of being piped into stdin."                            NL
 "TABLES"                                                            NL
 "  PIPEDATA       C(80)"                                            NL
 "    internal table serves as input to the started program, if"     NL
 "    READ is 'X' it is filled by the started program instead."      NL
   ;

   return docu;
} /* remote_pipe_docu */


static char * remote_file_docu( void )
{
   static char docu[] =
 "allows to read or write text files."                               NL
 ""                                                                  NL
 "IMPORTING"                                                         NL
 "  FILE           C(256)"                                           NL
 "    file name"                                                     NL
 "  WRITE          C(1)                 (Default SPACE)"             NL
 "    if WRITE is 'X' the internal table is written to the file,"    NL
 "    else it is filled from the file."                              NL
 "TABLES"                                                            NL
 "  FILEDATA       C(80)"                                            NL
 "    internal table serves as input to the file, if WRITE is 'X'"   NL
 "    else it is filled from the file."                              NL
   ;

   return docu;
} /* remote_file_docu */


static char * mail_docu( void )
{
   static char docu[] =
 "allows to send mail via e-mail (example only)."                    NL
 ""                                                                  NL
 "IMPORTING"                                                         NL
 "  USER           C(256)"                                           NL
 "    user name (e-mail address)."                                   NL
 "TABLES"                                                            NL
 "  MAIL           C(80)"                                            NL
 "    internal table containing mail."                               NL
   ;

   return docu;
} /* mail_docu */


static char * remote_exec_docu( void )
{
   static char docu[] =
 "allows to start programs and to pipe an internal table into "      NL
 "stdin without waiting for the program to end."                     NL
 ""                                                                  NL
 "IMPORTING"                                                         NL
 "  COMMAND        C(256)"                                           NL
 "    program name."                                                 NL
 "TABLES"                                                            NL
 "  PIPEDATA       C(80)"                                            NL
 "    internal table serves as input to the started program."        NL
   ;

   return docu;
} /* remote_exec_docu */


/*--------------------------------------------------------------------*/
/* Output help for starting program                                   */
/*--------------------------------------------------------------------*/
static void help(void)
{
#define NL "\n"
  printf( NL                                                             );
  printf( "Syntax for start and run in register mode:"                NL );
  printf( " "                                                         NL );
  printf( "  rfcexec [options]"                                       NL );
  printf( " "                                                         NL );
  printf( "  with"                                                    NL );
  printf( "  options = -D<destination with type 'R' in saprfc.ini>"   NL );
  printf( "          = -t             RFC-Trace on"                   NL );
  printf( "  or"                                                      NL );
  printf( "  options = -a<Program ID> e.g. <own host name>.rfcexec"   NL );
  printf( "          = -g<SAP gateway host name>        e.g.  hs0311" NL );
  printf( "          = -x<SAP gateway service>          e.g. sapgw00" NL );
  printf( "          = -t             RFC-Trace on"                   NL );
  printf( " "                                                         NL );
  return;
}

