/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1994-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include <stdio.h>
# include <ctype.h>
# include <stdlib.h>
# include <string.h>
# ifdef WIN32
# include <windows.h>
# include <sqlext.h>
# else
# include "odbcsql.h"
# endif

HENV henv;
HDBC hdbc;
HSTMT hstmt;

typedef struct tColData {
   int width;
   char *data;
} ColData;

void
ODBCErrors ()
{
  unsigned char buf[250];
  unsigned char sqlstate[15];

  while (SQLError (henv, hdbc, hstmt, sqlstate, NULL,
                                buf, sizeof(buf), NULL) == SQL_SUCCESS) {
     fprintf (stderr, "%s, SQLSTATE=%s\n", buf, sqlstate);
  }

  while (SQLError (henv, hdbc, SQL_NULL_HSTMT, sqlstate, NULL,
                                buf, sizeof(buf), NULL) == SQL_SUCCESS) {
     fprintf (stderr, "%s, SQLSTATE=%s\n", buf, sqlstate);
  }

  while (SQLError (henv, SQL_NULL_HDBC, SQL_NULL_HSTMT, sqlstate, NULL,
                                buf, sizeof(buf), NULL) == SQL_SUCCESS) {
      fprintf (stderr, "%s, SQLSTATE=%s\n", buf, sqlstate);
  }
}

void
doExec (char *cmd)
{
   SDWORD rowCount;
   SWORD numCols;
   char colName[50];
   short colType;
   UDWORD colPrecision;
   SDWORD colIndicator;
   short colScale;
   short colNullable;
   short displayWidth;
   ColData *colArray;
   int i, j;
   int rc;

   if (!strcmp (cmd, "tables")) {
      if (SQLTables (hstmt, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, 
                                            NULL, SQL_NTS) != SQL_SUCCESS) {
         ODBCErrors ();
         return;
      }
   } else if (!strcmp (cmd, "statistics")) {
      if (SQLStatistics (hstmt, (UCHAR *)"", SQL_NTS, (UCHAR *)"", SQL_NTS, (UCHAR *)"ncltty", SQL_NTS, 
                                    SQL_INDEX_ALL, SQL_QUICK) != SQL_SUCCESS) {
         ODBCErrors ();
         return;
      }
   } else {
      if (SQLPrepare (hstmt, (UCHAR *) cmd, SQL_NTS) != SQL_SUCCESS) {
         ODBCErrors ();
         return;
      }
      if (SQLExecute (hstmt) != SQL_SUCCESS) {
         ODBCErrors ();
         return;
      }
   }

   if (SQLNumResultCols (hstmt, &numCols) != SQL_SUCCESS) {
      ODBCErrors ();
      return;
   }
   if (numCols == 0) {
      SQLRowCount (hstmt, &rowCount);
      printf ("%d row processed.\n", rowCount);
      return;
   }

   colArray = (ColData *) malloc (sizeof(ColData) * numCols);
    
   for (i = 1; i <= numCols; i++) {
      if (SQLDescribeCol (hstmt, i, (UCHAR *) colName,
                  sizeof (colName), NULL, &colType, &colPrecision,
                  &colScale, &colNullable) != SQL_SUCCESS) {
          ODBCErrors ();
          return;
      }

      switch (colType) {
      default:
      case SQL_TIMESTAMP:
      case SQL_VARCHAR:
      case SQL_CHAR:
         displayWidth = (short) colPrecision;
         break;
      case SQL_BIT:
         displayWidth = 1;
         break;
      case SQL_TINYINT:
         displayWidth = 3;
         break;
      case SQL_SMALLINT:
         displayWidth = 5;
         break;
      case SQL_INTEGER:
         displayWidth = 10;
         break;
      case SQL_REAL:
         displayWidth = 9;
         break;
      case SQL_DOUBLE:
         displayWidth = 18;
         break;
      case SQL_DECIMAL:
      case SQL_NUMERIC:
         displayWidth = colPrecision + 2;  /* sign, comma */
         break;
      case SQL_VARBINARY:
      case SQL_LONGVARBINARY:
         displayWidth = (short) 20;
         break;
      }
        
      if (displayWidth < strlen (colName))
         displayWidth = strlen (colName);
      colArray[i-1].width = displayWidth;
      colArray[i-1].data = (char *) malloc (displayWidth + 1);
        
      printf ("%-*.*s", displayWidth, displayWidth, colName);
      if (i < numCols)
         putchar ('|');
   }
   putchar ('\n');

   for (i = 1; i <= numCols; i++) {
      for (j = 0; j < colArray[i-1].width; j++)
          putchar ('-');
      if (i < numCols)
         putchar ('+');
   }
   putchar ('\n');

   rowCount = 0;
   for ( ; ; ) {
      rc = SQLFetch (hstmt);

      if (rc == SQL_NO_DATA_FOUND)
         break;

      if (rc != SQL_SUCCESS) {
         ODBCErrors ();
         break;
      }
      for (i = 1; i <= numCols; i++) {
         if ((rc = SQLGetData (hstmt, i, SQL_CHAR, colArray[i-1].data,
                  colArray[i-1].width + 1, &colIndicator)) == SQL_ERROR) {
            ODBCErrors ();
            return;
         } else if (rc == SQL_SUCCESS_WITH_INFO) {
            j = colArray[i-1].width - 1;
            colArray[i-1].data[j--] = '.';
            colArray[i-1].data[j--] = '.';
            colArray[i-1].data[j--] = '.';
	 }

         if (colIndicator == SQL_NULL_DATA) {
            for (j = 0; j < colArray[i-1].width; j++)
                colArray[i-1].data[i] = '^';
            colArray[i-1].data[i] = '\0';
         }

         printf ("%-*.*s", colArray[i-1].width, colArray[i-1].width,
                           colArray[i-1].data);
         if (i < numCols)
            putchar ('|');
      }
      putchar ('\n');
      rowCount++;
   }

   printf (" %lu row(s) fetched.\n", rowCount);

   SQLFreeStmt (hstmt, SQL_CLOSE);
}


int
main (int argc, char *argv[])
{
   UCHAR *dsn;
   UCHAR *usr;
   UCHAR *pwd;
   RETCODE rc;
# ifdef WIN32
   WSADATA wsaData;
   if (WSAStartup(MAKEWORD (1,1), &wsaData) != 0) {
      fprintf (stderr, "WSAStartup");
      return 99;
   }
# endif
   if (argc != 2) {
      fprintf (stderr,
         "usage: picosql '{select ...|insert ...|update ...|delete ...|tables}'\n");
      return 2;
   }

   if (SQLAllocEnv (&henv) != SQL_SUCCESS) {
      fprintf (stderr, "Cannot allocate environment\n");
      return 2;
   }

   if (SQLAllocConnect (henv, &hdbc) != SQL_SUCCESS) {
      fprintf (stderr, "Cannot allocate connection\n");
      return 2;
   }
   if ((dsn = (UCHAR*) getenv ("PICOSQL_DSN")) == 0)
      dsn = (UCHAR*) "default";
   if ((usr = (UCHAR*) getenv ("PICOSQL_USR")) == 0)
      usr = (UCHAR*) "";
   if ((pwd = (UCHAR*) getenv ("PICOSQL_PWD")) == 0)
      pwd = (UCHAR*) "";

   rc = SQLConnect (hdbc, dsn, SQL_NTS, 
                          usr, SQL_NTS,
                          pwd, SQL_NTS);

   if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
      ODBCErrors();
      return 2;
   }

   if (SQLAllocStmt (hdbc, &hstmt) != SQL_SUCCESS) {
      ODBCErrors();
      return 2;
   }

   doExec (argv[1]);

   SQLFreeStmt (hstmt, SQL_DROP);
   SQLDisconnect (hdbc);
   SQLFreeConnect (hdbc);
   SQLFreeEnv (henv);

   return 0;
}
