| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620 | /* OpenDoors Online Software Programming Toolkit * (C) Copyright 1991 - 1999 by Brian Pirie. * * Oct-2001 door32.sys/socket modifications by Rob Swindell (www.synchro.net) * * 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: ODCore.c * * Description: Implements the core of OpenDoors, including chat mode *              and standard input/output functions that are *              used throughout OpenDoors. * *   Revisions: Date          Ver   Who  Change *              --------------------------------------------------------------- *              Oct 13, 1994  6.00  BP   New file header format. *              Oct 19, 1994  6.00  BP   Changed paging hours logic. *              Oct 21, 1994  6.00  BP   Further isolated com routines. *              Oct 22, 1994  6.00  BP   Name case conversion /w punct. *              Dec 08, 1994  6.00  BP   Allow custom chat mode deactivation. *              Dec 09, 1994  6.00  BP   Remove global dir entry structure. *              Dec 13, 1994  6.00  BP   Remove include of dir.h. *              Dec 31, 1994  6.00  BP   Remove #ifndef USEINLINE DOS code. *              Dec 31, 1994  6.00  BP   Remove old multitasker definitions. *              Jan 01, 1995  6.00  BP   Don't use ODComInbound(). *              Jan 01, 1995  6.00  BP   _waitdrain() -> ODWaitDrain(). *              Jan 01, 1995  6.00  BP   Use new millisecond timer functions. *              Jan 01, 1995  6.00  BP   Remove od_init() from _remotechar() *              Jan 01, 1995  6.00  BP   Split off odkrnl.c from odcore.c *              Aug 19, 1995  6.00  BP   32-bit portability. *              Nov 11, 1995  6.00  BP   Moved first_word() to odlist.c *              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   Create odcore.h. *              Nov 17, 1995  6.00  BP   Use new input queue mechanism. *              Dec 12, 1995  6.00  BP   Added od_set_color(). *              Dec 12, 1995  6.00  BP   Added entry, exit and kernel macros. *              Dec 13, 1995  6.00  BP   Moved chat mode code to ODKrnl.h. *              Dec 19, 1995  6.00  BP   Request reason for chat outside hours. *              Dec 23, 1995  6.00  BP   Allow space to continue at page pause. *              Dec 24, 1995  6.00  BP   Added abtGreyBlock. *              Dec 30, 1995  6.00  BP   Added ODCALL for calling convention. *              Jan 03, 1996  6.00  BP   Use OD_API_VAR_DEFN for od_control. *              Jan 04, 1996  6.00  BP   tODInQueueEvent -> tODInputEvent. *              Jan 23, 1996  6.00  BP   No od_set_statusline() under Win32. *              Jan 30, 1996  6.00  BP   Replaced od_yield() with od_sleep(). *              Jan 30, 1996  6.00  BP   Add ODInQueueGetNextEvent() timeout. *              Jan 09, 1996  6.00  BP   ODComOutbound() returns actual size. *              Jan 09, 1996  6.00  BP   Reduce kernel calls from od_disp...(). *              Feb 19, 1996  6.00  BP   Changed version number to 6.00. *              Mar 03, 1996  6.10  BP   Begin version 6.10. *              Mar 19, 1996  6.10  BP   MSVC15 source-level compatibility. *              Mar 21, 1996  6.10  BP   Added od_control_get(). *              Sep 01, 1996  6.10  BP   Update output area on od_set_per...(). *              Oct 19, 2001  6.20  RS   od_get_key now ignores linefeeds. *              Mar 14, 2002  6.22  RS   Fixed od_get_key(bWait=FALSE) *              Aug 10, 2003  6.23  SH   *nix support */#define BUILDING_OPENDOORS#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <time.h>#include <errno.h>#include "OpenDoor.h"#include "ODStr.h"#include "ODGen.h"#include "ODPlat.h"#include "ODCom.h"#include "ODKrnl.h"#include "ODScrn.h"#include "ODCore.h"#include "ODInQue.h"#ifdef ODPLAT_WIN32#include "ODFrame.h"#endif /* ODPLAT_WIN32 *//* GLOBAL VARIABLES SHARED THROUGHOUT OPENDOORS. *//* Global declaration of the OpenDoors control structure. */OD_API_VAR_DEFN tODControl #ifndef _WIN32	/* warning C4229: anachronism used : modifiers on data are ignored */OD_GLOBAL_CONV #endifod_control;/* OpenDoors global initialized flag. */BOOL bODInitialized = FALSE;/* Global serial port object handle. */tPortHandle hSerialPort;/* Global input queue object handle. */tODInQueueHandle hODInputQueue;/* Reentrancy control. */BOOL bIsCallbackActive = FALSE;BOOL bShellChatActive = FALSE;/* Global working space. */char szODWorkString[OD_GLOBAL_WORK_STRING_SIZE];/* Global instance of the text information structure for general use. */tODScrnTextInfo ODTextInfo;/* Logfile function hooks. */BOOL (*pfLogWrite)(INT) = NULL;void (*pfLogClose)(INT) = NULL;/* od_color_config() support for od_printf(). */char chColorCheck = 0;char *pchColorEndPos;/* Status line information. */BYTE btCurrentStatusLine = STATUS_NONE;OD_PERSONALITY_CALLBACK *pfCurrentPersonality = NULL;char szDesiredPersonality[33] = "";SET_PERSONALITY_FUNC *pfSetPersonality = NULL;/* Commonly used character sequences. */char abtBlackBlock[2] = {' ', 0x07};char abtGreyBlock[2] = {' ', 0x70};char szBackspaceWithDelete[4] = {8, ' ', 8, 0};/* Current output area on screen. */BYTE btOutputTop = 1;BYTE btOutputBottom = 23;/* PRIVATE VARIABLES. *//* Display color varaibles. */char bAnyColorChangeYet;/* Static character sequences. */static char szClearScreen[2] = {12, 0};/* Lookup table to map colors from PC values to ANSI color values. */static BYTE abtPCToANSIColorTable[8] = {30, 34, 32, 36, 31, 35, 33, 37};/* LOCAL HELPER FUNCTIONS. */static void ODAddANSIParameter(char *szControlSequence, int nParameterValue);/* ---------------------------------------------------------------------------- * ODWaitDrain() * * Waits for up to the specified number of milliseconds for the output serial * buffer to drain. * * Parameters: MaxWait - Specifies the maximum number of milliseconds to wait *                       before timing out. * *     Return: void */void ODWaitDrain(tODMilliSec MaxWait){   int nOutboundSize;   tODTimer Timer;   /* If we are operating in local mode, then don't do anything. */   if(od_control.baud == 0) return;   /* Otherwise, start a timer that is set to elapse after the maximum */   /* wait period.                                                     */   ODTimerStart(&Timer, MaxWait);   /* Loop until either the outbound buffer is empty, or the */   /* timer has elapsed.                                     */   for(;;)   {      /* Check whether any data is in the outbound serial queue. */      ODComOutbound(hSerialPort, &nOutboundSize);      /* If the queue is empty or the timer has elapsed, then stop */      /* waiting.                                                  */      if(nOutboundSize == 0 || ODTimerElapsed(&Timer)) break;      /* Otherwise, give other tasks a chance to run. */      od_sleep(0);      /* Give od_kernel() activities a chance to run. */      CALL_KERNEL_IF_NEEDED();   } }/* ---------------------------------------------------------------------------- * od_clr_scr() * * Clears the contents of the local and remote screens, if screen clearing is * enabled. * * Parameters: none * *     Return: void */ODAPIDEF void ODCALL od_clr_scr(void){   INT16 nOriginalAttrib;   /* Log function entry if running in trace mode */   TRACE(TRACE_API, "od_clr_scr()");   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Don't clear screen if disabled. */   if(!od_control.od_always_clear && !(od_control.user_attribute & 2)      && (od_control.od_extended_info || od_control.od_info_type == CUSTOM))   {      OD_API_EXIT();      return;   }   if(od_control.user_rip)   {      od_disp("!|*", 3, FALSE);      if(!od_control.od_default_rip_win)      {         od_disp("!|w0000270M12", 13, FALSE);      }   }      if(od_control.user_ansi)   {      od_disp("\x1b[2J\x1b[1;1H", 10, FALSE);   }   else {	   /* Send ascii 12 to modem, no local echo. */	   od_disp(szClearScreen, 1, FALSE);   }   /* Clear local window. */   ODScrnClear();   /* Get color set prior to screen clear. */   nOriginalAttrib = od_control.od_cur_attrib;   /* Current color state is unknown. */   od_control.od_cur_attrib = -1;   /* Set color to original value. This gurantees that local and */   /* remote systems both have the same current color set.       */   od_set_attrib(nOriginalAttrib);   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_input_str() * * Allows the user to input a string up to the specified length, using * characters in the specified range. This string input function is designed * to be compatible with all terminal types. * * Parameters: pszInput   - Pointer to string to store input in. * *             nMaxLength - Maximum number of characters to permit the user *                          to input. * *             chMin      - The minimum character value to permit. This must *                          be at least ASCII 32. * *             chMax      - The maximum character value to permit. * *     Return: void */ODAPIDEF void ODCALL od_input_str(char *pszInput,    INT nMaxLength,   unsigned char chMin,   unsigned char chMax){   char chKeyPressed;   INT nPosition;   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_input_str()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Start at the beginning of the string. */   nPosition = 0;   /* Check that input parameters are valid. */   if(pszInput == NULL || nMaxLength < 1 || chMin > chMax)   {      od_control.od_error = ERR_PARAMETER;      OD_API_EXIT();      return;   }   for(;;)   {      chKeyPressed = od_get_key(TRUE);      /* If user pressed enter. */      if(chKeyPressed == '\r' || chKeyPressed == '\n')      {         /* Terminate the string. */         pszInput[nPosition] = '\0';         /* Display CR-LF sequence. */         od_disp_str("\n\r");         /* Exit the function. */         OD_API_EXIT();         return;      }      /* If the user pressed backspace. */      else if(chKeyPressed == 8)      {         /* If we are not currently at the beginning of the string. */         if(nPosition > 0)         {            /* Send backspace sequence. */            od_disp_str(szBackspaceWithDelete);            /* Move current position back by one position in the string. */            --nPosition;         }      }      /* If this is a valid character to place in the string and we have */      /* not reached the maximum size of the string yet.                 */      else if(chKeyPressed >= chMin && chKeyPressed <= chMax         && nPosition < nMaxLength)      {         /* Display key that was pressed. */         od_putch(chKeyPressed);         /* Add the entered character to the string and increment our */         /* current position in the string.                           */         pszInput[nPosition++] = chKeyPressed;      }   }}/* ---------------------------------------------------------------------------- * od_clear_keybuffer() * * Clears any keystrokes from the inbound buffers. Both input from local and * remote systems is discarded, by clearing both OpenDoors' common input * event queue, and the serial port inbound buffer. This function is called * to cause any input by the user prior to the time the function was called * to be ignored. * * Parameters: none * *     Return: void */ODAPIDEF void ODCALL od_clear_keybuffer(void){   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_clear_keybuffer()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Empty any events in the common input event queue. */   ODInQueueEmpty(hODInputQueue);   /* If we are not operating in local mode ... */   if(od_control.baud != 0)   {      /* ... then remove any items in the serial port inbound buffer. */      ODComClearInbound(hSerialPort);   }   /* Call the OpenDoors kernel function. */   CALL_KERNEL_IF_NEEDED();   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_key_pending() * * Returns TRUE if there's a key pending, FALSE otherwise. * * Parameters: none * *     Return: TRUE if character is waiting, FALSE if no character is waiting. */ODAPIDEF BOOL ODCALL od_key_pending(void){   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_get_key()");   OD_API_ENTRY();   /* Call the OpenDoors kernel. */   CALL_KERNEL_IF_NEEDED();   if(!ODInQueueWaiting(hODInputQueue))   {         OD_API_EXIT();         return(FALSE);   }   OD_API_EXIT();   return(TRUE);}/* ---------------------------------------------------------------------------- * od_get_key() * * Inputs a single character, optionally waiting for the next character if no * character has been received yet. This function returns data received from * either the local or remote system, in the order in which it was received. * * Parameters: bWait - FALSE if od_get_key() should return right away with *                     a value of 0 if no characters have been received, or *                     TRUE if od_get_key() should wait for the next received *                     character. * *     Return: Character that was received, or 0 if no character is waiting. */ODAPIDEF char ODCALL od_get_key(BOOL bWait){   tODInputEvent InputEvent;   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_get_key()");   OD_API_ENTRY();   /* Call the OpenDoors kernel. */   CALL_KERNEL_IF_NEEDED();	do {	   /* If we aren't supposed to wait for input, then check whether any   */	   /* input is waiting in the input queue, and if not return right away */	   /* without any data.                                                 */	   if(!bWait)	   {		  if(!ODInQueueWaiting(hODInputQueue))		  {			 OD_API_EXIT();			 return(0);		  }	   }		/* Obtain the next character from the input queue. If we get to this */		/* point and there is no data waiting in the input queue, then the   */		/* ODInQueueGetNextEvent() function will block until a character     */		/* is available in the input queue.                                  */		ODInQueueGetNextEvent(hODInputQueue, &InputEvent, OD_NO_TIMEOUT);		/* Only keyboard input events are currently supported by od_get_key(). */		ASSERT(InputEvent.EventType == EVENT_CHARACTER);		/* Update OpenDoors control structure member that records whether the */		/* last input came from the local or remote user.                     */		od_control.od_last_input = InputEvent.bFromRemote ? 0 : 1;	} while(InputEvent.chKeyPress == '\n');	/* Ignore line-feed char */   /* Return the character that was pressed by the user. */   OD_API_EXIT();   return(InputEvent.chKeyPress);}/* ---------------------------------------------------------------------------- * od_carrier() * * Allows programs to determine the current state of the carrier detect * signal when OpenDoors' automatic carrier detection has been disabled. * * Parameters: none * *     Return: TRUE if the carrier detct signal is present, FALSE if it *             isn't. When operating in local mode, this function always *             returns FALSE. */ODAPIDEF BOOL ODCALL od_carrier(void){   BOOL bIsCarrier;   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Log function entry if running in trace mode */   TRACE(TRACE_API, "od_carrier()");   /* If we are operating in local mode, then return FALSE. */   if(od_control.baud == 0)   {      od_control.od_error = ERR_NOREMOTE;      OD_API_EXIT();      return(FALSE);   }   /* In remote mode, obtain the current state of the carrier detect signal. */   ODComCarrier(hSerialPort, &bIsCarrier);   /* Return the current state of the carrier detect signal. */   OD_API_EXIT();   return(bIsCarrier);}/* ---------------------------------------------------------------------------- * od_repeat() * * This function displays the same character the specified number of times on * the local and remote screens, using any available optimal control sequences * under the current display mode. * * Parameters: chValue - Character to repeat. * *             btTimes - Number of times to repeat the character. * *     Return: void */ODAPIDEF void ODCALL od_repeat(char chValue, BYTE btTimes){   char *pchCurStringPos;   BYTE btLeft;   char szBuffer[3];   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_repeat()");   /* Ensure that OpenDoors has been initialized. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* If the caller asked to repeat the character 0 times, then we can */   /* safely return right away without doing anything.                 */   if(btTimes == 0)   {      OD_API_EXIT();      return;   }   /* Generate string of repeat characters. */   pchCurStringPos = szODWorkString;    for(btLeft = btTimes; btLeft--;)   {      *pchCurStringPos++ = chValue;   }   *pchCurStringPos = '\0';   /* Display repeated string on local screen. */   ODScrnDisplayString(szODWorkString);   /* If we are operating in AVATAR mode. */   if(od_control.user_avatar)   {      /* Generate the AVATAR control sequence to repeat this character */      /* the specified number of times.                                */      szBuffer[0] = 25;      szBuffer[1] = chValue;      szBuffer[2] = btTimes;      od_disp(szBuffer, 3, FALSE);   }   /* If AVATAR mode is not available. */   else   {      /* Send the entire repeated string to the remote system. */      od_disp(szODWorkString, btTimes, FALSE);   }   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_page() * * This function is called when the user wished to page the system operator. * * Parameters: none * *     Return: void */ODAPIDEF void ODCALL od_page(void){   INT16 nCount;   tODTimer Timer;   time_t nUnixTime;   struct tm *TimeBlock;   INT16 nMinute;   BOOL bFailed = FALSE;   INT16 nOriginalAttrib;   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_page()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Save current display color attribute. */   nOriginalAttrib = od_control.od_cur_attrib;   /* Clear the screen. */   od_clr_scr();   od_set_attrib(od_control.od_chat_color1);   /* Ask reason for chat. */   od_disp_str(od_control.od_chat_reason);   od_set_attrib(od_control.od_chat_color2);   od_putch('[');   /* Use extended ASCII characters if operating in ANSI or AVATAR mode. */   if(od_control.user_ansi || od_control.user_avatar)   {      od_repeat('Ä',77);   }   else   {      od_repeat('-',77);   }   od_disp_str("]\n\r ");   od_input_str(od_control.user_reasonforchat,77,32,255);   /* If the user did not abort sysop paging by entering a blank reason */   /* for chat.                                                         */   if(strlen(od_control.user_reasonforchat) != 0)   {      /* Indicate that the user wants to chat. */      od_control.user_wantchat = TRUE;#ifdef ODPLAT_WIN32      ODFrameUpdateWantChat();#endif /* ODPLAT_WIN32 */      /* Determine whether or not sysop paging should be permitted at */      /* the current time.                                            */      nUnixTime = time(NULL);      TimeBlock = localtime(&nUnixTime);      nMinute = (60 * TimeBlock->tm_hour) + TimeBlock->tm_min;      if(od_control.od_pagestartmin < od_control.od_pageendmin)      {         if(nMinute < od_control.od_pagestartmin            || nMinute >= od_control.od_pageendmin)         {            bFailed = TRUE;         }      }      else if(od_control.od_pagestartmin > od_control.od_pageendmin)      {         if(nMinute < od_control.od_pagestartmin            && nMinute >= od_control.od_pageendmin)         {            bFailed = TRUE;         }      }      else      {         bFailed = FALSE;      }      /* If paging is set to PAGE_ENABLE, meaning that sysop paging should */      /* be permitted regardless of the time of day, then allow paging.    */      if(od_control.od_okaytopage == PAGE_ENABLE)      {         bFailed = FALSE;      }      /* If paging is explicitly disable by PAGE_DISABLE, or the current */      /* time of the day is not normally permitted for paging.           */      if(od_control.od_okaytopage == PAGE_DISABLE || bFailed)      {         /* Indicate this to user. */         od_disp_str("\n\r");         od_disp_str(od_control.od_no_sysop);         od_disp_str(od_control.od_press_key);         od_get_answer("\x0d\x0a");         /* Return from this function. */         goto cleanup;      }      /* Update status line right away. */      bForceStatusUpdate = TRUE;      CALL_KERNEL_IF_NEEDED();      /* Write sysop page information to the logfile, if the log file */      /* system is hooked up.                                         */      if(pfLogWrite != NULL)      {         (*pfLogWrite)(8);      }      /* Tell the user that we are now paging the system operator. */      od_set_attrib(od_control.od_chat_color1);      od_disp_str(od_control.od_paging);#ifdef OD_TEXTMODE      /* Display sysop page status line if it exists and the sysop status */      /* line is currently active.                                        */      if(od_control.od_page_statusline != -1 && btCurrentStatusLine != 8)      {         od_set_statusline(od_control.od_page_statusline);      }#endif /* OD_TEXTMODE */      /* Increment the total number of times that the user has paged */      /* the sysop.                                                  */      ++od_control.user_numpages;      /* Sysop hasn't responded yet. */      bChatted=FALSE;      /* Loop for length of sysop page. */      for(nCount = 0; nCount < od_control.od_page_len; ++nCount)      {         /* Start a timer that is set to elapse in exactly one second. */         ODTimerStart(&Timer, 1000);         /* Display another period character. */         od_putch('.');         /* Abort page if system operator answered */         if(bChatted) goto cleanup;         /* Send beep to local and remote systems. */         od_putch('\a');         /* Check whether system operator has answered after playing beep. */         if (bChatted) goto cleanup;         /* Wait for the timer to elapse, calling od_kernel() so that */         /* chat mode will start as soon as the sysop presses the     */         /* chat key.                                                 */         while(!ODTimerElapsed(&Timer))         {            CALL_KERNEL_IF_NEEDED();         }      }      /* If sysop page time has elapsed without a response from the */      /* sysop, then notify the user.                               */      od_disp_str(od_control.od_no_response);      od_disp_str(od_control.od_press_key);      od_get_answer("\x0d\x0a");      od_disp_str("\n\r\n\r");   }cleanup:   /* Restore original display color attribute. */   od_set_attrib(nOriginalAttrib);   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_disp() * * Function to send one or more character to the remote system, optionally * also echoing the same characters to the local screen. * * Parameters: pachBuffer - Pointer to buffer of characters to send. * *             nSize      - Number of characters to send from the buffer. * *             bLocalEcho - TRUE to also echo the characters to the local *                          screen, FALSE to just send the characters to the *                          remote system. * *     Return: void */ODAPIDEF void ODCALL od_disp(const char *pachBuffer, INT nSize, BOOL bLocalEcho){   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_disp()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Call the OpenDoors kernel, if needed. */#ifndef OD_MULTITHREADED   if(ODTimerElapsed(&RunKernelTimer))   {      CALL_KERNEL_IF_NEEDED();   }#endif /* !OD_MULTITHREADED */   /* If we are operating in remote mode, then send the buffer to the */   /* remote system.                                                  */   if(od_control.baud != 0)   {      ODComSendBuffer(hSerialPort, (BYTE *)pachBuffer, nSize);   }   /* If we are also to display the character on the local screen, then */   /* display the buffer on the local screen.                           */   if(bLocalEcho)   {      ODScrnDisplayBuffer(pachBuffer, nSize);   }   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_disp_str() * * Displays a string on both the local and remote systems. * * Parameters: pszToDisplay - Pointer to the string to be displayed. * *     Return: void */ODAPIDEF void ODCALL od_disp_str(const char *pszToDisplay){   /* Log function entry if running in trace mode */   TRACE(TRACE_API, "od_disp_str()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Call the OpenDoors kernel, if needed. */#ifndef OD_MULTITHREADED   if(ODTimerElapsed(&RunKernelTimer))   {      CALL_KERNEL_IF_NEEDED();   }#endif /* !OD_MULTITHREADED */   /* Send the string to the remote system, if we are running in remote mode. */   if(od_control.baud != 0)   {      ODComSendBuffer(hSerialPort, (BYTE *)pszToDisplay, strlen(pszToDisplay));   }   /* Display the screen on the local screen. */   ODScrnDisplayString(pszToDisplay);   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_set_statusline() * * Switches to one of the available status lines provided by the current * personality, or turns off the status line altogether. * * Parameters: nSetting - Indicates which status line (if any) should be *                        activated. * *     Return: void */ODAPIDEF void ODCALL od_set_statusline(INT nSetting){#ifdef OD_TEXTMODE   INT nDistance;   BYTE btCount#endif /* OD_TEXTMODE */   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_set_statusline()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY()#ifdef OD_TEXTMODE   /* If status line is disabled, then don't do anything. */   if(!od_control.od_status_on)   {      OD_API_EXIT();      return;   }   /* Ensure that the parameter is within the valid range. */   if(nSetting < 0 || nSetting > 8)   {      nSetting = 0;   }   /* If the specified status line is already active, and status line */   /* update isn't being forced, then return without doing anything.  */   if(!od_control.od_update_status_now && nSetting == btCurrentStatusLine)   {      OD_API_EXIT();      return;   }   /* Save the current cursor settings. */   ODStoreTextInfo();   /* Reset screen boundary to allow access to the entire screen. */   ODScrnSetBoundary(1,1,80,25);   /* If status line is being turned off. */   if(btCurrentStatusLine == STATUS_NONE)   {      if((nDistance = (INT)ODTextInfo.cury - ( 1 + (INT)btOutputBottom         - (INT)btOutputTop)) > 0)      {         ODScrnCopyText(1, (BYTE)((INT)btOutputTop + nDistance), 80,            (BYTE)((INT)btOutputBottom + nDistance), (BYTE)btOutputTop, 1);         ODTextInfo.cury = 1 + btOutputBottom - btOutputTop;      }      else if(ODTextInfo.cury < btOutputTop)      {         ODTextInfo.cury = btOutputTop;         ODScrnCopyText(1, (BYTE)(btOutputTop + 24 - btOutputBottom), 80, 25,            btOutputTop, 1);      }   }   od_control.od_current_statusline = btCurrentStatusLine = nSetting;   if(nSetting == 8)   {      ODScrnSetAttribute(0x07);      for(btCount = 1; btCount <= 25; ++btCount)      {         if(btCount < btOutputTop || btCount > btOutputBottom)         {            if(btCount == 25)            {               ODScrnPutText(80, 25, 80, 25, abtBlackBlock);               ODScrnSetCursorPos(1, 25);               ODScrnDisplayString("                                                                               ");            }            else            {               ODScrnSetCursorPos(1, 24);               ODScrnDisplayString("                                                                                ");            }         }      }      ODScrnSetAttribute(ODTextInfo.attribute);      ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);   }   else   {      ODScrnEnableCaret(FALSE);      ODScrnEnableScrolling(FALSE);      (*pfCurrentPersonality)((BYTE)nSetting);      ODScrnEnableCaret(TRUE);      ODScrnEnableScrolling(TRUE);      ODScrnSetBoundary(1, btOutputTop, 80, btOutputBottom);      ODScrnSetAttribute(ODTextInfo.attribute);      ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);   }#else /* !OD_TEXTMODE */   od_control.od_error = ERR_UNSUPPORTED;#endif /* !OD_TEXTMODE */   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * ODStoreTextInfo() * * Stores the current text settings into the OpenDoors global text information * structure. * * Parameters: none * *     Return: void */void ODStoreTextInfo(void){   ODScrnGetTextInfo(&ODTextInfo);}/* ---------------------------------------------------------------------------- * ODRestoreTextInfo() * * Restores display settings previously stored by ODStoreTextInfo() * * Parameters: none * *     Return: void */void ODRestoreTextInfo(void){   ODScrnSetBoundary(ODTextInfo.winleft, ODTextInfo.wintop,      ODTextInfo.winright, ODTextInfo.winbottom);   ODScrnSetAttribute(ODTextInfo.attribute);   ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);}/* ---------------------------------------------------------------------------- * ODStringToName() * * Reformats a string so that it has the correct capitalization for a name, * and removes any trailing line break character. * * Parameters: pszToConvert - Pointer to the string to reformat. * *     Return: void */void ODStringToName(char *pszToConvert){   /* Begin by changing the entire string to lower case. */   strlwr(pszToConvert);   /* Trim any newline character that may be at the end of the string. */   if(pszToConvert[strlen(pszToConvert) - 1] == '\n')   {      pszToConvert[strlen(pszToConvert) - 1] = '\0';   }   /* Trim any CR character that may be at the end of the string. */   if(pszToConvert[strlen(pszToConvert) - 1] == '\r')   {      pszToConvert[strlen(pszToConvert) - 1] = '\0';   }   /* Change the first character to lower case. */   *pszToConvert = toupper(*pszToConvert);   /* Loop through the rest of the string, capitalizing any other words */   /* in the string.                                                    */   while(*pszToConvert)   {      switch(*pszToConvert++)      {         case ' ':         case '\t':         case ',':         case '.':         case '-':            *pszToConvert = toupper(*pszToConvert);            break;      }   }}/* ---------------------------------------------------------------------------- * od_set_color() * * Sets the current display color for both local and remote output. * * Parameters: nForeground - New foreground (text) color. * *             nBackground - New background color. * *     Return: void */ODAPIDEF void ODCALL od_set_color(INT nForeground, INT nBackground){   /* Use od_set_attrib() to perform the actual color setting.          */   /* Here, we rely on od_set_attrib() to look after initialization,    */   /* API_ENTRY() and API_EXIT() calls, etc. This allows od_set_color() */   /* (which was previously just a macro) to be implemented with as     */   /* little overhead as possible.                                      */   od_set_attrib(nForeground | (nBackground << 4));}/* ---------------------------------------------------------------------------- * od_set_attrib() * * Sets the current display color for both local and remote output. * * Parameters: nColor - New Display color to set, or -1 for no change. * *     Return: void */ODAPIDEF void ODCALL od_set_attrib(INT nColor){   char szControlSequence[40];   /* Log function entry if running in trace mode */   TRACE(TRACE_API, "od_set_attrib()");   /* Ensure that OpenDoors has been initialized. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* If color value is -1, then make no change. */   if(nColor == -1)   {      OD_API_EXIT();      return;   }   /* If we are operating in AVATAR mode. */   if(od_control.user_avatar)   {      if(od_control.od_cur_attrib != nColor || od_control.od_full_color)      {         /* Change local text color. */         ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = nColor));         /* Generate AVATAR control sequence. */         szControlSequence[0] = 22;         szControlSequence[1] = 1;         szControlSequence[2] = nColor;         /* Send AVATAR control sequence. */         od_disp(szControlSequence, 3, FALSE);      }   }   /* If we are operating in ANSI mode. */   else if(od_control.user_ansi)   {      bAnyColorChangeYet = FALSE;      if(od_control.od_cur_attrib == -1 || od_control.od_full_color)      {ansi_reset:         /* Reset ANSI terminal status. */         ODAddANSIParameter(szControlSequence, 0);         /* If blink attribute is set. */         if(nColor & 0x80)         {            /* Add it to the ANSI color sequence. */            ODAddANSIParameter(szControlSequence, 5);         }         /* If high intensity attribute is set. */         if(nColor & 0x08)         {            /* Add it to the ANSI color sequence. */            ODAddANSIParameter(szControlSequence, 1);         }      }      /* If current color is known. */      else                                   {         /* If have to reset flashing or bright. */         if(((od_control.od_cur_attrib&0x80) &&            !(nColor & 0x80)) || ((od_control.od_cur_attrib & 0x08)            && !(nColor & 0x08)))         {            /* Must reset entire colour settings. */            od_control.od_cur_attrib = -1;            goto ansi_reset;         }                                                /* If flashing has to be turned on. */         if((nColor & 0x80) != (od_control.od_cur_attrib & 0x80))         {            /* Add it to the ANSI color sequence. */            ODAddANSIParameter(szControlSequence, 5);         }         /* If bright has to be turned on. */         if((nColor & 0x08) != (od_control.od_cur_attrib & 0x08)            || od_control.od_cur_attrib == -1)         {            /* Add it to the ANSI color sequence. */            ODAddANSIParameter(szControlSequence, 1);         }      }      /* If foreground color has changed. */      if((nColor & 0x07) != (od_control.od_cur_attrib & 0x07)         || od_control.od_cur_attrib == -1 || od_control.od_full_color)      {         /* Add translated color to sequence. */         ODAddANSIParameter(szControlSequence,            abtPCToANSIColorTable[nColor&0x07]);      }      /* If background color has changed. */      if((nColor & 0x70) != (od_control.od_cur_attrib & 0x70)         || od_control.od_cur_attrib == -1 || od_control.od_full_color)      {         /* Add translated color to sequence. */         ODAddANSIParameter(szControlSequence,            abtPCToANSIColorTable[(nColor & 0x70) >> 4] + 10);      }      /* If any change in color. */      if(bAnyColorChangeYet)      {         /* Append change-attribute command. */         strcat(szControlSequence, "m");         /* Send ANSI sequence to the modem. */         od_disp(szControlSequence, strlen(szControlSequence), FALSE);      }      /* Change local text color. */      ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = nColor));   }   else   {      od_control.od_error = ERR_NOGRAPHICS;   }   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * ODAddANSIParameter()                                *** PRIVATE FUNCTION *** * * Adds a parameter to an ANSI color sequence. * * Parameters: szControlSequence - The contents of the control sequence string *                                 generated so far. * *             nParameterValue   - Value of the parameter to add. * *     Return: void */static void ODAddANSIParameter(char *szControlSequence, int nParameterValue){   char szTemp[5];   if(bAnyColorChangeYet)   {      sprintf(szTemp, ";%d", nParameterValue);      strcat(szControlSequence, szTemp);   }   else   {      bAnyColorChangeYet = TRUE;      sprintf(szControlSequence, "x[%d", nParameterValue);      szControlSequence[0] = 27;   }}/* ---------------------------------------------------------------------------- * od_putch() * * Displays a character on the local and remote screens. * * Parameters: chToDisplay - The character to display. * *     Return: void */ODAPIDEF void ODCALL od_putch(char chToDisplay){   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_putch()");   /* Initialize OpenDoors if it hasn't been done already. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* Display the character on the local screen. */   ODScrnDisplayChar(chToDisplay);   /* If not operating in local mode, then send the character to the */   /* serial port.                                                   */   if(od_control.baud)   {      ODComSendByte(hSerialPort, chToDisplay);   }   /* If it is time to call the kernel, then do so. */   if(ODTimerElapsed(&RunKernelTimer))   {      CALL_KERNEL_IF_NEEDED();   }   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_set_dtr() * * Changes the state of the DTR line to the modem, if not running in local * mode. * * Parameters: bHigh - TRUE to raise DTR, FALSE to lower it. * *     Return: void */ODAPIDEF void ODCALL od_set_dtr(BOOL bHigh){   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_set_dtr()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   /* If we are running in local mode, then return with an error. */   if(!od_control.baud)   {      od_control.od_error = ERR_NOREMOTE;      OD_API_EXIT();      return;   }   /* Otherwise, change the state of the DTR line. */   ODComSetDTR(hSerialPort, bHigh);   OD_API_EXIT();}/* ---------------------------------------------------------------------------- * od_get_answer() * * Waits for the user to press one of the keys listed in pszOptions. Case is * not sensitive, although the pressed key is returned in the same case as it * is specified in pszOptions. * * Parameters: pszOptions - String listing characters to accept. * *     Return: void */ODAPIDEF char ODCALL od_get_answer(const char *pszOptions){   char *pchPossibleOption;   char chPressed;   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_get_answer()");   /* Initialize OpenDoors if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   for(;;)   {      /* Wait for the next key press by the user. */      chPressed = od_get_key(TRUE);      chPressed = tolower(chPressed);      /* Loop through list of possible options. */      pchPossibleOption = (char *)pszOptions;      while(*pchPossibleOption)      {         /* If the key pressed matches this possible option. */         if(tolower(*pchPossibleOption) == chPressed)         {            /* Then return the character in the case originally specified */            /* by the caller.                                             */            OD_API_EXIT();            return(*pchPossibleOption);         }         /* Move on to the next possible option. */         ++pchPossibleOption;      }      /* If the key pressed did not match a possible option, then we */      /* just loop again, getting the next key.                      */   }}/* ---------------------------------------------------------------------------- * od_color_config() * * Determines the color attribute that is described by the provided string. * This string is in the same format that is used for specifying colors in the * OpenDoors configuration file. * * Parameters: pszColorDesc - Color description string. * *     Return: The PC-style color attribute corresponding to the color *             description string. */ODAPIDEF BYTE ODCALL od_color_config(char *pszColorDesc){   BYTE btColor = 0x07;   char szToken[40];   char *pszStart=(char *)pszColorDesc;   char *pszEnd;   BYTE btLength;   BYTE btIdentifier;   BOOL bForeground = TRUE;   /* Log function entry if running in trace mode. */   TRACE(TRACE_API, "od_color_config()");   /* Initialize OpenDoros if it hasn't already been done. */   if(!bODInitialized) od_init();   OD_API_ENTRY();   while(*pszStart && *pszStart!=chColorCheck)   {      if(*pszStart == ' ' || *pszStart== '\t')      {         ++pszStart;      }      else      {         btLength = 0;         pszEnd = (char *)pszStart;         while(*pszEnd && *pszEnd != chColorCheck && *pszEnd != ' '            && *pszEnd != '\t')         {            ++btLength;            ++pszEnd;         }         if(btLength > 39) btLength = 39;         strncpy(szToken, pszStart, btLength);         szToken[btLength] = '\0';         strupr(szToken);         for(btIdentifier = 0; btIdentifier < 12; ++btIdentifier)            if(strcmp(od_config_colours[btIdentifier], szToken) == 0)            {               if(btIdentifier <= 9)               {                  if(btIdentifier >= 8) btIdentifier -= 2;                  if(bForeground)                  {                     bForeground=FALSE;                     btColor &=~ 0x07;                     btColor |= btIdentifier;                  }                  else                  {                     btColor &=~ 0x70;                     btColor |= (btIdentifier << 4);                  }               }               else if(btIdentifier == 10)               {                  btColor |= 0x08;               }               else if(btIdentifier == 11)               {                  btColor |= 0x80;               }               break;            }         pszStart = (char *)pszEnd;      }   }   pchColorEndPos = (char *)pszStart;   OD_API_EXIT();   return(btColor);}/* ---------------------------------------------------------------------------- * ODPagePrompt() * * Called to display the page prompt at the end of a screen of text. This page * prompt allows the user to stop further display, to display the next page, * or to display in continuous (non-stop) mode with page pausing disabled. * * Parameters: pbPausing   - Pointer to current page pausing enabled flag. * *     Return: FALSE if display should be continued, or TRUE to abort display. */BOOL ODPagePrompt(BOOL *pbPausing){   INT nPromptLength = strlen(od_control.od_continue);   tODScrnTextInfo TextInfo;   BOOL bToReturn = FALSE;   char chKeyPressed;   BYTE btCount;   /* Return right away if page pausing is disabled. */   if(!*pbPausing) return(FALSE);   /* Get current text color. */   ODScrnGetTextInfo(&TextInfo);   /* Set to prompt color. */   od_set_attrib(od_control.od_continue_col);   /* Display page prompt string. */   od_disp_str(od_control.od_continue);   /* Restore original text color. */   od_set_attrib(TextInfo.attribute);   /* Loop until the user makes a valid choice. */   for(;;)   {      /* Obtain the next key from the user. */      chKeyPressed = od_get_key(TRUE);      /* If user chooses to continue. */      if(chKeyPressed == tolower(od_control.od_continue_yes) ||         chKeyPressed == toupper(od_control.od_continue_yes) ||         chKeyPressed == 13 ||         chKeyPressed == ' ')      {         /* Remove the prompt and return. */         goto finished_pausing;      }      /* If user requested nonstop display. */      else if(chKeyPressed == tolower(od_control.od_continue_nonstop) ||              chKeyPressed == toupper(od_control.od_continue_nonstop))      {         /* Disable page pausing. */         *pbPausing = FALSE;         /* Remove the prompt and return. */         goto finished_pausing;      }      /* If user chooses to stop display. */      else if(chKeyPressed == tolower(od_control.od_continue_no) ||              chKeyPressed == toupper(od_control.od_continue_no) ||              chKeyPressed == 's' || chKeyPressed == 'S' || chKeyPressed == 3              || chKeyPressed == 11 || chKeyPressed == 0x18)      {         /* If we are operating in remote mode. */         if(od_control.baud)         {            /* Clear the output buffer. */            ODComClearOutbound(hSerialPort);         }         /* Tell the caller to stop displaying more text. */         bToReturn = TRUE;         /* Remove the prompt and return. */         goto finished_pausing;      }   }finished_pausing:   /* Remove the pause prompt. */   for(btCount = 0; btCount < nPromptLength; ++btCount)   {      od_disp_str(szBackspaceWithDelete);   }   return(bToReturn);}/* ---------------------------------------------------------------------------- * od_control_get() * * Returns a pointer to the od_control structure containing information * and settings associated with the current session. * * Parameters: None. * *     Return: A pointer to the od_control structure associated with this *             session. */ODAPIDEF tODControl * ODCALL od_control_get(void){   /* Log function entry if running in trace mode */   TRACE(TRACE_API, "od_disp_str()");   return(&od_control);}
 |