14dbdf5518
As seen on tech-userlevel.
1135 lines
28 KiB
C
1135 lines
28 KiB
C
#include <cdk.h>
|
|
#include <sybfront.h>
|
|
#include <sybdb.h>
|
|
|
|
#ifdef HAVE_XCURSES
|
|
char *XCursesProgramName="syb";
|
|
#endif
|
|
|
|
#define MAXWIDTH 5000
|
|
#define MAXHISTORY 1000
|
|
|
|
/*
|
|
* This structure is used for keeping command history.
|
|
*/
|
|
struct history_st {
|
|
int count;
|
|
int current;
|
|
char *command[MAXHISTORY];
|
|
};
|
|
|
|
/*
|
|
* Define some global variables.
|
|
*/
|
|
char *GPUsage = "[-p Command Prompt] [-U User] [-P Password] [-S Server] [-h help]";
|
|
char *GPCurrentDatabase = 0;
|
|
extern char *dberrstr;
|
|
|
|
/*
|
|
* Because the error/message callback do not allow you to pass in
|
|
* data of your own, we have to make the screen pointer global. :(
|
|
*/
|
|
CDKSCREEN *GPCdkScreen = 0;
|
|
|
|
/*
|
|
* Define function prototypes.
|
|
*/
|
|
DBPROCESS *loginToSybase (CDKSCREEN *screen, char *login, char *password);
|
|
DBPROCESS *sybaseLogin (CDKSCREEN *screen, char *login, char *password, int attempts);
|
|
char *assembleTitle (DBPROCESS *dbProcess);
|
|
char *assembleRow (DBPROCESS *dbProcess);
|
|
char *uc (char *word);
|
|
int getColWidth (DBPROCESS *dbProcess, int col);
|
|
void runIsqlCommand (CDKSCREEN *cdkscreen, CDKSWINDOW *swindow, DBPROCESS *process);
|
|
void useDatabase (CDKSWINDOW *swindow, DBPROCESS *dbProc, char *command);
|
|
void intro (CDKSCREEN *screen);
|
|
void help (CDKENTRY *entry);
|
|
void loadHistory (struct history_st *history);
|
|
void saveHistory (struct history_st *history, int count);
|
|
|
|
/*
|
|
* Define callback prototypes.
|
|
*/
|
|
void viewHistoryCB (EObjectType cdktype, void *object, void *clientData, chtype key);
|
|
void swindowHelpCB (EObjectType cdktype, void *object, void *clientData, chtype key);
|
|
void historyUpCB (EObjectType cdktype, void *object, void *clientData, chtype key);
|
|
void historyDownCB (EObjectType cdktype, void *object, void *clientData, chtype key);
|
|
void listHistoryCB (EObjectType cdktype, void *object, void *clientData, chtype key);
|
|
|
|
/*
|
|
* Define Sybase error/message callbacks. This is required by DBLib.
|
|
*/
|
|
int err_handler (DBPROCESS *dbProcess, DBINT mesgNumber, int mesgState,
|
|
int severity, char *mesgText);
|
|
int msg_handler (DBPROCESS *dbProcess, DBINT mesgNumber, int mesgState,
|
|
int severity, char *mesgText);
|
|
|
|
/*
|
|
* Written by: Mike Glover
|
|
* Purpose:
|
|
* This creates a very simple interface to Sybase.
|
|
*/
|
|
int main (int argc, char **argv)
|
|
{
|
|
/* Declare variables. */
|
|
CDKSWINDOW *commandOutput = 0;
|
|
CDKENTRY *commandEntry = 0;
|
|
DBPROCESS *dbProcess = 0;
|
|
WINDOW *cursesWin = 0;
|
|
char *dsquery = 0;
|
|
char *command = 0;
|
|
char *prompt = 0;
|
|
char *upper = 0;
|
|
char *login = 0;
|
|
char *password = 0;
|
|
char *server = 0;
|
|
int count = 0;
|
|
int width = 0;
|
|
int ret = 0;
|
|
struct history_st history;
|
|
char *mesg[5], temp[1000];
|
|
|
|
/* Set up the history. */
|
|
GPCurrentDatabase = strdup ("master");
|
|
history.current = 0;
|
|
history.count = 0;
|
|
|
|
/* Check the command line for options. */
|
|
while (1)
|
|
{
|
|
/* Are there any more command line options to parse. */
|
|
if ((ret = getopt (argc, argv, "p:U:P:S:h")) == -1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
switch (ret)
|
|
{
|
|
case 'p':
|
|
prompt = copyChar (optarg);
|
|
break;
|
|
|
|
case 'U':
|
|
login = copyChar (optarg);
|
|
break;
|
|
|
|
case 'P':
|
|
password = copyChar (optarg);
|
|
break;
|
|
|
|
case 'S':
|
|
server = copyChar (optarg);
|
|
break;
|
|
|
|
case 'h':
|
|
printf ("Usage: %s %s\n", argv[0], GPUsage);
|
|
exit (0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Set up the command prompt. */
|
|
if (prompt == 0)
|
|
{
|
|
if (server == 0)
|
|
{
|
|
if ((dsquery = getenv ("DSQUERY")) != 0)
|
|
{
|
|
sprintf (temp, "</B/24>[%s] Command >", dsquery);
|
|
prompt = copyChar (temp);
|
|
}
|
|
else
|
|
{
|
|
prompt = copyChar ("</B/24>Command >");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf (temp, "</B/24>[%s] Command >", server);
|
|
prompt = copyChar (temp);
|
|
}
|
|
}
|
|
|
|
/* Set up CDK. */
|
|
cursesWin = initscr();
|
|
GPCdkScreen = initCDKScreen (cursesWin);
|
|
|
|
/* Start color. */
|
|
initCDKColor();
|
|
|
|
/* Initialize DB-Library. */
|
|
if (dbinit() == FAIL)
|
|
{
|
|
mesg[0] = "<C></U>Fatal Error";
|
|
mesg[1] = "<C>Could not connect to the Sybase database.";
|
|
popupLabel (GPCdkScreen, mesg, 2);
|
|
exit (1);
|
|
}
|
|
|
|
/* Load the history. */
|
|
loadHistory (&history);
|
|
|
|
/* Create the scrolling window. */
|
|
commandOutput = newCDKSwindow (GPCdkScreen, CENTER, TOP, -8, -2,
|
|
"<C></B/5>Command Output Window",
|
|
MAXWIDTH, TRUE, FALSE);
|
|
|
|
/* Create the entry field. */
|
|
width = COLS - strlen (prompt) - 1;
|
|
commandEntry = newCDKEntry (GPCdkScreen, CENTER, BOTTOM,
|
|
0, prompt, A_BOLD|COLOR_PAIR(8),
|
|
COLOR_PAIR(24)|'_', vMIXED,
|
|
width, 1, 512, FALSE, FALSE);
|
|
|
|
/* Create the key bindings. */
|
|
bindCDKObject (vENTRY, commandEntry, KEY_UP, &historyUpCB, &history);
|
|
bindCDKObject (vENTRY, commandEntry, KEY_DOWN, &historyDownCB, &history);
|
|
bindCDKObject (vENTRY, commandEntry, CONTROL('^'), &listHistoryCB, &history);
|
|
bindCDKObject (vENTRY, commandEntry, TAB, &viewHistoryCB, commandOutput);
|
|
bindCDKObject (vSWINDOW, commandOutput, '?', swindowHelpCB, commandEntry);
|
|
|
|
/* Draw the screen. */
|
|
refreshCDKScreen (GPCdkScreen);
|
|
|
|
/* Display the introduction window. */
|
|
intro (GPCdkScreen);
|
|
|
|
/* Make them login first. */
|
|
dbProcess = sybaseLogin (GPCdkScreen, login, password, 3);
|
|
if (dbProcess == 0)
|
|
{
|
|
destroyCDKScreen (GPCdkScreen);
|
|
endCDK ();
|
|
exit (1);
|
|
}
|
|
|
|
/* Do this forever. */
|
|
for (;;)
|
|
{
|
|
/* Get the command. */
|
|
command = activateCDKEntry (commandEntry, 0);
|
|
|
|
/* Strip off leading and trailing white space. */
|
|
stripWhiteSpace (vBOTH, command);
|
|
|
|
/* Upper case the command. */
|
|
upper = uc (command);
|
|
|
|
/* Check the output of the command. */
|
|
if (strcmp (upper, "QUIT") == 0 ||
|
|
strcmp (upper, "EXIT") == 0 ||
|
|
strcmp (upper, "Q") == 0 ||
|
|
strcmp (upper, "E") == 0 ||
|
|
commandEntry->exitType == vESCAPE_HIT)
|
|
{
|
|
/* Save the history. */
|
|
saveHistory (&history, 100);
|
|
|
|
/* Exit. */
|
|
dbclose (dbProcess);
|
|
dbexit();
|
|
|
|
/* All done. */
|
|
destroyCDKEntry (commandEntry);
|
|
destroyCDKSwindow (commandOutput);
|
|
delwin (cursesWin);
|
|
freeChar (upper);
|
|
endCDK();
|
|
exit (0);
|
|
}
|
|
else if (strcmp (command, "login") == 0)
|
|
{
|
|
DBPROCESS *newLogin = sybaseLogin (GPCdkScreen, 0, 0, 3);
|
|
if (newLogin == 0)
|
|
{
|
|
addCDKSwindow (commandOutput, "Login Error: Could not switch to new user.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
/* Close the old connection. */
|
|
dbclose (dbProcess);
|
|
dbProcess = newLogin;
|
|
|
|
/* Add a message to the scrolling window. */
|
|
addCDKSwindow (commandOutput,
|
|
"Logged into database as new user.",
|
|
BOTTOM);
|
|
count = 0;
|
|
}
|
|
}
|
|
else if (strcmp (command, "logout") == 0)
|
|
{
|
|
/* Close the old connection. */
|
|
dbclose (dbProcess);
|
|
dbProcess = 0;
|
|
|
|
/* Add a message to the scrolling window. */
|
|
addCDKSwindow (commandOutput, "Logged out.", BOTTOM);
|
|
count = 0;
|
|
}
|
|
else if (strcmp (command, "clear") == 0)
|
|
{
|
|
/* Clear the scrolling window. */
|
|
cleanCDKSwindow (commandOutput);
|
|
}
|
|
else if (strcmp (command, "history") == 0)
|
|
{
|
|
listHistoryCB (vENTRY, (void *)commandEntry, (void *)&history, 0);
|
|
}
|
|
else if (strcmp (command, "tables") == 0)
|
|
{
|
|
/* Check if we are logged in. */
|
|
if (dbProcess == 0)
|
|
{
|
|
addCDKSwindow (commandOutput, "You must login first.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
sprintf (command, "select * from sysobjects where type = 'U'");
|
|
|
|
/* Put the command into the ISQL buffer. */
|
|
dbcmd (dbProcess, command);
|
|
|
|
/* Put the command into the scrolling window. */
|
|
sprintf (temp, "</R>%d><!R> %s", count+1, command);
|
|
addCDKSwindow (commandOutput, temp, BOTTOM);
|
|
|
|
/* Increment the counter. */
|
|
count++;
|
|
}
|
|
}
|
|
else if (strcmp (command, "help") == 0)
|
|
{
|
|
/* Display the help. */
|
|
help(commandEntry);
|
|
}
|
|
else if (command[0] == 'u' && command[1] == 's' &&
|
|
command[2] == 'e' && command[3] == ' ')
|
|
{
|
|
/* They want to use a database. */
|
|
useDatabase (commandOutput, dbProcess, command);
|
|
count = 0;
|
|
}
|
|
else if (strcmp (command, "go") == 0)
|
|
{
|
|
/* Check if we are logged in. */
|
|
if (dbProcess == 0)
|
|
{
|
|
addCDKSwindow (commandOutput, "You must login first.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
/* Put the command into the scrolling window. */
|
|
sprintf (temp, "</R>%d><!R> %s", count+1, command);
|
|
addCDKSwindow (commandOutput, temp, BOTTOM);
|
|
count = 0;
|
|
|
|
/* Run the command. */
|
|
runIsqlCommand (GPCdkScreen, commandOutput, dbProcess);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check if we are logged in. */
|
|
if (dbProcess == 0)
|
|
{
|
|
addCDKSwindow (commandOutput, "You must login first.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
/* Put the command into the ISQL buffer. */
|
|
dbcmd (dbProcess, command);
|
|
|
|
/* Put the command into the scrolling window. */
|
|
sprintf (temp, "</R>%d><!R> %s", count+1, command);
|
|
addCDKSwindow (commandOutput, temp, BOTTOM);
|
|
|
|
/* Increment the counter. */
|
|
count++;
|
|
}
|
|
}
|
|
|
|
/* Keep the history. */
|
|
history.command[history.count] = copyChar (command);
|
|
history.count++;
|
|
history.current = history.count;
|
|
|
|
/* Clear the entry field. */
|
|
cleanCDKEntry (commandEntry);
|
|
|
|
/* Free up the memory used by the upper pointer. */
|
|
freeChar (upper);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This lets a person 'use' a database.
|
|
*/
|
|
void useDatabase (CDKSWINDOW *swindow, DBPROCESS *dbProc, char *command)
|
|
{
|
|
char *database = 0;
|
|
char temp[256];
|
|
char **words;
|
|
int wordCount, x;
|
|
|
|
/* Split the command line up and get the database name. */
|
|
words = CDKsplitString (command, ' ');
|
|
wordCount = CDKcountStrings (words);
|
|
|
|
/* Look for the name. */
|
|
for (x=1; x < wordCount; x++)
|
|
{
|
|
if (strlen (words[x]) != 0)
|
|
{
|
|
database = copyChar (words[x]);
|
|
}
|
|
}
|
|
CDKfreeStrings(words);
|
|
|
|
/* Try to actually use the database. */
|
|
if (dbuse(dbProc, database) == FAIL)
|
|
{
|
|
/* We aren't allowed to use that database. */
|
|
sprintf (temp, "Command: %s", command);
|
|
addCDKSwindow (swindow, temp, BOTTOM);
|
|
addCDKSwindow (swindow, "</B/16>Error<!B!16> You are not allowed to use that database.", BOTTOM);
|
|
return;
|
|
}
|
|
|
|
/* Set the global database name. */
|
|
if (database == 0)
|
|
{
|
|
/* Put a syntax error in the scrolling window. */
|
|
sprintf (temp, "Command: %s", command);
|
|
addCDKSwindow (swindow, temp, BOTTOM);
|
|
addCDKSwindow (swindow, "</B/16>Error<!B!16> Syntax Error", BOTTOM);
|
|
return;
|
|
}
|
|
|
|
/* Clear out the old database name and set the new one. */
|
|
freeChar (GPCurrentDatabase);
|
|
GPCurrentDatabase = database;
|
|
|
|
/* Add a message into the scrolling window. */
|
|
sprintf (temp, "Command: %s", command);
|
|
addCDKSwindow (swindow, temp, BOTTOM);
|
|
sprintf (temp, "Default Database set to %s", GPCurrentDatabase);
|
|
addCDKSwindow (swindow, temp, BOTTOM);
|
|
}
|
|
|
|
/*
|
|
* This does the requisite checking for failed login attempts.
|
|
*/
|
|
DBPROCESS *sybaseLogin (CDKSCREEN *screen, char *accountName, char *accountPassword, int attemptCount)
|
|
{
|
|
DBPROCESS *dbProcess = 0;
|
|
char *login = accountName;
|
|
char *password = accountPassword;
|
|
int count = 0;
|
|
int lines = 0;
|
|
char *mesg[5];
|
|
|
|
/* Give them X attempts, then kick them out. */
|
|
while (count < attemptCount)
|
|
{
|
|
/* Try to login. */
|
|
dbProcess = loginToSybase (GPCdkScreen, login, password);
|
|
|
|
/*
|
|
* If the dbprocess is null the account/password
|
|
* pair does not exist.
|
|
*/
|
|
if (dbProcess == 0)
|
|
{
|
|
/*
|
|
* If the login and account names were provided,
|
|
* set them to null and allow the user to enter
|
|
* the name and password by hand.
|
|
*/
|
|
login = 0;
|
|
password = 0;
|
|
|
|
/* Spit out the login error message. */
|
|
lines = 0;
|
|
mesg[lines++] = "<C></B/5>Login Error";
|
|
mesg[lines++] = " ";
|
|
mesg[lines++] = "<C>The login/password pair does not exist.";
|
|
mesg[lines++] = " ";
|
|
mesg[lines++] = "<C>Please try again.";
|
|
|
|
popupLabel (GPCdkScreen, mesg, lines);
|
|
|
|
eraseCDKScreen (GPCdkScreen);
|
|
refreshCDKScreen (GPCdkScreen);
|
|
count++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Did we expire the login attempts? */
|
|
if (count > attemptCount-1)
|
|
{
|
|
lines = 0;
|
|
mesg[lines++] = "<C>Login Error";
|
|
mesg[lines++] = " ";
|
|
mesg[lines++] = "<C>Too many attempyts to login.";
|
|
mesg[lines++] = "<C>Exiting.";
|
|
|
|
popupLabel (GPCdkScreen, mesg, lines);
|
|
|
|
return 0;
|
|
}
|
|
return dbProcess;
|
|
}
|
|
|
|
/*
|
|
* Let the user login.
|
|
*/
|
|
DBPROCESS *loginToSybase (CDKSCREEN *screen, char *accountName, char *accountPassword)
|
|
{
|
|
CDKENTRY *loginEntry = 0;
|
|
CDKENTRY *passwordEntry = 0;
|
|
LOGINREC *dbLogin = 0;
|
|
char *hostAccount = 0;
|
|
char *login = accountName;
|
|
char *password = accountPassword;
|
|
char *mesg[10], temp[256];
|
|
|
|
/* Draw the screen. */
|
|
refreshCDKScreen (screen);
|
|
|
|
/* Define the login entry field. */
|
|
if (login == 0)
|
|
{
|
|
loginEntry = newCDKEntry (screen, CENTER, CENTER,
|
|
"\n<C></B/5>Sybase Login\n",
|
|
"Account Name: ",
|
|
A_BOLD|COLOR_PAIR(8),
|
|
COLOR_PAIR(24)|'_', vMIXED,
|
|
20, 1, 20, TRUE, FALSE);
|
|
|
|
/* Use the current account name as the default answer. */
|
|
hostAccount = getlogin();
|
|
setCDKEntryValue (loginEntry, hostAccount);
|
|
|
|
/* Get the login. */
|
|
while (1)
|
|
{
|
|
/* Redraw the screen. */
|
|
eraseCDKScreen (loginEntry->screen);
|
|
refreshCDKScreen (loginEntry->screen);
|
|
|
|
/* Get the login to the sybase account. */
|
|
login = copyChar (activateCDKEntry (loginEntry, 0));
|
|
|
|
/* Check if they hit escape. */
|
|
if (loginEntry->exitType == vESCAPE_HIT)
|
|
{
|
|
mesg[0] = "<C></U>Error";
|
|
mesg[1] = "A user name must be provided.";
|
|
popupLabel (screen, mesg, 2);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Destroy the widget. */
|
|
destroyCDKEntry (loginEntry);
|
|
}
|
|
|
|
/* Get the password if we need too. */
|
|
if (password == 0)
|
|
{
|
|
sprintf (temp, "\n<C></B/5>%s's Password\n", login);
|
|
passwordEntry = newCDKEntry (screen, CENTER, CENTER,
|
|
temp, "Account Password: ",
|
|
A_BOLD|COLOR_PAIR(8),
|
|
COLOR_PAIR(24)|'_', vHMIXED,
|
|
20, 0, 20, TRUE, FALSE);
|
|
setCDKEntryHiddenChar (passwordEntry, '*');
|
|
|
|
/* Get the password. (the account may not have a password.) */
|
|
password = copyChar (activateCDKEntry (passwordEntry, 0));
|
|
if ((passwordEntry->exitType == vESCAPE_HIT) ||
|
|
((int)strlen(password) == 0))
|
|
{
|
|
password = "";
|
|
}
|
|
|
|
/* Destroy the widget. */
|
|
destroyCDKEntry (passwordEntry);
|
|
}
|
|
|
|
/*
|
|
* Try to connect to the database and get a LOGINREC structre.
|
|
*/
|
|
if ((dbLogin = dblogin()) == 0)
|
|
{
|
|
mesg[0] = "<C></U>Fatal Error";
|
|
mesg[1] = "<C>Could not connect to the Sybase database.";
|
|
popupLabel (screen, mesg, 2);
|
|
refreshCDKScreen (screen);
|
|
exit (1);
|
|
}
|
|
|
|
/*
|
|
* Set the login and password and try to login to the database.
|
|
*/
|
|
DBSETLUSER (dbLogin, login);
|
|
DBSETLPWD (dbLogin, password);
|
|
DBSETLAPP (dbLogin, "cdk_syb");
|
|
|
|
/* Create a dbprocess structure to communicate with the database. */
|
|
return dbopen (dbLogin, 0);
|
|
}
|
|
|
|
/*
|
|
* This actually runs the command.
|
|
*/
|
|
void runIsqlCommand (CDKSCREEN *screen, CDKSWINDOW *swindow, DBPROCESS *dbProcess)
|
|
{
|
|
/* Declare local variables. */
|
|
RETCODE returnCode;
|
|
int rowCount;
|
|
|
|
/* Add in a output seperation line. */
|
|
addCDKSwindow (swindow, "<C><#HL(5)> Start of Output <#HL(5)>", BOTTOM);
|
|
|
|
/* Run the command. */
|
|
dbsqlexec (dbProcess);
|
|
|
|
/* Check the return code of the commands. */
|
|
while ((returnCode = dbresults (dbProcess)) != NO_MORE_RESULTS)
|
|
{
|
|
if (returnCode == FAIL)
|
|
{
|
|
/* Oops, the command bombed. */
|
|
addCDKSwindow (swindow, "</5/16>Command failed.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
if (!(DBCMDROW (dbProcess)))
|
|
{
|
|
/* The command could not return any rows. */
|
|
addCDKSwindow (swindow, "</5/16>Command could not return rows.", BOTTOM);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The command returned some rows, print out the title.
|
|
*/
|
|
char *row = assembleTitle (dbProcess);
|
|
addCDKSwindow (swindow, row, BOTTOM);
|
|
freeChar (row);
|
|
|
|
/* For each row returned, assemble the info. */
|
|
rowCount = 0;
|
|
while (dbnextrow (dbProcess) != NO_MORE_ROWS)
|
|
{
|
|
row = assembleRow (dbProcess);
|
|
addCDKSwindow (swindow, row, BOTTOM);
|
|
freeChar (row);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add in a output seperation line. */
|
|
addCDKSwindow (swindow, "<C><#HL(5)> End of Output <#HL(5)>", BOTTOM);
|
|
addCDKSwindow (swindow, "", BOTTOM);
|
|
|
|
/* Can the query... */
|
|
dbcanquery (dbProcess);
|
|
}
|
|
|
|
/*
|
|
* This creates a single line from the column widths and values.
|
|
*/
|
|
char *assembleTitle (DBPROCESS *dbProc)
|
|
{
|
|
char *colName = 0;
|
|
int colWidth = 0;
|
|
int colNameLen = 0;
|
|
int colCount = dbnumcols (dbProc);
|
|
int x = 0;
|
|
char temp[MAXWIDTH];
|
|
char row[MAXWIDTH];
|
|
|
|
/* Clean the row out. */
|
|
memset (row, '\0', MAXWIDTH);
|
|
|
|
/* Start assembling the row. */
|
|
for (x=1; x <= colCount; x++)
|
|
{
|
|
colName = dbcolname (dbProc, x);
|
|
colWidth = getColWidth (dbProc, x);
|
|
colNameLen = (int)strlen (colName);
|
|
|
|
/* If we need to pad, then pad. */
|
|
if (colNameLen < colWidth)
|
|
{
|
|
/* Create a string the same length as the col width. */
|
|
memset (temp, '\0', MAXWIDTH);
|
|
memset (temp, ' ', (colWidth-colNameLen));
|
|
|
|
/* Copy the name. */
|
|
sprintf (row, "%s %s%s", row, colName, temp);
|
|
}
|
|
else
|
|
{
|
|
/* Copy the name. */
|
|
sprintf (row, "%s %s", row, colName);
|
|
}
|
|
}
|
|
return (char *)strdup (row);
|
|
}
|
|
|
|
/*
|
|
* This assembles a single row.
|
|
*/
|
|
char *assembleRow (DBPROCESS *dbProcess)
|
|
{
|
|
char *dataValue = 0;
|
|
int colCount = dbnumcols (dbProcess);
|
|
int columnType = 0;
|
|
int colWidth = 0;
|
|
int valueLen = 0;
|
|
char value[MAXWIDTH];
|
|
char temp[MAXWIDTH];
|
|
char row[MAXWIDTH];
|
|
char format[20];
|
|
int x;
|
|
|
|
/* Clean out the row. */
|
|
memset (row, '\0', MAXWIDTH);
|
|
|
|
/* Start assembling the row. */
|
|
for (x=1; x <= colCount; x++)
|
|
{
|
|
columnType = (int)dbcoltype (dbProcess, x);
|
|
colWidth = (int)getColWidth (dbProcess, x);
|
|
valueLen = (int)dbdatlen (dbProcess, x);
|
|
|
|
/* Check the column type. */
|
|
if (columnType == SYBINT1)
|
|
{
|
|
DBINT object_id = *((DBINT *)dbdata(dbProcess, x));
|
|
sprintf (format, "%%-%dd", colWidth);
|
|
sprintf (value, format, (int)object_id);
|
|
}
|
|
else if (columnType == SYBINT2)
|
|
{
|
|
DBINT object_id = *((DBINT *)dbdata(dbProcess, x));
|
|
sprintf (format, "%%-%dd", colWidth);
|
|
sprintf (value, format, (int)object_id);
|
|
}
|
|
else if (columnType == SYBINT4)
|
|
{
|
|
DBINT object_id = *((DBINT *)dbdata(dbProcess, x));
|
|
sprintf (format, "%%-%dd", colWidth);
|
|
sprintf (value, format, (int)object_id);
|
|
}
|
|
else if (columnType == SYBREAL)
|
|
{
|
|
DBREAL object_id = *((DBREAL *)dbdata(dbProcess, x));
|
|
sprintf (format, "%%-%d.2f", colWidth);
|
|
sprintf (value, format, object_id);
|
|
}
|
|
else if (columnType == SYBFLT8)
|
|
{
|
|
DBFLT8 object_id = *((DBFLT8 *)dbdata(dbProcess, x));
|
|
sprintf (format, "%%-%d.2f", colWidth);
|
|
sprintf (value, format, object_id);
|
|
}
|
|
else
|
|
{
|
|
if (valueLen <= 0)
|
|
{
|
|
strcpy (value, " ");
|
|
}
|
|
else
|
|
{
|
|
memset (value, '\0', MAXWIDTH);
|
|
dataValue = (DBCHAR *)dbdata (dbProcess, x);
|
|
strncpy (value, dataValue, valueLen);
|
|
}
|
|
}
|
|
|
|
/* If we need to pad, then pad. */
|
|
if (valueLen < colWidth)
|
|
{
|
|
/* Copy the value into the string. */
|
|
memset (temp, '\0', MAXWIDTH);
|
|
memset (temp, ' ', (colWidth-valueLen));
|
|
sprintf (row, "%s %s%s", row, value, temp);
|
|
}
|
|
else
|
|
{
|
|
sprintf (row, "%s %s", row, value);
|
|
}
|
|
}
|
|
return copyChar (row);
|
|
}
|
|
|
|
/*
|
|
* This function returns the correct width of a column, taking
|
|
* into account the width of the title, the width of the data
|
|
* element.
|
|
*/
|
|
int getColWidth (DBPROCESS *dbProcess, int col)
|
|
{
|
|
char *colName = dbcolname (dbProcess, col);
|
|
int colNameLen = (int)strlen(colName);
|
|
int colWidth = dbcollen (dbProcess, col);
|
|
int columnType = (int)dbcoltype (dbProcess, col);
|
|
|
|
/* If the colType is int/real/float adjust accordingly. */
|
|
if (columnType == SYBINT1 || columnType == SYBINT2 ||
|
|
columnType == SYBINT4)
|
|
{
|
|
colWidth = 5;
|
|
}
|
|
else if (columnType == SYBREAL || columnType == SYBFLT8)
|
|
{
|
|
colWidth = 8;
|
|
}
|
|
|
|
/* Is the name of the column wider than the col width? */
|
|
if (colNameLen >= colWidth)
|
|
{
|
|
return (colNameLen+1);
|
|
}
|
|
return colWidth;
|
|
}
|
|
|
|
/*
|
|
* This callback allows the user to play with the scrolling window.
|
|
*/
|
|
void viewHistoryCB (EObjectType cdktype, void *object, void *clientData, chtype key)
|
|
{
|
|
CDKSWINDOW *swindow = (CDKSWINDOW *)clientData;
|
|
CDKENTRY *entry = (CDKENTRY *)object;
|
|
|
|
/* Let them play... */
|
|
activateCDKSwindow (swindow, 0);
|
|
|
|
/* Redraw the entry field. */
|
|
drawCDKEntry (entry, ObjOf(entry)->box);
|
|
}
|
|
|
|
/*
|
|
* This displays a little introduction screen.
|
|
*/
|
|
void intro (CDKSCREEN *screen)
|
|
{
|
|
int lines = 0;
|
|
char *mesg[10];
|
|
|
|
/* Create the message. */
|
|
mesg[lines++] = "";
|
|
mesg[lines++] = "<C></B/16>Sybase Command Interface";
|
|
mesg[lines++] = "<C>Written by Mike Glover";
|
|
mesg[lines++] = "";
|
|
mesg[lines++] = "<C>Type </B>help<!B> to get help.";
|
|
|
|
/* Display the message. */
|
|
popupLabel (screen, mesg, lines);
|
|
}
|
|
|
|
/*
|
|
* This function displays help.
|
|
*/
|
|
void help (CDKENTRY *entry)
|
|
{
|
|
int lines = 0;
|
|
char *mesg[25];
|
|
|
|
/* Create the help message. */
|
|
mesg[lines++] = "<C></B/29>Help";
|
|
mesg[lines++] = "";
|
|
mesg[lines++] = "</B/24>When in the command line.";
|
|
mesg[lines++] = "<B=Up Arrow > Scrolls back one command.";
|
|
mesg[lines++] = "<B=Down Arrow> Scrolls forward one command.";
|
|
mesg[lines++] = "<B=Tab > Activates the scrolling window.";
|
|
mesg[lines++] = "<B=help > Displays this help window.";
|
|
mesg[lines++] = "";
|
|
mesg[lines++] = "</B/24>When in the scrolling window.";
|
|
mesg[lines++] = "<B=l or L > Loads a file into the window.";
|
|
mesg[lines++] = "<B=s or S > Saves the contents of the window to a file.";
|
|
mesg[lines++] = "<B=Up Arrow > Scrolls up one line.";
|
|
mesg[lines++] = "<B=Down Arrow> Scrolls down one line.";
|
|
mesg[lines++] = "<B=Page Up > Scrolls back one page.";
|
|
mesg[lines++] = "<B=Page Down > Scrolls forward one page.";
|
|
mesg[lines++] = "<B=Tab or Esc> Returns to the command line.";
|
|
mesg[lines++] = "<B=? > Displays this help window.";
|
|
mesg[lines++] = "";
|
|
mesg[lines++] = "<C> (</B/24>Refer to the scrolling window online manual for more help<!B!24>.)";
|
|
|
|
/* Pop up the help message. */
|
|
popupLabel (entry->screen, mesg, lines);
|
|
}
|
|
|
|
/*
|
|
* This converts a word to upper case.
|
|
*/
|
|
char *uc (char *word)
|
|
{
|
|
int length = strlen (word);
|
|
char *upper = (char *)malloc (sizeof (char *) * (length+2));
|
|
int x;
|
|
|
|
/* Start converting the case. */
|
|
for (x=0; x < length; x++)
|
|
{
|
|
if (isalpha ((int)word[x]))
|
|
{
|
|
upper[x] = toupper(word[x]);
|
|
}
|
|
else
|
|
{
|
|
upper[x] = word[x];
|
|
}
|
|
}
|
|
upper[length] = '\0';
|
|
return upper;
|
|
}
|
|
|
|
/*
|
|
* The following two functions are the error and message handler callbacks.
|
|
* which will be called by Sybase when and error occurs.
|
|
*/
|
|
int err_handler (DBPROCESS *dbProcess, DBINT mesgNumber, int mesgState, int severity, char *mesgText)
|
|
{
|
|
/* Declare local variables. */
|
|
char *mesg[10], temp[256];
|
|
int errorCount = 0;
|
|
|
|
/* Check if the process is dead. */
|
|
if ((dbProcess == 0) || (DBDEAD(dbProcess)))
|
|
{
|
|
mesg[0] = "</B/32>Database Process Error";
|
|
mesg[1] = "<C>The database process seems to have died.";
|
|
mesg[2] = "<C>Try logging in again with the </R>login<!R> command";
|
|
popupLabel (GPCdkScreen, mesg, 3);
|
|
return INT_EXIT;
|
|
}
|
|
else
|
|
{
|
|
mesg[0] = "</B/32>DB-Library Error";
|
|
sprintf (temp, "<C>%s", dberrstr);
|
|
mesg[1] = copyChar (temp);
|
|
errorCount = 2;
|
|
}
|
|
|
|
/* Display the message if we have an error. */
|
|
if (errorCount > 0)
|
|
{
|
|
popupLabel (GPCdkScreen, mesg, --errorCount);
|
|
}
|
|
|
|
/* Clean up. */
|
|
if (errorCount == 2)
|
|
{
|
|
freeChar (mesg[1]);
|
|
}
|
|
return (1);
|
|
}
|
|
int mesg_handler (DBPROCESS *dbProcess, DBINT mesgNumber, int mesgState, int severity, char *mesgText)
|
|
{
|
|
/* Declare local variables. */
|
|
char *mesg[10], temp[256];
|
|
|
|
/* Create the message. */
|
|
mesg[0] = "<C></B/16>SQL Server Message";
|
|
sprintf (temp, "</R>Message Number<!R> %ld", mesgNumber);
|
|
mesg[1] = copyChar (temp);
|
|
sprintf (temp, "</R>State <!R> %d", mesgState);
|
|
mesg[2] = copyChar (temp);
|
|
sprintf (temp, "</R>Severity <!R> %d", severity);
|
|
mesg[3] = copyChar (temp);
|
|
sprintf (temp, "</R>Message <!R> %s", mesgText);
|
|
mesg[4] = copyChar (temp);
|
|
popupLabel (GPCdkScreen, mesg, 5);
|
|
|
|
/* Clean up. */
|
|
freeChar (mesg[1]); freeChar (mesg[2]);
|
|
freeChar (mesg[3]); freeChar (mesg[4]);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* This is for the scrolling window help callback.
|
|
*/
|
|
void swindowHelpCB (EObjectType cdktype, void *object, void *clientData, chtype key)
|
|
{
|
|
CDKENTRY *entry = (CDKENTRY *)clientData;
|
|
help(entry);
|
|
}
|
|
|
|
/*
|
|
* This is the callback for the down arrow.
|
|
*/
|
|
void historyUpCB (EObjectType cdktype, void *object, void *clientData, chtype key)
|
|
{
|
|
CDKENTRY *entry = (CDKENTRY *)object;
|
|
struct history_st *history = (struct history_st *) clientData;
|
|
|
|
/* Make sure we don't go out of bounds. */
|
|
if (history->current == 0)
|
|
{
|
|
Beep();
|
|
return;
|
|
}
|
|
|
|
/* Decrement the counter. */
|
|
history->current--;
|
|
|
|
/* Display the command. */
|
|
setCDKEntryValue (entry, history->command[history->current]);
|
|
drawCDKEntry (entry, ObjOf(entry)->box);
|
|
}
|
|
|
|
/*
|
|
* This is the callback for the down arrow.
|
|
*/
|
|
void historyDownCB (EObjectType cdktype, void *object, void *clientData, chtype key)
|
|
{
|
|
CDKENTRY *entry = (CDKENTRY *)object;
|
|
struct history_st *history = (struct history_st *) clientData;
|
|
|
|
/* Make sure we don't go out of bounds. */
|
|
if (history->current == history->count)
|
|
{
|
|
Beep();
|
|
return;
|
|
}
|
|
|
|
/* Increment the counter... */
|
|
history->current++;
|
|
|
|
/* If we are at the end, clear the entry field. */
|
|
if (history->current == history->count)
|
|
{
|
|
cleanCDKEntry (entry);
|
|
drawCDKEntry (entry, ObjOf(entry)->box);
|
|
return;
|
|
}
|
|
|
|
/* Display the command. */
|
|
setCDKEntryValue (entry, history->command[history->current]);
|
|
drawCDKEntry (entry, ObjOf(entry)->box);
|
|
}
|
|
|
|
/*
|
|
* This callback allows the user to pick from the history list from a
|
|
* scrolling list.
|
|
*/
|
|
void listHistoryCB (EObjectType cdktype, void *object, void *clientData, chtype key)
|
|
{
|
|
CDKSCROLL *scrollList = 0;
|
|
CDKENTRY *entry = (CDKENTRY *)object;
|
|
struct history_st *history = (struct history_st *) clientData;
|
|
int height = (history->count < 10 ? history->count+3 : 13);
|
|
int selection;
|
|
|
|
/* No history, no list. */
|
|
if (history->count == 0)
|
|
{
|
|
/* Popup a little window telling the user there are no commands. */
|
|
char *mesg[] = {"<C></B/16>No Commands Entered", "<C>No History"};
|
|
popupLabel (entry->screen, mesg, 2);
|
|
|
|
/* Redraw the screen. */
|
|
eraseCDKEntry (ObjOf(entry));
|
|
drawCDKScreen (entry->screen);
|
|
|
|
/* And leave... */
|
|
return;
|
|
}
|
|
|
|
/* Create the scrolling list of previous commands. */
|
|
scrollList = newCDKScroll (entry->screen, CENTER, CENTER, RIGHT,
|
|
height, -10, "<C></B/29>Command History",
|
|
history->command, history->count,
|
|
NUMBERS, A_REVERSE, TRUE, FALSE);
|
|
|
|
/* Get the command to execute. */
|
|
selection = activateCDKScroll (scrollList, 0);
|
|
destroyCDKScroll (scrollList);
|
|
|
|
/* Check the results of the selection. */
|
|
if (selection >= 0)
|
|
{
|
|
/* Get the command and stick it back in the entry field. */
|
|
setCDKEntryValue (entry, history->command[selection]);
|
|
}
|
|
|
|
/* Redraw the screen. */
|
|
eraseCDKEntry (ObjOf(entry));
|
|
drawCDKScreen (ScreenOf(entry));
|
|
}
|
|
|
|
/*
|
|
* This loads the history into the editor from the RC file.
|
|
*/
|
|
void loadHistory (struct history_st *history)
|
|
{
|
|
char *home = 0;
|
|
char filename[1000];
|
|
|
|
/* Create the RC filename. */
|
|
if ((home = getenv ("HOME")) == 0)
|
|
{
|
|
home = ".";
|
|
}
|
|
sprintf (filename, "%s/.sybrc", home);
|
|
|
|
/* Set some variables. */
|
|
history->current = 0;
|
|
history->count = 0;
|
|
|
|
/* Read the file. */
|
|
if ((history->count = readFile (filename, history->command, MAXHISTORY)) != -1)
|
|
{
|
|
history->current = history->count;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This saves the history into RC file.
|
|
*/
|
|
void saveHistory (struct history_st *history, int count)
|
|
{
|
|
FILE *fd = 0;
|
|
char *home = 0;
|
|
char filename[1000];
|
|
int x;
|
|
|
|
/* Create the RC filename. */
|
|
if ((home = getenv ("HOME")) == 0)
|
|
{
|
|
home = ".";
|
|
}
|
|
sprintf (filename, "%s/.sybrc", home);
|
|
|
|
/* Open the file for writing. */
|
|
if ((fd = fopen (filename, "w")) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Start saving the history. */
|
|
for (x=0; x < history->count; x++)
|
|
{
|
|
fprintf (fd, "%s\n", history->command[x]);
|
|
}
|
|
fclose (fd);
|
|
return;
|
|
}
|