//============================================================================
// Copyright (c) 1996, Microsoft Corporation
//
// File:    script.c
//
// History:
//  Abolade-Gbadegesin  03-29-96    Created.
//
// This file contains functions implementing the NT port
// of Win9x dial-up scripting, listed in alphabetical order.
//
// See scriptp.h for details on the NT implementation.
//============================================================================


#include <scriptp.h>
#include <lmwksta.h>        // For NetWkstaUserGetInfo
#include <lmapibuf.h>       // For NetApiBufferFree


//
// Handle of module-instance for this DLL
//
HANDLE              g_hinst;
//
// global critical section used to synhronize access to IP address strings
//
CRITICAL_SECTION    g_cs;
//
// name of file to which script syntax errors are logged
//
CHAR                c_szScriptLog[] = RASSCRIPT_LOG;
//
// event handle which would be notified in case of IPAddress Change
//
HANDLE                          hIpAddressSet = INVALID_HANDLE_VALUE;

#define NET_SVCS_GROUP      "-k netsvcs"

//----------------------------------------------------------------------------
// Function:    DLLMAIN
//
// DLL entry-point for RASSCRIPT
//----------------------------------------------------------------------------

BOOL
WINAPI
RasScriptDllMain(
    IN      HINSTANCE   hinstance,
    IN      DWORD       dwReason,
    IN      PVOID       pUnused
    ) {

    BOOL bRetVal = TRUE;

    if (dwReason == DLL_PROCESS_ATTACH) {

        g_hinst = (HANDLE)hinstance;

        try 
        {
            InitializeCriticalSection(&g_cs);
        }
        except (EXCEPTION_EXECUTE_HANDLER) 
        {
            bRetVal = FALSE;
        }
    }
    else
    if (dwReason == DLL_PROCESS_DETACH) {

        DeleteCriticalSection(&g_cs);
    }

    return bRetVal;
}




//----------------------------------------------------------------------------
// Function:    RasScriptExecute
//
// Examines the given connection, and if there is a script for the connection,
// executes the script to completion.
// Returns the error code from script processing if a script is given,
// and returns NO_ERROR otherwise.
//----------------------------------------------------------------------------

DWORD
APIENTRY
RasScriptExecute(
    IN      HRASCONN        hrasconn,
    IN      PBENTRY*        pEntry,
    IN      CHAR*           pszUserName,
    IN      CHAR*           pszPassword,
    OUT     CHAR*           pszIpAddress
    ) {


    DWORD dwErr;
    HANDLE hevent = NULL, hscript = NULL;
    HANDLE hEvents[2];

    RASSCRPT_TRACE("RasScriptExecute");

    do {
        //
        // create event on which to receive notification
        //

        hevent = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!hevent) {
            RASSCRPT_TRACE1("error %d creating event", dwErr = GetLastError());
            break;
        }


                // Create a separate event for SCRIPTCODE_IpAddressSet
                // event. We hit a timing window ow where we lose this
                // event (when we get a script complete event immediately
                // after a SCRIPTCODE_IpAddressSet event. bug 75226.
                hIpAddressSet = CreateEvent (NULL, FALSE, FALSE, NULL);

                if (!hIpAddressSet) {

                        RASSCRPT_TRACE1("error %d creating event", dwErr = GetLastError());
                        break;

                }


        //
        // initialize script processing
        //

        dwErr = RasScriptInit(
                    hrasconn, pEntry, pszUserName, pszPassword, 0, hevent,
                    &hscript
                    );

        if (dwErr != NO_ERROR) {
            RASSCRPT_TRACE1("error %d initializing scripting", dwErr);
            break;
        }


        hEvents[0] = hevent;
        hEvents[1] = hIpAddressSet;

        //
        // loop waiting for script to finish running
        //

        for ( ; ; ) {

            dwErr = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);

            if (dwErr - WAIT_OBJECT_0 == 0) {

                //
                // Retrieve the code for the event which occurred
                //

                DWORD dwCode = RasScriptGetEventCode(hscript);

                RASSCRPT_TRACE1("RasScriptExecute: eventcode %d", dwCode);


                //
                // Handle the event
                //

                if (dwCode == SCRIPTCODE_Done ||
                    dwCode == SCRIPTCODE_Halted ||
                    dwCode == SCRIPTCODE_HaltedOnError) {

                    RASSCRPT_TRACE("script processing completed");

                    dwErr = NO_ERROR;

                    break;
                }
            }

            else
            if (dwErr - WAIT_OBJECT_0 == 1) {

                    //
                    // The IP address has been changed;
                    // read the new IP address into the caller's buffer
                    //

                    RASSCRPT_TRACE("IP address changed");

                    dwErr = RasScriptGetIpAddress(hscript, pszIpAddress);
                    RASSCRPT_TRACE2("RasScriptGetIpAddress(e=%d,a=%s)",dwErr,pszIpAddress);
            }
        }

    } while(FALSE);


    if (hscript) { RasScriptTerm(hscript); }

    if (hevent) { CloseHandle(hevent); }

    if (hIpAddressSet) { CloseHandle (hIpAddressSet); }

    return dwErr;
}





//----------------------------------------------------------------------------
// Function:    RasScriptGetEventCode
//
// This function should be called to retrieve the event-code
// when the scripting thread signals an event.
// The event codes which may be returned are as follows:
//
//  NO_ERROR:                   no code has been set
//  SCRIPTCODE_Done:            the script has finished running;
//                              the thread blocks until RasScriptTerm is called.
//  SCRIPTCODE_InputNotify:     data is available in the buffer; if the buffer
//                              is full, the thread blocks until
//                              RasScriptReceive is called and the data
//                              is read successfully.
//  SCRIPTCODE_KeyboardEnable:  the keyboard should be enabled.
//  SCRIPTCODE_KeyboardDisable: the keyboard should be disabled.
//  SCRIPTCODE_IpAddressSet:    the IP address has changed; the new address
//                              can be retrieved via RasScriptGetIPAddress.
//  SCRIPTCODE_HaltedOnError:   the script has halted due to an error.
//----------------------------------------------------------------------------

DWORD
RasScriptGetEventCode(
    IN      HANDLE      hscript
    ) {

    SCRIPTCB* pscript = (SCRIPTCB *)hscript;

    RASSCRPT_TRACE("RasGetEventCode");

    if (!pscript) { return ERROR_INVALID_PARAMETER; }

    return pscript->dwEventCode;
}




//----------------------------------------------------------------------------
// Function:    RasScriptGetIpAddress
//
// This function retrieves the current IP address as set by the script.
//----------------------------------------------------------------------------

DWORD
RasScriptGetIpAddress(
    IN      HANDLE      hscript,
    OUT     CHAR*       pszIpAddress
    ) {

    SCRIPTCB* pscript = (SCRIPTCB *)hscript;

    RASSCRPT_TRACE("RasGetIpAddress");

    if (!pscript || !pszIpAddress) { return ERROR_INVALID_PARAMETER; }


    //
    // Access to the IP address string must be synchronized
    // since it may also be accessed via RxSetIPAddress
    //

    EnterCriticalSection(&g_cs);

    if (pscript->pszIpAddress) {

        lstrcpy(pszIpAddress, pscript->pszIpAddress);
    }
    else {

        lstrcpy(pszIpAddress, "0.0.0.0");
    }

    LeaveCriticalSection(&g_cs);

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// Function:    RasScriptInit
//
// Initializes for script processing on the given HRASCONN.
//
// This function creates a thread which handles script input and output
// on the given connection's port.
//
// If there is no script for the connection, this function returns an error
// unless the flag RASSCRIPT_NotifyOnInput is specified, in which case
// the thread loops posting receive-data requests on the connection's port
// until RasScriptTerm is called.
//
// If there is a script for the connection, the thread runs the script
// to completion. If the flag RASSCRIPT_NotifyOnInput is specified,
// the caller is notified when data is received on the port. The caller
// can then retrieve the data by calling RasScriptReceive.
//
// Notification may be event-based or message-based. By default, notification
// is event-based, and "Hnotifier" is treated as an event-handle.
// The event is signalled to by the scripting thread, and the caller retrieves
// the event code by calling RasScriptGetEventCode.
//
// Setting the flag RASSCRIPT_HwndNotify selects message-based notification,
// and indicates that "Hnotifier" is an HWND. The WM_RASSCRIPT event is sent
// to the window by the scripting thread, and "LParam" in the message sent
// contains the event code. See RasScriptGetEventCode for descriptions
// of the codes sent by the scripting thread.
//----------------------------------------------------------------------------

DWORD
APIENTRY
RasScriptInit(
    IN      HRASCONN        hrasconn,
    IN      PBENTRY*        pEntry,
    IN      CHAR*           pszUserName,
    IN      CHAR*           pszPassword,
    IN      DWORD           dwFlags,
    IN      HANDLE          hNotifier,
    OUT     HANDLE*         phscript
    ) {

    DWORD dwErr, dwSyntaxError = NO_ERROR;
    static const CHAR szSwitch[] = MXS_SWITCH_TXT;
    SCRIPTCB* pscript = NULL;
#ifdef UNICODEUI
//
// Define structures to use depending on whether or not the RAS UI
// is being built with Unicode.
//
#define PUISTR  CHAR*
#define PUIRCS  RASCONNSTATUSA*
#define PUIRC   RASCREDENTIALSA*
    RASCONNSTATUSW rcs;
    WCHAR* pszSwitch = StrDupWFromA(MXS_SWITCH_TXT);
#else
#define PUISTR  CHAR*
#define PUIRCS  RASCONNSTATUSA*
#define PUIRC   RASCREDENTIALSA*
    RASCONNSTATUSA rcs;
    CHAR* pszSwitch = szSwitch;
#endif

    RASSCRPT_TRACE_INIT("RASSCRPT");
    
    RASSCRPT_TRACE("RasScriptInit");


    //
    // validate arguments
    //

    if (phscript) { *phscript = NULL; }

    if (!hrasconn ||
        !pEntry ||
        !pszUserName ||
        !pszPassword ||
        !hNotifier ||
        !phscript) {

        RASSCRPT_TRACE("RasScriptInit: required parameter not specified");

#ifdef UNICODEUI
        Free(pszSwitch);
#endif
        return ERROR_INVALID_PARAMETER;
    }


    //
    // initialize script processing
    //

    do {

        DWORD dwsize;
        DWORD dwthread;
        HANDLE hthread;


        //
        // Load required DLL function pointers.
        //
        dwErr = LoadRasapi32Dll();
        if (dwErr)
            break;
        dwErr = LoadRasmanDll();
        if (dwErr)
            break;
        //
        // Initialize RAS
        //
        dwErr = g_pRasInitialize();

        if ( dwErr )
            break;

        /*
        //
        // Connect to the local rasman server
        //
        dwErr = g_pRasRpcConnect ( NULL, NULL );

        if (dwErr)
            break; */

        //
        // allocate space for a control block
        //

        pscript = Malloc(sizeof(*pscript));

        if (!pscript) {
            dwErr = GetLastError();
            RASSCRPT_TRACE2("error %d allocating %d bytes", dwErr, sizeof(*pscript));
            break;
        }


        //
        // initialize the control block
        //

        ZeroMemory(pscript, sizeof(*pscript));


        //
        // copy the argument fields
        //

        pscript->hrasconn = hrasconn;
        pscript->pEntry = pEntry;
        pscript->dwFlags = dwFlags;
        pscript->hNotifier = hNotifier;
        pscript->hport = g_pRasGetHport(hrasconn);

        if (pscript->pEntry->pszIpAddress) {

            //
            // Copy the IP address for the entry
            //

            pscript->pszIpAddress =
                    Malloc(lstrlenUI(pscript->pEntry->pszIpAddress) + 1);

            if (pscript->pszIpAddress) {

                StrCpyAFromUI(
                    pscript->pszIpAddress, pscript->pEntry->pszIpAddress
                    );
            }
            else {

                RASSCRPT_TRACE("error copying entry's IP address");

                dwErr = ERROR_NOT_ENOUGH_MEMORY;

                break;
            }
        }


        //
        // Initialize our Win9x-compatible session-config-info structure
        //

        ZeroMemory(&pscript->sci, sizeof(pscript->sci));

        pscript->sci.dwSize = sizeof(pscript->sci);
        StrCpyAFromUI(pscript->sci.szEntryName, pEntry->pszEntryName);
        lstrcpy(pscript->sci.szUserName, pszUserName);
        lstrcpy(pscript->sci.szPassword, pszPassword);


        //
        // See if the user name is missing;
        // if so, read the currently-logged on user's name
        //

        if (!pscript->sci.szUserName[0]) {

            WKSTA_USER_INFO_1* pwkui1 = NULL;

            //
            // Not all params were specified, so read the dial-params
            // for this phonebook entry
            //

            dwErr = NetWkstaUserGetInfo(NULL, 1, (LPBYTE*)&pwkui1);
            RASSCRPT_TRACE2("NetWkstaUserGetInfo(e=%d,u=(%ls))", dwErr,
                   (pwkui1) ? pwkui1->wkui1_username : L"null");

            if (dwErr == NO_ERROR && pwkui1 != NULL) {

                StrCpyAFromUI(pscript->sci.szUserName,
                    (LPCWSTR)pwkui1->wkui1_username);

                NetApiBufferFree(pwkui1);
            }
        }



        //
        // See if there is a script for this connection's state;
        // if there is one then the device-type will be "switch"
        // and the device-name will be the script path
        //

        ZeroMemory(&rcs, sizeof(rcs));

        rcs.dwSize = sizeof(rcs);

        dwErr = g_pRasGetConnectStatus(hrasconn, (PUIRCS)&rcs);

        if (dwErr != NO_ERROR) {
            RASSCRPT_TRACE1("error %d getting connect status", dwErr);
            break;
        }



        //
        // Check the device-type (will be "switch" for scripted entries)
        // and the device name (will be a filename for scripted entries)
        //

        if (lstrcmpiUI(rcs.szDeviceType, pszSwitch) == 0 &&
            GetFileAttributesUI(rcs.szDeviceName) != 0xFFFFFFFF) {

            CHAR szDevice[RAS_MaxDeviceName + 1], *pszDevice = szDevice;

            StrCpyAFromUI(szDevice, rcs.szDeviceName);


            //
            // The device-type is "Switch" and the device-name
            // contains the name of an existing file;
            // initialize the SCRIPTDATA structure.
            //

            dwErr = RsInitData(pscript, pszDevice);


            //
            // If there was a syntax error in the script, we continue
            // with the initialization, but record the error code.
            // on any other error, we immediately terminate initialization.
            //

            if (dwErr == ERROR_SCRIPT_SYNTAX) {
                dwSyntaxError = dwErr;
            }
            else
            if (dwErr != NO_ERROR) { break; }
        }



        //
        // Initialize RASMAN fields, allocating buffers for RASMAN I/O
        //

        dwsize = SIZE_RecvBuffer;
        dwErr = g_pRasGetBuffer(&pscript->pRecvBuffer, &dwsize);
        RASSCRPT_TRACE2("RasGetBuffer:e=%d,s=%d", dwErr, dwsize);

        if (dwErr != NO_ERROR) {
            RASSCRPT_TRACE1("error %d allocating receive-buffer", dwErr);
            break;
        }

        dwsize = SIZE_SendBuffer;
        dwErr = g_pRasGetBuffer(&pscript->pSendBuffer, &dwsize);
        RASSCRPT_TRACE2("RasGetBuffer:e=%d,s=%d", dwErr, dwsize);

        if (dwErr != NO_ERROR) {
            RASSCRPT_TRACE1("error %d alloacting send-buffer", dwErr);
            break;
        }



        //
        // Create synchronization events used to control the background thread
        //

        pscript->hRecvRequest = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!pscript->hRecvRequest) {
            RASSCRPT_TRACE1("error %d creating receive-event", dwErr = GetLastError());
            break;
        }

        pscript->hRecvComplete = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!pscript->hRecvComplete) {
            RASSCRPT_TRACE1("error %d creating received-event", dwErr = GetLastError());
            break;
        }

        pscript->hStopRequest = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!pscript->hStopRequest) {
            RASSCRPT_TRACE1("error %d creating stop-event", dwErr = GetLastError());
            break;
        }

        pscript->hStopComplete = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (!pscript->hStopComplete) {
            RASSCRPT_TRACE1("error %d creating stopped-event", dwErr = GetLastError());
            break;
        }



        //
        // Create the thread which will receive data and process the script
        //

        hthread = CreateThread(
                    NULL, 0, RsThread, (PVOID)pscript, 0, &dwthread
                    );

        if (!hthread) {
            RASSCRPT_TRACE1("error %d creating script-thread", dwErr = GetLastError());
            break;
        }

        CloseHandle(hthread);

        pscript->dwFlags |= RASSCRIPT_ThreadCreated;


        if ((VOID*)pszSwitch != (VOID*)szSwitch) { Free0(pszSwitch); }


        //
        // we've successfully initialized, return control to caller
        //

        *phscript = (HANDLE)pscript;


        //
        // if there was a syntax error in the script, return the special
        // error code (ERROR_SCRIPT_SYNTAX) to indicate the problem;
        // otherwise return  NO_ERROR.
        //

        return (dwSyntaxError ? dwSyntaxError : NO_ERROR);

    } while(FALSE);


    //
    // an error occurred, so do cleanup
    //

    if ((VOID*)pszSwitch != (VOID*)szSwitch) { Free0(pszSwitch); }

    RasScriptTerm((HANDLE)pscript);

    return (dwErr ? dwErr : ERROR_UNKNOWN);
}




//----------------------------------------------------------------------------
// Function:    RasScriptReceive
//
// Called to retrieve the contents of the scripting thread's input buffer.
// When this function completes successfully, if the input buffer was full
// and the scripting thread was blocked, the thread continues executing.
//
// On input, "PdwBufferSize" should contain the size of "PBuffer", unless
// "PBuffer" is NULL, in which case "*PdwBufferSize" is treated as 0.
// On output, "PdwBufferSize" contains the size required to read
// the input buffer, and if the return value is NO_ERROR, then "PBuffer"
// contains the data in the input buffer. If the return value is
// ERROR_INSUFFICIENT_BUFFER, "PBuffer" was not large enough.
//----------------------------------------------------------------------------

DWORD
APIENTRY
RasScriptReceive(
    IN      HANDLE      hscript,
    IN      BYTE*       pBuffer,
    IN OUT  DWORD*      pdwBufferSize
    ) {

    SCRIPTCB* pscript = (SCRIPTCB *)hscript;

    RASSCRPT_TRACE("RasScriptReceive");

    //
    // return if the caller didn't request input-notification
    // or if no buffer-size is available
    //

    if (!pscript || !pdwBufferSize ||
        !(pscript->dwFlags & RASSCRIPT_NotifyOnInput)) {
        return ERROR_INVALID_PARAMETER;
    }


    //
    // return if no buffer or if buffer too small
    //

    if (!pBuffer || *pdwBufferSize < pscript->dwRecvSize) {
        *pdwBufferSize = pscript->dwRecvSize;
        return ERROR_INSUFFICIENT_BUFFER;
    }


    //
    // copy the data, and notify the thread that the data has been read
    //

    CopyMemory(pBuffer, pscript->pRecvBuffer, pscript->dwRecvSize);

    *pdwBufferSize = pscript->dwRecvSize;

    SetEvent(pscript->hRecvComplete);

    return NO_ERROR;
}




//----------------------------------------------------------------------------
// Function:    RasScriptSend
//
// This function transmits bytes over the connection's port.
//
// "DwBufferSize" contains the number of bytes to insert from "PBuffer"
//----------------------------------------------------------------------------

DWORD
APIENTRY
RasScriptSend(
    IN      HANDLE      hscript,
    IN      BYTE*       pBuffer,
    IN      DWORD       dwBufferSize
    ) {

    DWORD dwsize;
    DWORD dwErr;
    SCRIPTCB *pscript = (SCRIPTCB *)hscript;

    RASSCRPT_TRACE("RasScriptSend");


    if (!pscript || !pBuffer || !dwBufferSize) {
        return ERROR_INVALID_PARAMETER;
    }


    //
    // send all the data in the buffer
    //

    for (dwsize = min(dwBufferSize, SIZE_SendBuffer);
         dwBufferSize;
         dwBufferSize -= dwsize, pBuffer += dwsize,
         dwsize = min(dwBufferSize, SIZE_SendBuffer)) {

        CopyMemory(pscript->pSendBuffer, pBuffer, dwsize);

        dwErr = g_pRasPortSend(
                    pscript->hport, pscript->pSendBuffer, dwsize
                    );
        RASSCRPT_TRACE1("g_pRasPortSend=%d", dwErr);
        DUMPB(pBuffer, dwsize);
    }


    return NO_ERROR;
}



//----------------------------------------------------------------------------
// Function:    RasScriptTerm
//
// This function terminates script processing, stopping the scripting thread.
// The return code is the code from processing the script, and it may be
//
//  NO_ERROR:           the script had finished running, or the connection
//                      had no script and the scripting thread was acting
//                      in simple I/O mode.
//  ERROR_MORE_DATA:    the script was still running.
//----------------------------------------------------------------------------

DWORD
APIENTRY
RasScriptTerm(
    IN      HANDLE      hscript
    ) {

    SCRIPTCB* pscript = hscript;

    RASSCRPT_TRACE("RasScriptTerm");

    if (!pscript) { return ERROR_INVALID_PARAMETER; }


    //
    // stop the thread if it is running
    //

    if (pscript->dwFlags & RASSCRIPT_ThreadCreated) {

        SetEvent(pscript->hStopRequest);

        WaitForSingleObject(pscript->hStopComplete, INFINITE);
    }

    if (pscript->pdata) { RsDestroyData(pscript); }

    if (pscript->hStopRequest) { CloseHandle(pscript->hStopRequest); }

    if (pscript->hStopComplete) { CloseHandle(pscript->hStopComplete); }

    if (pscript->hRecvRequest) { CloseHandle(pscript->hRecvRequest); }

    if (pscript->hRecvComplete) { CloseHandle(pscript->hRecvComplete); }


    if (pscript->pRecvBuffer) { g_pRasFreeBuffer(pscript->pRecvBuffer); }

    if (pscript->pSendBuffer) { g_pRasFreeBuffer(pscript->pSendBuffer); }

    Free0(pscript->pszIpAddress);

    Free(pscript);

    RASSCRPT_TRACE_TERM();

    return NO_ERROR;
}




//----------------------------------------------------------------------------
// Function:    RsDestroyData
//
// This function destroys the SCRIPTDATA portion of a SCRIPTCB.
//----------------------------------------------------------------------------

DWORD
RsDestroyData(
    IN      SCRIPTCB*   pscript
    ) {

    SCRIPTDATA* pdata = pscript->pdata;

    if (!pdata) { return ERROR_INVALID_PARAMETER; }

    if (pdata->pmoduledecl) { Decl_Delete((PDECL)pdata->pmoduledecl); }

    if (pdata->pastexec) {
        Astexec_Destroy(pdata->pastexec); Free(pdata->pastexec);
    }

    if (pdata->pscanner) { Scanner_Destroy(pdata->pscanner); }

    return NO_ERROR;
}




//----------------------------------------------------------------------------
// Function:    RsInitData
//
// This function initializes the SCRIPTDATA portion of a SCRIPTCB,
// preparing for script-processing.
//----------------------------------------------------------------------------

DWORD
RsInitData(
    IN      SCRIPTCB*   pscript,
    IN      LPCSTR      pszScriptPath
    ) {

    RES res;
    DWORD dwErr = ERROR_SUCCESS;
    SCRIPTDATA *pdata;

    RASSCRPT_TRACE("RsInitData");

    do {

        //
        // allocate space for the SCRIPTDATA;
        //

        pscript->pdata = pdata = Malloc(sizeof(*pdata));

        if (!pdata) {
            RASSCRPT_TRACE1("error %d allocating SCRIPTDATA", dwErr = GetLastError());
            break;
        }


        //
        // initialize the structure
        //

        ZeroMemory(pdata, sizeof(*pdata));

        pdata->hscript = (HANDLE)pscript;
        lstrcpy(pdata->script.szPath, pszScriptPath);


        //
        // create a scanner and use it to open the script
        //

        res = Scanner_Create(&pdata->pscanner, &pscript->sci);

        if (RFAILED(res)) {
            RASSCRPT_TRACE1("failure %d creating scanner", res);
            break;
        }

        res = Scanner_OpenScript(pdata->pscanner, pszScriptPath);

        if (res == RES_E_FAIL || RFAILED(res)) {
            RASSCRPT_TRACE1("failure %d opening script", res);
            break;
        }


        //
        // allocate a script-execution handler
        //

        pdata->pastexec = Malloc(sizeof(*pdata->pastexec));

        if (!pdata->pastexec) {
            RASSCRPT_TRACE1("error %d allocating ASTEXEC", dwErr = GetLastError());
            break;
        }

        ZeroMemory(pdata->pastexec, sizeof(*pdata->pastexec));


        //
        // initialize the script-execution handler
        //

        res = Astexec_Init(
                pdata->pastexec, pscript, &pscript->sci,
                Scanner_GetStxerrHandle(pdata->pscanner)
                );

        if (!RSUCCEEDED(res)) {
            RASSCRPT_TRACE1("failure %d initializing ASTEXEC", res);
            break;
        }

        Astexec_SetHwnd(pdata->pastexec, (HWND)pdata);


        //
        // parse the script using the created scanner
        // and writing into the execution-handler's symbol-table
        //

        res = ModuleDecl_Parse(
                &pdata->pmoduledecl, pdata->pscanner,
                pdata->pastexec->pstSystem
                );

        if (RSUCCEEDED(res)) {

            //
            // generate code for the script
            //

            res = ModuleDecl_Codegen(pdata->pmoduledecl, pdata->pastexec);
        }


        //
        // see if anything went wrong
        //

        if (RFAILED(res)) {

            //
            // there was an error parsing the script.
            // we return the special error code ERROR_SCRIPT_SYNTAX
            // and log the errors to a file.
            //
            // This is not necessarily a fatal error, and so returning
            // the above error doesn't cause script-initialization to fail,
            // since if the user is in interactive mode, the connection
            // may be completed manually by typing into the terminal window.
            //
            // If we are not in interactive mode, this is a fatal error,
            // and RasScriptExecute handles the condition correctly
            // by terminating the script immediately
            //

            RASSCRPT_TRACE1("failure %d parsing script", res);

            RxLogErrors(
                (HANDLE)pscript, (VOID*)Scanner_GetStxerrHandle(pdata->pscanner)
                );

            Decl_Delete((PDECL)pdata->pmoduledecl);
            Astexec_Destroy(pdata->pastexec); Free(pdata->pastexec);
            Scanner_Destroy(pdata->pscanner);

            pscript->pdata = NULL;

            dwErr = ERROR_SCRIPT_SYNTAX;

            return dwErr;
        }


        //
        // all went well, return
        //

        return NO_ERROR;

    } while(FALSE);


    //
    // an error occurred, so do cleanup
    //

    if (pscript->pdata) { RsDestroyData(pscript); }

    return (dwErr ? dwErr : ERROR_UNKNOWN);
}



//----------------------------------------------------------------------------
// Function:    RsPostReceive
//
// Internal function:
// posts receive-request to RASMAN
//----------------------------------------------------------------------------

DWORD
RsPostReceive(
    IN      SCRIPTCB*   pscript
    ) {

    DWORD dwSize;
    DWORD dwErr;

    RASSCRPT_TRACE("RsPostReceive");

    dwSize = SIZE_RecvBuffer;

    dwErr = g_pRasPortReceive(
                pscript->hport, pscript->pRecvBuffer, &dwSize, SECS_RecvTimeout,
                pscript->hRecvRequest
                );

    RASSCRPT_TRACE2("RsPostReceive=%d,%d", dwErr, dwSize);

    return dwErr;
}

BOOL
IsRasmanProcess()
{
    CHAR *pszCmdLine = NULL;
    BOOL fRet = FALSE;

    pszCmdLine = GetCommandLineA();

    if(     (NULL != pszCmdLine)
        &&  (strstr(pszCmdLine, NET_SVCS_GROUP)))
    {
        fRet = TRUE;
    }

    return fRet;    
}


DWORD
RsPostReceiveEx(
    IN SCRIPTCB* pscript
    ) {

    DWORD dwSize = 0;
    DWORD dwErr = ERROR_SUCCESS;

    RASSCRPT_TRACE("RsPostReceiveEx");

    if(IsRasmanProcess())
    {
        goto done;
    }

    RASSCRPT_TRACE("Calling RsPostReceiveEx");

    dwSize = SIZE_RecvBuffer;
    dwErr = g_pRasPortReceiveEx(
            pscript->hport,
            pscript->pRecvBuffer,
            &dwSize
            );

done:

    RASSCRPT_TRACE2("RsPostReceiveEx=%d, %d",dwErr, dwSize );

    return dwErr;

}




//----------------------------------------------------------------------------
// Function:    RsSignal
//
// Internal function:
// this is called to signal the notifier for a script, which may involve
// setting an event or sending a message.
//----------------------------------------------------------------------------

VOID
RsSignal(
    IN  SCRIPTCB*   pscript,
    IN  DWORD       dwEventCode
    ) {

    RASSCRPT_TRACE1("RsSignal: %d", dwEventCode);

    InterlockedExchange(&pscript->dwEventCode, dwEventCode);

    if (pscript->dwFlags & RASSCRIPT_HwndNotify) {

        SendNotifyMessage(
            (HWND)pscript->hNotifier, WM_RASAPICOMPLETE, 0, dwEventCode
            );
    }
    else {

        SetEvent(pscript->hNotifier);
    }
}




//----------------------------------------------------------------------------
// Function:    RsThread
//
// This function is the entry-point for the script processing thread.
//
// The scripting thread operates in a loop, posting receive requests
// and receiving incoming data. If a script is associated with the port,
// the thread also runs the script.
//----------------------------------------------------------------------------

DWORD
RsThread(
    IN      PVOID       pParam
    ) {

    WORD wSize;
#define POS_STOP    0
#define POS_RECV    1
#define POS_LAST    2
    BOOL bFirstRecv = TRUE;
    HANDLE hEvents[POS_LAST];
    SCRIPTCB* pscript = (SCRIPTCB *)pParam;
    SCRIPTDATA* pdata = pscript->pdata;
    DWORD dwErr, dwTicksBefore, dwTicksAfter, dwTicksElapsed;


    RASSCRPT_TRACE("RsThread");

    //
    // post receive-request to RASMAN
    //

    dwErr = RsPostReceive(pscript);
    if (dwErr != NO_ERROR && dwErr != PENDING) {

        RASSCRPT_TRACE1("error %d posting receive to RASMAN", dwErr);

        RsPostReceiveEx ( pscript );

        RsSignal(pscript, SCRIPTCODE_Halted);

        SetEvent(pscript->hStopComplete);

        return dwErr;
    }


    //
    // set up event array; we place the stop-request event first
    // in the array since the receive-event will be signalled more often
    // and placing it first might result in starvation
    // (waits are always satisfied by the first signalled object)
    //

    hEvents[POS_STOP] = pscript->hStopRequest;
    hEvents[POS_RECV] = pscript->hRecvRequest;

    if (pdata) { pdata->dwTimeout = INFINITE; }

    while (TRUE) {


        //
        // wait for receive to complete, for stop signal,
        // or for timeout to expire
        //
        // save the tick count so we can tell how long the wait lasted
        //

        dwTicksBefore = GetTickCount();

        dwErr = WaitForMultipleObjects(
                    POS_LAST, hEvents, FALSE, pdata ? pdata->dwTimeout:INFINITE
                    );

        dwTicksAfter = GetTickCount();


        //
        // see if the tick count wrapped around, and if so
        // adjust so we always get the correct elapsed time
        // from the expression (dwTicksAfter - dwTicksBefore)
        //

        if (dwTicksAfter < dwTicksBefore) {
            dwTicksAfter += MAXDWORD - dwTicksBefore;
            dwTicksBefore = 0;
        }

        dwTicksElapsed = dwTicksAfter - dwTicksBefore;

        RASSCRPT_TRACE1("RsThread: waited for %d milliseconds", dwTicksElapsed);


        //
        // if the timeout isn't INFINITE, decrement it by
        // the amount of time we've already waited
        //

        if (pdata && pdata->dwTimeout != INFINITE) {

            if (dwTicksElapsed >= pdata->dwTimeout) {
                pdata->dwTimeout = INFINITE;
            }
            else {
                pdata->dwTimeout -= dwTicksElapsed;
            }
        }


        //
        // Handle the return-code from WaitForMultipleObjects
        //

        if (dwErr == (WAIT_OBJECT_0 + POS_STOP)) {

            //
            // stop-request signalled, break
            //

            RASSCRPT_TRACE("RsThread: stop event signalled");

            RsSignal(pscript, SCRIPTCODE_Halted);

            break;
        }
        else
        if (dwErr == WAIT_TIMEOUT) {


            if (!pdata) { continue; }


            //
            // wait timed out, so that means we were blocked
            // on a "delay" or "waitfor ... until" statement;
            //

            Astexec_ClearPause(pdata->pastexec);


            //
            // if we blocked because of a "waitfor ... until",
            // finish processing the statement
            //

            if (Astexec_IsWaitUntil(pdata->pastexec)) {

                Astexec_SetStopWaiting(pdata->pastexec);

                Astexec_ClearWaitUntil(pdata->pastexec);
            }


            //
            // continue processing the script
            //

            if (RsThreadProcess(pscript) == ERROR_NO_MORE_ITEMS) {

                //
                // the script has stopped; if done, break;
                // otherwise, continue receiving data
                //

                if (pscript->dwEventCode == SCRIPTCODE_Done) {

                    break;
                }
                else {

                    //
                    // Cleanup the script, but continue receiving data
                    //

                    RsDestroyData(pscript);

                    pdata = pscript->pdata = NULL;
                }
            }
        }
        else
        if (dwErr == (WAIT_OBJECT_0 + POS_RECV)) {

            //
            // receive completed
            //

            RASMAN_INFO info;
            DWORD dwStart, dwRead;

            RASSCRPT_TRACE("RsThread: receive event signalled");


            //
            // Get the data received
            //
            dwErr = RsPostReceiveEx ( pscript );

            if (    NO_ERROR != dwErr
                &&  PENDING  != dwErr )
            {
                RASSCRPT_TRACE1("error %d in RsPostReceiveEx", dwErr);

                RsSignal(pscript, SCRIPTCODE_Halted );

                break;
            }

            //
            // get the number of bytes received
            //

            dwErr = g_pRasGetInfo(NULL, pscript->hport, &info);

            if (dwErr != NO_ERROR) {

                RASSCRPT_TRACE1("error %d retrieving RASMAN_INFO", dwErr);

                RsSignal(pscript, SCRIPTCODE_Halted);

                break;
            }

            if(  (info.RI_LastError != NO_ERROR)
              && (info.RI_ConnState != CONNECTING))
            {
                RASSCRPT_TRACE("Link dropped! port no longer in connecting state");

                RsSignal(pscript, SCRIPTCODE_Halted);

                break;
            }
            
            if (info.RI_LastError != NO_ERROR) {
                RASSCRPT_TRACE1("last error: %d", info.RI_LastError);
                continue;
            }

            RASSCRPT_TRACE1("RsThread: received %d bytes", info.RI_BytesReceived);


            //
            // on the first receive, we proceed even if there aren't any
            // characters read, since we need to run the first script commands
            //

            if (!bFirstRecv && info.RI_BytesReceived == 0) {

                //
                // something went wrong, post another receive request
                //

                dwErr = RsPostReceive(pscript);

                if (    dwErr != NO_ERROR
                    &&  dwErr != PENDING)
                {
                    RASSCRPT_TRACE1("error %d in RsPostReceive", dwErr);

                    RsSignal(pscript, SCRIPTCODE_Halted);

                    break;
                }

                continue;
            }

            bFirstRecv = FALSE;

            pscript->dwRecvSize = info.RI_BytesReceived;
            pscript->dwRecvRead = 0;

            DUMPB(pscript->pRecvBuffer, pscript->dwRecvSize);


            //
            // if the creator wants to know when data arrives,
            // signal the creator's notification now;
            // wait till the creator reads the data before proceeding
            //

            if (info.RI_BytesReceived &&
                (pscript->dwFlags & RASSCRIPT_NotifyOnInput)) {

                RsSignal(pscript, SCRIPTCODE_InputNotify);

                WaitForSingleObject(pscript->hRecvComplete, INFINITE);
            }


            //
            // if we have no script that's all we have to do,
            // so just post another receive request and go back to waiting
            //

            if (!pdata) {

                dwErr = RsPostReceive(pscript);

                if (    dwErr != NO_ERROR
                    &&  dwErr != PENDING )
                {
                    RASSCRPT_TRACE1("error %d in RsPostReceive",dwErr);

                    RsSignal(pscript, SCRIPTCODE_Halted);

                    break;
                }

                continue;
            }


            //
            // read the data into the script's circular buffer
            //

            ReadIntoBuffer(pdata, &dwStart, &dwRead);


            //
            // do more script processing
            //

            if (RsThreadProcess(pscript) == ERROR_NO_MORE_ITEMS) {

                //
                // the script has stopped; if done, break;
                // otherwise, continue receiving data
                //

                if (pscript->dwEventCode == SCRIPTCODE_Done) {

                    break;
                }
                else {

                    //
                    // Cleanup the script, but continue receiving data
                    //

                    RsDestroyData(pscript);

                    pdata = pscript->pdata = NULL;
                }
            }
        }
    }


    //
    // cancel any pending receives
    //

    g_pRasPortCancelReceive(pscript->hport);


    SetEvent(pscript->hStopComplete);

    RASSCRPT_TRACE("RsThread done");

    return NO_ERROR;
}



//----------------------------------------------------------------------------
// Function:    RsThreadProcess
//
// Called to process the script until it is blocked
// by a "waitfor" statement or a "delay" statement.
//----------------------------------------------------------------------------

DWORD
RsThreadProcess(
    IN      SCRIPTCB*   pscript
    ) {

    RES res;
    DWORD dwErr;
    SCRIPTDATA *pdata = pscript->pdata;

    RASSCRPT_TRACE("RsThreadProcess");


    //
    // now step through the script until we are blocked
    // by a "delay" statement or a "waitfor" statement
    //

    dwErr = NO_ERROR;

    do {

        //
        // break if its time to stop
        //

        if (WaitForSingleObject(pscript->hStopRequest, 0) == WAIT_OBJECT_0) {

            SetEvent(pscript->hStopRequest);

            break;
        }


        //
        // process next command
        //

        res = Astexec_Next(pdata->pastexec);

//        if (res != RES_OK) { break; }


        //
        // examine the resulting state
        //

        if (Astexec_IsDone(pdata->pastexec) ||
            Astexec_IsHalted(pdata->pastexec)) {

            //
            // the script has come to an end, so set our stop event
            // and break out of this loop
            //

            RASSCRPT_TRACE("RsThreadProcess: script completed");

            //
            // do stop-completion notification
            //

            if (Astexec_IsDone(pdata->pastexec)) {
                RsSignal(pscript, SCRIPTCODE_Done);
            }
            else
            if (!RFAILED(res)) {
                RsSignal(pscript, SCRIPTCODE_Halted);
            }
            else {
                RsSignal(pscript, SCRIPTCODE_HaltedOnError);
            }


            dwErr = ERROR_NO_MORE_ITEMS;

            break;
        }
        else
        if (Astexec_IsReadPending(pdata->pastexec)) {

            //
            // we're blocked waiting for input,
            // so post another receive request and go back
            // to waiting for data;
            // if we're blocked on a "waitfor ... until"
            // then the timeout will be in pdata->dwTimeout,
            // otherwise pdata->dwTimeout will be INFINITE
            // which is exactly how long we'll be waiting
            //

            RsPostReceive(pscript);

            RASSCRPT_TRACE("RsThreadProcess: script waiting for input");

            break;
        }
        else
        if (Astexec_IsPaused(pdata->pastexec)) {

            //
            // we're blocked with a timeout, so pick up
            // the timeout value from pdata->dwTimeout.
            // we don't want to listen for input
            // while we're blocked, so we don't post another receive-request
            //

            RASSCRPT_TRACE("RsThreadProcess: script paused");

            break;
        }

    } while (TRUE);

    return dwErr;
}




//----------------------------------------------------------------------------
// Function:    RxLogErrors
//
// Logs script syntax errors to a file named %windir%\system32\ras\script.log
//----------------------------------------------------------------------------

DWORD
RxLogErrors(
    IN      HANDLE      hscript,
    IN      HSA         hsaStxerr
    ) {

    HANDLE hfile;
    CHAR *pszPath;
    STXERR stxerr;
    SCRIPTDATA *pdata;
    SCRIPTCB *pscript = hscript;
    DWORD i, cel, dwErr, dwSize;

    RASSCRPT_TRACE("RxLogErrors");

    if (!pscript || !pscript->pdata) { return ERROR_INVALID_PARAMETER; }

    pdata = pscript->pdata;



    //
    // get the pathname for the logfile
    //

    dwSize = ExpandEnvironmentStrings(c_szScriptLog, NULL, 0);

    pszPath = Malloc((dwSize + 1) * sizeof(CHAR));
    if (!pszPath) { return ERROR_NOT_ENOUGH_MEMORY; }

    ExpandEnvironmentStrings(c_szScriptLog, pszPath, dwSize);


    //
    // create the file, overwriting it if it already exists
    //

    hfile = CreateFile(
                pszPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
                FILE_ATTRIBUTE_NORMAL, NULL
                );
    Free(pszPath);

    if (INVALID_HANDLE_VALUE == hfile) {
        dwErr = GetLastError();
        RASSCRPT_TRACE1("error %d creating logfile", dwErr);
        return dwErr;
    }


    //
    // truncate the previous contents of the file, if any
    //

    SetFilePointer(hfile, 0, 0, FILE_BEGIN);
    SetEndOfFile(hfile);


    //
    // get the number of syntax errors
    //

    cel = SAGetCount(hsaStxerr);


    //
    // append each error to the file
    //

    for (i = 0; i < cel; i++) {

        UINT ids;
        CHAR* pszErr;
        BOOL bRet = SAGetItem(hsaStxerr, i, &stxerr);


        if (!bRet) { continue; }

        ids = IdsFromRes(Stxerr_GetRes(&stxerr));

        if (ids == 0) { continue; }


        //
        // format the error message
        //

        ConstructMessage(
            &pszErr, g_hinst, MAKEINTRESOURCE(ids), pdata->script.szPath,
            Stxerr_GetLine(&stxerr), Stxerr_GetLexeme(&stxerr)
            );

        if (!pszErr) { continue; }


        //
        // write the message to the log file
        //

        dwSize = lstrlen(pszErr);

        WriteFile(hfile, pszErr, lstrlen(pszErr), &dwSize, NULL);

        WriteFile(hfile, "\r\n", 2, &dwSize, NULL);


        //
        // free the message pointer
        //

        GFree(pszErr);
    }

    CloseHandle(hfile);

    return 0;
}



//----------------------------------------------------------------------------
// Function:    RxReadFile
//
// Transfers data out of a RASMAN buffer into the circular buffer used
// by the Win9x scripting code
//----------------------------------------------------------------------------

BOOL
RxReadFile(
    IN      HANDLE      hscript,
    IN      BYTE*       pBuffer,
    IN      DWORD       dwBufferSize,
    OUT     DWORD*      pdwBytesRead
    ) {

    SCRIPTCB* pscript = (SCRIPTCB*)hscript;
    DWORD dwRecvSize = pscript->dwRecvSize - pscript->dwRecvRead;

    RASSCRPT_TRACE("RxReadFile");

    if (!pdwBytesRead) { return FALSE; }

    *pdwBytesRead = 0;
    if ((INT)dwRecvSize <= 0) { return FALSE; }

    if (!dwBufferSize) { return FALSE; }

    *pdwBytesRead = min(dwBufferSize, dwRecvSize);
    CopyMemory(
        pBuffer, pscript->pRecvBuffer + pscript->dwRecvRead, *pdwBytesRead
        );
    pscript->dwRecvRead += *pdwBytesRead;

    RASSCRPT_TRACE2("RxReadFile(rr=%d,br=%d)",pscript->dwRecvRead,*pdwBytesRead);

    return TRUE;
}



//----------------------------------------------------------------------------
// Function:    RxSetIPAddress
//
// Sets the IP address for the script's RAS entry
//----------------------------------------------------------------------------

DWORD
RxSetIPAddress(
    IN      HANDLE      hscript,
    IN      LPCSTR      lpszAddress
    ) {

    DWORD dwErr = NO_ERROR;
    SCRIPTCB *pscript = (SCRIPTCB *)hscript;

    RASSCRPT_TRACE1("RxSetIPAddress: %s", lpszAddress);


    EnterCriticalSection(&g_cs);


    //
    // Free the existing IP address, if any
    //

    Free0(pscript->pszIpAddress);


    //
    // Allocate space for a copy of the address
    //

    pscript->pszIpAddress = Malloc(lstrlen(lpszAddress) + 1);

    if (!pscript->pszIpAddress) { dwErr = ERROR_NOT_ENOUGH_MEMORY; }
    else {

        //
        // Copy the new IP address
        //

        lstrcpy(pscript->pszIpAddress, lpszAddress);
    }

    LeaveCriticalSection(&g_cs);



    //
    // If successful, signal the caller that the IP address has changed
    //

    if (dwErr != NO_ERROR) {
        RASSCRPT_TRACE1("error %d writing phonebook file", dwErr);
    }
    else {


                if (    INVALID_HANDLE_VALUE != hIpAddressSet
                        &&      !(pscript->dwFlags  & RASSCRIPT_HwndNotify))
                {
                DWORD dwEventCode = SCRIPTCODE_IpAddressSet;
                        RASSCRPT_TRACE1("RxSetIPAddress: %d", dwEventCode);

                        InterlockedExchange(&pscript->dwEventCode, dwEventCode);
                        SetEvent (hIpAddressSet);

                }

                else if (pscript->dwFlags & RASSCRIPT_HwndNotify)
                RsSignal(pscript, SCRIPTCODE_IpAddressSet);
    }

    return dwErr;
}



//----------------------------------------------------------------------------
// Function:    RxSetKeyboard
//
// Signals the script-owner to enable or disable keyboard input.
//----------------------------------------------------------------------------

DWORD
RxSetKeyboard(
    IN      HANDLE      hscript,
    IN      BOOL        bEnable
    ) {

    RASSCRPT_TRACE("RxSetKeyboard");

    RsSignal(
        (SCRIPTCB *)hscript,
        bEnable ? SCRIPTCODE_KeyboardEnable : SCRIPTCODE_KeyboardDisable
        );

    return NO_ERROR;
}

//----------------------------------------------------------------------------
// Function:    RxSendCreds
//
// Sends users password over the wire.
//----------------------------------------------------------------------------
DWORD
RxSendCreds(
    IN HANDLE hscript,
    IN CHAR controlchar
    ) {

    SCRIPTCB *pscript = (SCRIPTCB *) hscript;
    DWORD dwErr;

    RASSCRPT_TRACE("RasSendCreds");

    dwErr = RasSendCreds(pscript->hport, controlchar);

    RASSCRPT_TRACE1("RasSendCreds done. 0x%x", dwErr);

    return (dwErr == NO_ERROR) ? RES_OK : RES_E_FAIL;
    
}


//----------------------------------------------------------------------------
// Function:    RxSetPortData
//
// Changes settings for the COM port.
//----------------------------------------------------------------------------

DWORD
RxSetPortData(
    IN      HANDLE      hscript,
    IN      VOID*       pStatement
    ) {

    RES res;
    STMT* pstmt;
    RAS_PARAMS* pparam;
    DWORD dwErr, dwFlags;
    RASMAN_PORTINFO *prmpi;
    SCRIPTCB *pscript = (SCRIPTCB *)hscript;
    BYTE aBuffer[sizeof(RASMAN_PORTINFO) + sizeof(RAS_PARAMS) * 2];

    RASSCRPT_TRACE("RxSetPortData");


    //
    // Retrieve the 'set port' statement
    //

    pstmt = (STMT*)pStatement;

    dwFlags = SetPortStmt_GetFlags(pstmt);


    //
    // Set up the RASMAN_PORTINFO to be passed to RasPortSetInfo
    //

    prmpi = (RASMAN_PORTINFO*)aBuffer;

    prmpi->PI_NumOfParams = 0;

    pparam = prmpi->PI_Params;


    //
    // Collect the changes into the port-info structure
    //

    if (IsFlagSet(dwFlags, SPF_DATABITS)) {

        lstrcpyA(pparam->P_Key, SER_DATABITS_KEY);

        pparam->P_Type = Number;

        pparam->P_Attributes = 0;

        pparam->P_Value.Number = SetPortStmt_GetDatabits(pstmt);

        RASSCRPT_TRACE1("GetDatabits==%d", pparam->P_Value.Number);

        ++prmpi->PI_NumOfParams;

        ++pparam;
    }


    if (IsFlagSet(dwFlags, SPF_STOPBITS)) {

        lstrcpyA(pparam->P_Key, SER_STOPBITS_KEY);

        pparam->P_Type = Number;

        pparam->P_Attributes = 0;

        pparam->P_Value.Number = SetPortStmt_GetStopbits(pstmt);


        //
        // The only 'stopbits' settings supported are 1 and 2;
        // in order to set stopbits of 1, we need to pass 0
        // to RasPortSetInfo, so the value is adjusted here.
        //

        if (pparam->P_Value.Number == 1) { --pparam->P_Value.Number; }

        RASSCRPT_TRACE1("GetStopbits==%d", pparam->P_Value.Number);

        ++prmpi->PI_NumOfParams;

        ++pparam;
    }

    if (IsFlagSet(dwFlags, SPF_PARITY)) {

        lstrcpyA(pparam->P_Key, SER_PARITY_KEY);

        pparam->P_Type = Number;

        pparam->P_Attributes = 0;

        pparam->P_Value.Number = SetPortStmt_GetParity(pstmt);

        RASSCRPT_TRACE1("GetParity==%d", pparam->P_Value.Number);

        ++prmpi->PI_NumOfParams;

        ++pparam;
    }


    //
    // Send the changes down to RASMAN
    //

    if (!prmpi->PI_NumOfParams) { dwErr = NO_ERROR; }
    else {

        dwErr = g_pRasPortSetInfo(pscript->hport, prmpi);

        RASSCRPT_TRACE1("g_pRasPortSetInfo==%d", dwErr);

        if (dwErr != NO_ERROR) {

            Stxerr_Add(
                pscript->pdata->pastexec->hsaStxerr, "set port",
                Ast_GetLine(pstmt), RES_E_FAIL
                );
        }
    }

    return (dwErr == NO_ERROR) ? RES_OK : RES_E_FAIL;
}



//----------------------------------------------------------------------------
// Function:    RxWriteFile
//
// Transmits the given buffer thru RASMAN on a port
//----------------------------------------------------------------------------

VOID
RxWriteFile(
    IN      HANDLE      hscript,
    IN      BYTE*       pBuffer,
    IN      DWORD       dwBufferSize,
    OUT     DWORD*      pdwBytesWritten
    ) {

    RASSCRPT_TRACE("RxWriteFile");

    if (!pdwBytesWritten) { return; }

    RasScriptSend(hscript, pBuffer, dwBufferSize);

    *pdwBytesWritten = dwBufferSize;

    RASSCRPT_TRACE1("RxWriteFile(bw=%d)", *pdwBytesWritten);
}