This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
magicka/deps/odoors/ODBlock.c
2017-03-19 07:49:46 +10:00

833 lines
28 KiB
C

/* 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: ODBlock.c
*
* Description: Implements the text block manipulation functions.
*
* Revisions: Date Ver Who Change
* ---------------------------------------------------------------
* Oct 13, 1994 6.00 BP New file header format.
* Dec 09, 1994 6.00 BP Standardized coding style.
* 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 16, 1995 6.00 BP Removed oddoor.h, added odcore.h.
* Dec 12, 1995 6.00 BP Added entry, exit and kernel macros.
* Dec 30, 1995 6.00 BP Added ODCALL for calling convention.
* Feb 19, 1996 6.00 BP Changed version number to 6.00.
* Mar 03, 1996 6.10 BP Begin version 6.10.
* Aug 10, 2003 6.23 SH *nix support
*/
#define BUILDING_OPENDOORS
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "OpenDoor.h"
#include "ODCore.h"
#include "ODGen.h"
#include "ODScrn.h"
#include "ODKrnl.h"
/* Set to TRUE when od_puttext() should leave the cursor in its original */
/* position */
static BOOL bScrollAction = TRUE;
/* ----------------------------------------------------------------------------
* od_puttext()
*
* Displays the contents of the buffer passed in block. Leaves cursor in
* original position, unless bScrollAction is FALSE. Leaves colour at
* original value.
*
* Parameters: nLeft - Column number of left edge of block of text to
* transfer, where 1 is the leftmost column of the
* screen.
*
* nTop - Row number of the top edge of block of text to
* to transfer, where 1 is the top row of the screen.
*
* nRight - Column number of the right edge of block.
*
* nBottom - Row number of bottom edge of block.
*
* pBlock - Pointer to buffer that has been filled in the format
* used by od_gettext().
*
* Return: TRUE on success, FALSE on failure.
*/
ODAPIDEF BOOL ODCALL od_puttext(INT nLeft, INT nTop, INT nRight, INT nBottom,
void *pBlock)
{
INT nRowLength = nRight - nLeft +1;
INT nRowBytes = nRowLength * 2;
char *pchTest;
char *pchMemory;
char *pBuffer=NULL;
char *pchScreenBlock;
INT nBlockRow = 0;
INT nOutRow;
INT nOutColour = 999;
INT nOutColumn, nCheckColumn;
char *pchMemBlock;
INT nMoveCost = od_control.user_avatar ? 4 : 7;
BYTE btMaxRight, btMaxBottom;
/* Log function entry if running in trace mode. */
TRACE(TRACE_API, "od_puttext()");
/* Ensure that OpenDoors is initialized before proceeding. */
if(!bODInitialized) od_init();
OD_API_ENTRY();
/* Get current display setting profile. */
ODScrnGetTextInfo(&ODTextInfo);
/* Calculate the maximum values for bottom and right of block. */
btMaxRight=ODTextInfo.winright-ODTextInfo.winleft+1;
btMaxBottom=ODTextInfo.winbottom-ODTextInfo.wintop+1;
/* Check that parameters seem reasonable. */
if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom
|| nTop > nBottom || nLeft > nRight || pBlock==NULL)
{
od_control.od_error = ERR_PARAMETER;
OD_API_EXIT();
return(FALSE);
}
/* Ensure that ANSI and/or AVATAR mode is available. */
if(!od_control.user_ansi && !od_control.user_avatar)
{
od_control.od_error = ERR_NOGRAPHICS;
OD_API_EXIT();
return(FALSE);
}
/* If OpenDoors is operating in remote mode. */
if(od_control.baud != 0)
{
/* Allocate temporary buffer to store original screen contents while */
/* buffer paste is being performed. */
if((pBuffer=malloc(nRowBytes*(nBottom-nTop+1)))==NULL)
{
od_control.od_error = ERR_MEMORY;
OD_API_EXIT();
return(FALSE);
}
/* Get current screen contents of area to be pasted into, storing */
/* into the temporary buffer. */
if(!ODScrnGetText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
pBuffer))
{
od_control.od_error = ERR_PARAMETER;
free(pBuffer);
OD_API_EXIT();
return(FALSE);
}
}
/* Display the block to be pasted on the local screen. */
if(!ODScrnPutText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
pBlock))
{
od_control.od_error = ERR_PARAMETER;
if(pBuffer)
free(pBuffer);
OD_API_EXIT();
return(FALSE);
}
/* If operating in remote mode. */
if(od_control.baud != 0)
{
/* Loop for each line in the buffer to be pasted */
for(nOutRow=nTop;nOutRow<=nBottom;++nOutRow,++nBlockRow)
{
/* Setup pointer to beginning of line of original screen contents. */
pchScreenBlock=(char *)pBuffer+(nRowBytes*nBlockRow);
/* Setup pointer to beginning of line of block to be displayed. */
pchMemBlock=(char *)pBlock+(nRowBytes*nBlockRow);
/* Loop for each column on this line. */
for(nOutColumn=0;nOutColumn<nRowLength;)
{
/* Loop from this character onwards, counting number of */
/* characters that don't need to be changed. */
nCheckColumn=nOutColumn;
pchMemory=((char *)pchMemBlock)+(nCheckColumn<<1);
pchTest=((char *)pchScreenBlock)+(nCheckColumn<<1);
for(;nCheckColumn<nRowLength;++nCheckColumn)
{
if(od_control.od_full_put) break;
/* If both buffers have space characters. */
if((*pchMemory==' ' || *pchMemory==0) && (*pchTest==' ' || *pchTest=='\0'))
{
/* If background colours differ, then stop comparison loop. */
if((pchTest[1]&0x70) != (pchMemory[1]&0x70))
{
break;
}
}
/* If both have different character and colour attributes. */
else if(*((WORD *)pchTest) != *((WORD *)pchMemory))
{
/* Then stop comparison loop now. */
break;
}
/* Increment source and background pointers by two bytes. */
pchTest+=2;
pchMemory+=2;
}
/* If no futher text to change on this line. */
if(nCheckColumn==nRowLength)
{
/* Move to the next line. */
goto next_line;
}
/* If this is the first text to be displayed on this line. */
if(nOutColumn == 0)
{
/* Move the cursor to the first text to be changed on line. */
nOutColumn = nCheckColumn;
/* If AVATAR mode is available. */
if(od_control.user_avatar)
{
/* Send the avatar cursor positioning command. */
szODWorkString[0]=22;
szODWorkString[1]=8;
szODWorkString[2]=nOutRow;
szODWorkString[3]=nLeft+nOutColumn;
od_disp(szODWorkString,4,FALSE);
}
else
{
/* Otherwise, send the ANSI cursor positioning command. */
sprintf(szODWorkString,"x[%d;%dH",nOutRow,nLeft + nOutColumn);
szODWorkString[0]=27;
od_disp(szODWorkString, strlen(szODWorkString), FALSE);
}
}
/* If the number of characters after current cursor position */
/* which must be changed, is greater than the number of */
/* characters required to reposition the cursor on this line, */
/* then move the cursor to skip unchanged characters. */
else if((nCheckColumn-nOutColumn)>nMoveCost)
{
nOutColumn=nCheckColumn;
/* If AVATAR mode is available. */
if(od_control.user_avatar)
{
/* Advance cursor appropriate number of characters */
/* using the AVATAR cursor position command. */
szODWorkString[0]=22;
szODWorkString[1]=8;
szODWorkString[2]=nOutRow;
szODWorkString[3]=nLeft+nOutColumn;
od_disp(szODWorkString,4,FALSE);
}
else
{
/* Otherwise, advance cursor appropriate number of */
/* characters using the ANSI cursor position command. */
sprintf(szODWorkString,"x[%d;%dH",nOutRow,nLeft + nOutColumn);
szODWorkString[0]=27;
od_disp(szODWorkString,strlen(szODWorkString),FALSE);
}
}
/* Output text for the number of characters found to be */
/* different. */
pchTest=(char *)&pchMemBlock[nOutColumn*2];
for(;nOutColumn<=nCheckColumn;++nOutColumn)
{
if(pchTest[1] != nOutColour)
{
od_set_attrib(nOutColour=pchTest[1]);
}
od_disp(pchTest,1,FALSE);
pchTest++;
pchTest++;
}
}
next_line:
;
}
/* If not disabled, update cursor position. */
if(bScrollAction)
{
od_set_cursor(ODTextInfo.cury,ODTextInfo.curx);
}
/* Deallocate temporary buffer. */
free(pBuffer);
}
/* Restore the original display attribute. */
od_set_attrib(ODTextInfo.attribute);
/* Return with success. */
OD_API_EXIT();
return(TRUE);
}
/* ----------------------------------------------------------------------------
* od_gettext()
*
* Retrieves text from the screen (based on what is current displayed on the
* local display), storing it in the buffer provided by the caller.
*
* Parameters: nLeft - Column number of left edge of block of text to
* transfer, where 1 is the leftmost column of the
* screen.
*
* nTop - Row number of the top edge of block of text to
* to transfer, where 1 is the top row of the screen.
*
* nRight - Column number of the right edge of block.
*
* nBottom - Row number of bottom edge of block.
*
* pBlock - Pointer to buffer large enough to hold two bytes
* for each character in the block.
*
* Return: TRUE on success, FALSE on failure.
*/
ODAPIDEF BOOL ODCALL od_gettext(INT nLeft, INT nTop, INT nRight, INT nBottom,
void *pBlock)
{
BYTE btMaxRight, btMaxBottom;
/* Log function entry if running in trace mode. */
TRACE(TRACE_API, "od_gettext()");
/* Initialize OpenDoors if not already done. */
if(!bODInitialized) od_init();
OD_API_ENTRY();
ODScrnGetTextInfo(&ODTextInfo);
btMaxRight=ODTextInfo.winright-ODTextInfo.winleft+1;
btMaxBottom=ODTextInfo.winbottom-ODTextInfo.wintop+1;
if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom || !pBlock)
{
od_control.od_error = ERR_PARAMETER;
OD_API_EXIT();
return(FALSE);
}
if(!od_control.user_ansi && !od_control.user_avatar)
{
od_control.od_error = ERR_NOGRAPHICS;
OD_API_EXIT();
return(FALSE);
}
OD_API_EXIT();
return(ODScrnGetText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight, (BYTE)nBottom,
pBlock));
}
/* ----------------------------------------------------------------------------
* od_scroll()
*
* Scrolls the specified area of the screen by the specified number of
* lines, in either the up or down directions. The cursor is left at its
* original locaiton, and the display attribute is left at its original
* setting. New lines are created in the current display colour.
*
* Parameters: nLeft - Column number of left edge of area to scroll, where
* 1 is the leftmost column of the screen.
*
* nTop - Row number of the top edge of the area to scroll,
* where 1 is the top row of the screen.
*
* nRight - Column number of the right edge of area to scroll.
*
* nBottom - Row number of bottom edge of area to scroll.
*
* nDistance - Number of lines to scroll the text. A value of 0
* has no effect on the specified area, a positive
* value moves the text upwards (leaving blank lines
* at the bottom of the specified area), while a
* negative value moves the text upwards. If the
* distance is equal to the number of lines in the
* area, then the entire area is cleared.
*
* nFlags - Bitwise-or (|) of SCROLL_* flags. SCROLL_NORMAL
* is the default. SCROLL_NO_CLEAR does not clear
* the new area at the top/bottom of the scroll
* region if doing so would be slower.
*
* Return: TRUE on success, FALSE on failure.
*/
ODAPIDEF BOOL ODCALL od_scroll(INT nLeft, INT nTop, INT nRight, INT nBottom,
INT nDistance, WORD nFlags)
{
BYTE btWidth, btHeight;
BYTE btCount;
BYTE btFirst, btLast;
char szAVTSeq[7];
void *pBlock;
char szBlank[81];
BYTE btKeepHeight;
BYTE btMaxRight;
BYTE btMaxBottom;
tODScrnTextInfo TextState;
/* Log function entry if running in trace mode. */
TRACE(TRACE_API, "od_scroll()");
/* Ensure that OpenDoors has been initialized before proceeding. */
if(!bODInitialized) od_init();
OD_API_ENTRY();
/* Get current display setting information. */
ODScrnGetTextInfo(&TextState);
/* Determine the height and width of the area to be scrolled. */
btWidth=nRight-nLeft+1;
btHeight=nBottom-nTop+1;
/* Determine the number of lines currently in the area that will still */
/* be visible after scrolling. */
btKeepHeight=btHeight-((nDistance>=0) ? nDistance : -nDistance);
/* Determine the maximum bottom and left coordinates of an area to be */
/* scrolled. */
btMaxRight=TextState.winright-TextState.winleft+1;
btMaxBottom=TextState.winbottom-TextState.wintop+1;
/* Check that parameters are valid. */
if(nLeft<1 || nTop<1 || nRight>btMaxRight || nBottom>btMaxBottom ||
nLeft > nRight || nTop > nBottom)
{
od_control.od_error = ERR_PARAMETER;
OD_API_EXIT();
return(FALSE);
}
/* Check that ANSI or AVATAR graphics mode is available. */
if(!od_control.user_ansi && !od_control.user_avatar)
{
od_control.od_error = ERR_NOGRAPHICS;
OD_API_EXIT();
return(FALSE);
}
/* If distance to be scrolled is 0, then we are finished already and */
/* can return immediately. */
if(nDistance == 0)
{
OD_API_EXIT();
return(TRUE);
}
/* If distance is positive, then we are moving text upwards. */
if(nDistance>0)
{
/* Ensure that distance is not greater than size of scrolled area. */
if(nDistance>btHeight)
{
nDistance=btHeight;
}
/* Calculate first and last line to be moved. */
btFirst=nBottom-(nDistance-1);
btLast=nBottom;
}
/* If distance is negative, then we are moving text downwards. */
else /* if(nDistance<0) */
{
/* Ensure that distance is not greater than size of scrolled area. */
if(nDistance<-btHeight)
{
nDistance=-btHeight;
}
/* Calculate first and last line to be moved. */
btFirst=nTop;
btLast=nTop-nDistance-1;
}
/* If AVATAR mode is available */
if(od_control.user_avatar)
{
/* Generate AVATAR sequence which causes the remote terminal to */
/* scroll an area of its screen. */
szAVTSeq[0]=22;
/* If scrolling text upwards. */
if(nDistance>0)
{
/* Specify control sequence for scrolling upwards. */
szAVTSeq[1]=10;
szAVTSeq[2]=nDistance;
/* Move text appropriate direction on local screen. */
ODScrnCopyText((BYTE)nLeft, (BYTE)(nTop + nDistance), (BYTE)nRight,
(BYTE)nBottom, (BYTE)nLeft, (BYTE)nTop);
}
/* If scrolling text downwards. */
else /* if(disatnce<0) */
{
/* Specify control sequence for scrolling downwards. */
szAVTSeq[1]=11;
szAVTSeq[2]=-nDistance;
/* Move text appropriate direction on local screen. */
ODScrnCopyText((BYTE)nLeft, (BYTE)nTop, (BYTE)nRight,
(BYTE)(nBottom + nDistance), (BYTE)nLeft, (BYTE)(nTop - nDistance));
}
/* Specify area to be scrolled to the AVATAR terminal. */
szAVTSeq[3]=nTop;
szAVTSeq[4]=nLeft;
szAVTSeq[5]=nBottom;
szAVTSeq[6]=nRight;
/* Send the control sequence to the AVATAR terminal. */
od_disp(szAVTSeq,7,FALSE);
/* Generate string containing a blank line of text. */
for(btCount=0;btCount<btWidth;++btCount) szBlank[btCount]=' ';
szBlank[btCount]='\0';
/* Blank-out lines that will no longer be visiable. */
for(;btFirst<=btLast;++btFirst)
{
ODScrnSetCursorPos((BYTE)nLeft, btFirst);
ODScrnDisplayString(szBlank);
}
/* Reset cursor position on local display. */
ODScrnSetCursorPos(TextState.curx,TextState.cury);
}
/* Otherwise, we are using ANSI mode. */
else /* if(od_control.user_ansi) */
{
/* If any of the original text will still be available after */
/* scrolling. */
if(btKeepHeight>0)
{
/* Allocate some temporary memory to hold text to be "got". */
if((pBlock=malloc(btKeepHeight*btWidth*2))==NULL)
{
/* If memory allocation failed, then scrolling fails. */
od_control.od_error = ERR_MEMORY;
OD_API_EXIT();
return(FALSE);
}
/* If we are scrolling text upwards. */
if(nDistance > 0)
{
/* Move text that will still be visible, using od_gettext() */
/* and od_puttext(). */
od_gettext(nLeft,nTop+nDistance,nRight,nBottom,pBlock);
bScrollAction=FALSE;
od_puttext(nLeft,nTop,nRight,nBottom-nDistance,pBlock);
bScrollAction=TRUE;
}
/* If we are scrolling text downwards. */
else /* if(nDistance < 0) */
{
/* Move text that will still be visible, using od_gettext() */
/* and od_puttext(). */
od_gettext(nLeft,nTop,nRight,nBottom+nDistance,pBlock);
bScrollAction=FALSE;
od_puttext(nLeft,nTop-nDistance,nRight,nBottom,pBlock);
bScrollAction=TRUE;
}
/* Deallocate temporary memory block. */
free(pBlock);
}
/* If new area clearing has not been disabled. */
if(!(nFlags&SCROLL_NO_CLEAR))
{
/* Loop for lines that should be blank. */
for(;btFirst<=btLast;++btFirst)
{
/* Move cursor to the beginning of this line. */
od_set_cursor(btFirst,nLeft);
/* If right boarder of area to be scrolled is the edge of the */
/* screen, then we can use a quick control sequence to clear */
/* the rest of the line. Call od_clr_line() to do this. */
if(nRight == 80)
{
od_clr_line();
}
/* If right boarder of area to be scrolled is not at the edge */
/* of the screen, then each line must be manually erased, by */
/* sending the appropriate number of blanks (spaces). */
else /* if(right != 80) */
{
od_repeat(' ',btWidth);
}
}
}
/* Reset the cursor to its original position. */
od_set_cursor(TextState.cury,TextState.curx);
}
/* Return with success */
OD_API_EXIT();
return(TRUE);
}
/* ----------------------------------------------------------------------------
* od_save_screen()
*
* Stores the contents of the entire screen into a buffer, along with
* the current cursor location and display colour. Supports all display
* modes.
*
* Parameters: pBuffer - Pointer to a buffer of at least 4004 bytes in size,
* where the information on the current screen state
* will be stored.
*
* Return: TRUE on success, FALSE on failure.
*/
ODAPIDEF BOOL ODCALL od_save_screen(void *pBuffer)
{
char btHeight;
tODScrnTextInfo TextState;
/* Log function entry if running in trace mode. */
TRACE(TRACE_API, "od_save_screen()");
/* Ensure that OpenDoors is initialized before proceeding. */
if(!bODInitialized) od_init();
OD_API_ENTRY();
/* Check parameters and current output window size. */
ODScrnGetTextInfo(&TextState);
if(TextState.winleft!=1 || TextState.winright!=80 || !pBuffer)
{
od_control.od_error = ERR_PARAMETER;
OD_API_EXIT();
return(FALSE);
}
/* Store current cursor location in buffer. */
((char *)pBuffer)[0]=TextState.curx;
((char *)pBuffer)[1]=TextState.cury;
/* Store current display colour in buffer. */
((char *)pBuffer)[2]=TextState.attribute;
/* Store height of buffer stored. */
((char *)pBuffer)[3]=btHeight=TextState.winbottom-TextState.wintop+1;
/* Store screen contents using local screen gettext() function. */
OD_API_EXIT();
return(ODScrnGetText(1,1,80,btHeight,(char *)pBuffer+4));
}
/* ----------------------------------------------------------------------------
* od_restore_screen()
*
* Restores the screen contents, along with the current text colour and cursor
* location, as previously stored by od_save_screen().
*
* Parameters: pBuffer - Pointer to buffer which was filled by a previous call
* to od_save_screen().
*
* Return: TRUE on success, FALSE on failure.
*/
ODAPIDEF BOOL ODCALL od_restore_screen(void *pBuffer)
{
char *pch;
char btPos;
char chLast;
char *pchTextBuffer;
char btHeight;
int nToReturn=TRUE;
tODScrnTextInfo TextState;
char btLine;
char btDistance=0;
char btCursorRow;
/* Log function entry if running in trace mode. */
TRACE(TRACE_API, "od_restore_screen()");
/* Ensure that OpenDoors is initialized before proceeding. */
if(!bODInitialized) od_init();
OD_API_ENTRY();
/* Check parameters and current output window size. */
ODScrnGetTextInfo(&TextState);
if(TextState.winleft!=1 || TextState.winright!=80 || !pBuffer)
{
od_control.od_error = ERR_PARAMETER;
OD_API_EXIT();
return(FALSE);
}
/* Determine current window height were text will be restored. */
btHeight=TextState.winbottom-TextState.wintop+1;
/* If current display window height is too small for entire buffer */
/* to be re-displayed. */
if(btHeight<((char *)pBuffer)[3])
{
/* Then we will always display as much of the END of the buffer */
/* as possible. */
btDistance = btHeight - ((char *)pBuffer)[3];
}
else if(btHeight > ((char *)pBuffer)[3])
{
/* Otherwise, ensure that we don't try to display more lines that */
/* are in the buffer. */
btHeight=((char *)pBuffer)[3];
}
/* Clear the remote and local screens. */
od_clr_scr();
/* If ANSI or AVATAR modes are available. */
if(od_control.user_avatar || od_control.user_ansi)
{
/* Then we can efficiently display the buffer using od_puttext(). */
bScrollAction=FALSE;
nToReturn=od_puttext(1,1,80,btHeight,(char *)pBuffer+(4+((int)btDistance*160)));
bScrollAction=TRUE;
/* Restore original cursor location. */
od_set_cursor(((char *)pBuffer)[1],((char *)pBuffer)[0]);
/* Restore original display attribute. */
od_set_attrib(((char *)pBuffer)[2]);
}
/* If we are operating in ASCII mode. */
else /* if (!od_control.od_avatar && !od_control.caller_ansi) */
{
/* Then the buffer is displayed one line at a time, beginning */
/* at the top of the screen, up to the saved cusrsor location. */
/* Set pointer to beginning of buffer to be displayed. */
pchTextBuffer=(char *)pBuffer+4;
/* Get final cursor row number. */
btCursorRow=((char *)pBuffer)[1];
/* Loop for each line in the buffer. */
for(btLine=1;btLine<=btHeight;++btLine)
{
/* Set pointer to last character of line. */
pch=(char *)pchTextBuffer+158;
/* Loop backwards until a non-blank character is found, or we */
/* reach the beginning of the line. */
for(chLast=80;chLast>1;)
{
/* If this is a blank character. */
if(*pch==32 || *pch==0)
{
/* Move to previous character. */
--chLast;
pch-=2;
}
/* If this is not a blank character, then stop looping. */
else
{
break;
}
}
/* If this is the line on which the cursor resides. */
if(btLine==btCursorRow)
{
/* If last non-blank character of line is at or past the final */
/* cursor location, then we backup the last character to be */
/* displayed to the cursor before the final cursor position. */
/* This code could be improved to be able to display text on */
/* the entire cursor line by displaying the entire line, */
/* sending a C/R, and redisplaying first portion of line to */
/* end up with the cursor in the desired position. */
if(chLast>=((char *)pBuffer)[0])
{
chLast=((char *)pBuffer)[0]-1;
}
}
/* Display all characters on this line */
pch = (char *)pchTextBuffer;
for(btPos=1;btPos<=chLast;++btPos)
{
od_putch(*pch);
pch+=2;
}
/* If this is the row where the cursor should be left, then we */
/* stop displaying now. */
if(btLine==btCursorRow)
{
break;
}
/* If cursor hasn't been wrapped, then we should send a C/R - */
/* L/F sequence. */
if(chLast != 80)
{
od_disp_str("\n\r");
pchTextBuffer+=160;
}
}
}
/* Return with the appropriate success/failure status. */
OD_API_EXIT();
return(nToReturn);
}