/* $Id: syb.c,v 1.13 2016/12/04 15:22:16 tom Exp $ */ #include #include #include #ifdef HAVE_XCURSES char *XCursesProgramName="syb"; #endif #define MAXWIDTH 5000 #define MAXHISTORY 1000 /* * This structure is used for keeping command history. */ struct history_st { unsigned used; int count; int current; char **cmd_history; }; /* * 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. */ BINDFN_PROTO(viewHistoryCB); BINDFN_PROTO(swindowHelpCB); BINDFN_PROTO(historyUpCB); BINDFN_PROTO(historyDownCB); BINDFN_PROTO(listHistoryCB); /* * 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; 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 = copyChar ("master"); history.used = 0; 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); ExitProgram (EXIT_SUCCESS); break; } } /* Set up the command prompt. */ if (prompt == 0) { if (server == 0) { if ((dsquery = getenv ("DSQUERY")) != 0) { sprintf (temp, "[%s] Command >", dsquery); prompt = copyChar (temp); } else { prompt = copyChar ("Command >"); } } else { sprintf (temp, "[%s] Command >", server); prompt = copyChar (temp); } } GPCdkScreen = initCDKScreen (NULL); /* Start color. */ initCDKColor(); /* Initialize DB-Library. */ if (dbinit() == FAIL) { mesg[0] = "Fatal Error"; mesg[1] = "Could not connect to the Sybase database."; popupLabel (GPCdkScreen, mesg, 2); ExitProgram (EXIT_FAILURE); } /* Load the history. */ loadHistory (&history); /* Create the scrolling window. */ commandOutput = newCDKSwindow (GPCdkScreen, CENTER, TOP, -8, -2, "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, CTRL('^'), &listHistoryCB, &history); bindCDKObject (vENTRY, commandEntry, KEY_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 (); ExitProgram (EXIT_FAILURE); } /* 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); freeChar (upper); endCDK(); ExitProgram (EXIT_SUCCESS); } 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, "%d> %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, "%d> %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, "%d> %s", count+1, command); addCDKSwindow (commandOutput, temp, BOTTOM); /* Increment the counter. */ count++; } } /* Keep the history. */ history.used = CDKallocStrings(&(history.cmd_history), command, history.count++, history.used); 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, "Error 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, "Error 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++] = "Login Error"; mesg[lines++] = " "; mesg[lines++] = "The login/password pair does not exist."; mesg[lines++] = " "; mesg[lines++] = "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++] = "Login Error"; mesg[lines++] = " "; mesg[lines++] = "Too many attempyts to login."; mesg[lines++] = "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, "\nSybase 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 (ScreenOf(loginEntry)); refreshCDKScreen (ScreenOf(loginEntry)); /* Get the login to the sybase account. */ login = copyChar (activateCDKEntry (loginEntry, 0)); /* Check if they hit escape. */ if (loginEntry->exitType == vESCAPE_HIT) { mesg[0] = "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%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) || (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] = "Fatal Error"; mesg[1] = "Could not connect to the Sybase database."; popupLabel (screen, mesg, 2); refreshCDKScreen (screen); ExitProgram (EXIT_FAILURE); } /* * 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, "<#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, "Command failed.", BOTTOM); } else { if (!(DBCMDROW (dbProcess))) { /* The command could not return any rows. */ addCDKSwindow (swindow, "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, "<#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 = 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 copyChar (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 = 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. */ int 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); return (TRUE); } /* * This displays a little introduction screen. */ void intro (CDKSCREEN *screen) { int lines = 0; char *mesg[10]; /* Create the message. */ mesg[lines++] = ""; mesg[lines++] = "Sybase Command Interface"; mesg[lines++] = "Written by Mike Glover"; mesg[lines++] = ""; mesg[lines++] = "Type help 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++] = "Help"; mesg[lines++] = ""; mesg[lines++] = "When in the command line."; mesg[lines++] = " Scrolls back one command."; mesg[lines++] = " Scrolls forward one command."; mesg[lines++] = " Activates the scrolling window."; mesg[lines++] = " Displays this help window."; mesg[lines++] = ""; mesg[lines++] = "When in the scrolling window."; mesg[lines++] = " Loads a file into the window."; mesg[lines++] = " Saves the contents of the window to a file."; mesg[lines++] = " Scrolls up one line."; mesg[lines++] = " Scrolls down one line."; mesg[lines++] = " Scrolls back one page."; mesg[lines++] = " Scrolls forward one page."; mesg[lines++] = " Returns to the command line."; mesg[lines++] = " Displays this help window."; mesg[lines++] = ""; mesg[lines++] = " (Refer to the scrolling window online manual for more help.)"; /* Pop up the help message. */ popupLabel (ScreenOf(entry), 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++) { int ch = (unsigned char)(word[x]); if (isalpha (ch)) { upper[x] = toupper(ch); } 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] = "Database Process Error"; mesg[1] = "The database process seems to have died."; mesg[2] = "Try logging in again with the login command"; popupLabel (GPCdkScreen, mesg, 3); return INT_EXIT; } else { mesg[0] = "DB-Library Error"; sprintf (temp, "%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] = "SQL Server Message"; sprintf (temp, "Message Number %ld", mesgNumber); mesg[1] = copyChar (temp); sprintf (temp, "State %d", mesgState); mesg[2] = copyChar (temp); sprintf (temp, "Severity %d", severity); mesg[3] = copyChar (temp); sprintf (temp, "Message %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. */ int swindowHelpCB (EObjectType cdktype, void *object, void *clientData, chtype key) { CDKENTRY *entry = (CDKENTRY *)clientData; help(entry); return (TRUE); } /* * This is the callback for the down arrow. */ int 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 (TRUE); } /* Decrement the counter. */ history->current--; /* Display the command. */ setCDKEntryValue (entry, history->cmd_history[history->current]); drawCDKEntry (entry, ObjOf(entry)->box); return (TRUE); } /* * This is the callback for the down arrow. */ int 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 (TRUE); } /* 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 (TRUE); } /* Display the command. */ setCDKEntryValue (entry, history->cmd_history[history->current]); drawCDKEntry (entry, ObjOf(entry)->box); return (TRUE); } /* * This callback allows the user to pick from the history list from a * scrolling list. */ int 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[] = {"No Commands Entered", "No History"}; popupLabel (ScreenOf(entry), mesg, 2); /* Redraw the screen. */ eraseCDKEntry (entry); drawCDKScreen (ScreenOf(entry)); /* And leave... */ return (TRUE); } /* Create the scrolling list of previous commands. */ scrollList = newCDKScroll (ScreenOf(entry), CENTER, CENTER, RIGHT, height, -10, "Command History", history->cmd_history, 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->cmd_history[selection]); } /* Redraw the screen. */ eraseCDKEntry (entry); drawCDKScreen (ScreenOf(entry)); return (TRUE); } /* * 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 = CDKreadFile (filename, &(history->cmd_history))) != -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->cmd_history[x]); } fclose (fd); return; }