#ifndef lint
static char *RCSid = "$Id: rxqueue.c,v 1.12 2003/10/06 09:54:42 florian Exp $";
#endif

/*
 *  The Regina Rexx Interpreter
 *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define NO_CTYPE_REPLACEMENT
#include "rexx.h"

#ifdef HAVE_CONFIG_H
# include "config.h"
#else
# include "configur.h"
#endif

#ifdef WIN32
# if defined(_MSC_VER)
#  if _MSC_VER >= 1100
/* Stupid MSC can't compile own headers without warning at least in VC 5.0 */
#   pragma warning(disable: 4115 4201 4214 4514)
#  endif
#  include <windows.h>
#  if _MSC_VER >= 1100
#   pragma warning(default: 4115 4201 4214)
#  endif
# else
#  include <windows.h>
# endif
# include <io.h>
#else
# ifdef HAVE_SYS_SOCKET_H
#  include <sys/socket.h>
# endif
# ifdef HAVE_NETINET_IN_H
#  include <netinet/in.h>
#  endif
# ifdef HAVE_SYS_SELECT_H
#  include <sys/select.h>
# endif
# ifdef HAVE_NETDB_H
#  include <netdb.h>
# endif
# ifdef HAVE_ARPA_INET_H
#  include <arpa/inet.h>
# endif
#endif

#ifdef __LCC__
# include <winsock2.h>
#endif

#include <string.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif

#include "extstack.h"

#define SUCCESS(a) ((a)&&(a)->value[0] == '0')

/*
 * debugging is turned off. You can turn it on by the command line option "-D".
 */
static int debug = 0 ;
#define DEBUGDUMP(x) { if ( debug ) \
                          {x;}      \
                     }

char *buff=NULL;
unsigned int bufflen=0;

#if !defined(HAVE_STRERROR)
/*
 * Sigh! This must probably be done this way, although it's incredibly
 * backwards. Some versions of gcc comes with a complete set of ANSI C
 * include files, which contains the definition of strerror(). However,
 * that function does not exist in the default libraries of SunOS.
 * To circumvent that problem, strerror() is #define'd to get_sys_errlist()
 * in config.h, and here follows the definition of that function.
 * Originally, strerror() was #defined to sys_errlist[x], but that does
 * not work if string.h contains a declaration of the (non-existing)
 * function strerror().
 *
 * So, this is a mismatch between the include files and the library, and
 * it should not create problems for Regina. However, the _user_ will not
 * encounter any problems until he compiles Regina, so we'll have to
 * clean up after a buggy installation of the C compiler!
 */
const char *get_sys_errlist( int num )
{
   extern char *sys_errlist[] ;
   return sys_errlist[num] ;
}
#endif

streng *Str_upper( streng *str )
{
   int i;

   for ( i = 0; i < PSTRENGLEN( str ); i++ )
   {
      if ( islower( str->value[i] ) )
         str->value[i] = (char)toupper( str->value[i] );
   }
   return str;
}

int send_all( int sock, char *action )
{
   /*
    * Send everything in stdin to the socket. Each LF encountered
    * results in a send.
    * This code borrowed from rxstack.c in REXX/imc
    */
   int c,rc;
   unsigned int len;
   streng *result;

   for ( ; ; )
   {
      len = 0;
      while ( ( c = getchar() ) != EOF )
      {
         if ( c == REGINA_EOL )
         {
#if defined(DOS) || defined(OS2) || defined(WIN32)
            if (len && ( buff[len-1] == REGINA_CR ) )
               len--;
#endif
            break;
         }
         if ( len >= bufflen
         && (( buff = realloc( buff, bufflen <<= 1 ) ) == NULL ) )
         {
           showerror( ERR_STORAGE_EXHAUSTED, 0, ERR_STORAGE_EXHAUSTED_TMPL );
           exit( ERR_STORAGE_EXHAUSTED );
         }
         buff[len++] = (char)c;
      }
      if ( (c == EOF || feof(stdin) )
      &&  !len)
         break;
      DEBUGDUMP(printf("--- Queue %s ---\n", action[0] == RXSTACK_QUEUE_FIFO ? "FIFO" : "LIFO"););
      rc = send_command_to_rxstack( NULL, sock, action, buff, len );
      if ( rc != -1 )
      {
         result = read_result_from_rxstack( NULL, sock, RXSTACK_HEADER_SIZE );
         DROPSTRENG( result );
      }
   }
   return 0;
}

char *get_unspecified_queue( void )
{
   char *rxq = getenv( "RXQUEUE" );

   if ( rxq == NULL )
      rxq = "SESSION";
   return rxq;
}

char *force_remote( char *rxq )
{
   char *h ;

   if ( strchr(rxq, '@' ) == NULL )
   {
      if ( ( h = malloc( strlen( rxq ) + 2 ) ) != NULL )
      {
         strcpy( h, rxq ) ;
         strcat( h, "@" ) ;
         return h ;
      }
   }
   return rxq ;
}

char *get_action( char *parm )
{
   char *action=NULL;

   if ( strcmp( parm, "/fifo" ) == 0 )
      action = RXSTACK_QUEUE_FIFO_STR;
   else if ( strcmp( parm, "/lifo" ) == 0 )
      action = RXSTACK_QUEUE_LIFO_STR;
   else if ( strcmp( parm, "/clear" ) == 0 )
      action = RXSTACK_EMPTY_QUEUE_STR;
   else if ( strcmp( parm, "/pull" ) == 0 )
      action = RXSTACK_PULL_STR;
   else if ( strcmp( parm, "/queued" ) == 0 )
      action = RXSTACK_NUMBER_IN_QUEUE_STR;
   else
   {
      showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INVALID_SWITCH, ERR_RXSTACK_INVALID_SWITCH_TMPL, "/fifo, /lifo, /clear, /queued, /pull" );
      /* not setting action will exit */
   }
   return action;
}

void junk_return_from_rxstack( int sock, streng *header )
{
   int length;
   streng *result;

   length = get_length_from_header( NULL, header );
   if ( length )
   {
      result = read_result_from_rxstack( NULL, sock, length );
      if ( result )
         DROPSTRENG( result );

   }
}

int cleanup( void )
{
#ifdef WIN32
   WSACleanup();
#endif
   if ( buff )
      free( buff );
   return 0;
}

int main( int argc, char *argv[])
{
   int sock,rc=0,num;
   char *action;
   streng *queue=NULL,*server_name=NULL;
   char *in_queue=NULL;
   streng *result;
   Queue q;
#ifdef WIN32
   WORD wsver = (WORD)MAKEWORD(1,1);
   WSADATA wsaData;
#endif

#ifdef WIN32
   if ( WSAStartup( wsver, &wsaData ) != 0 )
   {
      showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_NO_WINSOCK, ERR_RXSTACK_NO_WINSOCK_TMPL, WSAGetLastError() );
      exit(ERR_RXSTACK_NO_WINSOCK);
   }
#endif

   argv++ ;
   argc-- ;

   if ( getenv( "RXDEBUG" ) != NULL )
      debug = 1 ;

   if ( ( argc >= 1 )
     && ( ( strcmp( *argv, "-D" ) == 0 ) || ( strcmp( *argv, "/D" ) == 0 ) ) )
   {
      debug = 1 ;
      putenv( "RXDEBUG=1" ) ;
      argc-- ;
      argv++ ;
   }

   action = NULL;
   /*
    * Process the command line
    */
   if ( argc == 0 )
   {
      /*
       * "rxqueue"
       */
      action = RXSTACK_QUEUE_FIFO_STR;
      in_queue = get_unspecified_queue();
   }
   else if ( argc == 1 )
   {
      /*
       * "rxqueue queue"
       * or
       * "rxqueue /switch"
       */
      if ( argv[0][0] == '/' )
      {
         /*
          * Only parameter is a switch
          */
         in_queue = get_unspecified_queue();
         action = get_action( argv[0] );
      }
      else
      {
         /*
          * Only parameter is a queue name
          */
         in_queue = argv[0];
         action = RXSTACK_QUEUE_FIFO_STR;
      }
   }
   else if ( argc == 2 )
   {
      /*
       * "rxqueue queue /switch"
       */
      in_queue = argv[0];
      if ( argv[1][0] == '/' )
      {
         /*
          * Parameter is a switch
          */
         action = get_action( argv[1] );
      }
   }
   else
   {
      /*
       * ERROR
       */
      fprintf(stderr, "Invalid number of parameters\n");
      rc = 1;
   }
   in_queue = force_remote( in_queue ) ;
   if ( action )
   {
      queue = MAKESTRENG( strlen( in_queue ) );
      if ( queue == NULL )
      {
         showerror( ERR_STORAGE_EXHAUSTED, 0, ERR_STORAGE_EXHAUSTED_TMPL );
         exit(ERR_STORAGE_EXHAUSTED);
      }
      memcpy( PSTRENGVAL( queue ), in_queue, PSTRENGLEN( queue ) );
      queue->len = strlen( in_queue ) ;
      /*
       * Parse the queue to determine server address
       * and queue name
       */
      if ( parse_queue( NULL, queue, &q ) == 1 )
      {
         if ( PSTRENGLEN( queue ) == 0 )
         {
            DROPSTRENG( queue );
            queue = MAKESTRENG( 7 );
            if ( queue == NULL )
            {
               showerror( ERR_STORAGE_EXHAUSTED, 0, ERR_STORAGE_EXHAUSTED_TMPL );
               exit(ERR_STORAGE_EXHAUSTED);
            }
            memcpy( PSTRENGVAL( queue ), "SESSION", 7 );
            queue->len = 7 ;
         }
         DEBUGDUMP(printf( "queue: <%.*s> server: %.*s<%d> Port:%d\n", PSTRENGLEN( queue ), PSTRENGVAL( queue ), PSTRENGLEN( q.u.e.name ), PSTRENGVAL( q.u.e.name ), q.u.e.address, q.u.e.portno ););
         sock = connect_to_rxstack( NULL, &q );
         if ( sock < 0 )
         {
            cleanup();
            exit(ERR_RXSTACK_CANT_CONNECT);
         }
         /*
          * Now process the actual command
          */
         switch( action[0] )
         {
            case RXSTACK_QUEUE_FIFO:
            case RXSTACK_QUEUE_LIFO:
               DEBUGDUMP(printf("--- Queue %s ", action[0] == RXSTACK_QUEUE_FIFO ? "FIFO" : "LIFO"););
               /*
                * Allocate the initial buffer
                */
               if (( buff = (char *)malloc( bufflen = 256 ) ) == NULL )
               {
                  showerror( ERR_STORAGE_EXHAUSTED, 0, ERR_STORAGE_EXHAUSTED_TMPL );
                  rc = ERR_STORAGE_EXHAUSTED;
                  break;
               }
               /*
                * Set the current queue
                */
               DEBUGDUMP(printf("(Set Queue) ---\n"););
               rc = send_command_to_rxstack( NULL, sock, RXSTACK_SET_QUEUE_STR, PSTRENGVAL( queue) , PSTRENGLEN( queue ) );
               if ( rc == -1 )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               result = read_result_from_rxstack( NULL, sock, RXSTACK_HEADER_SIZE );
               if ( !SUCCESS( result ) )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  DROPSTRENG( result );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               /*
                * Success. We need to throw away the returned queue name. We
                * don't need it.
                */
               junk_return_from_rxstack( sock, result );
               send_all( sock, action );
               DROPSTRENG( result );
               rc = 0;
               break;
            case RXSTACK_EMPTY_QUEUE:
               DEBUGDUMP(printf("--- Empty Queue ---\n"););
               rc = send_command_to_rxstack( NULL, sock, action, PSTRENGVAL( queue) , PSTRENGLEN( queue ) );
               if ( rc == -1 )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Emptying queue" );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               result = read_result_from_rxstack( NULL, sock, RXSTACK_HEADER_SIZE );
               if ( !SUCCESS( result ) )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Emptying queue" );
                  DROPSTRENG( result );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               DROPSTRENG( result );
               rc = 0;
               break;
            case RXSTACK_NUMBER_IN_QUEUE:
               /*
                * Set the current queue
                */
               DEBUGDUMP(printf("--- Set Queue ---\n"););
               rc = send_command_to_rxstack( NULL, sock, RXSTACK_SET_QUEUE_STR, PSTRENGVAL( queue ), PSTRENGLEN( queue ) );
               if ( rc == -1 )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               /*
                * Read the return response header
                */
               result = read_result_from_rxstack( NULL, sock, RXSTACK_HEADER_SIZE );
               if ( !SUCCESS( result ) )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  DROPSTRENG( result );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               /*
                * Success. We need to throw away the returned queue name. We
                * don't need it.
                */
               junk_return_from_rxstack( sock, result );
               DROPSTRENG( result );
               DEBUGDUMP(printf("--- Number in Queue ---\n"););
               num = get_number_in_queue_from_rxstack( NULL, sock, &rc );
               if ( rc == 0 )
                  printf("%d\n", num );
               break;
            case RXSTACK_PULL:
               /*
                * Set the current queue
                */
               DEBUGDUMP(printf("--- Set Queue ---\n"););
               rc = send_command_to_rxstack( NULL, sock, RXSTACK_SET_QUEUE_STR, PSTRENGVAL( queue ), PSTRENGLEN( queue ) );
               if ( rc == -1 )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               result = read_result_from_rxstack( NULL, sock, RXSTACK_HEADER_SIZE );
               if ( !SUCCESS( result ) )
               {
                  showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Setting default queue" );
                  DROPSTRENG( result );
                  rc = ERR_RXSTACK_INTERNAL;
                  break;
               }
               /*
                * Success. We need to throw away the returned queue name. We
                * don't need it.
                */
               junk_return_from_rxstack( sock, result );
               DROPSTRENG( result );
               /*
                * Loop until the number of lines on the queue is zero
                */
               for ( ; ; )
               {
                  DEBUGDUMP(printf("--- Pull ---\n"););
                  rc = get_line_from_rxstack( NULL, sock, &result, 0 );
                  if ( rc == 0 )
                  {
                     printf( "%.*s\n", PSTRENGLEN( result ), PSTRENGVAL( result ) ) ;
                  }
                  else if ( ( rc == 1 ) || ( rc == 4 ) )
                  {
                     /*
                      * Queue empty.
                      */
                     rc = 0;
                     break;
                  }
                  else
                  {
                     showerror( ERR_EXTERNAL_QUEUE, ERR_RXSTACK_INTERNAL, ERR_RXSTACK_INTERNAL_TMPL, rc, "Getting line from queue" );
                     rc = ERR_RXSTACK_INTERNAL;
                     break;
                  }
                  DROPSTRENG( result );
               }
               break;
         }
         /*
          * Tell the server we want to disconnect
          * Don't expect any return result.
          */
         DEBUGDUMP(printf("--- Exit ---\n"););
         send_command_to_rxstack( NULL, sock, RXSTACK_EXIT_STR, NULL, 0 );
         close(sock);
      }
      else
      {
         DEBUGDUMP(printf( "queue: <%.*s> server: %.*s<%d> Port:%d\n", PSTRENGLEN( queue ), PSTRENGVAL( queue ), PSTRENGLEN( q.u.e.name ), PSTRENGVAL( q.u.e.name ), q.u.e.address, q.u.e.portno ););
         rc = 1;
      }
   }
   DROPSTRENG( server_name );
   DROPSTRENG( queue );
   cleanup();
   return rc;
}
