static char sccsid[] = "@(#)startrfc    20.18     SAP     95/11/28";


/*
 *   S  T  A  R  T  R  F  C
 *
 *   generic rfc client program allowing to do RFC calls
 *   from the command line
 *
 *
 *   (C) Copyright SAP AG 1993
 */

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

/*
 * include RFC API
 */
#include "saprfc.h"
#include "sapitab.h"

/*
 * local prototypes & declarations
 */

/* static funcitions cant be exported in Windows, so dont make rfc_error static */
void DLL_CALL_BACK_FUNCTION rfc_error(char * error_id);

static void tables_out( void );
static int parse_commandline(char ** argv,
			     RFC_FUNCTIONNAME functionname,
			     RFC_PARAMETER  * exporting,
			     RFC_PARAMETER  * importing,
			     RFC_TABLE      * tables );
static void parse_export( char * string, RFC_PARAMETER * parameter );
static void parse_tables( char * string, RFC_TABLE * table );
static void help(void);

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

static void * dupstr( void * string );  /* strdup */

/* some limit for parameter to allow static allocation */
#define MAX_PARA 64

typedef struct
{
  FILE   * file;
  ITAB_H * itab;
}
OUT;

static ITAB_H out_itab;
static int    errorline;
static char   buffer[4000];  /* maximum line size */

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

main( int argc, char ** argv )
{
   static RFC_OPTIONS        rfc_opt;
   static RFC_CONNOPT_CPIC   rfc_connopt_cpic;
   static RFC_CONNOPT_R3ONLY rfc_connopt_r3only;
   RFC_FUNCTIONNAME          functionname;
   static RFC_PARAMETER             exporting[MAX_PARA];
   static RFC_PARAMETER             importing[MAX_PARA];
   static RFC_TABLE                 tables[MAX_PARA];
   RFC_ENV                   new_env;
   RFC_HANDLE                handle;
   RFC_RC                    rc;
   char *                    exception_ptr;

   if (argc < 2)
   {
     help();
     return 1;
   }

#ifdef SAPonApple
   MacInit(&argc, &argv);
#endif

   /*
    * sign on defaults
    */
   rfc_opt.client   = "000";
   rfc_opt.user     = "SAP*";
   rfc_opt.language = "E";
   rfc_opt.password = "PASS";
   rfc_opt.trace    = 0;
   rfc_opt.mode     = RFC_MODE_CPIC;

   /*
    * read connection and sign on data from the command line
    */

   if(RfcFileArgv(&argc, &argv) == RFC_FAILURE)
	   printf("Error handling reading or writing commandline arguments from/to file\n");
   RfcConnArgv( argv, &rfc_opt, &rfc_connopt_cpic,
		&rfc_connopt_r3only );

   /*
    * install error handler
    */

   new_env.allocate = NULL;
   new_env.errorhandler = rfc_error;
   RfcEnvironment( &new_env );

   /*
    * Create table of output tables
    */
   out_itab = ItCreate( "OUT",          /* some name */
			sizeof( OUT ),  /* width of the table */
			0, 0 );         /* internal use,
					 * can always be zero
					 */
   if( out_itab == NULL )
   {
     fprintf( stderr, "No memory\n" );
     return 1;
   }

   /*
    * look for function name and parameters,
    * read in tables from files
    */
   parse_commandline( argv, functionname,
                      exporting, importing, tables );

   if( argv[1] != NULL )
   {
      if( strcmp( argv[1], "-?" ) != 0 )
	 fprintf( stderr, "unknown option %s\n", argv[1] );
      help();
      return 1;
   }
   if( functionname[0] == 0 )
   {
      help();
      return 1;
   }

   /* connect */
   handle = RfcOpen( &rfc_opt );

   if( handle == RFC_HANDLE_NULL )
   {
      rfc_error( "open" );
      return 1;
   }

   /* issue the RFC call */
   rc = RfcCallReceive( handle,
                        functionname,
                        exporting,
                        importing,
                        tables,
                        &exception_ptr );

   /* check return code */
   switch( rc )
   {
      case RFC_OK:
        break;
      case RFC_EXCEPTION :
      case RFC_SYS_EXCEPTION :
        rfc_error( exception_ptr );
        break;
      default :
        rfc_error( "call/receive error" );
       break;
   }

   /* close the connection */
   RfcClose( handle );

   /* export received tables */
   tables_out();

   return 0;
}

/*
 * parse the command line for function name and parameters
 */
static int parse_commandline(
                      char ** argv,
                      RFC_FUNCTIONNAME functionname,
                      RFC_PARAMETER  * exporting,
                      RFC_PARAMETER  * importing,
                      RFC_TABLE      * tables )
{
  char ** argv_out = argv;
  char *  str;
  int     token_2;
  int     exp_count = 0,
          tab_count = 0;

  importing = importing;   /* to avoid compiler warnings */

  while( argv[0] != NULL )
  {
    if( argv[0][0] == '-' )
    {
  	str = argv[0] + 2;
	if( argv[0][1] == 0 || str[0] == 0 )
	{
	   str = argv[1];
	   token_2 = 1;
	}
	else token_2 = 0;

        /* option found */
        switch( argv[0][1] )
        {
           case 'F' :  /* function name */
             if( str == NULL ) return 1;
             strncpy( functionname, str,
		      sizeof( RFC_FUNCTIONNAME ) - 1);
             break;
           case 'E' :  /* exporting parameter */
             if( str == NULL ) return 1;
             if( exp_count >= MAX_PARA ) return 2;
             parse_export( str, exporting + exp_count );
             exp_count++;
             break;
           case 'T' :  /* table */
             if( str == NULL ) return 1;
             if( tab_count >= MAX_PARA ) return 2;
             parse_tables( str, tables + tab_count );
             tab_count++;
             break;
           default  :
	   *argv_out++ = argv[0];
           token_2 = 0;
	   break;
	}
	if( token_2 ) argv += 2;
	else          argv++;
    }
    else
    {
      *argv_out++ = *argv++;
    }
  }
  *argv_out = NULL;
  return 0;
}

static void parse_export( char * string, RFC_PARAMETER * parameter )
{
    char * ptr = strchr( string, '=' );
    char * name;
    char * value;

    /*
     * supported syntax :
     *
     * -E NAME=VALUE
     *
     */

    if( ptr == NULL )
    {
       fprintf( stderr, "format error : %s\n", string );
       help();
       exit(1);
    }
    *ptr++ = 0;
    name   = dupstr( string );
    value  = dupstr( ptr );
    if( name == NULL || value == NULL )
    {
       fprintf( stderr, "no memory.\n" );
       exit(1);
    }
    parameter[0].name = name;
    parameter[0].nlen = strlen( name );
    parameter[0].addr = value;
    parameter[0].leng = strlen( value );
    parameter[0].type = TYPC;

    return;
}

static void parse_tables( char * string, RFC_TABLE * table )
{
    int    t_size;
    char   str_b[256];
    char * t_name;
    char * t_io1;
    char * t_io2;
    char * t_io;
    char * f_name;
    char * name;
    char * ptr;
    ITAB_H itab_h;
    FILE * file_r, * file_w;
    OUT  * out;

    /*
     * supported syntax :
     *
     * -T NAME,123,r=/tmp/file1,w=/tmp/file2
     *
     */

    strncpy( str_b, string , sizeof( str_b ) - 1 );
    str_b[sizeof( str_b ) - 1] = 0;

    t_name = strtok( str_b, "," );
    ptr    = strtok( NULL, "," );
    if( ptr == NULL ) goto format_error;
    t_size = atoi( ptr );
    ptr    = strtok( NULL, "," );
    if( ptr == NULL ) goto format_error;
    t_io1  = ptr;
    t_io2  = strtok( NULL, "," );

    if( t_name[0] == 0 || t_size <= 0 || t_io1[0] == 0 )
    {
      goto format_error;
    }
    itab_h = ItCreate( t_name, t_size, 0 , 0 );
    if( itab_h == NULL ) goto no_memory;

    t_io = t_io1;
    name = dupstr( t_name );
    if( name == NULL ) goto no_memory;

    table[0].name     = name;
    table[0].nlen     = strlen( name );
    table[0].ithandle = itab_h;
    table[0].leng     = t_size;
    table[0].type     = TYPC;

    while( t_io != NULL )
    {

      if( t_io[0] != 0 )
      {
	 if( t_io[1] != '=' ) goto format_error;

	 switch( t_io[0] )
	 {
	   case 'r' :
	     f_name = t_io + 2;
	     if( strcmp( f_name, "-" ) == 0 )
		file_r = stdin;
	     else
		file_r = fopen( f_name, "r" );
	     if( file_r == NULL )
	     {
	       perror( f_name );
	       exit( 1 );
	     }
	     file2itab( itab_h, file_r );
	     break;
	   case 'w' :
	     f_name = t_io + 2;
	     if( strcmp( f_name, "-" ) == 0 )
		file_w = stdout;
	     else
		file_w = fopen( f_name, "w" );
	     if( file_w == NULL )
	     {
	       perror( f_name );
	       exit( 1 );
	     }
	     out = ItAppLine( out_itab );
	     if( out == NULL ) goto no_memory;
	     out->file = file_w;
	     out->itab = itab_h;
	     break;
	   default :
	     goto format_error;
	 }
      }
      if( t_io == t_io1 ) t_io = t_io2;
      else                t_io = NULL;

    }

    return;

format_error :
    fprintf( stderr, "format error : %s\n", string );
    help();
    exit(1);

no_memory :
      fprintf( stderr, "no memory.\n" );
      exit(1);
}

static void tables_out( void )
{
   int i;

   for( i = 1; ;i++ )
   {
     OUT * out = (OUT *) ItGetLine( out_itab, i );

     if( out == NULL ) break;
     itab2file( out->file, out->itab );
   }
   return;
}


void DLL_CALL_BACK_FUNCTION rfc_error( char * operation )
{
   RFC_ERROR_INFO error_info;
   memset( &error_info, 0, sizeof( error_info ) );
   RfcLastError( &error_info );
   fprintf( stderr, "RFC error : operation/code %s\n", operation );
   fprintf( stderr, "Error info :\n" );
   fprintf( stderr, "key     : %s\n", error_info.key );
   fprintf( stderr, "status  : %s\n", error_info.status );
   fprintf( stderr, "message : %s\n", error_info.message );
   fprintf( stderr, "internal: %s\n", error_info.intstat );
   return;
}

static int itab2file( FILE * file, ITAB_H itab_h )
{
   int    l = ItLeng( itab_h );
   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, file );
     if( rc == EOF )
     {
       errorline = __LINE__;
       return 1;
     }
   }
   return 0;
}

static int file2itab( ITAB_H itab_h, FILE * file )
{
   int    l = ItLeng( itab_h );

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

     p = fgets( buffer, sizeof(buffer), file );
     if( p == NULL )
     {
	if( ferror( file ) )
	{
	   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 );
     }
   }
}

static void RfcConnArgvHelp ( FILE * file )
{
   fprintf( file, 
      "RFC connection options:\n\n" );
   fprintf( file, 
      "  -d <destination>      name of the RFC destination.\n"
      "           Necessary, if you are using a 'sideinfo' file.\n\n" );
   fprintf( file,
      "  -2                    SNA mode on.\n"
      "           You must set this if you want to connect to R/2.\n"
      "           All other conection data must be suppplied by a\n"
      "           sideinfo file.\n\n" );
   fprintf( file,
      "  -3                    R/3 mode on.\n"
      "           You must set this if you want to connect to R/3.\n"
      "           Specify the following options:\n\n" );
   fprintf( file,
      "      -h <hostname>         hostname of the R/3 target system.\n\n" );
   fprintf( file,
      "      -s <system number>    system number of the target system.\n"
      "           this determines the TCP/IP service to be used to connect\n"
      "           to the R/3 system. The default value is 0 and the default\n"
      "           service being used then is sapgw00.\n\n" );
#ifndef SAPonWINDOWS
   fprintf( file,
      "      -gui               start sapgui\n"
      "           to allow dynpro and graphics processing\n"
      "           (3.0C or later required on the target system).\n\n" );  
#endif                       
   fprintf( file, 
      "      Using an intermediate SAP gateway, specify:\n"
      "           -g <gateway host>\n"
      "           -x <gateway service>\n"
      "           (must not be used with -gui or -debug option).\n\n" );      
#ifndef SAPonWINDOWS
   fprintf( file,
      "  -balanced             load balancing mode.\n"
      "           Another way to connect to R/3, if the R/3 system is 3.0C\n"
      "           or later and workload balancing is active on that system.\n"
      "           Requests are automatically routed to the application server\n"
      "           having the best response times in the moment.\n"
      "           Specify the following options:\n\n" );  
   fprintf( file,
      "      -h <host name>     hostname of R/3's message server.\n\n" );
   fprintf( file,
      "      -s <system name>   name of the target system.\n"
      "           This determines the TCP/IP service to be used to connect\n"
      "           to the R/3 system. \n"
      "           The system name is a 3 letter word. If the system name\n"
      "           is XXX, the service being used is sapXXX.\n\n" );
   fprintf( file,
      "      -g <group name>    name of application server group.\n"
      "           The default is PUBLIC.\n\n" );
   fprintf( file,
      "      -gui               start sapgui\n"
      "           to allow dynpro and graphics processing.\n\n" );                   
#endif
   fprintf( file,
      "additional options:\n\n" );
   fprintf( file,
      "  -t              turn trace on.\n"
      "           all operations are written to the trace file 'dev_rfc'\n\n" );
#ifndef SAPonWINDOWS
   fprintf( file,
      "  -debug          turn ABAP/4 debugging mode on.\n"
      "           this can only be done, if sapgui is installed on the client\n"
      "           system and the target system has version 3.0C or later.\n\n" );
#endif
   fprintf( file, 
      "RFC logon data:\n\n");
   fprintf( file, 
      "  -u <userid>      SAP userid.\n\n" );
   fprintf( file, 
      "  -p <password>    password.\n\n" );
   fprintf( file, 
      "  -c <client>      client.\n\n" );
   fprintf( file, 
      "  -l <language>    logon language.\n\n" );

   return;
}



static void help( void )
{
#define NL "\n"
   printf( NL );
   printf("RFC command line interface"                              NL NL );
   printf("Syntax :"                                                NL );
   printf("         startrfc [connect options] <function options>"  NL NL );
   printf("with"                                                    NL );
   printf("function options ="                                      NL );
   printf("          -F <function module"                           NL );
   printf("          -E <parameter>=<value>"                        NL );
   printf("          -T <table name>,<width>,[r=<file>][,w=<file>]" NL );
   printf("             where <file> is a path name to read from (r)" NL );
   printf("             or write to (w) the internal table. "         NL );
   printf("             If <file> is -, stdin or stdout is used."   NL NL );

   RfcConnArgvHelp( stdout );

   printf( "further options:"                                       NL NL );
   printf( "         -i <input file for argv>       "               NL );
   printf( "         -o <output file for argv>      "               NL );
   printf("          -?                             this text"      NL NL );
}

static void * dupstr( void * string )
{
   int    l    = strlen( string );
   void * ptr  = malloc( l + 1 );

   if( ptr != NULL )
   {
      strcpy( ptr, string );
   }
   return ptr;
}
