home *** CD-ROM | disk | FTP | other *** search
/ synchro.net / synchro.net.tar / synchro.net / main / BBS / ODOORS62.ZIP / ODCore.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-19  |  47.0 KB  |  1,576 lines

  1. /* OpenDoors Online Software Programming Toolkit
  2.  * (C) Copyright 1991 - 1999 by Brian Pirie.
  3.  *
  4.  * Oct-2001 door32.sys/socket modifications by Rob Swindell (www.synchro.net)
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  *
  21.  *        File: ODCore.c
  22.  *
  23.  * Description: Implements the core of OpenDoors, including chat mode
  24.  *              and standard input/output functions that are
  25.  *              used throughout OpenDoors.
  26.  *
  27.  *   Revisions: Date          Ver   Who  Change
  28.  *              ---------------------------------------------------------------
  29.  *              Oct 13, 1994  6.00  BP   New file header format.
  30.  *              Oct 19, 1994  6.00  BP   Changed paging hours logic.
  31.  *              Oct 21, 1994  6.00  BP   Further isolated com routines.
  32.  *              Oct 22, 1994  6.00  BP   Name case conversion /w punct.
  33.  *              Dec 08, 1994  6.00  BP   Allow custom chat mode deactivation.
  34.  *              Dec 09, 1994  6.00  BP   Remove global dir entry structure.
  35.  *              Dec 13, 1994  6.00  BP   Remove include of dir.h.
  36.  *              Dec 31, 1994  6.00  BP   Remove #ifndef USEINLINE DOS code.
  37.  *              Dec 31, 1994  6.00  BP   Remove old multitasker definitions.
  38.  *              Jan 01, 1995  6.00  BP   Don't use ODComInbound().
  39.  *              Jan 01, 1995  6.00  BP   _waitdrain() -> ODWaitDrain().
  40.  *              Jan 01, 1995  6.00  BP   Use new millisecond timer functions.
  41.  *              Jan 01, 1995  6.00  BP   Remove od_init() from _remotechar()
  42.  *              Jan 01, 1995  6.00  BP   Split off odkrnl.c from odcore.c
  43.  *              Aug 19, 1995  6.00  BP   32-bit portability.
  44.  *              Nov 11, 1995  6.00  BP   Moved first_word() to odlist.c
  45.  *              Nov 11, 1995  6.00  BP   Removed register keyword.
  46.  *              Nov 14, 1995  6.00  BP   Added include of odscrn.h.
  47.  *              Nov 16, 1995  6.00  BP   Create odcore.h.
  48.  *              Nov 17, 1995  6.00  BP   Use new input queue mechanism.
  49.  *              Dec 12, 1995  6.00  BP   Added od_set_color().
  50.  *              Dec 12, 1995  6.00  BP   Added entry, exit and kernel macros.
  51.  *              Dec 13, 1995  6.00  BP   Moved chat mode code to ODKrnl.h.
  52.  *              Dec 19, 1995  6.00  BP   Request reason for chat outside hours.
  53.  *              Dec 23, 1995  6.00  BP   Allow space to continue at page pause.
  54.  *              Dec 24, 1995  6.00  BP   Added abtGreyBlock.
  55.  *              Dec 30, 1995  6.00  BP   Added ODCALL for calling convention.
  56.  *              Jan 03, 1996  6.00  BP   Use OD_API_VAR_DEFN for od_control.
  57.  *              Jan 04, 1996  6.00  BP   tODInQueueEvent -> tODInputEvent.
  58.  *              Jan 23, 1996  6.00  BP   No od_set_statusline() under Win32.
  59.  *              Jan 30, 1996  6.00  BP   Replaced od_yield() with od_sleep().
  60.  *              Jan 30, 1996  6.00  BP   Add ODInQueueGetNextEvent() timeout.
  61.  *              Jan 09, 1996  6.00  BP   ODComOutbound() returns actual size.
  62.  *              Jan 09, 1996  6.00  BP   Reduce kernel calls from od_disp...().
  63.  *              Feb 19, 1996  6.00  BP   Changed version number to 6.00.
  64.  *              Mar 03, 1996  6.10  BP   Begin version 6.10.
  65.  *              Mar 19, 1996  6.10  BP   MSVC15 source-level compatibility.
  66.  *              Mar 21, 1996  6.10  BP   Added od_control_get().
  67.  *              Sep 01, 1996  6.10  BP   Update output area on od_set_per...().
  68.  *              Oct 19, 2001  6.20  RS   od_get_key now ignores linefeeds.
  69.  */
  70.  
  71. #define BUILDING_OPENDOORS
  72.  
  73. #include <stdio.h>
  74. #include <stdlib.h>
  75. #include <string.h>
  76. #include <ctype.h>
  77. #include <time.h>
  78. #include <errno.h>
  79.  
  80. #include "OpenDoor.h"
  81. #include "ODGen.h"
  82. #include "ODPlat.h"
  83. #include "ODCom.h"
  84. #include "ODKrnl.h"
  85. #include "ODScrn.h"
  86. #include "ODCore.h"
  87. #include "ODInQue.h"
  88. #ifdef ODPLAT_WIN32
  89. #include "ODFrame.h"
  90. #endif /* ODPLAT_WIN32 */
  91.  
  92.  
  93. /* GLOBAL VARIABLES SHARED THROUGHOUT OPENDOORS. */
  94.  
  95. /* Global declaration of the OpenDoors control structure. */
  96. OD_API_VAR_DEFN tODControl 
  97. #ifndef _WIN32    /* warning C4229: anachronism used : modifiers on data are ignored */
  98. OD_GLOBAL_CONV 
  99. #endif
  100. od_control;
  101.  
  102. /* OpenDoors global initialized flag. */
  103. BOOL bODInitialized = FALSE;
  104.  
  105. /* Global serial port object handle. */
  106. tPortHandle hSerialPort;
  107.  
  108. /* Global input queue object handle. */
  109. tODInQueueHandle hODInputQueue;
  110.  
  111. /* Reentrancy control. */
  112. BOOL bIsCallbackActive = FALSE;
  113. BOOL bShellChatActive = FALSE;
  114.  
  115. /* Global working space. */
  116. char szODWorkString[OD_GLOBAL_WORK_STRING_SIZE];
  117.  
  118. /* Global instance of the text information structure for general use. */
  119. tODScrnTextInfo ODTextInfo;
  120.  
  121. /* Logfile function hooks. */
  122. BOOL (*pfLogWrite)(INT) = NULL;
  123. void (*pfLogClose)(INT) = NULL;
  124.  
  125. /* od_color_config() support for od_printf(). */
  126. char chColorCheck = 0;
  127. char *pchColorEndPos;
  128.  
  129. /* Status line information. */
  130. BYTE btCurrentStatusLine = STATUS_NONE;
  131. OD_PERSONALITY_CALLBACK *pfCurrentPersonality = NULL;
  132. char szDesiredPersonality[33] = "";
  133. extern SET_PERSONALITY_FUNC *pfSetPersonality = NULL;
  134.  
  135. /* Commonly used character sequences. */
  136. char abtBlackBlock[2] = {' ', 0x07};
  137. char abtGreyBlock[2] = {' ', 0x70};
  138. char szBackspaceWithDelete[4] = {8, ' ', 8, 0};
  139.  
  140. /* Current output area on screen. */
  141. BYTE btOutputTop = 1;
  142. BYTE btOutputBottom = 23;
  143.  
  144.  
  145. /* PRIVATE VARIABLES. */
  146.  
  147. /* Display color varaibles. */
  148. char bAnyColorChangeYet;
  149.  
  150. /* Static character sequences. */
  151. static char szClearScreen[2] = {12, 0};
  152.  
  153. /* Lookup table to map colors from PC values to ANSI color values. */
  154. static BYTE abtPCToANSIColorTable[8] = {30, 34, 32, 36, 31, 35, 33, 37};
  155.  
  156.  
  157. /* LOCAL HELPER FUNCTIONS. */
  158. static void ODAddANSIParameter(char *szControlSequence, int nParameterValue);
  159.  
  160.  
  161. /* ----------------------------------------------------------------------------
  162.  * ODWaitDrain()
  163.  *
  164.  * Waits for up to the specified number of milliseconds for the output serial
  165.  * buffer to drain.
  166.  *
  167.  * Parameters: MaxWait - Specifies the maximum number of milliseconds to wait
  168.  *                       before timing out.
  169.  *
  170.  *     Return: void
  171.  */
  172. void ODWaitDrain(tODMilliSec MaxWait)
  173. {
  174.    int nOutboundSize;
  175.    tODTimer Timer;
  176.  
  177.    /* If we are operating in local mode, then don't do anything. */
  178.    if(od_control.baud == 0) return;
  179.  
  180.    /* Otherwise, start a timer that is set to elapse after the maximum */
  181.    /* wait period.                                                     */
  182.    ODTimerStart(&Timer, MaxWait);
  183.  
  184.    /* Loop until either the outbound buffer is empty, or the */
  185.    /* timer has elapsed.                                     */
  186.    for(;;)
  187.    {
  188.       /* Check whether any data is in the outbound serial queue. */
  189.       ODComOutbound(hSerialPort, &nOutboundSize);
  190.  
  191.       /* If the queue is empty or the timer has elapsed, then stop */
  192.       /* waiting.                                                  */
  193.       if(nOutboundSize == 0 || ODTimerElapsed(&Timer)) break;
  194.  
  195.       /* Otherwise, give other tasks a chance to run. */
  196.       od_sleep(0);
  197.  
  198.       /* Give od_kernel() activities a chance to run. */
  199.       CALL_KERNEL_IF_NEEDED();
  200.    } 
  201. }
  202.  
  203.  
  204. /* ----------------------------------------------------------------------------
  205.  * od_clr_scr()
  206.  *
  207.  * Clears the contents of the local and remote screens, if screen clearing is
  208.  * enabled.
  209.  *
  210.  * Parameters: none
  211.  *
  212.  *     Return: void
  213.  */
  214. ODAPIDEF void ODCALL od_clr_scr(void)
  215. {
  216.    INT16 nOriginalAttrib;
  217.  
  218.    /* Log function entry if running in trace mode */
  219.    TRACE(TRACE_API, "od_clr_scr()");
  220.  
  221.    if(!bODInitialized) od_init();
  222.  
  223.    OD_API_ENTRY();
  224.  
  225.    /* Don't clear screen if disabled. */
  226.    if(!od_control.od_always_clear && !(od_control.user_attribute & 2)
  227.       && (od_control.od_extended_info || od_control.od_info_type == CUSTOM))
  228.    {
  229.       OD_API_EXIT();
  230.       return;
  231.    }
  232.  
  233.    if(od_control.user_rip)
  234.    {
  235.       od_disp("!|*", 3, FALSE);
  236.       if(!od_control.od_default_rip_win)
  237.       {
  238.          od_disp("!|w0000270M12", 13, FALSE);
  239.       }
  240.    }
  241.  
  242.    /* Send ascii 12 to modem, no local echo. */
  243.    od_disp(szClearScreen, 1, FALSE);
  244.  
  245.    /* Clear local window. */
  246.    ODScrnClear();
  247.  
  248.    /* Get color set prior to screen clear. */
  249.    nOriginalAttrib = od_control.od_cur_attrib;
  250.  
  251.    /* Current color state is unknown. */
  252.    od_control.od_cur_attrib = -1;
  253.  
  254.    /* Set color to original value. This gurantees that local and */
  255.    /* remote systems both have the same current color set.       */
  256.    od_set_attrib(nOriginalAttrib);
  257.  
  258.    OD_API_EXIT();
  259. }
  260.  
  261.  
  262.  
  263. /* ----------------------------------------------------------------------------
  264.  * od_input_str()
  265.  *
  266.  * Allows the user to input a string up to the specified length, using
  267.  * characters in the specified range. This string input function is designed
  268.  * to be compatible with all terminal types.
  269.  *
  270.  * Parameters: pszInput   - Pointer to string to store input in.
  271.  *
  272.  *             nMaxLength - Maximum number of characters to permit the user
  273.  *                          to input.
  274.  *
  275.  *             chMin      - The minimum character value to permit. This must
  276.  *                          be at least ASCII 32.
  277.  *
  278.  *             chMax      - The maximum character value to permit.
  279.  *
  280.  *     Return: void
  281.  */
  282. ODAPIDEF void ODCALL od_input_str(char *pszInput, 
  283.    INT nMaxLength,
  284.    unsigned char chMin,
  285.    unsigned char chMax)
  286. {
  287.    char chKeyPressed;
  288.    INT nPosition;
  289.  
  290.    /* Log function entry if running in trace mode. */
  291.    TRACE(TRACE_API, "od_input_str()");
  292.  
  293.    /* Initialize OpenDoors if it hasn't already been done. */
  294.    if(!bODInitialized) od_init();
  295.  
  296.    OD_API_ENTRY();
  297.  
  298.    /* Start at the beginning of the string. */
  299.    nPosition = 0;
  300.  
  301.    /* Check that input parameters are valid. */
  302.    if(pszInput == NULL || nMaxLength < 1 || chMin > chMax)
  303.    {
  304.       od_control.od_error = ERR_PARAMETER;
  305.       OD_API_EXIT();
  306.       return;
  307.    }
  308.  
  309.    for(;;)
  310.    {
  311.       chKeyPressed = od_get_key(TRUE);
  312.  
  313.       /* If user pressed enter. */
  314.       if(chKeyPressed == '\r' || chKeyPressed == '\n')
  315.       {
  316.          /* Terminate the string. */
  317.          pszInput[nPosition] = '\0';
  318.  
  319.          /* Display CR-LF sequence. */
  320.          od_disp_str("\n\r");
  321.  
  322.          /* Exit the function. */
  323.          OD_API_EXIT();
  324.          return;
  325.       }
  326.  
  327.       /* If the user pressed backspace. */
  328.       else if(chKeyPressed == 8)
  329.       {
  330.          /* If we are not currently at the beginning of the string. */
  331.          if(nPosition > 0)
  332.          {
  333.             /* Send backspace sequence. */
  334.             od_disp_str(szBackspaceWithDelete);
  335.  
  336.             /* Move current position back by one position in the string. */
  337.             --nPosition;
  338.          }
  339.       }
  340.  
  341.       /* If this is a valid character to place in the string and we have */
  342.       /* not reached the maximum size of the string yet.                 */
  343.       else if(chKeyPressed >= chMin && chKeyPressed <= chMax
  344.          && nPosition < nMaxLength)
  345.       {
  346.          /* Display key that was pressed. */
  347.          od_putch(chKeyPressed);
  348.  
  349.          /* Add the entered character to the string and increment our */
  350.          /* current position in the string.                           */
  351.          pszInput[nPosition++] = chKeyPressed;
  352.       }
  353.    }
  354. }
  355.  
  356.  
  357. /* ----------------------------------------------------------------------------
  358.  * od_clear_keybuffer()
  359.  *
  360.  * Clears any keystrokes from the inbound buffers. Both input from local and
  361.  * remote systems is discarded, by clearing both OpenDoors' common input
  362.  * event queue, and the serial port inbound buffer. This function is called
  363.  * to cause any input by the user prior to the time the function was called
  364.  * to be ignored.
  365.  *
  366.  * Parameters: none
  367.  *
  368.  *     Return: void
  369.  */
  370. ODAPIDEF void ODCALL od_clear_keybuffer(void)
  371. {
  372.    /* Log function entry if running in trace mode. */
  373.    TRACE(TRACE_API, "od_clear_keybuffer()");
  374.  
  375.    /* Initialize OpenDoors if it hasn't already been done. */
  376.    if(!bODInitialized) od_init();
  377.  
  378.    OD_API_ENTRY();
  379.  
  380.    /* Empty any events in the common input event queue. */
  381.    ODInQueueEmpty(hODInputQueue);
  382.  
  383.    /* If we are not operating in local mode ... */
  384.    if(od_control.baud != 0)
  385.    {
  386.       /* ... then remove any items in the serial port inbound buffer. */
  387.       ODComClearInbound(hSerialPort);
  388.    }
  389.  
  390.    /* Call the OpenDoors kernel function. */
  391.    CALL_KERNEL_IF_NEEDED();
  392.  
  393.    OD_API_EXIT();
  394. }
  395.  
  396.  
  397. /* ----------------------------------------------------------------------------
  398.  * od_get_key()
  399.  *
  400.  * Inputs a single character, optionally waiting for the next character if no
  401.  * character has been received yet. This function returns data received from
  402.  * either the local or remote system, in the order in which it was received.
  403.  *
  404.  * Parameters: bWait - FALSE if od_get_key() should return right away with
  405.  *                     a value of 0 if no characters have been received, or
  406.  *                     TRUE if od_get_key() should wait for the next received
  407.  *                     character.
  408.  *
  409.  *     Return: Character that was received, or 0 if no character is waiting.
  410.  */
  411. ODAPIDEF char ODCALL od_get_key(BOOL bWait)
  412. {
  413.    tODInputEvent InputEvent;
  414.  
  415.    /* Initialize OpenDoors if it hasn't already been done. */
  416.    if(!bODInitialized) od_init();
  417.  
  418.    /* Log function entry if running in trace mode. */
  419.    TRACE(TRACE_API, "od_get_key()");
  420.  
  421.    OD_API_ENTRY();
  422.  
  423.    /* Call the OpenDoors kernel. */
  424.    CALL_KERNEL_IF_NEEDED();
  425.  
  426.    /* If we aren't supposed to wait for input, then check whether any   */
  427.    /* input is waiting in the input queue, and if not return right away */
  428.    /* without any data.                                                 */
  429.    if(!bWait)
  430.    {
  431.       if(!ODInQueueWaiting(hODInputQueue))
  432.       {
  433.          OD_API_EXIT();
  434.          return(0);
  435.       }
  436.    }
  437.  
  438.     do {
  439.  
  440.         /* Obtain the next character from the input queue. If we get to this */
  441.         /* point and there is no data waiting in the input queue, then the   */
  442.         /* ODInQueueGetNextEvent() function will block until a character     */
  443.         /* is available in the input queue.                                  */
  444.         ODInQueueGetNextEvent(hODInputQueue, &InputEvent, OD_NO_TIMEOUT);
  445.  
  446.         /* Only keyboard input events are currently supported by od_get_key(). */
  447.         ASSERT(InputEvent.EventType == EVENT_CHARACTER);
  448.  
  449.         /* Update OpenDoors control structure member that records whether the */
  450.         /* last input came from the local or remote user.                     */
  451.         od_control.od_last_input = InputEvent.bFromRemote ? 0 : 1;
  452.  
  453.     } while(InputEvent.chKeyPress == '\n');    /* Ignore line-feed char */
  454.  
  455.    /* Return the character that was pressed by the user. */
  456.    OD_API_EXIT();
  457.    return(InputEvent.chKeyPress);
  458. }
  459.  
  460.  
  461.  
  462. /* ----------------------------------------------------------------------------
  463.  * od_carrier()
  464.  *
  465.  * Allows programs to determine the current state of the carrier detect
  466.  * signal when OpenDoors' automatic carrier detection has been disabled.
  467.  *
  468.  * Parameters: none
  469.  *
  470.  *     Return: TRUE if the carrier detct signal is present, FALSE if it
  471.  *             isn't. When operating in local mode, this function always
  472.  *             returns FALSE.
  473.  */
  474. ODAPIDEF BOOL ODCALL od_carrier(void)
  475. {
  476.    BOOL bIsCarrier;
  477.  
  478.    /* Initialize OpenDoors if it hasn't already been done. */
  479.    if(!bODInitialized) od_init();
  480.  
  481.    OD_API_ENTRY();
  482.  
  483.    /* Log function entry if running in trace mode */
  484.    TRACE(TRACE_API, "od_carrier()");
  485.  
  486.    /* If we are operating in local mode, then return FALSE. */
  487.    if(od_control.baud == 0)
  488.    {
  489.       od_control.od_error = ERR_NOREMOTE;
  490.       OD_API_EXIT();
  491.       return(FALSE);
  492.    }
  493.  
  494.    /* In remote mode, obtain the current state of the carrier detect signal. */
  495.    ODComCarrier(hSerialPort, &bIsCarrier);
  496.  
  497.    /* Return the current state of the carrier detect signal. */
  498.    OD_API_EXIT();
  499.    return(bIsCarrier);
  500. }
  501.  
  502.  
  503. /* ----------------------------------------------------------------------------
  504.  * od_repeat()
  505.  *
  506.  * This function displays the same character the specified number of times on
  507.  * the local and remote screens, using any available optimal control sequences
  508.  * under the current display mode.
  509.  *
  510.  * Parameters: chValue - Character to repeat.
  511.  *
  512.  *             btTimes - Number of times to repeat the character.
  513.  *
  514.  *     Return: void
  515.  */
  516. ODAPIDEF void ODCALL od_repeat(char chValue, BYTE btTimes)
  517. {
  518.    char *pchCurStringPos;
  519.    BYTE btLeft;
  520.    char szBuffer[3];
  521.  
  522.    /* Log function entry if running in trace mode. */
  523.    TRACE(TRACE_API, "od_repeat()");
  524.  
  525.    /* Ensure that OpenDoors has been initialized. */
  526.    if(!bODInitialized) od_init();
  527.  
  528.    OD_API_ENTRY();
  529.  
  530.    /* If the caller asked to repeat the character 0 times, then we can */
  531.    /* safely return right away without doing anything.                 */
  532.    if(btTimes == 0)
  533.    {
  534.       OD_API_EXIT();
  535.       return;
  536.    }
  537.  
  538.    /* Generate string of repeat characters. */
  539.    pchCurStringPos = szODWorkString; 
  540.    for(btLeft = btTimes; btLeft--;)
  541.    {
  542.       *pchCurStringPos++ = chValue;
  543.    }
  544.    *pchCurStringPos = '\0';
  545.  
  546.    /* Display repeated string on local screen. */
  547.    ODScrnDisplayString(szODWorkString);
  548.  
  549.    /* If we are operating in AVATAR mode. */
  550.    if(od_control.user_avatar)
  551.    {
  552.       /* Generate the AVATAR control sequence to repeat this character */
  553.       /* the specified number of times.                                */
  554.       szBuffer[0] = 25;
  555.       szBuffer[1] = chValue;
  556.       szBuffer[2] = btTimes;
  557.       od_disp(szBuffer, 3, FALSE);
  558.    }
  559.  
  560.    /* If AVATAR mode is not available. */
  561.    else
  562.    {
  563.       /* Send the entire repeated string to the remote system. */
  564.       od_disp(szODWorkString, btTimes, FALSE);
  565.    }
  566.  
  567.    OD_API_EXIT();
  568. }
  569.  
  570.  
  571. /* ----------------------------------------------------------------------------
  572.  * od_page()
  573.  *
  574.  * This function is called when the user wished to page the system operator.
  575.  *
  576.  * Parameters: none
  577.  *
  578.  *     Return: void
  579.  */
  580. ODAPIDEF void ODCALL od_page(void)
  581. {
  582.    INT16 nCount;
  583.    tODTimer Timer;
  584.    time_t nUnixTime;
  585.    struct tm *TimeBlock;
  586.    INT16 nMinute;
  587.    BOOL bFailed = FALSE;
  588.    INT16 nOriginalAttrib;
  589.  
  590.    /* Log function entry if running in trace mode. */
  591.    TRACE(TRACE_API, "od_page()");
  592.  
  593.    /* Initialize OpenDoors if it hasn't already been done. */
  594.    if(!bODInitialized) od_init();
  595.  
  596.    OD_API_ENTRY();
  597.  
  598.    /* Save current display color attribute. */
  599.    nOriginalAttrib = od_control.od_cur_attrib;
  600.  
  601.    /* Clear the screen. */
  602.    od_clr_scr();
  603.    od_set_attrib(od_control.od_chat_color1);
  604.  
  605.    /* Ask reason for chat. */
  606.    od_disp_str(od_control.od_chat_reason);
  607.    od_set_attrib(od_control.od_chat_color2);
  608.    od_putch('[');
  609.  
  610.    /* Use extended ASCII characters if operating in ANSI or AVATAR mode. */
  611.    if(od_control.user_ansi || od_control.user_avatar)
  612.    {
  613.       od_repeat('─',77);
  614.    }
  615.    else
  616.    {
  617.       od_repeat('-',77);
  618.    }
  619.    od_disp_str("]\n\r ");
  620.    od_input_str(od_control.user_reasonforchat,77,32,255);
  621.  
  622.    /* If the user did not abort sysop paging by entering a blank reason */
  623.    /* for chat.                                                         */
  624.    if(strlen(od_control.user_reasonforchat) != 0)
  625.    {
  626.       /* Indicate that the user wants to chat. */
  627.       od_control.user_wantchat = TRUE;
  628. #ifdef ODPLAT_WIN32
  629.       ODFrameUpdateWantChat();
  630. #endif /* ODPLAT_WIN32 */
  631.  
  632.       /* Determine whether or not sysop paging should be permitted at */
  633.       /* the current time.                                            */
  634.       nUnixTime = time(NULL);
  635.       TimeBlock = localtime(&nUnixTime);
  636.       nMinute = (60 * TimeBlock->tm_hour) + TimeBlock->tm_min;
  637.       if(od_control.od_pagestartmin < od_control.od_pageendmin)
  638.       {
  639.          if(nMinute < od_control.od_pagestartmin
  640.             || nMinute >= od_control.od_pageendmin)
  641.          {
  642.             bFailed = TRUE;
  643.          }
  644.       }
  645.       else if(od_control.od_pagestartmin > od_control.od_pageendmin)
  646.       {
  647.          if(nMinute < od_control.od_pagestartmin
  648.             && nMinute >= od_control.od_pageendmin)
  649.          {
  650.             bFailed = TRUE;
  651.          }
  652.       }
  653.       else
  654.       {
  655.          bFailed = FALSE;
  656.       }
  657.  
  658.       /* If paging is set to PAGE_ENABLE, meaning that sysop paging should */
  659.       /* be permitted regardless of the time of day, then allow paging.    */
  660.       if(od_control.od_okaytopage == PAGE_ENABLE)
  661.       {
  662.          bFailed = FALSE;
  663.       }
  664.  
  665.       /* If paging is explicitly disable by PAGE_DISABLE, or the current */
  666.       /* time of the day is not normally permitted for paging.           */
  667.       if(od_control.od_okaytopage == PAGE_DISABLE || bFailed)
  668.       {
  669.          /* Indicate this to user. */
  670.          od_disp_str("\n\r");
  671.          od_disp_str(od_control.od_no_sysop);
  672.          od_disp_str(od_control.od_press_key);
  673.          od_get_answer("\x0d\x0a");
  674.  
  675.          /* Return from this function. */
  676.          goto cleanup;
  677.       }
  678.  
  679.       /* Update status line right away. */
  680.       bForceStatusUpdate = TRUE;
  681.       CALL_KERNEL_IF_NEEDED();
  682.  
  683.       /* Write sysop page information to the logfile, if the log file */
  684.       /* system is hooked up.                                         */
  685.       if(pfLogWrite != NULL)
  686.       {
  687.          (*pfLogWrite)(8);
  688.       }
  689.  
  690.       /* Tell the user that we are now paging the system operator. */
  691.       od_set_attrib(od_control.od_chat_color1);
  692.       od_disp_str(od_control.od_paging);
  693.  
  694. #ifdef OD_TEXTMODE
  695.       /* Display sysop page status line if it exists and the sysop status */
  696.       /* line is currently active.                                        */
  697.       if(od_control.od_page_statusline != -1 && btCurrentStatusLine != 8)
  698.       {
  699.          od_set_statusline(od_control.od_page_statusline);
  700.       }
  701. #endif /* OD_TEXTMODE */
  702.  
  703.       /* Increment the total number of times that the user has paged */
  704.       /* the sysop.                                                  */
  705.       ++od_control.user_numpages;
  706.  
  707.       /* Sysop hasn't responded yet. */
  708.       bChatted=FALSE;
  709.  
  710.       /* Loop for length of sysop page. */
  711.       for(nCount = 0; nCount < od_control.od_page_len; ++nCount)
  712.       {
  713.          /* Start a timer that is set to elapse in exactly one second. */
  714.          ODTimerStart(&Timer, 1000);
  715.  
  716.          /* Display another period character. */
  717.          od_putch('.');
  718.  
  719.          /* Abort page if system operator answered */
  720.          if(bChatted) goto cleanup;
  721.  
  722.          /* Send beep to local and remote systems. */
  723.          od_putch('\a');
  724.  
  725.          /* Check whether system operator has answered after playing beep. */
  726.          if (bChatted) goto cleanup;
  727.  
  728.          /* Wait for the timer to elapse, calling od_kernel() so that */
  729.          /* chat mode will start as soon as the sysop presses the     */
  730.          /* chat key.                                                 */
  731.          while(!ODTimerElapsed(&Timer))
  732.          {
  733.             CALL_KERNEL_IF_NEEDED();
  734.          }
  735.       }
  736.  
  737.       /* If sysop page time has elapsed without a response from the */
  738.       /* sysop, then notify the user.                               */
  739.       od_disp_str(od_control.od_no_response);
  740.       od_disp_str(od_control.od_press_key);
  741.       od_get_answer("\x0d\x0a");
  742.       od_disp_str("\n\r\n\r");
  743.    }
  744.  
  745. cleanup:
  746.    /* Restore original display color attribute. */
  747.    od_set_attrib(nOriginalAttrib);
  748.  
  749.    OD_API_EXIT();
  750. }
  751.  
  752.  
  753. /* ----------------------------------------------------------------------------
  754.  * od_disp()
  755.  *
  756.  * Function to send one or more character to the remote system, optionally
  757.  * also echoing the same characters to the local screen.
  758.  *
  759.  * Parameters: pachBuffer - Pointer to buffer of characters to send.
  760.  *
  761.  *             nSize      - Number of characters to send from the buffer.
  762.  *
  763.  *             bLocalEcho - TRUE to also echo the characters to the local
  764.  *                          screen, FALSE to just send the characters to the
  765.  *                          remote system.
  766.  *
  767.  *     Return: void
  768.  */
  769. ODAPIDEF void ODCALL od_disp(char *pachBuffer, INT nSize, BOOL bLocalEcho)
  770. {
  771.    /* Log function entry if running in trace mode. */
  772.    TRACE(TRACE_API, "od_disp()");
  773.  
  774.    /* Initialize OpenDoors if it hasn't already been done. */
  775.    if(!bODInitialized) od_init();
  776.  
  777.    OD_API_ENTRY();
  778.  
  779.    /* Call the OpenDoors kernel, if needed. */
  780. #ifndef OD_MULTITHREADED
  781.    if(ODTimerElapsed(&RunKernelTimer))
  782.    {
  783.       CALL_KERNEL_IF_NEEDED();
  784.    }
  785. #endif /* !OD_MULTITHREADED */
  786.  
  787.    /* If we are operating in remote mode, then send the buffer to the */
  788.    /* remote system.                                                  */
  789.    if(od_control.baud != 0)
  790.    {
  791.       ODComSendBuffer(hSerialPort, (BYTE *)pachBuffer, nSize);
  792.    }
  793.  
  794.    /* If we are also to display the character on the local screen, then */
  795.    /* display the buffer on the local screen.                           */
  796.    if(bLocalEcho)
  797.    {
  798.       ODScrnDisplayBuffer(pachBuffer, nSize);
  799.    }
  800.  
  801.    OD_API_EXIT();
  802. }
  803.  
  804.  
  805. /* ----------------------------------------------------------------------------
  806.  * od_disp_str()
  807.  *
  808.  * Displays a string on both the local and remote systems.
  809.  *
  810.  * Parameters: pszToDisplay - Pointer to the string to be displayed.
  811.  *
  812.  *     Return: void
  813.  */
  814. ODAPIDEF void ODCALL od_disp_str(char *pszToDisplay)
  815. {
  816.    /* Log function entry if running in trace mode */
  817.    TRACE(TRACE_API, "od_disp_str()");
  818.  
  819.    /* Initialize OpenDoors if it hasn't already been done. */
  820.    if(!bODInitialized) od_init();
  821.  
  822.    OD_API_ENTRY();
  823.  
  824.    /* Call the OpenDoors kernel, if needed. */
  825. #ifndef OD_MULTITHREADED
  826.    if(ODTimerElapsed(&RunKernelTimer))
  827.    {
  828.       CALL_KERNEL_IF_NEEDED();
  829.    }
  830. #endif /* !OD_MULTITHREADED */
  831.  
  832.    /* Send the string to the remote system, if we are running in remote mode. */
  833.    if(od_control.baud != 0)
  834.    {
  835.       ODComSendBuffer(hSerialPort, (BYTE *)pszToDisplay, strlen(pszToDisplay));
  836.    }
  837.  
  838.    /* Display the screen on the local screen. */
  839.    ODScrnDisplayString(pszToDisplay);
  840.  
  841.    OD_API_EXIT();
  842. }
  843.  
  844.  
  845. /* ----------------------------------------------------------------------------
  846.  * od_set_statusline()
  847.  *
  848.  * Switches to one of the available status lines provided by the current
  849.  * personality, or turns off the status line altogether.
  850.  *
  851.  * Parameters: nSetting - Indicates which status line (if any) should be
  852.  *                        activated.
  853.  *
  854.  *     Return: void
  855.  */
  856. ODAPIDEF void ODCALL od_set_statusline(INT nSetting)
  857. {
  858. #ifdef OD_TEXTMODE
  859.    INT nDistance;
  860.    BYTE btCount
  861. #endif /* OD_TEXTMODE */
  862.  
  863.    /* Log function entry if running in trace mode. */
  864.    TRACE(TRACE_API, "od_set_statusline()");
  865.  
  866.    /* Initialize OpenDoors if it hasn't already been done. */
  867.    if(!bODInitialized) od_init();
  868.  
  869.    OD_API_ENTRY()
  870.  
  871. #ifdef OD_TEXTMODE
  872.  
  873.    /* If status line is disabled, then don't do anything. */
  874.    if(!od_control.od_status_on)
  875.    {
  876.       OD_API_EXIT();
  877.       return;
  878.    }
  879.  
  880.    /* Ensure that the parameter is within the valid range. */
  881.    if(nSetting < 0 || nSetting > 8)
  882.    {
  883.       nSetting = 0;
  884.    }
  885.  
  886.    /* If the specified status line is already active, and status line */
  887.    /* update isn't being forced, then return without doing anything.  */
  888.    if(!od_control.od_update_status_now && nSetting == btCurrentStatusLine)
  889.    {
  890.       OD_API_EXIT();
  891.       return;
  892.    }
  893.  
  894.    /* Save the current cursor settings. */
  895.    ODStoreTextInfo();
  896.  
  897.    /* Reset screen boundary to allow access to the entire screen. */
  898.    ODScrnSetBoundary(1,1,80,25);
  899.  
  900.    /* If status line is being turned off. */
  901.    if(btCurrentStatusLine == STATUS_NONE)
  902.    {
  903.       if((nDistance = (INT)ODTextInfo.cury - ( 1 + (INT)btOutputBottom
  904.          - (INT)btOutputTop)) > 0)
  905.       {
  906.          ODScrnCopyText(1, (BYTE)((INT)btOutputTop + nDistance), 80,
  907.             (BYTE)((INT)btOutputBottom + nDistance), (BYTE)btOutputTop, 1);
  908.          ODTextInfo.cury = 1 + btOutputBottom - btOutputTop;
  909.       }
  910.       else if(ODTextInfo.cury < btOutputTop)
  911.       {
  912.          ODTextInfo.cury = btOutputTop;
  913.          ODScrnCopyText(1, (BYTE)(btOutputTop + 24 - btOutputBottom), 80, 25,
  914.             btOutputTop, 1);
  915.       }
  916.    }
  917.  
  918.    od_control.od_current_statusline = btCurrentStatusLine = nSetting;
  919.  
  920.    if(nSetting == 8)
  921.    {
  922.       ODScrnSetAttribute(0x07);
  923.  
  924.       for(btCount = 1; btCount <= 25; ++btCount)
  925.       {
  926.          if(btCount < btOutputTop || btCount > btOutputBottom)
  927.          {
  928.             if(btCount == 25)
  929.             {
  930.                ODScrnPutText(80, 25, 80, 25, abtBlackBlock);
  931.                ODScrnSetCursorPos(1, 25);
  932.                ODScrnDisplayString("                                                                               ");
  933.             }
  934.             else
  935.             {
  936.                ODScrnSetCursorPos(1, 24);
  937.                ODScrnDisplayString("                                                                                ");
  938.             }
  939.          }
  940.       }
  941.  
  942.       ODScrnSetAttribute(ODTextInfo.attribute);
  943.       ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);
  944.    }
  945.  
  946.    else
  947.    {
  948.       ODScrnEnableCaret(FALSE);
  949.       ODScrnEnableScrolling(FALSE);
  950.  
  951.       (*pfCurrentPersonality)((BYTE)nSetting);
  952.  
  953.       ODScrnEnableCaret(TRUE);
  954.       ODScrnEnableScrolling(TRUE);
  955.  
  956.       ODScrnSetBoundary(1, btOutputTop, 80, btOutputBottom);
  957.       ODScrnSetAttribute(ODTextInfo.attribute);
  958.       ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);
  959.    }
  960.  
  961. #else /* !OD_TEXTMODE */
  962.  
  963.    od_control.od_error = ERR_UNSUPPORTED;
  964.  
  965. #endif /* !OD_TEXTMODE */
  966.  
  967.    OD_API_EXIT();
  968. }
  969.  
  970.  
  971. /* ----------------------------------------------------------------------------
  972.  * ODStoreTextInfo()
  973.  *
  974.  * Stores the current text settings into the OpenDoors global text information
  975.  * structure.
  976.  *
  977.  * Parameters: none
  978.  *
  979.  *     Return: void
  980.  */
  981. void ODStoreTextInfo(void)
  982. {
  983.    ODScrnGetTextInfo(&ODTextInfo);
  984. }
  985.  
  986.  
  987. /* ----------------------------------------------------------------------------
  988.  * ODRestoreTextInfo()
  989.  *
  990.  * Restores display settings previously stored by ODStoreTextInfo()
  991.  *
  992.  * Parameters: none
  993.  *
  994.  *     Return: void
  995.  */
  996. void ODRestoreTextInfo(void)
  997. {
  998.    ODScrnSetBoundary(ODTextInfo.winleft, ODTextInfo.wintop,
  999.       ODTextInfo.winright, ODTextInfo.winbottom);
  1000.    ODScrnSetAttribute(ODTextInfo.attribute);
  1001.    ODScrnSetCursorPos(ODTextInfo.curx, ODTextInfo.cury);
  1002. }
  1003.  
  1004.  
  1005. /* ----------------------------------------------------------------------------
  1006.  * ODStringToName()
  1007.  *
  1008.  * Reformats a string so that it has the correct capitalization for a name,
  1009.  * and removes any trailing line break character.
  1010.  *
  1011.  * Parameters: pszToConvert - Pointer to the string to reformat.
  1012.  *
  1013.  *     Return: void
  1014.  */
  1015. void ODStringToName(char *pszToConvert)
  1016. {
  1017.    /* Begin by changing the entire string to lower case. */
  1018.    strlwr(pszToConvert);
  1019.  
  1020.    /* Trim any newline character that may be at the end of the string. */
  1021.    if(pszToConvert[strlen(pszToConvert) - 1] == '\n')
  1022.    {
  1023.       pszToConvert[strlen(pszToConvert) - 1] = '\0';
  1024.    }
  1025.  
  1026.    /* Change the first character to lower case. */
  1027.    *pszToConvert = toupper(*pszToConvert);
  1028.  
  1029.    /* Loop through the rest of the string, capitalizing any other words */
  1030.    /* in the string.                                                    */
  1031.    while(*pszToConvert)
  1032.    {
  1033.       switch(*pszToConvert++)
  1034.       {
  1035.          case ' ':
  1036.          case '\t':
  1037.          case ',':
  1038.          case '.':
  1039.          case '-':
  1040.             *pszToConvert = toupper(*pszToConvert);
  1041.             break;
  1042.       }
  1043.    }
  1044. }
  1045.  
  1046.  
  1047. /* ----------------------------------------------------------------------------
  1048.  * od_set_color()
  1049.  *
  1050.  * Sets the current display color for both local and remote output.
  1051.  *
  1052.  * Parameters: nForeground - New foreground (text) color.
  1053.  *
  1054.  *             nBackground - New background color.
  1055.  *
  1056.  *     Return: void
  1057.  */
  1058. ODAPIDEF void ODCALL od_set_color(INT nForeground, INT nBackground)
  1059. {
  1060.    /* Use od_set_attrib() to perform the actual color setting.          */
  1061.    /* Here, we rely on od_set_attrib() to look after initialization,    */
  1062.    /* API_ENTRY() and API_EXIT() calls, etc. This allows od_set_color() */
  1063.    /* (which was previously just a macro) to be implemented with as     */
  1064.    /* little overhead as possible.                                      */
  1065.    od_set_attrib(nForeground | (nBackground << 4));
  1066. }
  1067.  
  1068.  
  1069. /* ----------------------------------------------------------------------------
  1070.  * od_set_attrib()
  1071.  *
  1072.  * Sets the current display color for both local and remote output.
  1073.  *
  1074.  * Parameters: nColor - New Display color to set, or -1 for no change.
  1075.  *
  1076.  *     Return: void
  1077.  */
  1078. ODAPIDEF void ODCALL od_set_attrib(INT nColor)
  1079. {
  1080.    char szControlSequence[40];
  1081.  
  1082.    /* Log function entry if running in trace mode */
  1083.    TRACE(TRACE_API, "od_set_attrib()");
  1084.  
  1085.    /* Ensure that OpenDoors has been initialized. */
  1086.    if(!bODInitialized) od_init();
  1087.  
  1088.    OD_API_ENTRY();
  1089.  
  1090.    /* If color value is -1, then make no change. */
  1091.    if(nColor == -1)
  1092.    {
  1093.       OD_API_EXIT();
  1094.       return;
  1095.    }
  1096.  
  1097.    /* If we are operating in AVATAR mode. */
  1098.    if(od_control.user_avatar)
  1099.    {
  1100.       if(od_control.od_cur_attrib != nColor || od_control.od_full_color)
  1101.       {
  1102.          /* Change local text color. */
  1103.          ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = nColor));
  1104.  
  1105.          /* Generate AVATAR control sequence. */
  1106.          szControlSequence[0] = 22;
  1107.          szControlSequence[1] = 1;
  1108.          szControlSequence[2] = nColor;
  1109.  
  1110.          /* Send AVATAR control sequence. */
  1111.          od_disp(szControlSequence, 3, FALSE);
  1112.       }
  1113.    }
  1114.  
  1115.    /* If we are operating in ANSI mode. */
  1116.    else if(od_control.user_ansi)
  1117.    {
  1118.       bAnyColorChangeYet = FALSE;
  1119.  
  1120.       if(od_control.od_cur_attrib == -1 || od_control.od_full_color)
  1121.       {
  1122. ansi_reset:
  1123.          /* Reset ANSI terminal status. */
  1124.          ODAddANSIParameter(szControlSequence, 0);
  1125.  
  1126.          /* If blink attribute is set. */
  1127.          if(nColor & 0x80)
  1128.          {
  1129.             /* Add it to the ANSI color sequence. */
  1130.             ODAddANSIParameter(szControlSequence, 5);
  1131.          }
  1132.  
  1133.          /* If high intensity attribute is set. */
  1134.          if(nColor & 0x08)
  1135.          {
  1136.             /* Add it to the ANSI color sequence. */
  1137.             ODAddANSIParameter(szControlSequence, 1);
  1138.          }
  1139.       }
  1140.  
  1141.       /* If current color is known. */
  1142.       else                             
  1143.       {
  1144.          /* If have to reset flashing or bright. */
  1145.          if(((od_control.od_cur_attrib&0x80) &&
  1146.             !(nColor & 0x80)) || ((od_control.od_cur_attrib & 0x08)
  1147.             && !(nColor & 0x08)))
  1148.          {
  1149.             /* Must reset entire colour settings. */
  1150.             od_control.od_cur_attrib = -1;
  1151.             goto ansi_reset;
  1152.          }
  1153.                                        
  1154.          /* If flashing has to be turned on. */
  1155.          if((nColor & 0x80) != (od_control.od_cur_attrib & 0x80))
  1156.          {
  1157.             /* Add it to the ANSI color sequence. */
  1158.             ODAddANSIParameter(szControlSequence, 5);
  1159.          }
  1160.  
  1161.          /* If bright has to be turned on. */
  1162.          if((nColor & 0x08) != (od_control.od_cur_attrib & 0x08)
  1163.             || od_control.od_cur_attrib == -1)
  1164.          {
  1165.             /* Add it to the ANSI color sequence. */
  1166.             ODAddANSIParameter(szControlSequence, 1);
  1167.          }
  1168.       }
  1169.  
  1170.  
  1171.       /* If foreground color has changed. */
  1172.       if((nColor & 0x07) != (od_control.od_cur_attrib & 0x07)
  1173.          || od_control.od_cur_attrib == -1 || od_control.od_full_color)
  1174.       {
  1175.          /* Add translated color to sequence. */
  1176.          ODAddANSIParameter(szControlSequence,
  1177.             abtPCToANSIColorTable[nColor&0x07]);
  1178.       }
  1179.  
  1180.       /* If background color has changed. */
  1181.       if((nColor & 0x70) != (od_control.od_cur_attrib & 0x70)
  1182.          || od_control.od_cur_attrib == -1 || od_control.od_full_color)
  1183.       {
  1184.          /* Add translated color to sequence. */
  1185.          ODAddANSIParameter(szControlSequence,
  1186.             abtPCToANSIColorTable[(nColor & 0x70) >> 4] + 10);
  1187.       }
  1188.  
  1189.       /* If any change in color. */
  1190.       if(bAnyColorChangeYet)
  1191.       {
  1192.          /* Append change-attribute command. */
  1193.          strcat(szControlSequence, "m");
  1194.  
  1195.          /* Send ANSI sequence to the modem. */
  1196.          od_disp(szControlSequence, strlen(szControlSequence), FALSE);
  1197.       }
  1198.  
  1199.       /* Change local text color. */
  1200.       ODScrnSetAttribute((BYTE)(od_control.od_cur_attrib = nColor));
  1201.    }
  1202.    else
  1203.    {
  1204.       od_control.od_error = ERR_NOGRAPHICS;
  1205.    }
  1206.  
  1207.    OD_API_EXIT();
  1208. }
  1209.  
  1210.  
  1211. /* ----------------------------------------------------------------------------
  1212.  * ODAddANSIParameter()                                *** PRIVATE FUNCTION ***
  1213.  *
  1214.  * Adds a parameter to an ANSI color sequence.
  1215.  *
  1216.  * Parameters: szControlSequence - The contents of the control sequence string
  1217.  *                                 generated so far.
  1218.  *
  1219.  *             nParameterValue   - Value of the parameter to add.
  1220.  *
  1221.  *     Return: void
  1222.  */
  1223. static void ODAddANSIParameter(char *szControlSequence, int nParameterValue)
  1224. {
  1225.    char szTemp[5];
  1226.  
  1227.    if(bAnyColorChangeYet)
  1228.    {
  1229.       sprintf(szTemp, ";%d", nParameterValue);
  1230.       strcat(szControlSequence, szTemp);
  1231.    }
  1232.    else
  1233.    {
  1234.       bAnyColorChangeYet = TRUE;
  1235.       sprintf(szControlSequence, "x[%d", nParameterValue);
  1236.       szControlSequence[0] = 27;
  1237.    }
  1238. }
  1239.  
  1240.  
  1241. /* ----------------------------------------------------------------------------
  1242.  * od_putch()
  1243.  *
  1244.  * Displays a character on the local and remote screens.
  1245.  *
  1246.  * Parameters: chToDisplay - The character to display.
  1247.  *
  1248.  *     Return: void
  1249.  */
  1250. ODAPIDEF void ODCALL od_putch(char chToDisplay)
  1251. {
  1252.    /* Log function entry if running in trace mode. */
  1253.    TRACE(TRACE_API, "od_putch()");
  1254.  
  1255.    /* Initialize OpenDoors if it hasn't been done already. */
  1256.    if(!bODInitialized) od_init();
  1257.  
  1258.    OD_API_ENTRY();
  1259.  
  1260.    /* Display the character on the local screen. */
  1261.    ODScrnDisplayChar(chToDisplay);
  1262.  
  1263.    /* If not operating in local mode, then send the character to the */
  1264.    /* serial port.                                                   */
  1265.    if(od_control.baud)
  1266.    {
  1267.       ODComSendByte(hSerialPort, chToDisplay);
  1268.    }
  1269.  
  1270.    /* If it is time to call the kernel, then do so. */
  1271.    if(ODTimerElapsed(&RunKernelTimer))
  1272.    {
  1273.       CALL_KERNEL_IF_NEEDED();
  1274.    }
  1275.  
  1276.    OD_API_EXIT();
  1277. }
  1278.  
  1279.  
  1280. /* ----------------------------------------------------------------------------
  1281.  * od_set_dtr()
  1282.  *
  1283.  * Changes the state of the DTR line to the modem, if not running in local
  1284.  * mode.
  1285.  *
  1286.  * Parameters: bHigh - TRUE to raise DTR, FALSE to lower it.
  1287.  *
  1288.  *     Return: void
  1289.  */
  1290. ODAPIDEF void ODCALL od_set_dtr(BOOL bHigh)
  1291. {
  1292.    /* Log function entry if running in trace mode. */
  1293.    TRACE(TRACE_API, "od_set_dtr()");
  1294.  
  1295.    /* Initialize OpenDoors if it hasn't already been done. */
  1296.    if(!bODInitialized) od_init();
  1297.  
  1298.    OD_API_ENTRY();
  1299.  
  1300.    /* If we are running in local mode, then return with an error. */
  1301.    if(!od_control.baud)
  1302.    {
  1303.       od_control.od_error = ERR_NOREMOTE;
  1304.       OD_API_EXIT();
  1305.       return;
  1306.    }
  1307.  
  1308.    /* Otherwise, change the state of the DTR line. */
  1309.    ODComSetDTR(hSerialPort, bHigh);
  1310.  
  1311.    OD_API_EXIT();
  1312. }
  1313.  
  1314.  
  1315. /* ----------------------------------------------------------------------------
  1316.  * od_get_answer()
  1317.  *
  1318.  * Waits for the user to press one of the keys listed in pszOptions. Case is
  1319.  * not sensitive, although the pressed key is returned in the same case as it
  1320.  * is specified in pszOptions.
  1321.  *
  1322.  * Parameters: pszOptions - String listing characters to accept.
  1323.  *
  1324.  *     Return: void
  1325.  */
  1326. ODAPIDEF char ODCALL od_get_answer(char *pszOptions)
  1327. {
  1328.    char *pchPossibleOption;
  1329.    char chPressed;
  1330.  
  1331.    /* Log function entry if running in trace mode. */
  1332.    TRACE(TRACE_API, "od_get_answer()");
  1333.  
  1334.    /* Initialize OpenDoors if it hasn't already been done. */
  1335.    if(!bODInitialized) od_init();
  1336.  
  1337.    OD_API_ENTRY();
  1338.  
  1339.    for(;;)
  1340.    {
  1341.       /* Wait for the next key press by the user. */
  1342.       chPressed = od_get_key(TRUE);
  1343.       chPressed = tolower(chPressed);
  1344.  
  1345.       /* Loop through list of possible options. */
  1346.       pchPossibleOption = (char *)pszOptions;
  1347.       while(*pchPossibleOption)
  1348.       {
  1349.          /* If the key pressed matches this possible option. */
  1350.          if(tolower(*pchPossibleOption) == chPressed)
  1351.          {
  1352.             /* Then return the character in the case originally specified */
  1353.             /* by the caller.                                             */
  1354.             OD_API_EXIT();
  1355.             return(*pchPossibleOption);
  1356.          }
  1357.  
  1358.          /* Move on to the next possible option. */
  1359.          ++pchPossibleOption;
  1360.       }
  1361.  
  1362.       /* If the key pressed did not match a possible option, then we */
  1363.       /* just loop again, getting the next key.                      */
  1364.    }
  1365. }
  1366.  
  1367.  
  1368. /* ----------------------------------------------------------------------------
  1369.  * od_color_config()
  1370.  *
  1371.  * Determines the color attribute that is described by the provided string.
  1372.  * This string is in the same format that is used for specifying colors in the
  1373.  * OpenDoors configuration file.
  1374.  *
  1375.  * Parameters: pszColorDesc - Color description string.
  1376.  *
  1377.  *     Return: The PC-style color attribute corresponding to the color
  1378.  *             description string.
  1379.  */
  1380. ODAPIDEF BYTE ODCALL od_color_config(char *pszColorDesc)
  1381. {
  1382.    BYTE btColor = 0x07;
  1383.    char szToken[40];
  1384.    char *pszStart=(char *)pszColorDesc;
  1385.    char *pszEnd;
  1386.    BYTE btLength;
  1387.    BYTE btIdentifier;
  1388.    BOOL bForeground = TRUE;
  1389.  
  1390.    /* Log function entry if running in trace mode. */
  1391.    TRACE(TRACE_API, "od_color_config()");
  1392.  
  1393.    /* Initialize OpenDoros if it hasn't already been done. */
  1394.    if(!bODInitialized) od_init();
  1395.  
  1396.    OD_API_ENTRY();
  1397.  
  1398.    while(*pszStart && *pszStart!=chColorCheck)
  1399.    {
  1400.       if(*pszStart == ' ' || *pszStart== '\t')
  1401.       {
  1402.          ++pszStart;
  1403.       }
  1404.       else
  1405.       {
  1406.          btLength = 0;
  1407.          pszEnd = (char *)pszStart;
  1408.          while(*pszEnd && *pszEnd != chColorCheck && *pszEnd != ' '
  1409.             && *pszEnd != '\t')
  1410.          {
  1411.             ++btLength;
  1412.             ++pszEnd;
  1413.          }
  1414.  
  1415.          if(btLength > 39) btLength = 39;
  1416.          strncpy(szToken, pszStart, btLength);
  1417.          szToken[btLength] = '\0';
  1418.          strupr(szToken);
  1419.  
  1420.          for(btIdentifier = 0; btIdentifier < 12; ++btIdentifier)
  1421.             if(strcmp(od_config_colours[btIdentifier], szToken) == 0)
  1422.             {
  1423.                if(btIdentifier <= 9)
  1424.                {
  1425.                   if(btIdentifier >= 8) btIdentifier -= 2;
  1426.  
  1427.                   if(bForeground)
  1428.                   {
  1429.                      bForeground=FALSE;
  1430.                      btColor &=~ 0x07;
  1431.                      btColor |= btIdentifier;
  1432.                   }
  1433.                   else
  1434.                   {
  1435.                      btColor &=~ 0x70;
  1436.                      btColor |= (btIdentifier << 4);
  1437.                   }
  1438.                }
  1439.  
  1440.                else if(btIdentifier == 10)
  1441.                {
  1442.                   btColor |= 0x08;
  1443.                }
  1444.  
  1445.                else if(btIdentifier == 11)
  1446.                {
  1447.                   btColor |= 0x80;
  1448.                }
  1449.  
  1450.                break;
  1451.             }
  1452.  
  1453.          pszStart = (char *)pszEnd;
  1454.       }
  1455.    }
  1456.  
  1457.    pchColorEndPos = (char *)pszStart;
  1458.  
  1459.    OD_API_EXIT();
  1460.  
  1461.    return(btColor);
  1462. }
  1463.  
  1464.  
  1465. /* ----------------------------------------------------------------------------
  1466.  * ODPagePrompt()
  1467.  *
  1468.  * Called to display the page prompt at the end of a screen of text. This page
  1469.  * prompt allows the user to stop further display, to display the next page,
  1470.  * or to display in continuous (non-stop) mode with page pausing disabled.
  1471.  *
  1472.  * Parameters: pbPausing   - Pointer to current page pausing enabled flag.
  1473.  *
  1474.  *     Return: FALSE if display should be continued, or TRUE to abort display.
  1475.  */
  1476. BOOL ODPagePrompt(BOOL *pbPausing)
  1477. {
  1478.    INT nPromptLength = strlen(od_control.od_continue);
  1479.    tODScrnTextInfo TextInfo;
  1480.    BOOL bToReturn = FALSE;
  1481.    char chKeyPressed;
  1482.    BYTE btCount;
  1483.  
  1484.    /* Return right away if page pausing is disabled. */
  1485.    if(!*pbPausing) return(FALSE);
  1486.  
  1487.    /* Get current text color. */
  1488.    ODScrnGetTextInfo(&TextInfo);
  1489.  
  1490.    /* Set to prompt color. */
  1491.    od_set_attrib(od_control.od_continue_col);
  1492.  
  1493.    /* Display page prompt string. */
  1494.    od_disp_str(od_control.od_continue);
  1495.  
  1496.    /* Restore original text color. */
  1497.    od_set_attrib(TextInfo.attribute);
  1498.  
  1499.    /* Loop until the user makes a valid choice. */
  1500.    for(;;)
  1501.    {
  1502.       /* Obtain the next key from the user. */
  1503.       chKeyPressed = od_get_key(TRUE);
  1504.  
  1505.       /* If user chooses to continue. */
  1506.       if(chKeyPressed == tolower(od_control.od_continue_yes) ||
  1507.          chKeyPressed == toupper(od_control.od_continue_yes) ||
  1508.          chKeyPressed == 13 ||
  1509.          chKeyPressed == ' ')
  1510.       {
  1511.          /* Remove the prompt and return. */
  1512.          goto finished_pausing;
  1513.       }
  1514.  
  1515.       /* If user requested nonstop display. */
  1516.       else if(chKeyPressed == tolower(od_control.od_continue_nonstop) ||
  1517.               chKeyPressed == toupper(od_control.od_continue_nonstop))
  1518.       {
  1519.          /* Disable page pausing. */
  1520.          *pbPausing = FALSE;
  1521.  
  1522.          /* Remove the prompt and return. */
  1523.          goto finished_pausing;
  1524.       }
  1525.  
  1526.       /* If user chooses to stop display. */
  1527.       else if(chKeyPressed == tolower(od_control.od_continue_no) ||
  1528.               chKeyPressed == toupper(od_control.od_continue_no) ||
  1529.               chKeyPressed == 's' || chKeyPressed == 'S' || chKeyPressed == 3
  1530.               || chKeyPressed == 11 || chKeyPressed == 0x18)
  1531.       {
  1532.          /* If we are operating in remote mode. */
  1533.          if(od_control.baud)
  1534.          {
  1535.             /* Clear the output buffer. */
  1536.             ODComClearOutbound(hSerialPort);
  1537.          }
  1538.  
  1539.          /* Tell the caller to stop displaying more text. */
  1540.          bToReturn = TRUE;
  1541.  
  1542.          /* Remove the prompt and return. */
  1543.          goto finished_pausing;
  1544.       }
  1545.    }
  1546.  
  1547. finished_pausing:
  1548.    /* Remove the pause prompt. */
  1549.    for(btCount = 0; btCount < nPromptLength; ++btCount)
  1550.    {
  1551.       od_disp_str(szBackspaceWithDelete);
  1552.    }
  1553.  
  1554.    return(bToReturn);
  1555. }
  1556.  
  1557.  
  1558. /* ----------------------------------------------------------------------------
  1559.  * od_control_get()
  1560.  *
  1561.  * Returns a pointer to the od_control structure containing information
  1562.  * and settings associated with the current session.
  1563.  *
  1564.  * Parameters: None.
  1565.  *
  1566.  *     Return: A pointer to the od_control structure associated with this
  1567.  *             session.
  1568.  */
  1569. ODAPIDEF tODControl * ODCALL od_control_get(void)
  1570. {
  1571.    /* Log function entry if running in trace mode */
  1572.    TRACE(TRACE_API, "od_disp_str()");
  1573.  
  1574.    return(&od_control);
  1575. }
  1576.