/*****************************************************************************
    Name: astsmp.c

    Description:

    Author: Alexander Nikolayev
            Autodesk, Inc.
            Moscow, Russia.

    (c) Copyright 1991-93 Autodesk Inc.
   ***************************************************************************
      Permission to use, copy, modify, and distribute this software and its
      documentation for the purpose of creating applications for AutoCAD, is
      hereby granted in accordance with the terms of the License Agreement
      accompanying this product.
   ***************************************************************************

      AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK
      SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR
      FITNESS FOR A PARTICULAR USE. AUTODESK, INC. DOES NOT WARRANT THAT
      THE OPERATION OF THE PROGRAM WILL BE UNINTERRUPTED OR ERROR FREE.

   ***************************************************************************
                           RESTRICTED RIGHTS LEGEND

      Use, duplication, or disclosure by the U.S. Government is subject
      to restrictions set forth in FAR 52.227-19 (Commerical Computer
      Software - Restricted Rights) and DFAR 252.227-7013 (c) (1) (ii)
      (Rights in Technical Data and Computer Software), as applicable.
   ***************************************************************************

    Entry Points:

    Modification History:

    Bugs and restriction on use:

    Notes:

*****************************************************************************/

/****************************************************************************/
/* INCLUDES */
/****************************************************************************/
#include <adslib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <asiappl.h>


/****************************************************************************/
/* TYPEDEFS */
/****************************************************************************/
char      * bittoXstr       _((char *, int,  unsigned char *, int));
void        write_string    _((char *));

void        display_section _((char *));
void        print_row       _((ASICURSOR, char **));
void        print_rowset    _((ASICURSOR));
void        scan_rowset     _((ASICURSOR));

void        do_statement    _((char *));
EAsiBoolean do_csr_update   _((ASICURSOR csr));
void        do_cursor       _((ASISTATEMENT));
void        do_single_select _((ASISTATEMENT));
void        do_ordinary     _((ASISTATEMENT));
void        print_coldsc    _((ASIHANDLE));
void        print_pardsc    _((ASIHANDLE));
EAsiBoolean initParams      _((ASIHANDLE, struct hbinds **));
void        readParams      _((ASIHANDLE, struct hbinds *));
void        termParams      _((struct hbinds *));
EAsiBoolean initDst         _((ASIHANDLE, struct hbinds **));
void        print_currow    _((ASICURSOR  csr));

void        nullbody        _((void));
void        do_settings     _((void));
void        do_connect      _((void));
void        do_disconnect   _((void));
void        do_sqled        _((void));
void        do_script       _((void));
void        create_env      _((void));
void        drop_env        _((void));

/**************************************************************************/
/*  TYPEDEFS */
/**************************************************************************/
/* The following structure defines buffers to be bound with SQl
   statement parameters */
struct hbinds {
  char      buffer [256];  // Host buffer
  short     indp;          // Indicator variable
};

/****************************************************************************/
/* GLOBALS */
/****************************************************************************/


typedef struct {
    char *cmdname;
    void (*cmdfunc)();
} CMDRX;                              /* Typedef for command definition */

static CMDRX cmd[] =  {
                        {"C:ASISETUP",      do_settings},
                        {"C:ASISQLED",      do_sqled},
                        {"C:ASISCRIPT",     do_script},
                        {"C:ASICONNECT",    do_connect},
                        {"C:ASIDISCONNECT", do_disconnect},
                        {"C:ASICREATEENV",  create_env},
                        {"C:ASIDROPENV",    drop_env}
                      };

/* Diagnostics parameters */
struct {
  EAsiDiag dPar;       // Diagnostics parameter
  char     name[32];   // Name
} diag [10] =
{
  { kAsiServerName,         "Server"             },
  { kAsiConnectionName,     "Connection"         },
  { kAsiConstraintCatalog,  "Constraint catalog" },
  { kAsiConstraintSchema,   "Constraint schema"  },
  { kAsiConstraintName,     "Constraint"         },
  { kAsiCatalogName,        "Catalog"            },
  { kAsiSchemaName,         "Schema"             },
  { kAsiTableName,          "Table"              },
  { kAsiColumnName,         "Column"             },
  { kAsiCursorName,         "Cursor"             }
};


static ASIENV      appl        = 0;          /* Asi application */
static ASISESSION  session     = 0;          /* Asi session */
static EAsiBoolean isConnected = kAsiFalse;  /* Connection is active */

static char        EnvName  [128];           /* Current environment name */
static char        UserName [128];           /* Current user name */



static EAsiBoolean oldScrFile  = kAsiFalse;     // Old style script file
static EAsiBoolean askCsrMode  = kAsiFalse;     // Ask for cursor mode
static EAsiBoolean showDsc     = kAsiFalse;     // Show result descriptor

// Default cursor type
static EAsiCurSns  csrSns      = kAsiSnsUndef; // Sensitivity
static EAsiCurScr  csrScr      = kAsiNonScroll;// Scrollabilty

static EAsiBoolean showOne     = kAsiFalse;     // Scroll by one row
static EAsiBoolean stopPage    = kAsiTrue;      // Stop after each page
static EAsiBoolean pauseAfter  = kAsiFalse;     // Pause after statement execution
static EAsiBoolean pauseAfterE = kAsiTrue;      // Pause after error
static EAsiBoolean doAgain     = kAsiFalse;     // Ask for statement reexecution
static EAsiBoolean echoToFile  = kAsiFalse;     // Echo to file
static char        FileName[128];               // Echo file namne
static EAsiBoolean timeMeasure = kAsiFalse;     // Print elapsed time
static FILE      * efDsc       = 0;             // Echo file handle

// Temporary buffers
static  char      buffer [128];       // Buffer for line input
static  char      buff   [1024];      // another buffer
static  char      stm    [1024];      // SQL statement

// Temporary buffer for build character strings
static  char      bufgen [1024];      // Buffer for convert Unicode string
                                      // to character string



/****************************************************************************/
/* FUNCTIONS */
/****************************************************************************/

/**************************************************************************
  Switch boolean value
**************************************************************************/
EAsiBoolean switch_boolean (EAsiBoolean t)
{
   return (t == kAsiTrue) ? kAsiFalse : kAsiTrue;
}

/***************************************************************************
  This function displays string on the screen and echoes it to the protocol
  file
***************************************************************************/
void write_string (char * buffer)
{
   ads_printf (buffer);

   if (echoToFile == kAsiTrue && efDsc != 0) {
      fprintf (efDsc, buffer);
   }
}

/****************************************************************************
  This function converts bit buffer to hexadecimal string
****************************************************************************/
char * bittoXstr (
   char          * buffer,   // Destination buffer
   int             len,      // Length of destination buffer
   unsigned char * bbuffer,  // source bit buffer
   int             blen      // Number of bits in bbuffer
)
{
   int    bytelength = 0;
   char * p = 0;
   int    ii;

   bytelength = blen / 8 + ((blen % 8 > 0) ? 1 : 0);

   for (ii = 0, p = buffer; ii < bytelength && p - buffer < len - 2; ii ++) {
      sprintf (p, "%02X", bbuffer[ii]);
      p +=2;
   }
   return buffer;

}



/***************************************************************************
  This function displays section from the asi.ini file
****************************************************************************/
void display_section (char *section)
{
   char      * buffer  = 0;  /* Destination buffer */
   char      * pEntry  = 0;
   char        pValue [128];
   ASICONFIG   config;        /* configuration file handle */

   /* Construct configuration file */
   if (asi_constr_cfg (&config) == kAsiBad) {
      ads_printf ("\nCannot construct ASI configuration file handle");
      return ;
   }

   buffer = malloc (sizeof (char) * 4096);
   if (buffer == 0) {
      asi_destroy_cfg (&config);
      ads_printf ("\nInsufficient memory for temporary buffer");
      return ;
   }
   /* Display contents */
   if (asi_getcfgstring (config, section, NULL, "", buffer, 4096) > 0) {
       for (pEntry = buffer; * pEntry != '\0'; pEntry += strlen (pEntry) + 1) {
          asi_getcfgstring (config, section, pEntry, "", pValue, 128);
          ads_printf ("\n%-16s %s", pEntry, pValue);
       }
   }
   ads_printf ("\n");
   free (buffer);
   asi_destroy_cfg (&config);   /* Destroy configuration handke */
   return;
}

/*************************************************************************
  This function displays the ENVIRONMENTS section from the asi.ini file
**************************************************************************/
void display_env (void)
{
   ads_printf ("\nAvailable environments:\n");
   display_section ("ENVIRONMENTS");
}


/***************************************************************************
  This function prints out diagnostics info
****************************************************************************/
static void print_diag (ASIHANDLE  handle)
{
   int         iCount;                // Temp variables
   int         ii, jj;                // Indexing variable
   char        buff [128];

   // Print number of conditions in diagnostics area
   if ((iCount = asi_condqty (handle)) > 0) {
      ads_printf("\nNumber of conditions: %d",iCount);
      // Print position where error was detected
      if (asi_synerrpos (handle) != 0) {
          ads_printf("\nError position:       %d",asi_synerrpos (handle));
      }
      ads_printf("\n#        SQLSTATE    Message");
      for (jj = 0; jj < iCount; jj++ ) {
           asi_errmsg (handle, buff, 256, jj);
           ads_printf("\n%10d%-12s%s", jj, asi_sqlstate (handle, jj), buff);

           // Print diagnostics parameters
           for (ii = 0; ii < 10; ii ++) {
              if (asi_getdiagpar (handle, diag[ii].dPar, buff, 256, jj) == kAsiTrue) {
                 ads_printf ("\n%20s: ", diag[ii].name);
                 ads_printf ("%s", buff);
              }
           }
      }
   }

} // print_diag()

/*******************************************************************************************
  This function prints resulting table descriptor
*******************************************************************************************/
static void print_coldsc (ASIHANDLE  handle)
{
   int           iCount, ii;              // Temp variables
   ASICOLDSC     coldsc;                  /* Column descriptoir handle */
   ASIDATADSC    datadsc;                 /* Data descriptor handle */
   ASIIDENT      name;                    /* Identifier: column name */


   if (showDsc == kAsiFalse) {
      return;
   }

   iCount = asi_colcount (handle);        /* Get number of columns */
   ads_printf("\n     Table structure");
   for (ii = 0; ii < iCount; ii ++) {
        if (asi_cds (handle, ii, &coldsc) == kAsiGood) { /* Get column descriptor */
            // Print column name
            asi_cdsc_name (coldsc, &name);              /* Get column name */
            ads_printf("\n %30S", asi_get_ident (name, buff, 128));
            asi_destroy_ident (&name);

            /* Get column data descriptor and print it */
            asi_cdsc_ddsc (coldsc, &datadsc);
            if (asi_cdsc_ddsc (coldsc, &datadsc) == kAsiGood) {
                ads_printf("   %s", asi_ddsc_sqltype (datadsc, buff, 256));
                asi_destroy_ddsc (&datadsc);
            }
        }
   }
}


///////////////////////////////////////////////////////////////////
static void print_pardsc (ASIHANDLE  handle)
{
   int            iCount, ii;        // Temp variables
   ASIPARDSC      pardsc;            /* Parameter descriptor */
   ASIDATADSC     datadsc;           /* Data descriptor */
   ASIIDENT       name;              /* Parameter name */

   iCount = asi_parcount (handle);   /* Number of parameters */
   if (iCount == 0) {
      return ;      /* No parameters */
   }

   ads_printf("\n     Parameters in statement");
   ads_printf("\n %30s%15s%s","Name","Nulability","   SQL Type");

   for (ii = 0; ii < iCount; ii ++) {
        /* Get parameter descriptor */
        if (asi_pds (handle, ii, &pardsc) != kAsiBad) {
            /* Print parameter name */
            asi_pdsc_name (pardsc, &name);
            ads_printf("\n %30s", asi_get_ident (name,  buff, 256));
            asi_destroy_ident (&name);

            /* Print parameter nullability */
            if (asi_pdsc_nullable (pardsc) == kAsiTrue ) {
                ads_printf("%15s","yes");
            } else {
                ads_printf("%15s","no");
            }
            /* Get parameter data descriptor and print it */
            if (asi_pdsc_ddsc (pardsc, &datadsc) == kAsiGood) {
                ads_printf("   %s", asi_ddsc_sqltype (datadsc, buff, 256));
                asi_destroy_ddsc (&datadsc);
            }
        }
   }
}



/****************************************************************************
  This function initializes input parameters and binnds them with
  the host program buffers
****************************************************************************/
EAsiBoolean initParams (ASIHANDLE  Stm, struct hbinds **  hosts)
{
   int             iCount;
   int             ii;
   struct hbinds * buffers;

   iCount = asi_parcount (Stm);
   if (iCount != 0) {
      // Allocate host buffers
      buffers = (struct hbinds *) malloc (sizeof (struct hbinds) * iCount);
      if (buffers == 0) {
         return kAsiBad;
      }
      // bind them with SQL statement
      for (ii = 0; ii < iCount; ii ++) {
         asi_bndnum (Stm, ii, buffers[ii].buffer, & buffers[ii].indp, 256, kAsiHchar);
      }
      * hosts = buffers;
   }
   return kAsiGood;
}



/****************************************************************************
   This function reads values for input parameters and places them
   into buffers bound with SQL statement parameters
****************************************************************************/
void readParams (ASIHANDLE  Stm, struct hbinds *  buffers)
{
   int             iCount;
   int             ii;
   ASIPARDSC       pardsc;
   ASIIDENT        name;

   iCount = asi_parcount (Stm);
   for (ii = 0; ii < iCount; ii ++) {
      if (asi_pds (Stm, ii, &pardsc) != kAsiBad) {
          // Print parameter name
          asi_pdsc_name (pardsc, &name);
          ads_printf("\n %-30s", asi_get_ident (name, buff, 256));
          asi_destroy_ident (&name);

          // Get parameter value
          ads_getstring (1, ": ", buffer);
          if (*buffer == '.' && * (buffer + 1) == '\0') {
              buffers[ii].indp = -1;    /* NULL Value */
          } else {
              strncpy (buffers[ii].buffer, buffer, 128);
          }
      }
   }
   return;
}

/***************************************************************************
  This function releases host buffers
***************************************************************************/
void termParams (struct hbinds *  buffers)
{
   if (buffers != 0) {
      free (buffers);
   }
}



/***************************************************************************
  This function initializes destinations for output values and bind them with
  the output parameters
***************************************************************************/
EAsiBoolean initDst (ASIHANDLE Stm, struct hbinds **  hosts)
{
   int             iCount;
   int             ii;
   struct hbinds * buffers;

   iCount = asi_colcount (Stm);
   if (iCount != 0) {
      // Allocate host buffers
      buffers = (struct hbinds *) malloc (sizeof (struct hbinds) * iCount);
      if (buffers == 0) {
         return kAsiBad;
      }
      // bind them with output parameters of SQL statement
      for (ii = 0; ii < iCount; ii ++) {
         asi_sob (Stm, ii, buffers[ii].buffer, & buffers[ii].indp, 256, kAsiHchar);
      }
      * hosts = buffers;
   }
   return kAsiGood;

}

/*************************************************************************
  This function does positioned update
*************************************************************************/
static EAsiBoolean do_csr_update (ASICURSOR csr)
{
   ASICOLDSC    coldsc;          // Column descriptor
   ASIIDENT     name;            // Column name
   EAsiHostType type;            // Type of buffer

   int          ii;
   int          iCount;

   iCount = asi_colcount (csr);
   for (ii = 0; ii < iCount; ii++) {
       if (asi_cds (csr, ii, &coldsc) == kAsiGood) {
           // Print column name
           asi_cdsc_name (coldsc, &name);
           ads_printf("%-30s", asi_get_ident (name, buff, 128));
           ads_getstring (1, ": ", buffer);
           if (* buffer == '\0') {
              continue;
           }
           if (*buffer == '.' && *(buffer + 1) == '\0') {
              type = kAsiHnull;
           } else {
              type = kAsiHchar;
           }
           if (asi_update (csr, name, type, strlen (buffer), buffer, (ASIIDENT) 0) == kAsiBad) {
              print_diag (csr);
              ads_printf ("\nColumn %s update error", asi_get_ident (name, buff, 128));
           }
           asi_destroy_ident (&name);
       }
   }
   return kAsiTrue;
}

/****************************************************************************
  This function prints current row
****************************************************************************/
static void print_currow (ASICURSOR  csr)
{
   // Column descriptor and name
   ASICOLDSC          coldsc;
   ASIIDENT           name;

   // Value of the column
   char               buffer [128];
   unsigned char      bbuffer[128];   // Destination for bits
   short              indp;

   // Temporary variables
   int                ii;

   ASIDATADSC         ddsc;
   EAsiBoolean        rCode = kAsiBad;


   ads_printf ("\n");
   for (ii = 0; ii < asi_colcount (csr); ii ++) {
        // Print column name
        if (asi_cds (csr, ii, &coldsc) == kAsiGood) {
            asi_cdsc_name (coldsc, &name);
            ads_printf("\n%-30s: ", asi_get_ident (name, buff, 128));
            asi_destroy_ident (&name);
        }

        // Print column data
        asi_cdsc_ddsc (coldsc, &ddsc);   // Get data descriptor
        if (asi_ddsc_type (ddsc) == kAsiBit || asi_ddsc_type (ddsc) == kAsiBitVar) {
            if ((rCode = asi_cvl (csr, ii, bbuffer, &indp, 128*8, kAsiHbinary)) == kAsiGood) {
                // Indicator holds the actual number of bits
                // stored in the destination buffer
                if (indp >= 0) {
                   ads_printf("[%s]", bittoXstr ((char *) buffer, 256, bbuffer, (int) indp));
                } else {
                   ads_printf(".");
                }
            }
        } else {
            // Get result as cha
            if ((rCode = asi_cvl (csr, ii, buffer, &indp, 128, kAsiHchar)) == kAsiGood) {
               if (indp >= 0) {
                   ads_printf("[%s]", buffer);
               } else {
                   ads_printf(".");
               }
            }
        }
        if (rCode == kAsiBad) {
            // Print diagnostics
            print_diag (csr);
        }
        asi_destroy_ddsc (&ddsc);
   }
}


/**************************************************************************
  This function prepares formats for browsing selection rowset
**************************************************************************/
char **make_formats (ASICURSOR csr)
{
   char       ** formats  = 0;
   ASICOLDSC     coldsc;       // Column descriptor
   ASIDATADSC    dDsc;        // Data descriptor
   ASIIDENT      name;
   int           ii;


   formats = (char **) malloc (sizeof (char *) * asi_colcount (csr));
   if (formats == 0) {
      return 0;
   }

   for (ii = 0; ii < asi_colcount (csr); ii ++) {
      asi_cds (csr, ii, &coldsc);
      asi_cdsc_name (coldsc, &name);
      asi_get_ident (name, buff, 256);
      asi_cdsc_ddsc (coldsc, & dDsc);
      if (dDsc != 0) {
        formats[ii] = (char *) malloc (32);
        if (formats [ii] != 0) {
           if (asi_ddsc_type (dDsc) == kAsiChar || asi_ddsc_type (dDsc) == kAsiCharVar) {
                sprintf (formats[ii], "  %%-%ds",
                         ASI_MAX (strlen (buff), asi_ddsc_length (dDsc)));
           } else if (asi_ddsc_type (dDsc) == kAsiBit ||
                      asi_ddsc_type (dDsc) == kAsiBitVar) {
                sprintf (formats[ii], "b  %%-%ds",
                      ASI_MAX (strlen (buff), asi_ddsc_length (dDsc) / 4 + 1));
           } else {
                sprintf (formats[ii], " %%%ds ",
                         ASI_MAX ( strlen (buff), asi_ddsc_length (dDsc)));
           }
           asi_destroy_ddsc (&dDsc);
        }
        asi_destroy_ident (&name);
      }
   }
   return formats;
}


/***************************************************************************
  This function prints rowset header
***************************************************************************/

void print_header (ASICURSOR  csr, char ** formats)
{
   ASICOLDSC     coldsc;            // Column descriptor
   ASIIDENT      name;
   int           ii;

   write_string ("\n");
   for (ii = 0; ii < asi_colcount (csr); ii ++) {
      asi_cds (csr, ii, &coldsc);
      asi_cdsc_name (coldsc, &name);
      sprintf (buffer, formats[ii]+1, asi_get_ident (name, buff, 256));
      write_string (buffer);
      asi_destroy_ident (&name);
   }
   write_string ("\n");
   write_string ("========================================================");
}

/**********************************************************************
  This function prints row on one line
***********************************************************************/
void print_row (ASICURSOR csr, char ** formats)
{
   short              indp;
   int                ii;
   unsigned char      bbuffer [256];      // Bit destination
   EAsiBoolean        rCode = kAsiBad;

   write_string ("\n");
   for (ii = 0; ii < asi_colcount (csr); ii ++) {
        if (*formats[ii] == 'b') {
            if ((rCode = asi_cvl (csr, ii, bbuffer, &indp, 128*8, kAsiHbinary)) == kAsiGood &&
                indp > 0) {
                 bittoXstr (buff, 256, bbuffer, (int) indp);
            }
        } else {
            // Get result as char buffer
            rCode = asi_cvl (csr, ii, buff, &indp, 128, kAsiHchar);
        }
        if (indp < 0 || rCode == kAsiBad) {
            buff [0] = 0;
        }
        if (*formats[ii] == 'b') {
            sprintf(buffer, formats[ii]+1, buff);
        } else {
            sprintf(buffer, formats[ii]+1, buff);
        }
        write_string (buffer);
   }
}


/**********************************************************************
  This function browses resulting rowset
***********************************************************************/
void print_rowset (ASICURSOR csr)
{
    char ** formats  = 0;
    int     ii       = 0;

    formats = make_formats (csr);
    print_header (csr, formats);
    while (asi_fetch (csr) == kAsiGood) {
       print_row (csr, formats);
       ii ++;
       if (ii == 21 && stopPage == kAsiTrue) {
           ads_getstring (0, "\nPress return to continue or [e] to exit immediatelly ...", buffer);
           if (*buffer == 'e') {
              break;
           }
           print_header (csr, formats);
           ii = 0;
       }
    }
    for (ii = 0; ii < asi_colcount (csr); ii ++) {
       if (formats[ii] != 0) {
          free (formats[ii]);
       }
    }
    free (formats);
}

/**************************************************************************
  This functions scans selction set displaying only one row
**************************************************************************/
void scan_rowset (ASICURSOR csr)
{
     char        cBuffer[127];                 // Temporary buffer
     EAsiBoolean rCode         = kAsiFalse;     // Return code
     long        lOffset;                      // Relative and absolute fetch value

     cBuffer[0] = 'n';
     for (;;) {
         switch ( cBuffer[0] ) {
         case 'n':                   // Fetch next
            rCode = asi_fetch (csr);
            break;
         case 'f':                   // Fetch first
            rCode = asi_fetchFirst (csr);
            break;
         case 'p':                  // Fetch prior
            rCode = asi_fetchPrior (csr);
            break;
         case 'l':                  // Fetch last
            rCode = asi_fetchLast (csr);
            break;
         case 'r':                  // Fetch relative
            ads_getint ("\nEnter relative row number: ", (int*)&lOffset);
            rCode = asi_fetchRelative (csr, lOffset);
            break;
         case 'a':                 // Fetch absolute
            ads_getint ("\nEnter absolute row number: ", (int*)&lOffset);
            rCode = asi_fetchAbsolute (csr, lOffset);
            break;
         case 'd':                 // Delete current row
            rCode = asi_delete (csr);
            break;
         case 'u':                // Update current row
            rCode = do_csr_update (csr);
            break;
         default:
            cBuffer[0] = 'e';
            break;
         }
         if ( cBuffer[0] == 'e' ) {
             break;
         }
         if ( rCode == kAsiGood ) {
             print_currow (csr);
         }
         print_diag(csr);
         ads_getstring(0, "\nEnter [n]ext/[f]irst/[p]revios/[l]ast/[r]elative/[a]bsolute/[d]elete/[u]pdate: ", cBuffer);
     }
     return;
}


/**************************************************************************
  This function processes cursor
**************************************************************************/
void do_cursor (ASISTATEMENT Stm)
{
    ASICURSOR   Csr;                          // Cursor
    char        cBuffer[127];                 // Temporary buffer

    // Cursor attributes
    EAsiCurScr  scr          = csrScr;
    EAsiCurSns  sns          = csrSns;

    struct hbinds * iparams  = 0;


    if (askCsrMode == kAsiTrue) {
       ads_getstring(0, "\nEnter [s] for scrollable cursor: ", cBuffer);
       if (cBuffer[0] == 's') {
           scr = kAsiScroll;
           sns = kAsiInsensitive;
           ads_printf("\nScrollable cursor is assumed insensitive");
       }
       if (scr != kAsiScroll) {
           ads_getstring(0, "\nEnter [i] for insensitive cursor: ", cBuffer);
           if (cBuffer[0] == 'i') {
               sns = kAsiInsensitive;
           }
       }
    }
    // Construct cursor
    asi_constr_csr (&Csr);

    // Allocate cursor
    if ( asi_alloc_csr (Csr, Stm, "MyCursor", scr, sns) == kAsiGood ) {
        // Bind parameters
        if (initParams (Csr, & iparams) == kAsiBad) {
           asi_destroy_csr (&Csr);
           return;
        }
        // Read parameters and open cursor repeatedly
        do {
            readParams (Csr, iparams);    // Read parameters if any
            if ( asi_open (Csr) == kAsiBad ) {      // Open cursor
                print_diag(Csr);               // Print diagnostics
            } else {
                if (showOne == kAsiFalse) {
                    print_rowset (Csr);
                } else {
                    scan_rowset (Csr);
                }
                if ( asi_close (Csr) == kAsiBad ) {    // close cursor
                    print_diag(Csr);
                }
            }
            * cBuffer = '\0';
            if (doAgain == kAsiTrue) {
                ads_getstring(0, "\nEnter [r] for re-open cursor: ", cBuffer);
            }
        } while ( cBuffer[0] == 'r' );
        termParams (iparams);
    } else {
        print_diag(Csr);
    }
    // Destroy cursor
    asi_destroy_csr (&Csr);
}

/***************************************************************************
  This function processes single row select statement
  Select * into :a, etc...
***************************************************************************/
static void do_single_select (ASISTATEMENT  Stm)
{
    struct hbinds * iparams  = 0;
    struct hbinds * oparams  = 0;
    char            cBuffer[127];
    int             ii;

    if (initParams (Stm, & iparams) == kAsiBad) {
        return;
    }
    // Initialize output buffer and bind them with output parameters
    if (initDst (Stm, & oparams) == kAsiBad) {
        termParams (iparams);
        return;
    }

    do {
        readParams (Stm, iparams);
        if (asi_execute (Stm) == kAsiBad ) {
            print_diag(Stm);
        } else {
            for (ii = 0; ii < asi_colcount (Stm); ii ++) {
               if (oparams[ii].indp >= 0) {
                  ads_printf ("\n%s", oparams[ii].buffer);
               }  else {
                  ads_printf ("\nNull value");
               }
            }
        }
        if (doAgain == kAsiTrue) {
           ads_getstring(0, "\nEnter [r] to re-execute: ", cBuffer);
        }
    } while ( cBuffer[0] == 'r' );

    termParams (iparams);
    termParams (oparams);
}


/**************************************************************************
   This function processes statements other than cursor and single
   row select statement
**************************************************************************/
static void do_ordinary (ASISTATEMENT  Stm)
{
    struct hbinds * iparams  = 0;
    char            cBuffer[127];


    if (initParams (Stm, & iparams) == kAsiBad) {
        return;
    }
    do {
        readParams (Stm, iparams);
        if (asi_execute (Stm) == kAsiBad ) {
            print_diag(Stm);
        } else {
            if (asi_stm (Stm) == kAsiINSERT ||
                asi_stm (Stm) == kAsiDELETE_WHERE ||
                asi_stm (Stm) == kAsiUPDATE_WHERE) {
                ads_printf("\n %d row(s) affected", asi_rowqty (Stm));
            }
        }
        if (doAgain == kAsiTrue) {
           ads_getstring (0, "\nEnter [r] to re-execute: ", cBuffer);
        }
    } while ( cBuffer[0] == 'r' );
    termParams (iparams);
}



/******************************************************************************
  This function processes SQL statement
******************************************************************************/
void do_statement (char * stm)
{
   ASISTATEMENT Stm;     // SQL statement

   // Write to protocol file
   if (echoToFile == kAsiTrue) {
      fprintf (efDsc, "\n%s", stm);
   }
   /* If statement is prefixed by "N ", then the native DBMS statement is 
      assummed */


   if (stm[0]  == 'N' && stm[1] == ' ') {
      /* Native statement */
      asi_constr_native_stm (&Stm);
      if (asi_iexecute  (Stm, session, stm + 2) == kAsiBad) {
          ads_printf ("\nExecution of the native statement failed");
          print_diag(Stm);
      }

   } else {

      asi_constr_stm (&Stm);  /* Construct statement */


      // Prepare SQL statement
      //
      if ( asi_prepare (Stm, session,  stm) == kAsiBad ) {
          print_diag(Stm);
          ads_printf ("\n%s", stm + asi_synerrpos (Stm));
          if (pauseAfterE == kAsiTrue && pauseAfter == kAsiFalse) {
              ads_getstring (0, "\nPress return to continue ...", buffer);
          }
      } else {
          ads_printf("\nSQL statement type: %d", asi_stm (Stm));
          print_pardsc(Stm);
          switch (asi_stm (Stm)) {
          case kAsiCURSOR:        /* Cursor */
             print_coldsc(Stm);
             do_cursor (Stm);
             break;
          case kAsiSELECT:       /* Single row select statement */
             print_coldsc(Stm);
             do_single_select(Stm);
             break;
          default:              /* Other types of statements */
             do_ordinary(Stm);
             break;
          }
      }
   }
   if (pauseAfter == kAsiTrue) {
       ads_getstring (0, "\nPress return to continue ...", buffer);
   }
   asi_destroy_stm (&Stm);
}

/**************************************************************************
  This function creates environment description in asi.ini file
**************************************************************************/

void create_env ()
{
   static char env [128];
   static char drv [128];
   ASICONFIG   config;

   display_section ("ENVIRONMENTS");
   ads_getstring (0, "\nEnter new environemnt name : ", env);
   if (*env == '\0') {
      return;
   }
   display_section ("ASI DRIVERS");
   ads_getstring (0, "\nEnter driver name : ", env);
   if (*env == '\0') {
      ads_printf ("\nDriver that runs environment must be specified");
      return;
   }
   if (asi_constr_cfg (&config) == kAsiBad) {
      ads_printf ("\nCan't construct configuration handle");
      return ;
   }
   if (asi_createenv(config, env, drv) == kAsiBad) {
      ads_printf ("\nError during environment creation");
   }
   asi_destroy_cfg (&config);

}

/****************************************************************************
  This function drops environment description from the asi.ini file
****************************************************************************/
void drop_env ()
{
   static char env [128];
   ASICONFIG   config;

   display_section ("ENVIRONMENTS");
   ads_getstring (0, "\nEnter environemnt name to drop: ", env);
   if (*env == '\0') {
      return;
   }
   if (asi_constr_cfg (&config) == kAsiBad) {
      ads_printf ("\nCan't construct configuration handle");
      return ;
   }
   if (asi_dropenv(config, env) == kAsiBad) {
      ads_printf ("\nCannot drop environment");
   }
   asi_destroy_cfg (&config);
}


/**************************************************************************
  This function tunes ast sample program.
***************************************************************************/
void
/*FCN*/do_settings ()
{
    static char buffer [128];
    EAsiBoolean keepDoing = kAsiTrue;

    for (keepDoing = kAsiTrue; keepDoing == kAsiTrue;) {
        // Print current settings
        ads_printf ("\nCurrent settings:\n");
        ads_printf ("\n1.  Script file format              <%s>", oldScrFile == kAsiTrue ? "Old" : "New");
        ads_printf ("\n2.  Show resulting table descriptor <%s>", showDsc == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n3.  Prompt for cursor type          <%s>", askCsrMode == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n4.  Scroll selection set            <%s>", showOne == kAsiTrue ? "By one row" : "Browse");
        ads_printf ("\n5.  Stop at page end                <%s>", stopPage == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n6.  Pause after statement execution <%s>", pauseAfter == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n7.  Pause after error               <%s>", pauseAfterE == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n8.  Prompt for re-execution         <%s>", doAgain == kAsiTrue ? "Yes" : "No");
        ads_printf ("\n");
        ads_printf ("\nDefault cursor type:");
        ads_printf ("\n9.  Insensitive                     <%s>", csrSns == kAsiInsensitive ? "Yes": "No");
        ads_printf ("\n10. Scroll                          <%s>", csrScr == kAsiScroll ? "Yes": "No");
        ads_printf ("\n");
        if (echoToFile == kAsiFalse) {
            ads_printf ("\n11. Do not write output to protocol file");
        } else {
            ads_printf ("\n11. Write to protocol file          <%s>", FileName);
        }

        ads_getstring (0, "\n\nEnter number to switch item or return to exit: ", buffer);

        switch (atoi (buffer)) {
        case 1:
             oldScrFile = switch_boolean (oldScrFile);
             break;
        case 2:
             showDsc = switch_boolean (showDsc);
             break;
        case 3:
             askCsrMode = switch_boolean (askCsrMode);
             break;
        case 4:
             showOne = switch_boolean (showOne);
             break;
        case 5:
             stopPage = switch_boolean (stopPage);
             break;

        case 6:
             pauseAfter = switch_boolean (pauseAfter);
             break;

        case 7:
             pauseAfterE = switch_boolean (pauseAfterE);
             break;

        case 8:
             doAgain = switch_boolean (doAgain);
             break;

        case 9:
             if (csrSns == kAsiInsensitive)
                csrSns = kAsiSnsUndef;
             else
                csrSns = kAsiInsensitive;
             break;

        case 10:
             if (csrScr == kAsiScroll)
                csrScr = kAsiNonScroll;
             else
                csrScr = kAsiScroll;
             break;

        case 11:
             if (echoToFile == kAsiTrue) {
                if (efDsc != 0) {
                   fclose (efDsc);
                   efDsc = 0;
                }
                echoToFile = kAsiFalse;
             } else {
                ads_getstring (0, "\nEnter protocol file name: ", FileName);
                if (*FileName == '\0') {
                   break;
                }
                efDsc = fopen (FileName, "at");
                if (efDsc == 0) {
                    ads_printf ("\nCan't open file for append");
                    break;
                }
                {
                   char t[32], d[32];
                   echoToFile = kAsiTrue;
                   fprintf (efDsc, "\nStart writing %s, %s", _strdate (d), _strtime (t));
                }
             }
             break;
        case 0:
             keepDoing = kAsiFalse;
             break;

        }
    }
}


/***********************************************************************
  This functions connects to SQL environment
***********************************************************************/
void do_connect (void)
{
   // SQL environment name, username, and password.
   // Environment name is equal to the name of the DBMS driver executable.
   //
   static   char Password [128];

   static   char buffer[128];      // Temporary buffer

   if (session == 0) {
      // Construct session
      if (asi_constr_session (&session, appl) == kAsiBad) {
         ads_printf ("\nUnable to construct session");
         return;
      }
   }


   // Disconnect if already connection exists
   if (isConnected == kAsiTrue) {
      if (asi_disconnect (session) == kAsiBad) {
          ads_printf ("\nCan't terminate SQL session. Fix problems and try again ...");
          print_diag(session);
          return;
      }
      isConnected = kAsiFalse;
   }

   for (;;) {
       display_env ();    // Display available environments

       ads_getstring (0, "\nEnvironment: ", buffer);
       if ( * buffer == '\0' ) {
           break;
       }

       strncpy (EnvName, buffer, 128);

       ads_getstring (0, "User       : ", buffer);
       strncpy (UserName, buffer, 128);

       ads_getstring (0, "Password   : ", Password);

       if ( asi_connect (session, EnvName, UserName, Password) == kAsiGood ) {
           // display driver information
           asi_dbms (session, buffer, 128);
           ads_printf ("\nDBMS:          %s", buffer);

           asi_sqlobject (session, buffer, 128);
           ads_printf ("\nSQL Object Id: %s", buffer);

           asi_message (session, buffer, 128);
           ads_printf ("\nMessage:       %s", buffer);
           isConnected = kAsiTrue;
           break;
       }
       // Error found check diagnostic information...
       print_diag(session);
   }
   return ;
}

/****************************************************************************
  This function disconnects from SQL environment
****************************************************************************/
void do_disconnect ()
{
   if (isConnected == kAsiFalse) {
      ads_printf ("\nNo current connection. Use ASICONNECT");
      return;
   }
   if (asi_disconnect (session) == kAsiBad) {
      print_diag (session);
      return;
   }
   isConnected = kAsiTrue;
   return;
}

/***************************************************************************
   This function prompt user for SQL statement and pass it to
   statment processing function.
***************************************************************************/
void do_sqled ()
{
   EAsiBoolean      isNeedNew = kAsiTrue;  // End of read loop

   if (isConnected == kAsiFalse) {
       ads_printf ("\nNo current connection. Use ASICONNECT");
       return;
   }

   // Read SQL statements and process them
   //
   for (isNeedNew = kAsiTrue; isNeedNew == kAsiTrue; ) {
      * stm = '\0';
      ads_printf ("\n%s/%s", EnvName, UserName);
      for (;;) {
          // Print prompt and get line
          //
          ads_getstring (1, "> ", buffer);

          // Test for end and return if void response.
          //
          if ( * stm == '\0' && * buffer == '\0' ) {
              isNeedNew = kAsiFalse;
              break;
          }

          if ( buffer[strlen(buffer) - 1] == '\n' ) {
              buffer[strlen(buffer) - 1] = '\0';
          }
          if ( buffer[strlen(buffer) - 1] == ';' ) {
              buffer[strlen(buffer) - 1] = '\0';
              strcat (stm, buffer);
              do_statement (stm);
              * stm = 0;
              break;
          } else {
              strcat (stm, buffer);
              strcat (stm, " ");
          }
      }
   }
   return;
}

/****************************************************************************
   This function processes a new script file.
   Statement is terminated by ; symbol
****************************************************************************/
void do_new_file (FILE * fh)
{
   char        * p;

   * stm = '\0';
   while (fgets (buffer, 127, fh) != 0) {
      if (strncmp (buffer, "//", 2) == 0) {
          continue;                       // Skip comments
      }
      if (strncmp (buffer, "@@", 2) == 0) {
          ads_printf ("%s", buffer + 2);
          continue;                       // Echo
      }
      if ( buffer[strlen(buffer) - 1] == '\n' ) {
          buffer[strlen(buffer) - 1] = '\0';    // Kill LF
      }
      if (*buffer == '\0') {
          continue;                       // Skip empty line
      }

      ads_printf ("%s\n", buffer);              // Echo line

      // Kill trailing blanks
      for (p = buffer + strlen (buffer) - 1; p != buffer; p --) {
         if (* p == ' ') {
            * p = '\0';
         } else {
            break;
         }
      }
      if (*buffer == '\0') {
          continue;                       // Skip empty line
      }

      if ( buffer[strlen(buffer) - 1] == ';' ) {
          buffer[strlen(buffer) - 1] = '\0';
          strcat (stm, buffer);
          do_statement (stm);
          ads_printf ("\n");
          *stm = '\0';
      } else {
          strcat (stm, buffer);
          strcat (stm, " ");
      }
   }

   return;
}


/*****************************************************************************
  This function reads the old script file (R12)
  (Line continuation symbol is & at the end of line)
*****************************************************************************/
void do_old_file (FILE * fh)
{
   char        * p;

   * stm = '\0';
   while (fgets (buffer, 127, fh) != 0) {
      if (*buffer == '$') {
          continue;                       // Skip comments
      }
      if ( buffer[strlen(buffer) - 1] == '\n' ) {
          buffer[strlen(buffer) - 1] = '\0';    // Kill LF
      }
      if (*buffer == '\0') {
          continue;                       // Skip empty line
      }

      // Kill trailing blanks
      for (p = buffer + strlen (buffer) - 1; p != buffer; p --) {
         if (* p == ' ') {
            * p = '\0';
         } else {
            break;
         }
      }

      if (*buffer == '\0') {
          continue;                          // Skip empty line
      }

      ads_printf ("%s\n", buffer);              // Echo line
      if ( buffer[strlen(buffer) - 1] == '&' ) {
          buffer[strlen(buffer) - 1] = '\0';
          strcat (stm, " ");
          strcat (stm, buffer);
          ads_printf ("\n");
      } else {
          strcat (stm, buffer);
          do_statement (stm);
          * stm = '\0';
      }
   }
   return;
}


/***************************************************************************
  This function processes the script file
***************************************************************************/
void do_script (void)
{

   FILE        * fh = 0;

   if (isConnected == kAsiFalse) {
       ads_printf ("\nNo current connection. Use ASICONNECT");
       return;
   }

   ads_getstring (0, "\nEnter file name or return to exit: ", buffer);
   if (*buffer == 0) {
      return;
   }

   fh = fopen (buffer, "rt");
   if (fh == 0) {
      ads_printf ("\nCannot open file <%s>", buffer);
      return;
   }

   if (oldScrFile == kAsiFalse) {
      do_new_file (fh);
   } else {
      do_old_file (fh);
   }
   fclose (fh);
}


/****************************************************************************
  This function reads program setting from the asi.ini file
****************************************************************************/
void readSettings ()
{
   ASICONFIG   config;        /* configuration file handle */


   /* Construct configuration file */
   if (asi_constr_cfg (&config) == kAsiBad) {
      ads_printf ("\nCannot construct ASI configuration file handle");
      return ;
   }

   // Script file version
   if (asi_getcfgstring (config, "ASISAMPLE", "ScriptVersion", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "OLD") == 0) {
         oldScrFile = kAsiTrue;
      }
   }

   // Cursor mode query
   if (asi_getcfgstring (config, "ASISAMPLE", "AskCursor", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         askCsrMode = kAsiTrue;
      }
   }

   // Show result descriptor
   if (asi_getcfgstring (config, "ASISAMPLE", "ShowTableDsc", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         showDsc = kAsiTrue;
      }
   }

   // Read default cursor declartions
   if (asi_getcfgstring (config, "ASISAMPLE", "CsrSensitivity", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Insensitive") == 0) {
         csrSns = kAsiInsensitive;
      }
   }
   if (asi_getcfgstring (config, "ASISAMPLE", "CsrScrollability", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Scroll") == 0) {
         csrScr = kAsiScroll;
      }
   }

   // Read browse mode
   if (asi_getcfgstring (config, "ASISAMPLE", "Browse", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "No") == 0) {
         showOne = kAsiTrue;
      }
   }

   // Read page stop flag
   if (asi_getcfgstring (config, "ASISAMPLE", "PageStop", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "No") == 0) {
         stopPage = kAsiFalse;
      }
   }

   // Read "pause after statement execution" flag
   if (asi_getcfgstring (config, "ASISAMPLE", "PauseAfterExecution", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         pauseAfter = kAsiTrue;
      }
   }

   // Read "pause after error" flag
   if (asi_getcfgstring (config, "ASISAMPLE", "PauseAfterError", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "No") == 0) {
         pauseAfterE = kAsiFalse;
      }
   }

   // Re-execute statement
   if (asi_getcfgstring (config, "ASISAMPLE", "Re-execute", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         doAgain = kAsiTrue;
      }
   }

   // Echo to file
   if (asi_getcfgstring (config, "ASISAMPLE", "EchoToFile", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         echoToFile = kAsiTrue;
      }
   }

   // Echo file name
   if (asi_getcfgstring (config, "ASISAMPLE", "EchoFile", "", buffer, 128) > 0) {
       strcpy (FileName, buffer);
   }

   // Time stattistics
   if (asi_getcfgstring (config, "ASISAMPLE", "ElapseTime", "", buffer, 128) > 0) {
      if (strcmpi (buffer, "Yes") == 0) {
         timeMeasure = kAsiTrue;
      }
   }
   asi_destroy_cfg (&config);   /* Destroy configuration handle */

}

/***********************************************************************
  This function writes its configuration into the asi.ini file

***********************************************************************/
void writeSettings ()
{
   ASICONFIG config = 0;

   /* Construct configuration file */
   if (asi_constr_cfg (&config) == kAsiBad) {
      ads_printf ("\nCannot construct ASI configuration file handle");
      return ;
   }

   // Script file version
   if (oldScrFile == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "ScriptVersion", "Old");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "ScriptVersion", "New");
   }

   // Cursor mode query
   if (askCsrMode == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "AskCursor", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "AskCursor", "No");
   }

   // Show result descriptor
   if (showDsc == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "ShowTableDsc", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "ShowTableDsc", "No");
   }

   // Read default cursor declartions
   if (csrSns == kAsiInsensitive) {
      asi_setcfgstring (config, "ASISAMPLE", "CsrSensitivity", "Insensitive");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "CsrSensitivity", "");
   }
   if (csrScr == kAsiScroll) {
      asi_setcfgstring (config, "ASISAMPLE", "CsrScrollability", "Scroll");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "CsrScrollability", "");
   }

   //  browse mode
   if (showOne == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "Browse", "No");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "Browse", "Yes");
   }

   // page stop flag
   if (stopPage == kAsiFalse) {
      asi_setcfgstring (config, "ASISAMPLE", "PageStop", "No");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "PageStop", "Yes");
   }

   // "pause after statement execution" flag
   if (pauseAfter == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "PauseAfterExecution", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "PauseAfterExecution", "No");
   }

   // Read "pause after error" flag
   if (pauseAfterE == kAsiFalse) {
      asi_setcfgstring (config, "ASISAMPLE", "PauseAfterError", "No");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "PauseAfterError", "Yes");
   }

   // Re-execute statement
   if (doAgain == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "Re-execute", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "Re-execute", "No");
   }

   // Echo to file
   if (echoToFile == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "EchoToFile", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "EchoToFile", "No");
   }

   // Echo file name
   if (*FileName != '\0') {
      asi_setcfgstring (config, "ASISAMPLE", "EchoFile", FileName);
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "EchoFile", "");
   }

   // Time stattistics
   if (timeMeasure == kAsiTrue) {
      asi_setcfgstring (config, "ASISAMPLE", "ElapseTime", "Yes");
   } else {
      asi_setcfgstring (config, "ASISAMPLE", "ElapseTime", "No");
   }

   asi_destroy_cfg (&config);


}

/***********************************************************************
    This function is called to load external functions into AutoLISP.
    It also initializes asi application and reads program settings
    from the ASISAMPLE section in the asi.ini file
***********************************************************************/
static short loadfunc ()
{
    short i = sizeof(cmd) /
              sizeof(CMDRX);          /* Number of functions */



    /* Initialize Asi application */
    if (asi_initasi (&appl) == kAsiBad) {
       ads_printf ("\nCannot initialize ASI");
       return RSERR;
    }

    /* Display ASI version */
    asi_version (appl, buff, 128);
    ads_printf ("\n%s", buff);
    ads_printf ("\n--------------------------------------\n");

    readSettings ();           /* Read ASISAMPLE section from the asi.ini file */

    while (i--)                       /* For every function from cmd */
    if (ads_defun(cmd[i].cmdname, i) != RTNORM)
        return RSERR;                 /* Wrong function definition */

    return RSRSLT;                    /* All functions were defined right */
}                                     /* End of loadfunc () */


/****************************************************************************
    This function is called to unload external functions into AutoLISP.
****************************************************************************/
static short unloadfunc ()
{
    short i = sizeof(cmd) /
              sizeof(CMDRX);          /* Number of functions */

    writeSettings ();                 /* Write settings to asi.ini file */

    asi_termasi (&appl);              /* Terminate ASI */

    while (i--)                       /* For every function from cmd */
    if (ads_undef(cmd[i].cmdname, i) != RTNORM)
        return RSERR;                 /* Wrong function definition */

    return RTNORM ;                    /* All functions were defined right */
}                                     /* End of unloadfunc () */

/****************************************************************************
    This function is called to start external function.
****************************************************************************/
static short execfunc ()
{
    int i = ads_getfuncode ();        /* Function number */

    ads_retvoid ();                   /* command returns void */
    if (i == RTERROR)
        return RSERR;                 /* Error */

    (cmd[i].cmdfunc)();               /* Call function */

    return RSRSLT;                    /* OK */
}                                     /* End of execfunc () */


/****************************************************************************
    Main entry point.
****************************************************************************/
void
/*FCN*/main (int argc,                /* Argument count */
             char *argv[])            /* Argument vector */
{
    short scode = RSRSLT;             /* Default result code */
    int stat;                         /* Return value from ads_link */

    ads_init(argc, argv);             /* Initialize the interface */

    for (;;) {                        /* Main loop */
                                      /* Link with AutoCAD */
        if ((stat = ads_link (scode)) < 0) {
                                      /* AutoCAD error */
                                      /* Print error message */
            printf("\nBad status from ads_link() = %d\n", stat);
            fflush (stdout);          /* Fflush the print buffer */
            exit (1);                 /* Exit */
        }

        scode = RSRSLT;                // Default result value

        switch (stat) {                // Check for the following cases here
        case RQXLOAD:                  // Load function by (xload) command
           scode = loadfunc ();        // Register ADS external functions
                                       // and initialize ASI
           break;

        case RQSUBR:                  /* Invoke function */
           scode = execfunc ();       /* Call function */
           break;

        case RQXUNLD:
        case RQEND:
        case RQQUIT:
            unloadfunc() ;            /* Unload the commands and terminate ASI */
            break ;
        }
    }
}                                     /* End of main () */

/*EOF*/

