/*******************************************************************************
**                                                                        
** Source File Name = showda.c  1.4
**                                                                        
** Licensed Materials - Property of IBM                                   
**                                                                        
** (C) COPYRIGHT International Business Machines Corp. 1995, 1999
** All Rights Reserved.                                                   
**                                                                        
** US Government Users Restricted Rights - Use, duplication or            
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      
**                                                                        
**                                                                        
**    PURPOSE :                                                           
**      An input/output stored procedure sample.
**      Showda accepts any number of arguments, 
**      and modifies the argument values.
**                                                                        
**      Pass a CLOB OUTPUT buffer as the LAST parameter
**      and the information will be written to this  
**      buffer and can then be displayed by the client.  
**      See the sendda.c sample.
**                                                                        
**                                                                        
** For more information about these samples see the README file.
**
** For more information on programming in CLI see the:
**     - "Building CLI Applications" section of the Application Building Guide, and the
**     - CLI Guide and Reference.
**
** For more information on the SQL language see the SQL Reference.
**
*******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlda.h>
#include <sqlcli1.h>
#include "samputil.h"          /* Header file for CLI sample code */

#ifndef max
   #define  max(A, B) ((A) > (B) ? (A) : (B))
#endif
#ifndef min
   #define  min(A, B) ((A) < (B) ? (A) : (B))
#endif
#define  LOBLENGTH  23

struct lob_file {
   unsigned long name_length ;
   unsigned long data_length ;
   unsigned long file_option ;
   char          name[255] ;
} ;

/*  Global buufer to send info back to clients as an output CLOB */
void update_da (struct sqlda *sqldaPointer);

char returnbuf[1023] = "No Info found!\n" ;
char * prbuf = &returnbuf[0] ;

int SQL_API_FN showda( void         * reserved1,
                       void         * reserved2,
                       struct sqlda * inout_sqlda,
                       struct sqlca * ca
                     ) {

   /* Declare a local SQLCA */
   struct sqlca  sqlca;

   /* Delare CLI Variables */

   SQLINTEGER   sqlvarIndex;
   SQLINTEGER   i;
   SQL_STRUCTURE  sqlvar2 *sqlvar2;
   long         *len_ptr;
   short lob_ind ;

   /*-----------------------------------------------------------------*/
   /* Setup CLI required environment                                  */
   /*-----------------------------------------------------------------*/
   /*
    Skip the CLI setup since this example only inpsects and
    modifes the SQLDA, and does not execute any SQL statements
    */

    prbuf += sprintf(prbuf, "\nDumping SQLDA\n");
    prbuf += sprintf(prbuf, "SQLN = %d SQLD = %d \n", inout_sqlda->sqln, 
                     inout_sqlda->sqld);

    /* Update the SQLDA information */
    update_da(inout_sqlda);
 
    sqlvarIndex = inout_sqlda->sqld-1;  /* Last SQLVAR */

    /* If the last parameter is a CLOB, use it to return SQLDA info */

    if ( inout_sqlda->sqlvar[sqlvarIndex].sqltype == SQL_TYP_CLOB ||
         inout_sqlda->sqlvar[sqlvarIndex].sqltype == SQL_TYP_NCLOB
       ) {      /* copy global returnbuf to the last sqlvar entry */

       strcpy( ( char * ) inout_sqlda->sqlvar[sqlvarIndex].sqldata,
               returnbuf
             ) ;

       /* Get pointer to length */
       memcpy( (char *) &lob_ind,
               (char *) inout_sqlda->sqlvar[sqlvarIndex].sqlind,
               sizeof( short )
             ) ;
       if ( lob_ind == 0 ) {
          len_ptr = GETSQLDALENPTR( inout_sqlda, inout_sqlda->sqld-1 ) ;
          /* Set length of OUTOUT CLOB */
          if (len_ptr != NULL ) *len_ptr = prbuf - returnbuf;
          /* len_ptr should never be NULL */
       }
    }

   /*-----------------------------------------------------------------*/
   /* Return to caller                                                */
   /*   -  Copy the SQLCA                                             */
   /*   -  Commit or Rollback the inserts.                            */
   /*-----------------------------------------------------------------*/

   /*
    ext:
    Skip the usual exit steps, since this sample just inspects and
    modifies the SQLDA.
    */

    return( SQLZ_DISCONNECT_PROC ) ;

}
/*<-- */

/*******************************************************************************
* PROCEDURE : print_var
* The following procedure prints a SQLDA SQLVAR variable in the buffer prbuf.
*
*******************************************************************************/
void print_var (char *ptr, int type, long length)
{
   short idx, ind ;            /* Array idx variables */
   /* Variables for decoding packed decimal data */
   short bottom, point ;
   unsigned short top, precision, scale;

   int i;


   /*
   ** Determine the type of data, coerce or decode the data if necessary,
   ** then writes the data (or first 20 characters) in prbuf.
   */

   switch ( type ) {
      case SQL_TYP_INTEGER:   /* long */
      case SQL_TYP_NINTEGER:  /* long with null indicator */
         prbuf += sprintf(prbuf, "%ld", *(long *) ptr) ;
         break ;
      case SQL_TYP_SMALL:     /* short */
      case SQL_TYP_NSMALL:    /* short with null indicator */
         prbuf += sprintf(prbuf, "%d",  *(short *) ptr ) ;
         break ;
      case SQL_TYP_DECIMAL:   /* decimal */
      case SQL_TYP_NDECIMAL:  /* decimal with null indicator */
      /* Determine the scale and precision */
         /* Precsion is 1 byte of short, 3 byte of long length */
         /* Scale is 2 byte of short, 4 byte of long length */
         precision = ((char *)&(length))[2];
         scale = ((char *)&(length))[3];

/*****************************************************************************/
/* Note:  Precision can only be odd because internally only odd are stored.  */
/*        When and if it happens that an even precision can occur, the       */
/*        precision must be incremented by 1 in order for the proper         */
/*        calculation of "idx" and "point" to occur.                         */
/*****************************************************************************/
         if ((precision %2) == 0) precision += 1;

         /* Calculate the total number of bytes */
         idx = ( precision + 2 ) / 2 ;
         point = precision - scale ;

         /* Determine the sign */
         bottom = *(ptr + idx -1) & 0x000F ;   /* sign */
         if ( (bottom == 0x000D) || (bottom == 0x000B) ) {
            prbuf += sprintf(prbuf, "-") ;
         } else {
            prbuf += sprintf(prbuf, " ") ;
         }


         /* Decode and print the decimal number */
         for (ind=0; ind < idx; ind++) {
            top = *(ptr + ind) & 0x00F0 ;
            top = (top >> 4 ) ;
            bottom = *(ptr + ind) & 0x000F ;
               if ( point-- == 0 ) 
               {   prbuf += sprintf(prbuf, ".");
               }
               prbuf += sprintf(prbuf, "%d", top ) ;
/*****************************************************************************/
/* Ignore bottom of last half byte because its the sign.                     */
/*****************************************************************************/
            if ( ind < idx - 1 ) { /* sign half byte ? */
               if ( point-- == 0 )
               {   prbuf += sprintf(prbuf, ".");
               }
               prbuf += sprintf(prbuf, "%d", bottom ) ;
            }
         }
         if ( scale == 0 ) 
         {  prbuf += sprintf(prbuf, ".") ;
         }
         break ;

      case SQL_TYP_FLOAT:  /* double */
      case SQL_TYP_NFLOAT: /* double with null indicator */
         prbuf += sprintf(prbuf, "%e", *(double *) ptr) ;
         break ;
      case SQL_TYP_CLOB:  /* Character LOB */
      case SQL_TYP_NCLOB:
      case SQL_TYP_CHAR:  /* fixed length character string */
      case SQL_TYP_NCHAR: /* fixed length character string with null
                             indicator */
         prbuf += sprintf (prbuf, "%-20.20s", ptr);
         break;
      case SQL_TYP_DBCLOB:  /* Double-Byte Character LOB */
      case SQL_TYP_NDBCLOB:
      case SQL_TYP_BLOB:  /* Binary LOB */
      case SQL_TYP_NBLOB:
         for (i = 0; i<20 && i<length; i++)
         {  prbuf += sprintf(prbuf, "%02X", (int)ptr[i]);
         }
         prbuf += sprintf (prbuf, " ");
         break;
      case SQL_TYP_VARCHAR:
      case SQL_TYP_NVARCHAR:
         /* Print a maximum of the first 20 characters */
         prbuf += sprintf (prbuf, "%-20.*s", 
                           min( ((struct SQLCHAR *)ptr)->length, 20 ),
                           ((struct SQLCHAR *)ptr)->data);
         break;

      case SQL_TYP_CSTR:  /* null terminated varying length character string */
      case SQL_TYP_NCSTR: /* null terminate varying length character
                              string with null indicator */
         break ;
         prbuf += sprintf (prbuf, "%-20.20s", ptr);
      default:
         /* Unknown data type */
         prbuf += sprintf(prbuf, "%-20.20s", "UNKNOWN");
         break ;
   }
}

/*******************************************************************************
* PROCEDURE : update_var
*   Updates the SQLDA SQLVAR variables.
*
*******************************************************************************/
void update_var (char *ptr, int type, long length)
{
   short idx, ind ;            /* Array idx variables */
   /* Variables for decoding packed decimal data */
   short bottom, point ;
   unsigned short top, precision, scale;

   int i;
   char tc;

   /*
   ** Determine the type of data, then update
   */

   switch ( type ) {
      case SQL_TYP_INTEGER:   /* long */
      case SQL_TYP_NINTEGER:  /* long with null indicator */
         *(long *) ptr = 99999;
         break ;
      case SQL_TYP_SMALL:     /* short */
      case SQL_TYP_NSMALL:    /* short with null indicator */
         *(short *) ptr = 999;
         break ;
      case SQL_TYP_DECIMAL:   /* decimal */
      case SQL_TYP_NDECIMAL:  /* decimal with null indicator */
      /* Determine the scale and precision */
         /* Precsion is 1 byte of short, 3 byte of long length */
         /* Scale is 2 byte of short, 4 byte of long length */
         precision = ((char *)&(length))[2];
         scale = ((char *)&(length))[3];

         if ((precision %2) == 0) precision += 1;
         /* Calculate the total number of bytes */
         idx = ( precision + 2 ) / 2 ;
         /* Reverse the sign */
         bottom = (*(ptr + idx -1) & 0x000F);  /* sign */
         if ( (bottom == 0x000C) )  /* Positive, set negative */
         {  *(ptr + idx -1) = (*(ptr + idx -1) & 0xFFF0) | 0x000D;
         } else  
         {  *(ptr + idx -1) = (*(ptr + idx -1) & 0xFFF0) | 0x000C;
         }
         *( ptr + idx ) = '\0' ;
         break ;
      case SQL_TYP_FLOAT:  /* double */
      case SQL_TYP_NFLOAT: /* double with null indicator */
         *(double *) ptr = 99.99;
         break ;
      case SQL_TYP_CLOB:  /* Character LOB */
      case SQL_TYP_NCLOB:
      case SQL_TYP_CHAR:  /* fixed length character string */
      case SQL_TYP_NCHAR: /* fixed length character string with null
                             indicator */
         /* Reverse String */
         for (i=0;i<length/2;i++ ) {
           tc = ptr[i];
           ptr[i] = ptr[length-i-1];
           ptr[length-i-1] = tc;
         } /* endfor */

         break;
      case SQL_TYP_DBCLOB:  /* Double-Byte Character LOB */
      case SQL_TYP_NDBCLOB:
      case SQL_TYP_BLOB:  /* Binary LOB */
      case SQL_TYP_NBLOB:
         /* Reverse String */
         for (i=0;i<length/2;i++ ) {
           tc = ptr[i];
           ptr[i] = ptr[length-i-1];
           ptr[length-i] = tc;
         } /* endfor */
         break;
      case SQL_TYP_VARCHAR:
      case SQL_TYP_NVARCHAR:
         /* Reverse String */
         for (i=0;i<length/2;i++ ) {
           tc = ((struct SQLCHAR *)ptr)->data[i];
           ((struct SQLCHAR *)ptr)->data[i] =
               ((struct SQLCHAR *)ptr)->data[length-i-1];
           ((struct SQLCHAR *)ptr)->data[length-i-1] = tc;
         } /* endfor */
         break;

      case SQL_TYP_CSTR:  /* null terminated varying length character string */
      case SQL_TYP_NCSTR: /* null terminate varying length character
                              string with null indicator */
         break ;
         sprintf (ptr,"%-*.*s", length, length, "!");
      default:
         /* Unknown data type */
         break ;
   }
}

/*******************************************************************************
* PROCEDURE : update_da
*             Calls update_var to update the value of each SQLVAR entry.
*******************************************************************************/
void update_da (struct sqlda *sqldaPointer) {
   short variableIndex, numBytes, sqlvarIndex, nullIndicator;
   short sqltype;
   short isnull;
   short *indptr;
   long  *lenptr;
   long  length;
   long  width;
   char * dec_length ;

   struct lob {
      long length;
      char *data;
   } *lobPointer;

   /* Output the contents for all host variables */
   for(sqlvarIndex=0;sqlvarIndex < sqldaPointer->sqld; sqlvarIndex++) {

      sqltype = sqldaPointer->sqlvar[sqlvarIndex].sqltype;
      indptr = sqldaPointer->sqlvar[sqlvarIndex].sqlind;

      prbuf += sprintf(prbuf, "SQLVAR[%d] : Type = %ld ", sqlvarIndex, sqltype);

      if ( (indptr != NULL ) && (*(indptr) < 0) )
          isnull = 1;
      else
          isnull = 0;

       switch (sqltype) {
         case SQL_TYP_CLOB:
         case SQL_TYP_NCLOB:
         case SQL_TYP_BLOB:
         case SQL_TYP_NBLOB:
         case SQL_TYP_DBCLOB:
         case SQL_TYP_NDBCLOB:
         /* LOB type, get lob length */
            lenptr = (long *)GETSQLDALENPTR(sqldaPointer, sqlvarIndex);
            if (lenptr == NULL)
                length = ((struct lob *)
                         (sqldaPointer->sqlvar[sqlvarIndex].sqldata))->length;
            else
                length = *lenptr;
            width = GETSQLDALONGLEN(sqldaPointer, sqlvarIndex);

            if (isnull == 0) 
            {  prbuf += sprintf(prbuf, "Length = %ld Width=%ld\n", length, width);
            }
            break;
         case SQL_TYP_VARCHAR:
         case SQL_TYP_NVARCHAR:
            length = ((struct SQLCHAR *) 
                     (sqldaPointer->sqlvar[sqlvarIndex].sqldata))->length;
            width = sqldaPointer->sqlvar[sqlvarIndex].sqllen;
            if (isnull == 0) 
            {  prbuf += sprintf(prbuf, "Length = %ld Width=%ld\n", length, width);
            }
           
           break;
         case SQL_TYP_DECIMAL:   /* decimal */
         case SQL_TYP_NDECIMAL:  /* decimal with null indicator */
            /* Precision is 1 byte of sqllen */
            /* Scale is 2 byte of sqllen     */
            if (isnull == 1) break; /* Null, no info */
            prbuf += sprintf(prbuf, "Precision= %d Scale = %d\n",
                   ((char *)&(sqldaPointer->sqlvar[sqlvarIndex].sqllen))[0],
                   ((char *)&(sqldaPointer->sqlvar[sqlvarIndex].sqllen))[1]);
            dec_length = (char *) &length ;
            memcpy( dec_length,
                    ( char * ) &( sqldaPointer->sqlvar[sqlvarIndex].sqllen ),
                    2
                   ) ;
            memcpy( ( dec_length + 2 ),
                    ( char * ) &( sqldaPointer->sqlvar[sqlvarIndex].sqllen ),
                    2
                   ) ;
            break;
         default:
            length = (sqldaPointer->sqlvar[sqlvarIndex].sqllen);
            if (isnull == 0) 
            {  prbuf += sprintf(prbuf, "Length = %ld\n", length);
            }
            break;
         } /* endswitch */
      /* If indptr is not NULL, and the indicator is < 0, the value is null */
      if (isnull == 1)
      {   prbuf += sprintf(prbuf, "\n            Value = NULL");
      }
      else
      {   prbuf += sprintf(prbuf, "            Value = ");
          print_var(sqldaPointer->sqlvar[sqlvarIndex].sqldata,
                    sqltype, length);
          update_var(sqldaPointer->sqlvar[sqlvarIndex].sqldata,
                    sqltype, length);
      }
      prbuf += sprintf(prbuf, "\n") ;
   } /* endfor */
}