/* OpenDoors Online Software Programming Toolkit * (C) Copyright 1991 - 1999 by Brian Pirie. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * File: ODEmu.c * * Description: Code for the TTY/ANSI/AVATAR terminal emulation routines, * including .ASC/.ANS/.AVT/.RIP file display functions. * * Revisions: Date Ver Who Change * --------------------------------------------------------------- * Oct 13, 1994 6.00 BP New file header format. * Oct 21, 1994 6.00 BP Further isolated com routines. * Dec 09, 1994 6.00 BP Standardized coding style. * Dec 31, 1994 6.00 BP Remove #ifndef USEINLINE DOS code. * Jun 07, 1995 6.00 BP Added od_emu_simulate_modem. * Jul 18, 1995 6.00 BP Fixed warning in call to _ulongdiv(). * Aug 19, 1995 6.00 BP 32-bit portability. * Nov 11, 1995 6.00 BP Removed register keyword. * Nov 14, 1995 6.00 BP Added include of odscrn.h. * Nov 15, 1995 6.00 BP Terminal emulation speed optimization. * Nov 16, 1995 6.00 BP Removed oddoor.h, added odcore.h. * Dec 12, 1995 6.00 BP Added entry, exit and kernel macros. * Dec 24, 1995 6.00 BP Use od_connect_speed for modem sim. * Dec 30, 1995 6.00 BP Added ODCALL for calling convention. * Jan 21, 1996 6.00 BP Use ODScrnShowMessage(). * Jan 09, 1996 6.00 BP ODComOutbound() returns actual size. * Feb 19, 1996 6.00 BP Changed version number to 6.00. * Mar 03, 1996 6.10 BP Begin version 6.10. * Mar 06, 1996 6.10 BP Prevent TC generated N_LXMUL@ call. * Mar 19, 1996 6.10 BP MSVC15 source-level compatibility. * Oct 18, 2001 6.11 MD Added od_send_file_section() * Aug 10, 2003 6.23 SH *nix support */ #define BUILDING_OPENDOORS #include #include #include #include #include #include "OpenDoor.h" #include "ODStr.h" #include "ODCore.h" #include "ODGen.h" #include "ODCom.h" #include "ODTypes.h" #include "ODScrn.h" #include "ODKrnl.h" #include "ODUtil.h" /* Manifest constants. */ #define MODEM_SIMULATOR_TICK 54L #define LEVEL_NONE 0 #define LEVEL_ASCII 1 #define LEVEL_ANSI 2 #define LEVEL_AVATAR 3 #define LEVEL_RIP 4 /* Local helper function prototypes. */ static void ODEmulateFromBuffer(char *pszBuffer, BOOL bRemoteEcho); static FILE *ODEmulateFindCompatFile(const char *pszBaseName, INT *pnLevel); static void ODEmulateFillArea(BYTE btLeft, BYTE btTop, BYTE btRight, BYTE btBottom, char chToFillWith); /* Current terminal emulator state variables. */ static BYTE btANSISeqLevel = 0; static INT anANSIParams[10]; static char szCurrentParam[4] = ""; static BYTE btCurrentParamLength; static BYTE btSavedColumn=1; static BYTE btSavedRow = 1; static char szToRepeat[129]; static BYTE btRepeatCount; static BYTE btAvatarSeqLevel = 0; static char chPrevParam; static BYTE btNumParams; static BYTE btDefaultAttrib = 7; static BOOL bAvatarInsertMode = FALSE; static INT8 btScrollLines; static BYTE btScrollLeft, btScrollTop, btScrollRight, btScrollBottom; /* Variables for tracking hotkeys while displaying a menu file. */ static char *pszCurrentHotkeys=NULL; static char chHotkeyPressed; /* Lookup table to map colors from ANSI values to PC color values. */ static BYTE abtANSIToPCColorTable[8] = {0, 4, 2, 6, 1, 5, 3, 7}; /* ---------------------------------------------------------------------------- * od_hotkey_menu() * * Displays a .ASC/.ANS/.AVT/.RIP file, just as od_send_file() does. However, * unlike od_send_file(), od_hotkey_menu() also allows checks for keypresses * by the user. If any of the hotkeys listed in pszHotKeys are pressed while * the file is being displayed, or after the file has finished being displayed, * od_hotkey_menu() will return this value to the caller. This allows menu * screens to be easily displayed from on-disk files, while permitting the user * to choose a menu item at any time. * * Parameters: pszFileName - Pointer to the filename to display. * * pszHotKeys - Pointer to a string listing the valid keys. * Keys are not case sensitive. * * bWait - If TRUE, od_hotkey_menu() will not return until * the user presses one of the valid keys. If FALSE, * then od_hotkey_menu() will return as soon as it * has finished displaying the file, even if the * user has not pressed any key;. * * Return: The key pressed by the user, or '\0' if no key was pressed. */ ODAPIDEF char ODCALL od_hotkey_menu(char *pszFileName, char *pszHotKeys, BOOL bWait) { char chPressed; /* Log function entry if running in trace mode. */ TRACE(TRACE_API, "od_hotkey_menu()"); /* Ensure that OpenDoors has been initialized. */ if(!bODInitialized) od_init(); OD_API_ENTRY(); if(!pszHotKeys) { od_control.od_error = ERR_PARAMETER; OD_API_EXIT(); return('\0'); } /* Store pointer to string of hotkeys in global hotkey array for access */ /* from od_send_file(). */ pszCurrentHotkeys = (char *)pszHotKeys; /* Clear the hotkey status variable. */ chHotkeyPressed = '\0'; /* Display the menu file using od_send_file() primitive. */ if(!od_send_file(pszFileName)) { OD_API_EXIT(); return('\0'); } /* Clear the global hotkey array. */ pszCurrentHotkeys = NULL; /* If the file display was interrupted by the pressing of one of the */ /* hotkeys, return the pressed hotkey immediately. */ if(chHotkeyPressed != '\0') { OD_API_EXIT(); return(chHotkeyPressed); } /* If no hotkey has been pressed key, and the wait flag has been set, */ /* wait for the user to press a valid hotkey. */ if(bWait) { /* Wait for a valid hotkey using the od_get_answer() primitive. */ chPressed = od_get_answer(pszHotKeys); /* If a remote user is connected on this node. */ if(od_control.baud) { /* Clear the outbound buffer. */ ODComClearOutbound(hSerialPort); } /* Return the hotkey pressed by the user. */ OD_API_EXIT(); return(chPressed); } /* No hotkey has been pressed, so return 0. */ /* (Since 0 is used to terminate the string of valid hotkeys, it can */ /* never be a valid hotkey itself, and is therefore a safe value to */ /* indicate the "no key press" state.) */ OD_API_EXIT(); return(0); } /* ---------------------------------------------------------------------------- * od_send_file() * * Displays a .ASC/.ANS/.AVT/.RIP file to the local and remote screens. If * OpenDoors is unable to display the required file format locally, an * equivalent file that is locally displayable is selected. If no such * equivalent file can be found, then a message box is displayed, indicating * that the file is being transmitted to the remote system. * * Parameters: pszFileName - The name of the file to send. This parameter may * explicitly specify the full filename and * extension, or the extension may be omitted. In the * case that the extension is omitted, this function * automatically selects the appropriate file type * for the current display mode (RIP, ANSI, etc.). * * Return: TRUE on success, or FALSE on failure. */ ODAPIDEF BOOL ODCALL od_send_file(const char *pszFileName) { FILE *pfRemoteFile; FILE *pfLocalFile = NULL; BOOL bAnythingLocal = TRUE; void *pWindow; INT nFileLevel; BYTE btCount; BOOL bPausing; char chKey; char *pchParsing; char szMessage[74]; /* Log function entry if running in trace mode. */ TRACE(TRACE_API, "od_send_file()"); /* Initialize OpenDoors if it hasn't already been done. */ if(!bODInitialized) od_init(); OD_API_ENTRY(); if(!pszFileName) { od_control.od_error = ERR_PARAMETER; OD_API_EXIT(); return(FALSE); } /* Initialize local variables. */ btCount = 2; /* Turn on page pausing, if available. */ bPausing = od_control.od_page_pausing; /* If operating in auto-filename mode (no extension specified). */ if(strchr(pszFileName, '.') == NULL) { /* Begin by searching for a .RIP file. */ nFileLevel = LEVEL_RIP; /* If no .ASC/.ANS/.AVT/.RIP file. */ if((pfRemoteFile = ODEmulateFindCompatFile(pszFileName, &nFileLevel)) == NULL) { /* Then return with an error. */ od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } /* If the file found was a .RIP. */ if(nFileLevel == LEVEL_RIP) { /* Search for file to display locally. */ nFileLevel = LEVEL_AVATAR; /* No page pausing with .RIP display. */ bPausing = FALSE; if((pfLocalFile = ODEmulateFindCompatFile(pszFileName, &nFileLevel)) == NULL) { /* If there is no further .ASC/.ANS/.AVT/.RIP files, then no */ /* local display. */ bAnythingLocal = FALSE; } } else if(nFileLevel == LEVEL_NONE) { od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } /* Get filename of remote file. */ strcpy(szODWorkString, pszFileName); strcat(szODWorkString, ".rip"); strupr(szODWorkString); } else { /* If the full filename was specified, then attempt to open that file. */ if((pfRemoteFile = fopen(pszFileName,"rb")) == NULL) { /* If unable to open file, then return. */ od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } strcpy(szODWorkString, pszFileName); strupr(szODWorkString); if(strstr(szODWorkString, ".rip")) { /* No page pausing during .RIP display. */ bPausing = FALSE; /* Disable local display. */ bAnythingLocal = FALSE; } } /* Set default colour to grey on black. */ btDefaultAttrib = 0x07; /* Reset all terminal emulation. */ btAvatarSeqLevel = 0; btANSISeqLevel = 0; /* Turn off AVATAR insert mode. */ bAvatarInsertMode = FALSE; /* Reset [S]top/[P]ause control key status. */ chLastControlKey = 0; if(!bAnythingLocal) { strcpy(szODWorkString, od_control.od_sending_rip); strcat(szODWorkString, pszFileName); ODStringCopy(szMessage, szODWorkString, sizeof(szMessage)); pWindow = ODScrnShowMessage(szMessage, 0); } /* Loop to display each line in the file(s) with page pausing, etc. */ for(;;) { /* Call the OpenDoors kernel routine. */ CALL_KERNEL_IF_NEEDED(); /* If hotkeys are active. */ if(pszCurrentHotkeys != NULL) { /* If a key is waiting in buffer. */ while((chKey = (char)tolower(od_get_key(FALSE))) != 0) { /* Check for key in hotkey string. */ pchParsing = (char *)pszCurrentHotkeys; while(*pchParsing) { /* If key is found. */ if(tolower(*pchParsing) == chKey) { /* Return, indicating that hotkey was pressed. */ chHotkeyPressed = *pchParsing; goto abort_send; } ++pchParsing; } } } /* If a control key has been pressed. */ if(chLastControlKey) { switch(chLastControlKey) { /* If it was a stop control key. */ case 's': if(od_control.od_list_stop) { /* If enabled, clear keyboard buffer. */ abort_send: /* If operating in remote mode. */ if(od_control.baud) { /* Clear the outbound FOSSIL buffer. */ ODComClearOutbound(hSerialPort); } /* Return from function. */ goto end_transmission; } break; /* If control key was "pause". */ case 'p': /* If pause is enabled. */ if(od_control.od_list_pause) { /* Clear keyboard buffer. */ od_clear_keybuffer(); /* Wait for any keypress. */ od_get_key(TRUE); } } /* Clear control key status. */ chLastControlKey = 0; } /* Get next line, if any. */ if(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfRemoteFile) == NULL) { /* If different local file. */ if(pfLocalFile) { /* Display rest of it. */ while(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfLocalFile)) { /* Pass each line to terminal emulator. */ ODEmulateFromBuffer(szODWorkString, FALSE); } } /* Return from od_send_file(). */ goto end_transmission; } /* Set parsepos = last char in globworkstr. */ pchParsing = (char *)&szODWorkString; while(*++pchParsing) ; --pchParsing; /* Check for end of page state. */ if((*pchParsing == '\r' || *pchParsing == '\n') && ++btCount >= od_control.user_screen_length && bPausing) { /* Display page pause prompt. */ if(ODPagePrompt(&bPausing)) { /* If user chose to abort, then return from od_send_file(). */ goto end_transmission; } /* Reset line count. */ btCount = 2; } /* If the file is also to be displayed locally. */ if(bAnythingLocal) { /* If the local file is different from the remote file, then obtain */ /* the next line from the local file. */ if(pfLocalFile) { od_disp(szODWorkString, strlen(szODWorkString), FALSE); if(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfLocalFile) == NULL) { while(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfRemoteFile)) { od_disp(szODWorkString, strlen(szODWorkString), FALSE); } /* Return from od_send_file(). */ goto end_transmission; } ODEmulateFromBuffer(szODWorkString, FALSE); } else { /* Pass the string through the local terminal emulation */ /* system, and send a copy to the remote system. */ if(od_control.od_no_ra_codes) { ODEmulateFromBuffer(szODWorkString, FALSE); od_disp(szODWorkString, strlen(szODWorkString), FALSE); } else { ODEmulateFromBuffer(szODWorkString,TRUE); } } } else { /* If the file is not being displayed locally, then just send the */ /* entire line to the remote system (if any). */ od_disp(szODWorkString,strlen(szODWorkString),FALSE); } } end_transmission: /* Close remote file. */ fclose(pfRemoteFile); /* If there is a different local file, then close it too. */ if(pfLocalFile) { fclose(pfLocalFile); } /* If we are not displaying anything on the local system. */ if(!bAnythingLocal) { /* Wait while file is being sent. */ if(od_control.baud != 0) { int nOutboundSize; do { CALL_KERNEL_IF_NEEDED(); ODComOutbound(hSerialPort, &nOutboundSize); } while(nOutboundSize != 0); } /* Get rid of the window. */ ODScrnRemoveMessage(pWindow); } OD_API_EXIT(); return(TRUE); } /* ---------------------------------------------------------------------------- * od_send_file_section() * * Displays a .ASC/.ANS/.AVT/.RIP multi-section file to the local and remote * screens. If OpenDoors is unable to display the required file format locally, * an equivalent file that is locally displayable is selected. If no such * equivalent file can be found, then a message box is displayed, incdicating * that the file is being transmitted to the remote system. * * Note: This function works virtually identical to od_send_file() except it * checks for the section headers (if found). * * Parameters: pszFileName - The name of the file to send. This parameter may * explicitly specify the full filename and * extension, or the extension may be omitted. In the * case that the extension is omitted, this function * automatically selects the appropriate file type * for the current display mode (RIP, ANSI, etc.). * pszSectionname - Name of the section in which to send. This * parameter must include only the section name * and not the @# delimiter. * * Return: TRUE on success, or FALSE on failure. */ ODAPIDEF BOOL ODCALL od_send_file_section(char *pszFileName, char *pszSectionName) { FILE *pfRemoteFile; FILE *pfLocalFile = NULL; BOOL bAnythingLocal = TRUE; void *pWindow; INT nFileLevel; BYTE btCount; BOOL bPausing; char chKey; char *pchParsing; char szMessage[74]; char szFullSectionName[256]; BOOL bSectionFound = FALSE; UINT uSectionNameLength; /* Log function entry if running in trace mode. */ TRACE(TRACE_API, "od_send_file_section()"); /* Initialize OpenDoors if it hasn't already been done. */ if(!bODInitialized) od_init(); OD_API_ENTRY(); if(!pszFileName || !pszSectionName) { od_control.od_error = ERR_PARAMETER; OD_API_EXIT(); return(FALSE); } /* Initialize local variables. */ btCount = 2; /* Turn on page pausing, if available. */ bPausing = od_control.od_page_pausing; /* If operating in auto-filename mode (no extension specified). */ if(strchr(pszFileName, '.') == NULL) { /* Begin by searching for a .RIP file. */ nFileLevel = LEVEL_RIP; /* If no .ASC/.ANS/.AVT/.RIP file. */ if((pfRemoteFile = ODEmulateFindCompatFile(pszFileName, &nFileLevel)) == NULL) { /* Then return with an error. */ od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } /* If the file found was a .RIP. */ if(nFileLevel == LEVEL_RIP) { /* Search for file to display locally. */ nFileLevel = LEVEL_AVATAR; /* No page pausing with .RIP display. */ bPausing = FALSE; if((pfLocalFile = ODEmulateFindCompatFile(pszFileName, &nFileLevel)) == NULL) { /* If there is no further .ASC/.ANS/.AVT/.RIP files, then no */ /* local display. */ bAnythingLocal = FALSE; } } else if(nFileLevel == LEVEL_NONE) { od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } /* Get filename of remote file. */ strcpy(szODWorkString, pszFileName); strcat(szODWorkString, ".rip"); strupr(szODWorkString); } else { /* If the full filename was specified, then attempt to open that file. */ if((pfRemoteFile = fopen(pszFileName,"rb")) == NULL) { /* If unable to open file, then return. */ od_control.od_error = ERR_FILEOPEN; OD_API_EXIT(); return(FALSE); } strcpy(szODWorkString, pszFileName); strupr(szODWorkString); if(strstr(szODWorkString, ".rip")) { /* No page pausing during .RIP display. */ bPausing = FALSE; /* Disable local display. */ bAnythingLocal = FALSE; } } /* Set default colour to grey on black. */ btDefaultAttrib = 0x07; /* Reset all terminal emulation. */ btAvatarSeqLevel = 0; btANSISeqLevel = 0; /* Turn off AVATAR insert mode. */ bAvatarInsertMode = FALSE; /* Reset [S]top/[P]ause control key status. */ chLastControlKey = 0; if(!bAnythingLocal) { strcpy(szODWorkString, od_control.od_sending_rip); strcat(szODWorkString, pszFileName); ODStringCopy(szMessage, szODWorkString, sizeof(szMessage)); pWindow = ODScrnShowMessage(szMessage, 0); } /* Create section name information */ strcpy(szFullSectionName, "@#"); strncat(szFullSectionName, pszSectionName, 254); /* Get the length of the section name in it's full form */ uSectionNameLength = strlen(szFullSectionName); /* Loop to display each line in the file(s) with page pausing, etc. */ for(;;) { /* Call the OpenDoors kernel routine. */ CALL_KERNEL_IF_NEEDED(); /* If hotkeys are active. */ if(pszCurrentHotkeys != NULL) { /* If a key is waiting in buffer. */ while((chKey = (char)tolower(od_get_key(FALSE))) != 0) { /* Check for key in hotkey string. */ pchParsing = (char *)pszCurrentHotkeys; while(*pchParsing) { /* If key is found. */ if(tolower(*pchParsing) == chKey) { /* Return, indicating that hotkey was pressed. */ chHotkeyPressed = *pchParsing; goto abort_send; } ++pchParsing; } } } /* If a control key has been pressed. */ if(chLastControlKey) { switch(chLastControlKey) { /* If it was a stop control key. */ case 's': if(od_control.od_list_stop) { /* If enabled, clear keyboard buffer. */ abort_send: /* If operating in remote mode. */ if(od_control.baud) { /* Clear the outbound FOSSIL buffer. */ ODComClearOutbound(hSerialPort); } /* Return from function. */ goto end_transmission; } break; /* If control key was "pause". */ case 'p': /* If pause is enabled. */ if(od_control.od_list_pause) { /* Clear keyboard buffer. */ od_clear_keybuffer(); /* Wait for any keypress. */ od_get_key(TRUE); } } /* Clear control key status. */ chLastControlKey = 0; } /* Get next line, if any. */ if(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfRemoteFile) == NULL) { /* If different local file. */ if(pfLocalFile) { /* Display rest of it. */ while(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfLocalFile)) { if (!bSectionFound && strncmp(szFullSectionName, szODWorkString, uSectionNameLength) == 0) { /* Section Found, allow all lines up to EOF or new section to be displayed */ bSectionFound = TRUE; /* Read next line, ignore the section line */ continue; } else if (!bSectionFound) { /* Section not found yet, continue loop */ continue; } else if (bSectionFound && strncmp(szODWorkString, "@#", 2) == 0) { /* New Section Intercepted */ goto end_transmission; } /* Pass each line to terminal emulator. */ ODEmulateFromBuffer(szODWorkString, FALSE); } } /* Return from od_send_file(). */ goto end_transmission; } /* Check for section header @# + pszSectionName */ if (!bSectionFound && strncmp(szFullSectionName, szODWorkString, uSectionNameLength) == 0) { /* Flag the section as found */ bSectionFound = TRUE; /* Read next line, ignore section line */ continue; } else if (!bSectionFound) { /* Section hasn't been found yet */ continue; } else if (bSectionFound && strncmp(szODWorkString, "@#", 2) == 0) { /* New section found, terminate send */ goto end_transmission; } /* Set parsepos = last char in globworkstr. */ pchParsing = (char *)&szODWorkString; while(*++pchParsing) ; --pchParsing; /* Check for end of page state. */ if((*pchParsing == '\r' || *pchParsing == '\n') && ++btCount >= od_control.user_screen_length && bPausing) { /* Display page pause prompt. */ if(ODPagePrompt(&bPausing)) { /* If user chose to abort, then return from od_send_file(). */ goto end_transmission; } /* Reset line count. */ btCount = 2; } /* If the file is also to be displayed locally. */ if(bAnythingLocal) { /* If the local file is different from the remote file, then obtain */ /* the next line from the local file. */ if(pfLocalFile) { od_disp(szODWorkString, strlen(szODWorkString), FALSE); if(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfLocalFile) == NULL) { while(fgets(szODWorkString, OD_GLOBAL_WORK_STRING_SIZE-1, pfRemoteFile)) { od_disp(szODWorkString, strlen(szODWorkString), FALSE); } /* Return from od_send_file(). */ goto end_transmission; } ODEmulateFromBuffer(szODWorkString, FALSE); } else { /* Pass the string through the local terminal emulation */ /* system, and send a copy to the remote system. */ if(od_control.od_no_ra_codes) { ODEmulateFromBuffer(szODWorkString, FALSE); od_disp(szODWorkString, strlen(szODWorkString), FALSE); } else { ODEmulateFromBuffer(szODWorkString,TRUE); } } } else { /* If the file is not being displayed locally, then just send the */ /* entire line to the remote system (if any). */ od_disp(szODWorkString,strlen(szODWorkString),FALSE); } } end_transmission: /* Close remote file. */ fclose(pfRemoteFile); /* If there is a different local file, then close it too. */ if(pfLocalFile) { fclose(pfLocalFile); } /* If we are not displaying anything on the local system. */ if(!bAnythingLocal) { /* Wait while file is being sent. */ if(od_control.baud != 0) { int nOutboundSize; do { CALL_KERNEL_IF_NEEDED(); ODComOutbound(hSerialPort, &nOutboundSize); } while(nOutboundSize != 0); } /* Get rid of the window. */ ODScrnRemoveMessage(pWindow); } /* If the section was not found, return FALSE for error */ if (!bSectionFound) { OD_API_EXIT(); return (FALSE); } OD_API_EXIT(); return(TRUE); } /* ---------------------------------------------------------------------------- * ODEmulateFindCompatFile() *** PRIVATE FUNCTION *** * * Searches for an .ASC/.ANS/.AVT/.RIP file that is compatible with the * specified display capabilities level. * * Parameters: pszBaseName - Base filename to use. * * pnLevel - Highest file level to search for. This value is * updated to indicate the type of file found. * * Return: A pointer to a now-open file of the required type, or NULL if * no match was found. */ static FILE *ODEmulateFindCompatFile(const char *pszBaseName, INT *pnLevel) { FILE *pfCompatibleFile; ASSERT(pszBaseName != NULL); ASSERT(pnLevel != NULL); ASSERT(*pnLevel >= 0 && *pnLevel <= 4); /* Loop though .RIP/.AVT/.ANS/.ASC extensions. */ for(;*pnLevel > LEVEL_NONE; --*pnLevel) { /* Get base-filename passed in. */ strcpy(szODWorkString, pszBaseName); /* Try current extension. */ switch(*pnLevel) { case LEVEL_RIP: if(!od_control.user_rip) continue; strcat(szODWorkString, ".rip"); break; case LEVEL_AVATAR: if(!od_control.user_avatar) continue; strcat(szODWorkString, ".avt"); break; case LEVEL_ANSI: if(!od_control.user_ansi) continue; strcat(szODWorkString, ".ans"); break; case LEVEL_ASCII: strcat(szODWorkString, ".asc"); break; } /* If we are able to open this file, then return a pointer to */ /* the file. */ if((pfCompatibleFile = fopen(szODWorkString,"rb")) != NULL) return(pfCompatibleFile); } /* Return NULL if no file was found. */ return(NULL); } /* ---------------------------------------------------------------------------- * od_disp_emu() * * Sends an entire string to both local and remote systems. The characters * displayed locally are fed through the local terminal emulation sub-system, * allowing aribtrary ANSI/AVATAR control sequences to be displayed both * locally and remotely. * * Parameters: pszToDisplay - Pointer to string to display. * * bRemoteEcho - TRUE if characters should also be transmitted * to the remote system. * * Return: void */ ODAPIDEF void ODCALL od_disp_emu(const char *pszToDisplay, BOOL bRemoteEcho) { BOOL bTranslateRemote; /* Log function entry if running in trace mode. */ TRACE(TRACE_API, "od_disp_emu()"); /* Ensure that OpenDoors has been initialized. */ if(!bODInitialized) od_init(); OD_API_ENTRY(); /* Send pszToDisplay to remote system. */ if(bRemoteEcho) { if(od_control.od_no_ra_codes) { od_disp(pszToDisplay, strlen(pszToDisplay), FALSE); bTranslateRemote = FALSE; } else { bTranslateRemote = TRUE; } } else { bTranslateRemote = FALSE; } /* Pass string to be emulated to local terminal emulation function. */ ODEmulateFromBuffer(pszToDisplay, bTranslateRemote); OD_API_EXIT(); } /* ---------------------------------------------------------------------------- * od_emulate() * * Sends a single character to both local and remote systems. The characters * displayed locally are fed through the local terminal emulation sub-system, * allowing aribtrary ANSII/AVATAR control sequences to be displayed both * locally and remotely. * * Parameters: chToEmulate - Character to feed through the terminal emulator. * * Return: void */ ODAPIDEF void ODCALL od_emulate(char chToEmulate) { static char szBuffer[2]; *szBuffer = chToEmulate; /* Log function entry if running in trace mode. */ TRACE(TRACE_API, "od_emulate()"); /* Ensure that OpenDoors has been initialized. */ if(!bODInitialized) od_init(); OD_API_ENTRY(); /* Pass character to be emulated to local terminal emulation function. */ ODEmulateFromBuffer(szBuffer, TRUE); OD_API_EXIT(); } /* ---------------------------------------------------------------------------- * ODEmulateFromBuffer() *** PRIVATE FUNCTION *** * * Displays a string on the local screen, interpreting any terminal emulation * control sequences. The string may also be sent to the remote system at the * same time. * * Parameters: pszBuffer - Pointer to the string to transmit. * * bRemoteEcho - TRUE if string should also be sent to the remote * system, FALSE if it should not be. * * Return: void */ static void ODEmulateFromBuffer(char *pszBuffer, BOOL bRemoteEcho) { char chCurrent; static tODScrnTextInfo TextInfo; INT nTemp; BOOL bEchoThisChar; INT nCharsPerTick; INT nCharsThisTick; tODTimer ModemSimTimer; ASSERT(pszBuffer != NULL); /* If we should simulate modem transmission speed. */ if(od_control.od_emu_simulate_modem) { DWORD lCharsPerSecond; /* Determine character per second rate to simulate. */ if(od_control.baud == 0) { lCharsPerSecond = 960L; } else { ODDWordDivide(&lCharsPerSecond, (DWORD *)NULL, od_control.od_connect_speed, 10L); } /* Determine number of characters to send per timer tick. */ lCharsPerSecond = ODDWordMultiply(lCharsPerSecond, MODEM_SIMULATOR_TICK); ODDWordDivide(&lCharsPerSecond, (DWORD *)NULL, lCharsPerSecond, 1000L); nCharsPerTick = (INT)lCharsPerSecond; /* Start tick timer. */ ODTimerStart(&ModemSimTimer, MODEM_SIMULATOR_TICK); /* Reset number of characters that we have sent during this tick. */ nCharsThisTick = 0; } while(*pszBuffer) { /* Read the next character from the buffer into local variable for */ /* access speed efficiency. */ chCurrent = *pszBuffer; /* If we should simulate modem transmission speed. */ if(od_control.od_emu_simulate_modem) { /* If we have now sent all of the characters that should be sent */ /* during this tick. */ if(nCharsThisTick++ >= nCharsPerTick) { /* Wait for timer to elapse. */ ODTimerWaitForElapse(&ModemSimTimer); /* Restart tick timer. */ ODTimerStart(&ModemSimTimer, MODEM_SIMULATOR_TICK); /* Reset characters sent in this tick. */ nCharsThisTick = 0; } } bEchoThisChar = bRemoteEcho; /* Switch according to ANSI emulator state. */ switch(btANSISeqLevel) { /* If we are not in the middle of an ANSI command string. */ case 0: /* Switch according to current character. */ switch(chCurrent) { /* If this is the escape character. */ case 27: /* If we are not in the middle of an AVATAR sequence. */ if(btAvatarSeqLevel == 0) { /* Then treat this as the start an ANSI sequence. */ btANSISeqLevel = 1; break; } /* Deliberate fallthrough to default case. */ /* If not start of an ANSI sequence. */ default: /* Check our position in AVATAR sequence. */ switch(btAvatarSeqLevel) { /* If not in middle of an AVATAR command. */ case 0: /* Check the character we've been sent. */ switch(chCurrent) { /* Check for QBBS/RA pause for keypress code. */ case 0x01: if(od_control.od_no_ra_codes) { goto output_next_char; } /* Wait for user to press [ENTER] key. */ od_get_answer("\n\r"); bEchoThisChar = FALSE; break; /* QBBS/RA ^F user parameters. */ case 0x06: if(od_control.od_no_ra_codes) { goto output_next_char; } btAvatarSeqLevel = 21; bEchoThisChar = FALSE; break; /* QBBS/RA ^K user parameters. */ case 0x0b: if(od_control.od_no_ra_codes) { goto output_next_char; } btAvatarSeqLevel = 22; bEchoThisChar = FALSE; break; case 0x0c: bAvatarInsertMode = FALSE; ODScrnSetAttribute((BYTE)( od_control.od_cur_attrib = btDefaultAttrib)); ODScrnClear(); break; case 0x19: bAvatarInsertMode = FALSE; btAvatarSeqLevel = 1; break; case 0x16: /* ^V */ btAvatarSeqLevel = 3; break; default: output_next_char: /* Output next character. */ if(bAvatarInsertMode) { ODScrnGetTextInfo(&TextInfo); if(TextInfo.curx < 80) { ODScrnCopyText(TextInfo.curx, TextInfo.cury, 79, TextInfo.cury, (BYTE)(TextInfo.curx + 1), TextInfo.cury); } ODScrnDisplayChar(chCurrent); } else { ODScrnDisplayChar(chCurrent); } } break; case 1: bAvatarInsertMode = FALSE; chPrevParam = chCurrent; btAvatarSeqLevel = 2; break; case 2: for(nTemp = 0; nTemp < (INT)chCurrent; ++nTemp) { ODScrnDisplayChar(chPrevParam); } btAvatarSeqLevel = 0; break; case 3: switch(chCurrent) { case 0x01: bAvatarInsertMode = FALSE; btAvatarSeqLevel = 4; break; case 0x02: bAvatarInsertMode = FALSE; ODScrnGetTextInfo(&TextInfo); ODScrnSetAttribute((BYTE) (od_control.od_cur_attrib = TextInfo.attribute | 0x80)); btAvatarSeqLevel = 0; break; case 0x03: bAvatarInsertMode = FALSE; ODScrnGetTextInfo(&TextInfo); if(TextInfo.cury > 1) { ODScrnSetCursorPos(TextInfo.curx, (BYTE)(TextInfo.cury - 1)); } btAvatarSeqLevel = 0; break; case 0x04: bAvatarInsertMode = FALSE; ODScrnGetTextInfo(&TextInfo); if(TextInfo.cury < 25) { ODScrnSetCursorPos(TextInfo.curx, (BYTE)(TextInfo.cury + 1)); } btAvatarSeqLevel = 0; break; case 0x05: bAvatarInsertMode = FALSE; ODScrnGetTextInfo(&TextInfo); if(TextInfo.curx > 1) { ODScrnSetCursorPos((BYTE)(TextInfo.curx - 1), TextInfo.cury); } btAvatarSeqLevel = 0; break; case 0x06: bAvatarInsertMode = FALSE; ODScrnGetTextInfo(&TextInfo); if(TextInfo.curx < 80) { ODScrnSetCursorPos((BYTE)(TextInfo.curx + 1), TextInfo.cury); } btAvatarSeqLevel = 0; break; case 0x07: bAvatarInsertMode = FALSE; ODScrnClearToEndOfLine(); btAvatarSeqLevel = 0; break; case 0x08: bAvatarInsertMode = FALSE; btAvatarSeqLevel = 5; break; case 0x09: /* ^I */ bAvatarInsertMode = TRUE; btAvatarSeqLevel = 0; break; case 0x0a: /* ^J */ btScrollLines = -1; btAvatarSeqLevel = 7; break; case 0x0b: /* ^K */ btScrollLines = 1; btAvatarSeqLevel = 7; break; case 0x0c: /* ^L */ btAvatarSeqLevel = 14; break; case 0x0d: /* ^M */ btAvatarSeqLevel = 15; break; case 0x0e: /* ^N */ ODScrnGetTextInfo(&TextInfo); if(TextInfo.curx < 80) { ODScrnCopyText((BYTE)(TextInfo.curx + 1), TextInfo.cury, 80, TextInfo.cury, TextInfo.curx, TextInfo.cury); } ODScrnEnableScrolling(FALSE); ODScrnSetCursorPos(80, TextInfo.cury); ODScrnDisplayChar(' '); ODScrnEnableScrolling(TRUE); ODScrnSetCursorPos(TextInfo.curx, TextInfo.cury); btAvatarSeqLevel = 0; break; case 0x19: /* ^Y */ btAvatarSeqLevel = 19; break; default: btAvatarSeqLevel = 0; } break; case 4: ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = chCurrent)); btAvatarSeqLevel = 0; break; case 5: chPrevParam = chCurrent; btAvatarSeqLevel = 6; break; case 6: ODScrnSetCursorPos(chCurrent, chPrevParam); btAvatarSeqLevel = 0; break; case 7: if(btScrollLines < 1) { btScrollLines = chCurrent; } else { btScrollLines = -chCurrent; } btAvatarSeqLevel = 8; break; case 8: btScrollTop = chCurrent; btAvatarSeqLevel = 9; break; case 9: btScrollLeft = chCurrent; btAvatarSeqLevel = 10; break; case 10: btScrollBottom = chCurrent; btAvatarSeqLevel = 11; break; case 11: btScrollRight = chCurrent; btAvatarSeqLevel = 12; break; case 12: if(btScrollLines == 0 || abs(btScrollLines) > (btScrollBottom - btScrollTop)) { ODEmulateFillArea(btScrollLeft, btScrollTop, btScrollRight, btScrollBottom, ' '); } else if(btScrollLines < 0) { ODScrnCopyText(btScrollLeft, btScrollTop, btScrollRight, (BYTE)(btScrollBottom + btScrollLines), btScrollLeft, (BYTE)(btScrollTop - btScrollLines)); ODEmulateFillArea(btScrollLeft, btScrollTop, btScrollRight, (BYTE)(btScrollTop - (btScrollLines - 1)), ' '); } else { ODScrnCopyText(btScrollLeft, (BYTE)(btScrollTop + btScrollLines), btScrollRight, btScrollBottom, btScrollLeft, btScrollTop); ODEmulateFillArea(btScrollLeft, (BYTE)(btScrollBottom - (btScrollLines - 1)), btScrollRight, btScrollBottom, ' '); } btAvatarSeqLevel = 0; break; case 14: btScrollLines = (chCurrent & 0x7f); btScrollRight = ' '; btAvatarSeqLevel = 17; break; case 15: btScrollLines = (chCurrent & 0x7f); btAvatarSeqLevel = 16; break; case 16: btScrollRight = chCurrent; btAvatarSeqLevel = 17; break; case 17: btScrollTop = chCurrent; btAvatarSeqLevel = 18; break; case 18: ODScrnGetTextInfo(&TextInfo); ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = btScrollLines)); ODEmulateFillArea(TextInfo.curx, TextInfo.cury, (BYTE)(TextInfo.curx + chCurrent), (BYTE)(TextInfo.cury + btScrollTop), btScrollRight); btAvatarSeqLevel = 0; break; case 19: btScrollLines = (chCurrent & 0x7f); szToRepeat[btRepeatCount = 0] = '\0'; btAvatarSeqLevel = 20; break; case 20: if(btRepeatCount < btScrollLines) { szToRepeat[btRepeatCount] = chCurrent; szToRepeat[++btRepeatCount] = '\0'; } else { for(btRepeatCount = 0; btRepeatCount < (BYTE)chCurrent;++btRepeatCount) { ODScrnDisplayString(szToRepeat); } btAvatarSeqLevel = 0; } break; /* RA/QBBS ^F control codes. */ case 21: bEchoThisChar = FALSE; switch(chCurrent) { case 'A': od_disp_str(od_control.user_name); break; case 'B': od_disp_str(od_control.user_location); break; case 'C': od_disp_str(od_control.user_password); break; case 'D': od_disp_str(od_control.user_dataphone); break; case 'E': od_disp_str(od_control.user_homephone); break; case 'F': od_disp_str(od_control.user_lastdate); break; case 'G': od_disp_str(od_control.user_lasttime); break; case 'H': btScrollLines = 0; goto show_flags; case 'I': btScrollLines = 1; goto show_flags; case 'J': btScrollLines = 2; goto show_flags; case 'K': btScrollLines = 3; show_flags: for(btRepeatCount = 0; btRepeatCount < 8; ++btRepeatCount) { if((od_control.user_flags[btScrollLines] >> btRepeatCount) & 0x01) { szToRepeat[btRepeatCount] = 'X'; } else { szToRepeat[btRepeatCount] = '-'; } } szToRepeat[btRepeatCount] = '\0'; od_disp_str(szToRepeat); break; case 'L': od_printf("%lu", od_control.user_net_credit); break; case 'M': od_printf("%u", od_control.user_messages); break; case 'N': od_printf("%u", od_control.user_lastread); break; case 'O': od_printf("%u", od_control.user_security); break; case 'P': od_printf("%u", od_control.user_numcalls); break; case 'Q': od_printf("%ul", od_control.user_uploads); break; case 'R': od_printf("%ul", od_control.user_upk); break; case 'S': od_printf("%ul", od_control.user_downloads); break; case 'T': od_printf("%ul", od_control.user_downk); break; case 'U': od_printf("%d", od_control.user_time_used); break; case 'V': od_printf("%d", od_control.user_screen_length); break; case 'W': btRepeatCount = 0; while(od_control.user_name[btRepeatCount]) { if((szToRepeat[btRepeatCount] = od_control.user_name[btRepeatCount]) == ' ') { szToRepeat[btRepeatCount] = '\0'; break; } ++btRepeatCount; } od_disp_str(szToRepeat); break; case 'X': if(od_control.user_ansi) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case 'Y': if(od_control.user_attribute & 0x04) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case 'Z': if(od_control.user_attribute & 0x02) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case '0': if(od_control.user_attribute & 0x40) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case '1': if(od_control.user_attribute & 0x80) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case '2': if(od_control.user_attrib2 & 0x01) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case '3': od_disp_str(od_control.user_handle); break; case '4': od_disp_str(od_control.user_firstcall); break; case '5': od_disp_str(od_control.user_birthday); break; case '6': od_disp_str(od_control.user_subdate); break; case '7': /* days until subscrption expiry */ break; case '8': if(od_control.user_attrib2 & 0x02) { od_disp_str("ON"); } else { od_disp_str("OFF"); } break; case '9': od_printf("%lu:%lu", od_control.user_uploads, od_control.user_downloads); break; case ':': od_printf("%lu:%lu", od_control.user_upk, od_control.user_downk); break; case ';': if(od_control.user_attrib2 & 0x04) { od_disp_str("ON"); } else { od_disp_str("OFF"); } } btAvatarSeqLevel=0; break; /* QBBS/RA ^K control codes. */ case 22: bEchoThisChar = FALSE; switch(chCurrent) { case 'A': od_printf("%lu", od_control.system_calls); break; case 'B': od_disp_str(od_control.system_last_caller); break; case 'C': /* number of active messages */ break; case 'D': /* system starting message number */ break; case 'E': /* system ending message number */ break; case 'F': /* number of times user has paged sysop */ break; case 'G': /* day of the week (Monday, Tuesday, etc.) */ break; case 'H': /* number of users in user file */ break; case 'I': /* Time in 24 hour format */ break; case 'J': /* today's date */ break; case 'K': /* minutes connected this call */ break; case 'L': /* Seconds connected (0) */ break; case 'M': od_printf("%d", od_control.user_time_used); break; case 'N': od_disp_str("00"); break; case 'O': /* Minutes remaining today */ od_printf("%d", od_control.user_timelimit); break; case 'P': /* seconds remaining today (0) */ break; case 'Q': od_printf("0", od_control.user_timelimit); break; case 'R': /* current baud rate */ od_printf("0", od_control.baud); break; case 'S': /* day of the week (MON, TUE) */ break; case 'T': /* Daily download limit (in K) */ break; case 'U': /* Minutes until next system event */ break; case 'V': ODScrnDisplayString(od_control.event_starttime); break; case 'W': /* line number (from command line) */ break; case 'X': od_exit(2, TRUE); break; case 'Y': /* Name of current msg area */ break; case 'Z': /* name of current file area */ break; case '0': /* # of messages in area */ break; case '1': /* # of message area */ break; case '2': /* # of file area */ break; } btAvatarSeqLevel = 0; } } break; case 1: switch(chCurrent) { case '[': btANSISeqLevel = 2; btCurrentParamLength = 0; btNumParams = 0; break; default: btANSISeqLevel = 0; ODScrnDisplayChar(27); ODScrnDisplayChar(chCurrent); } break; default: if((chCurrent >= '0' && chCurrent <= '9') || chCurrent == '?') { if(btCurrentParamLength < 3) { szCurrentParam[btCurrentParamLength] = chCurrent; szCurrentParam[++btCurrentParamLength] = '\0'; } else { btANSISeqLevel = 0; } } else if(chCurrent == ';') { if(btNumParams < 10) { if(btCurrentParamLength != 0) { if(strcmp(szCurrentParam, "?9") == 0) { anANSIParams[btNumParams] = -2; } else { anANSIParams[btNumParams] = atoi(szCurrentParam); } szCurrentParam[0] = '\0'; btCurrentParamLength = 0; ++btNumParams; } else { anANSIParams[btNumParams++] = -1; } } else { btANSISeqLevel = 0; } } else { btANSISeqLevel = 0; if(btCurrentParamLength != 0 && btNumParams < 10) { if(strcmp(szCurrentParam,"?9") == 0) { anANSIParams[btNumParams] = -2; } else { anANSIParams[btNumParams] = atoi(szCurrentParam); } szCurrentParam[0] = '\0'; btCurrentParamLength = 0; ++btNumParams; } ODScrnGetTextInfo(&TextInfo); switch(chCurrent) { case 'A': if(btNumParams == 0) anANSIParams[0] = 1; if((nTemp = TextInfo.cury - anANSIParams[0]) < 1) { nTemp = 1; } if(nTemp > 25) nTemp=25; ODScrnSetCursorPos(TextInfo.curx, (BYTE)nTemp); break; case 'B': if(btNumParams == 0) anANSIParams[0] = 1; if((nTemp = TextInfo.cury + anANSIParams[0]) > 25) { nTemp = 25; } if(nTemp < 1) nTemp = 1; ODScrnSetCursorPos(TextInfo.curx, (BYTE)nTemp); break; case 'C': if(btNumParams == 0) anANSIParams[0] = 1; if((nTemp=TextInfo.curx + anANSIParams[0]) > 80) { nTemp = 80; } if(nTemp < 1) nTemp = 1; ODScrnSetCursorPos((BYTE)nTemp, TextInfo.cury); break; case 'D': if(btNumParams == 0) anANSIParams[0] = 1; if((nTemp = TextInfo.curx - anANSIParams[0]) < 1) { nTemp = 1; } if(nTemp > 80) nTemp = 80; ODScrnSetCursorPos((BYTE)nTemp, TextInfo.cury); break; case 'H': case 'f': if(btNumParams >= 2) { if(anANSIParams[0] == -1) { ODScrnSetCursorPos((BYTE)anANSIParams[1], 1); } else { ODScrnSetCursorPos((BYTE)anANSIParams[1], (BYTE)anANSIParams[0]); } } else if(btNumParams == 1) { if(anANSIParams[0] <= 0) { ODScrnSetCursorPos(1, TextInfo.cury); } else { ODScrnSetCursorPos(1, (BYTE)anANSIParams[0]); } } else /* if(num_params==0) */ { ODScrnSetCursorPos(1, 1); } break; case 'J': if(btNumParams >= 1 && anANSIParams[0] == 2) { /* Clear entire screen. */ ODScrnClear(); } else if(btNumParams == 0 || anANSIParams[0] == 0) { /* Not supported - Clears from cursor to end of */ /* screen. */ } else if(btNumParams>=1 && anANSIParams[0]==1) { /* Not supported - Clears from beginning of screen to */ /* cursor. */ } break; case 'K': if(btNumParams == 0 || anANSIParams[0] == 0) { /* Clear to end of line. */ ODScrnClearToEndOfLine(); } else if(btNumParams >= 1 && anANSIParams[0] == 1) { /* Not supported - should clear to beginning of line. */ } else if(btNumParams >= 1 && anANSIParams[0] == 2) { /* Not supported - should clear entire line. */ } break; case 'm': for(nTemp = 0; nTemp < btNumParams; ++nTemp) { if(anANSIParams[nTemp] == 0) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = 0x07)); } else if(anANSIParams[nTemp] == 1) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = TextInfo.attribute | 0x08)); } else if(anANSIParams[nTemp] == 2) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = TextInfo.attribute & (~0x08))); } else if(anANSIParams[nTemp] == 4) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = (TextInfo.attribute & 0xf8) | (1))); } else if(anANSIParams[nTemp] == 5) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = TextInfo.attribute | 0x80)); } else if(anANSIParams[nTemp] == 7) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = (TextInfo.attribute << 4) | (TextInfo.attribute >> 4))); } else if(anANSIParams[nTemp] == 8) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = (TextInfo.attribute & 0xf0) | ((TextInfo.attribute >> 4) & 0x07))); } else if(anANSIParams[nTemp] >= 30 && anANSIParams[nTemp] <= 37) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = (TextInfo.attribute & 0xf8) + abtANSIToPCColorTable[ (anANSIParams[nTemp] - 30)])); } else if(anANSIParams[nTemp] >= 40 && anANSIParams[nTemp]<=47) { ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = TextInfo.attribute = (TextInfo.attribute & 0x8f) + (abtANSIToPCColorTable[ anANSIParams[nTemp] - 40] << 4))); } } break; case 's': btSavedColumn = TextInfo.curx; btSavedRow = TextInfo.cury; break; case 'u': ODScrnSetCursorPos(btSavedColumn, btSavedRow); break; case '@': /* Not supported - inserts spaces at cursor. */ break; case 'P': /* Not supported - deletes characters at cursor. */ break; case 'L': /* Not supported - inserts lines at cursor. */ break; case 'M': /* Not supported - deletes lines at cursor. */ break; case 'r': /* Not supported - sets scrolling zone - 1st param is */ /* top row, 2nd param is bottom row. Cursor may go */ /* outside zone, but no scrolling occurs there. */ /* Also resets cursor to row 1, column 1. */ /* If only one param, bottom row is bottom of screen. */ break; case 'h': if(btNumParams >= 1 && anANSIParams[0] == 4) { /* Not suppored - turn insert mode on. */ } else if(btNumParams >= 1 && anANSIParams[0] == -2) { /* Home cursor. */ ODScrnSetCursorPos(1, 1); } break; case 'l': if(btNumParams >= 1 && anANSIParams[0] == 4) { /* Not suppored - turn insert mode off. */ } break; case 'E': /* Not supported - repeat CRLF specified # of times. */ break; case 'F': /* Not supported - repeat reverse CRLF specified # */ /* of times. Also not suppored ESC M - reverse */ /* linefeed, ESC D - LF, ESC E - CRLF */ break; } } } if(bEchoThisChar && od_control.baud != 0) { ODComSendByte(hSerialPort, chCurrent); } ++pszBuffer; } } /* ---------------------------------------------------------------------------- * ODEmulateFillArea() *** PRIVATE FUNCTION *** * * Fills an area of the local screen with the specified character, in the * current display color. * * Parameters: btLeft - The left column of the area to fill. * * btTop - The top row of the area to fill. * * btRight - The right column of the area to fill. * * btBottom - The bottom row of the area to fill. * * chToFillWith - Character to fill in the specified area with. * * Return: void */ static void ODEmulateFillArea(BYTE btLeft, BYTE btTop, BYTE btRight, BYTE btBottom, char chToFillWith) { BYTE btCount; BYTE btLast; static char szTemp[81]; static tODScrnTextInfo TextInfo; ODScrnGetTextInfo(&TextInfo); btLast = btRight - btLeft; for(btCount=0; btCount <= btLast; ++btCount) { szTemp[btCount] = chToFillWith; } szTemp[btCount] = 0; ODScrnEnableScrolling(FALSE); for(btCount = btTop; btCount <= btBottom; ++btCount) { ODScrnSetCursorPos(btLeft, btCount); ODScrnDisplayString(szTemp); } ODScrnSetCursorPos(TextInfo.curx, TextInfo.cury); ODScrnEnableScrolling(TRUE); }