/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: sy_io.c
 * Date: 02/05/91
 *
 * Description:
 *   This file contains the code for communicating with the
 *   Sybase SQL server.
 *
 * Revisions:
 ****************************************************************/
#include <stdio.h>
#include <varargs.h>

/* Sybase include files */
#include <sybfront.h>
#include <sybdb.h>
#include <syberror.h>

#include "dbsrv.h"

#ifdef SYSLOG
#include <syslog.h>
#endif

static char left_overs[512] ;
static int nleft = 0 ;
static int current_column = 1 ;
static DBPROCESS *dbproc = NULL;

/* Sybase error handler */
/*ARGSUSED*/
static
err_handler(db_process, severity, errno, oserr)
    DBPROCESS *db_process ;
{
	if (oserr != DBNOERR)
	{
		(void) fprintf(stderr, "Operating System Error:\n\t%s\n",
			dboserrstr(errno)) ;
		fflush(stderr) ;
	}
	return
		((db_process == NULL) || (DBDEAD(db_process))) ?
			INT_EXIT : INT_CANCEL ;
}

/* Sybase message handler */
/*ARGSUSED*/
static
msg_handler(db_process, msgno, msgstate, severity, msgtext)
    DBPROCESS *db_process ;
    char *msgtext ;
{
	if (severity)
	{
		(void) fprintf(stderr,
			"DataServer message %d, state %d, severity %d:\n\t%s\n",
			msgno, msgstate, severity, msgtext) ;
		fflush(stderr) ;
	}
	return DBNOSAVE ;
}

/*
 * Flushes any characters out to the SQL server.
 * Does nothing if none are there.
 */
static
flush_chrs()
{
	if (nleft > 0)
	{
		left_overs[nleft] = 0 ;
		nleft = 0 ;
		if (dbproc != NULL)
		{
#ifdef DEBUG
			printf("%s",left_overs) ;
#endif
			dbcmd(dbproc,left_overs) ;
		}
	}
}

/*
 * Adds a character to the current SQL command buffer.
 */
static
add_db_char(c)
	char c ;
{
	if (nleft >= (sizeof left_overs) - 1)
	{
		flush_chrs() ;
	}
	left_overs[nleft++] = c ;
}

/*
 * Adds a string to the current SQL command buffer.
 */
static
sy_puts(s)
	char *s ;
{
	while(*s)
	{
		add_db_char(*s++) ;
	}
}

#define QUOTE '\''

/*
 * Adds a quoted string to the current SQL command buffer.
 */
static
add_db_qu_str(s)
	char *s ;
{
	if (s != NULL)
	{
		add_db_char(QUOTE) ;
		while(*s)
		{
			if (*s == QUOTE)
				add_db_char(QUOTE) ;
			add_db_char(*s++) ;
		}
		add_db_char(QUOTE) ;
	}
	else
	{
		sy_puts("NULL") ;
	}
}

/*
 * Adds an integer number
 */
static
add_db_num(ctrl,i)
	char ctrl ;
	int i ;
{
	char buf[80] ;
	char ctrl_str[20] ;
	(void)sprintf(ctrl_str,"%%%c",ctrl) ;
	(void)sprintf(buf,ctrl_str,i) ;
	sy_puts(buf) ;
}

/*
 * Adds a long number
 */
static
add_db_long_num(ctrl,i)
	char ctrl ;
	long i ;
{
	char buf[80] ;
	char ctrl_str[20] ;
	(void)sprintf(ctrl_str,"%%l%c",ctrl) ;
	(void)sprintf(buf,ctrl_str,i) ;
	sy_puts(buf) ;
}

/*
 * Adds a double number
 */
static
add_db_double(ctrl,i)
	char ctrl ;
	double i ;
{
	char buf[80] ;
	char ctrl_str[20] ;
	(void)sprintf(ctrl_str,"%%l%c",ctrl) ;
	(void)sprintf(buf,ctrl_str,i) ;
	sy_puts(buf) ;
}

/*
 * Converts to lowercase.
 */
static
tolow(c)
	char c ;
{
	return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c ;
}

/*
 * Executes sybase command and gets results.  Returns -1 if error.
 */
static
sy_exec_res()
{
	return
		(sy_exec() != SUCCEED) ?
			-1 :
		(sy_results() != SUCCEED) ?
			-1 :
			0 ;
}

/*
 * Executes sql command.
 */
static
sy_exec()
{
	flush_chrs() ;
#ifdef DEBUG
	printf("\n") ;
#endif
	if (dbproc != NULL)
	{
		int rc ;
		dbcancel(dbproc) ;
		rc = dbsqlexec(dbproc) ;
		return rc ;
	}
	return FAIL ;
}

/* get a field from a row */
static
get_field(i,type,ptr)
	BYTE *ptr ;
{
	current_column = i + 1 ;
	if (dbproc != NULL && i >= 1 && i <= dbnumcols(dbproc))
	{
		BYTE *data = dbdata(dbproc,i) ;
		if (data == NULL)
		{
			return -1 ;
		}
		else
		{
			int len = dbdatlen(dbproc,i) ;
			int dtype = dbcoltype(dbproc,i) ;
			if (len)
			{
				dbconvert(dbproc,dtype,data,len,type,ptr,-1) ;
				return 0 ;
			}
		}
	}
	return -1 ;
}

/* get an integer field */
static
get_int_field(i)
{
	DBINT ival ;
	int res =
		get_field(i,SYBINT4,(BYTE *)&ival) < 0 ?
			0 :
			ival ;
	return res ;
}

/* get a string field */
static
get_str_field(i,s)
	char *s ;
{
	if (get_field(i,SYBCHAR,(BYTE *)s) < 0)
	{
		s[0] = 0 ;
		return -1 ;
	}
	return 0 ;
}

/* set up the results for the nect query */
static
sy_results()
{
	int rc = (dbproc == NULL) ? FAIL : dbresults(dbproc) ;
	return rc ;
}

/* get the next row */
static
sy_get_row()
{
	int rc = (dbproc == NULL) ? NO_MORE_ROWS : dbnextrow(dbproc) ;
	current_column = 1 ;
	return rc ;
}

/****************************************************************
 *                PUBLIC FUNCTIONS
 ****************************************************************/

sy_open(log_name,passwd,server,application)
	char *log_name ;
	char *passwd ;
	char *server ;
	char *application ;
{
	LOGINREC *login;
	int err = 0;
	int i;

	/* add error handler */
    dberrhandle(err_handler) ;

	/* add message handler */
    dbmsghandle(msg_handler) ;

	/* allocate login record */
	login = dblogin() ;
	if (login == NULL)
	{
#ifdef SYSLOG
		syslog(LOG_ERR,"dblogin could not allocate login record");
#endif
		return -1 ;
	}
	if (log_name != NULL)
	{
		DBSETLUSER(login,log_name) ;
	}
	if (passwd != NULL)
	{
		DBSETLPWD(login, passwd) ;
	}
	if (server != NULL)
	{
		DBSETLHOST(login, server) ;
	}
	if (application != NULL)
	{
		DBSETLAPP(login,application) ;
	}

	/* attempt to connect to Sybase dataserver */
	for (i = 0; i < 10; i++)
	{
		dbproc = dbopen(login, server);
		if (dbproc == NULL)
		{
			/* check sybase error flag */
			err = dberrno();
			switch (err)
			{
				case SYBECONN:
					/* we sleep a while then try again */
					sleep(i);
					err = 0;
					break;
				case SYBEMEM:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: unable to allocate memory: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: unable to allocate memory: dberno->%d\n",err);
#endif
					break;
				case SYBEDBPS:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: max number of dbproc's : dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: max number of dbproc's : dberno->%d\n",err);
#endif
					break;
				case SYBESOCK:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: unable to open socket: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: unable to open socket: dberno->%d\n",err);
#endif
					break;
				case SYBEINTF:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: server name not found: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: server name not found: dberno->%d\n",err);
#endif
					break;
				case SYBEUHST:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: unknown host: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: unknown host: dberno->%d\n",err);
#endif
					break;
				case SYBEPWD:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: login incorrect: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: login incorrect: dberno->%d\n",err);
#endif
					break;
				case SYBEOPIN:
#ifdef SYSLOG
					syslog(LOG_ERR,"ddx_init: could not open interface file: dberno->%d",err);
#else
					fprintf(stderr,"ddx_init: could not open interface file: dberno->%d\n",err);
#endif
					break;
				default:
					break;
			}
			if (err != 0) break;
		}
		else
		{
			/* connection was successful */
			return 0;
		}
	}
	return -1;
}

sy_get_one_row()
{
	return (sy_get_row() == MORE_ROWS) ? 0 : -1 ;
}

get_int_result()
{
	return get_int_field(current_column) ;
}

get_str_result(s)
	char *s ;
{
	return get_str_field(current_column,s) ;
}

sy_close()
{
	if (dbproc != NULL)
	{
		dbclose(dbproc) ;
		dbproc = NULL ;
	}
}

static dbDatabase cur_db ;

sy_call_exec(db,pname,fmt,va_alist)
	dbDatabase db ;
	char *pname ;
	char *fmt ;
	va_dcl
{
	va_list pvar ;
	va_start(pvar) ;
	if (cur_db == NULL || cur_db != db)
	{
		cur_db = db ;
		dbuse(dbproc,db->name) ;
	}
	sy_puts(pname) ;
	sy_puts(" ") ;
	while(*fmt)
	{
		switch(*fmt++)
		{
			case 'd' :
			{
				int i = va_arg(pvar,int) ;
				add_db_num('d',i) ;
				break ;
			}
			case 'q' :
			{
				char *p = va_arg(pvar,char *) ;
				add_db_qu_str(p) ;
				break ;
			}
		}
		if (*fmt)
		{
			add_db_char(',') ;
		}
	}
	va_end(pvar) ;
	if (sy_exec_res() < 0)
	{
		return -1 ;
	}
	return 0 ;
}
