Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2710 lines
71 KiB

  1. //THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. //ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. //THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright 1993-1995 Microsoft Corporation. All Rights Reserved.
  7. //
  8. // MODULE: terminal.c
  9. //
  10. // PURPOSE: Terminal screen simulation
  11. //
  12. // PLATFORMS: Windows 95
  13. //
  14. // FUNCTIONS:
  15. // TransferData()
  16. // TerminalDlgWndProc()
  17. // TerminalScreenWndProc()
  18. // OnCommand()
  19. // InitTerminalDlg()
  20. // TerminateTerminal()
  21. // GetInput()
  22. // SendByte()
  23. // AdjustTerminal()
  24. // UpdateTerminalCaption()
  25. // TerminalThread()
  26. //
  27. // SPECIAL INSTRUCTIONS: N/A
  28. //
  29. #include "proj.h" // includes common header files and global declarations
  30. #include "rcids.h" // includes the resource definitions
  31. #ifndef WINNT_RAS
  32. //
  33. // See scrpthlp.h for information on why this has been commented out.
  34. //
  35. #include "scrpthlp.h"// include context-sensitive help
  36. #endif // WINNT_RAS
  37. //****************************************************************************
  38. // Constants Declaration
  39. //****************************************************************************
  40. #define MAXTITLE 32
  41. #define MAXMESSAGE 256
  42. #define WM_MODEMNOTIFY (WM_USER + 998)
  43. #define WM_EOLFROMDEVICE (WM_USER + 999)
  44. #define WM_PROCESSSCRIPT (WM_USER + 1000)
  45. #ifndef WINNT_RAS
  46. //
  47. // The definitions below are overriden in nthdr2.h, and have been removed here
  48. // to avoid multiple definitions
  49. //
  50. #define SIZE_ReceiveBuf 1024
  51. #define SIZE_SendBuf 1
  52. #endif // !WINNT_RAS
  53. #define Y_MARGIN 4
  54. #define Y_SMALL_MARGIN 2
  55. #define X_SPACING 2
  56. #define MIN_X 170
  57. #define MIN_Y 80
  58. #define TERMINAL_BK_COLOR (RGB( 0, 0, 0 ))
  59. #define TERMINAL_FR_COLOR (RGB( 255, 255, 255 ))
  60. #define MAXTERMLINE 24
  61. #define READ_EVENT 0
  62. #define STOP_EVENT 1
  63. #define MAX_EVENT 2
  64. #define SENDTIMEOUT 50
  65. #define CE_DELIM 256
  66. #define TRACE_MARK "->"
  67. #define TRACE_UNMARK " "
  68. #define INVALID_SCRIPT_LINE 0xFFFFFFFF
  69. #define PROMPT_AT_COMPLETION 1
  70. //****************************************************************************
  71. // Type Definitions
  72. //****************************************************************************
  73. typedef struct tagFINDFMT
  74. {
  75. LPSTR pszFindFmt; // Allocated: formatted string to find
  76. LPSTR pszBuf; // Optional pointer to buffer; may be NULL
  77. UINT cbBuf;
  78. DWORD dwFlags; // FFF_*
  79. } FINDFMT;
  80. DECLARE_STANDARD_TYPES(FINDFMT);
  81. typedef struct tagTERMDLG {
  82. HANDLE hport;
  83. HANDLE hThread;
  84. HANDLE hEvent[MAX_EVENT];
  85. HWND hwnd;
  86. PBYTE pbReceiveBuf; // circular buffer
  87. PBYTE pbSendBuf;
  88. UINT ibCurFind;
  89. UINT ibCurRead;
  90. UINT cbReceiveMax; // count of read bytes
  91. HBRUSH hbrushScreenBackgroundE;
  92. HBRUSH hbrushScreenBackgroundD;
  93. HFONT hfontTerminal;
  94. PSCANNER pscanner;
  95. PMODULEDECL pmoduledecl;
  96. ASTEXEC astexec;
  97. SCRIPT script;
  98. WNDPROC WndprocOldTerminalScreen;
  99. BOOL fInputEnabled;
  100. BOOL fStartRestored;
  101. BOOL rgbDelim[CE_DELIM];
  102. // The following fields are strictly for test screen
  103. //
  104. BOOL fContinue;
  105. HWND hwndDbg;
  106. DWORD iMarkLine;
  107. } TERMDLG, *PTERMDLG, FAR* LPTERMDLG;
  108. #define IS_TEST_SCRIPT(ptd) (ptd->script.uMode == TEST_MODE)
  109. //****************************************************************************
  110. // Function prototypes
  111. //****************************************************************************
  112. INT_PTR CALLBACK TerminalDlgWndProc(HWND hwnd,
  113. UINT wMsg,
  114. WPARAM wParam,
  115. LPARAM lParam );
  116. LRESULT FAR PASCAL TerminalScreenWndProc(HWND hwnd,
  117. UINT wMsg,
  118. WPARAM wParam,
  119. LPARAM lParam );
  120. BOOL NEAR PASCAL OnCommand (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  121. LRESULT NEAR PASCAL InitTerminalDlg(HWND hwnd);
  122. void NEAR PASCAL TerminateTerminal(HWND hwnd, UINT id);
  123. BOOL NEAR PASCAL GetInput (HWND hwnd);
  124. VOID NEAR PASCAL AdjustTerminal (HWND hwnd, int wWidth, int wHeight);
  125. void NEAR PASCAL UpdateTerminalCaption(PTERMDLG ptd, UINT ids);
  126. void WINAPI TerminalThread (PTERMDLG pTerminaldialog);
  127. void PRIVATE Terminal_NextCommand(PTERMDLG ptd, HWND hwnd);
  128. // The following functions are used by test screen only
  129. //
  130. BOOL NEAR PASCAL DisplayScript (PTERMDLG ptd);
  131. LRESULT FAR PASCAL DbgScriptDlgProc(HWND hwnd,
  132. UINT wMsg,
  133. WPARAM wParam,
  134. LPARAM lParam );
  135. BOOL NEAR PASCAL InitDebugWindow (HWND hwnd);
  136. void NEAR PASCAL TrackScriptLine(PTERMDLG ptd, DWORD iLine);
  137. /*----------------------------------------------------------------------------
  138. ** Terminal dialog routines
  139. **----------------------------------------------------------------------------
  140. */
  141. BOOL PUBLIC TransferData(
  142. HWND hwnd,
  143. HANDLE hComm,
  144. PSESS_CONFIGURATION_INFO psci)
  145. /* Executes the Terminal dialog including error handling. 'hwndOwner' is
  146. ** the handle of the parent window. 'hport' is the open RAS Manager port
  147. ** handle to talk on. 'msgidTitle' is the string ID for the Terminal window
  148. ** caption.
  149. **
  150. ** Returns true if successful, false otherwise.
  151. */
  152. {
  153. PTERMDLG ptd;
  154. COMMTIMEOUTS commtimeout;
  155. DWORD id;
  156. int i;
  157. int iRet;
  158. // Allocate the terminal buffer
  159. //
  160. if ((ptd = (PTERMDLG)LocalAlloc(LPTR, sizeof(*ptd)))
  161. == NULL)
  162. return FALSE;
  163. if ((ptd->pbReceiveBuf = (PBYTE)LocalAlloc(LPTR,
  164. SIZE_ReceiveBuf+ SIZE_SendBuf))
  165. == NULL)
  166. {
  167. LocalFree((HLOCAL)ptd);
  168. return FALSE;
  169. };
  170. ptd->pbSendBuf = ptd->pbReceiveBuf + SIZE_ReceiveBuf;
  171. ptd->ibCurFind = 0;
  172. ptd->ibCurRead = 0;
  173. ptd->cbReceiveMax = 0;
  174. ptd->fInputEnabled= FALSE;
  175. ptd->fStartRestored = FALSE;
  176. ptd->iMarkLine = 0;
  177. // Initialize the terminal buffer
  178. //
  179. ptd->hport = hComm;
  180. ptd->hbrushScreenBackgroundE = (HBRUSH)GetStockObject( BLACK_BRUSH );
  181. ptd->hbrushScreenBackgroundD = (HBRUSH)GetStockObject( WHITE_BRUSH );
  182. ptd->hfontTerminal = (HFONT)GetStockObject( SYSTEM_FIXED_FONT );
  183. // Create the scanner
  184. if (RFAILED(Scanner_Create(&ptd->pscanner, psci)))
  185. {
  186. LocalFree((HLOCAL)ptd->pbReceiveBuf);
  187. LocalFree((HLOCAL)ptd);
  188. return FALSE;
  189. };
  190. // Is there a script for this connection?
  191. if (GetScriptInfo(psci->szEntryName, &ptd->script))
  192. {
  193. // Yes; open the script file for scanning
  194. RES res = Scanner_OpenScript(ptd->pscanner, ptd->script.szPath);
  195. if (RES_E_FAIL == res)
  196. {
  197. MsgBox(g_hinst,
  198. hwnd,
  199. MAKEINTRESOURCE(IDS_ERR_ScriptNotFound),
  200. MAKEINTRESOURCE(IDS_CAP_Script),
  201. NULL,
  202. MB_WARNING,
  203. ptd->script.szPath);
  204. ptd->fInputEnabled= TRUE;
  205. *ptd->script.szPath = '\0';
  206. ptd->script.uMode = NORMAL_MODE;
  207. }
  208. else if (RFAILED(res))
  209. {
  210. Scanner_Destroy(ptd->pscanner);
  211. //
  212. // .Net bug# 522307 Specifying the dialup script file as the COM
  213. // Port of the Modem will cause explorer to AV.
  214. //
  215. ptd->pscanner = NULL;
  216. LocalFree((HLOCAL)ptd->pbReceiveBuf);
  217. LocalFree((HLOCAL)ptd);
  218. return FALSE;
  219. }
  220. else
  221. {
  222. res = Astexec_Init(&ptd->astexec, hComm, psci,
  223. Scanner_GetStxerrHandle(ptd->pscanner));
  224. if (RSUCCEEDED(res))
  225. {
  226. // Parse the script
  227. res = ModuleDecl_Parse(&ptd->pmoduledecl, ptd->pscanner, ptd->astexec.pstSystem);
  228. if (RSUCCEEDED(res))
  229. {
  230. res = ModuleDecl_Codegen(ptd->pmoduledecl, &ptd->astexec);
  231. }
  232. if (RFAILED(res))
  233. {
  234. Stxerr_ShowErrors(Scanner_GetStxerrHandle(ptd->pscanner), hwnd);
  235. }
  236. }
  237. }
  238. }
  239. else
  240. {
  241. ptd->fInputEnabled= TRUE;
  242. ptd->script.uMode = NORMAL_MODE;
  243. ptd->fStartRestored = TRUE;
  244. };
  245. // Set comm timeout
  246. //
  247. commtimeout.ReadIntervalTimeout = MAXDWORD;
  248. commtimeout.ReadTotalTimeoutMultiplier = 0;
  249. commtimeout.ReadTotalTimeoutConstant = 0;
  250. commtimeout.WriteTotalTimeoutMultiplier= SENDTIMEOUT;
  251. commtimeout.WriteTotalTimeoutConstant = 0;
  252. SetCommTimeouts(hComm, &commtimeout);
  253. // Start receiving from the port
  254. //
  255. SetCommMask(hComm, EV_RXCHAR);
  256. // Create read thread and the synchronization objects
  257. for (i = 0; i < MAX_EVENT; i++)
  258. {
  259. ptd->hEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
  260. };
  261. ptd->hThread = CreateThread(NULL, 0,
  262. (LPTHREAD_START_ROUTINE) TerminalThread,
  263. ptd, 0, &id);
  264. // Create the terminal window
  265. #ifdef MODAL_DIALOG
  266. iRet = DialogBoxParam(g_hinst,
  267. MAKEINTRESOURCE(IDD_TERMINALDLG),
  268. hwnd,
  269. TerminalDlgWndProc,
  270. (LPARAM)(LPTERMDLG)ptd);
  271. #else
  272. if (CreateDialogParam(g_hinst,
  273. MAKEINTRESOURCE(IDD_TERMINALDLG),
  274. hwnd,
  275. TerminalDlgWndProc,
  276. (LPARAM)(LPTERMDLG)ptd))
  277. {
  278. MSG msg;
  279. while(GetMessage(&msg, NULL, 0, 0))
  280. {
  281. if ((!IsDialogMessage(ptd->hwnd, &msg)) &&
  282. ((ptd->hwndDbg == NULL) || !IsDialogMessage(ptd->hwndDbg, &msg)))
  283. {
  284. TranslateMessage(&msg); /* Translates virtual key codes */
  285. DispatchMessage(&msg); /* Dispatches message to window */
  286. };
  287. };
  288. iRet = (int)msg.wParam;
  289. DestroyWindow(ptd->hwnd);
  290. }
  291. else
  292. {
  293. iRet = IDCANCEL;
  294. };
  295. #endif // MODAL_DIALOG
  296. // The terminal dialog was terminated, free resources
  297. //
  298. SetEvent(ptd->hEvent[STOP_EVENT]);
  299. SetCommMask(hComm, 0);
  300. DEBUG_MSG (TF_ALWAYS, "Set stop event and cleared comm mask.");
  301. WaitForSingleObject(ptd->hThread, INFINITE);
  302. DEBUG_MSG (TF_ALWAYS, "Read thread was terminated.");
  303. for (i = 0; i < MAX_EVENT; i++)
  304. {
  305. CloseHandle(ptd->hEvent[i]);
  306. };
  307. CloseHandle(ptd->hThread);
  308. Decl_Delete((PDECL)ptd->pmoduledecl);
  309. Astexec_Destroy(&ptd->astexec);
  310. Scanner_Destroy(ptd->pscanner);
  311. //
  312. // .Net bug# 522307 Specifying the dialup script file as the COM
  313. // Port of the Modem will cause explorer to AV.
  314. //
  315. ptd->pscanner = NULL;
  316. LocalFree((HLOCAL)ptd->pbReceiveBuf);
  317. LocalFree((HLOCAL)ptd);
  318. return (iRet == IDOK);
  319. }
  320. /*----------------------------------------------------------------------------
  321. ** Terminal Window Procedure
  322. **----------------------------------------------------------------------------
  323. */
  324. void PRIVATE PostProcessScript(
  325. HWND hwnd)
  326. {
  327. MSG msg;
  328. if (!PeekMessage(&msg, hwnd, WM_PROCESSSCRIPT, WM_PROCESSSCRIPT,
  329. PM_NOREMOVE))
  330. {
  331. PostMessage(hwnd, WM_PROCESSSCRIPT, 0, 0);
  332. }
  333. }
  334. /*----------------------------------------------------------
  335. Purpose: Execute next command in the script
  336. Returns: --
  337. Cond: --
  338. */
  339. void PRIVATE Terminal_NextCommand(
  340. PTERMDLG ptd,
  341. HWND hwnd)
  342. {
  343. if (RES_OK == Astexec_Next(&ptd->astexec))
  344. {
  345. if (!Astexec_IsReadPending(&ptd->astexec) &&
  346. !Astexec_IsPaused(&ptd->astexec))
  347. {
  348. HWND hwndNotify;
  349. if (IS_TEST_SCRIPT(ptd))
  350. {
  351. // Do not start processing the next one yet
  352. //
  353. ptd->fContinue = FALSE;
  354. hwndNotify = ptd->hwndDbg;
  355. }
  356. else
  357. {
  358. hwndNotify = hwnd;
  359. };
  360. // Process the next command
  361. //
  362. PostProcessScript(hwndNotify);
  363. };
  364. };
  365. };
  366. INT_PTR CALLBACK TerminalDlgWndProc(HWND hwnd,
  367. UINT wMsg,
  368. WPARAM wParam,
  369. LPARAM lParam )
  370. {
  371. PTERMDLG ptd;
  372. switch (wMsg)
  373. {
  374. case WM_INITDIALOG:
  375. ptd = (PTERMDLG)lParam;
  376. DEBUG_MSG (TF_ALWAYS, "ptd = %x", ptd);
  377. SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
  378. ptd->hwnd = hwnd;
  379. Astexec_SetHwnd(&ptd->astexec, hwnd);
  380. return (InitTerminalDlg(hwnd));
  381. case WM_CTLCOLOREDIT:
  382. // Adjust the screen window only
  383. //
  384. if ((HWND)lParam == GetDlgItem(hwnd, CID_T_EB_SCREEN))
  385. {
  386. HBRUSH hBrush;
  387. COLORREF crColorBk, crColorTxt;
  388. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  389. if (ptd->fInputEnabled)
  390. {
  391. hBrush = ptd->hbrushScreenBackgroundE;
  392. crColorBk = TERMINAL_BK_COLOR;
  393. crColorTxt = TERMINAL_FR_COLOR;
  394. }
  395. else
  396. {
  397. hBrush = ptd->hbrushScreenBackgroundD;
  398. crColorBk = TERMINAL_FR_COLOR;
  399. crColorTxt = TERMINAL_BK_COLOR;
  400. };
  401. /* Set terminal screen colors to TTY-ish green on black.
  402. */
  403. if (hBrush)
  404. {
  405. SetBkColor( (HDC)wParam, crColorBk );
  406. SetTextColor((HDC)wParam, crColorTxt );
  407. return (LRESULT)hBrush;
  408. }
  409. };
  410. break;
  411. case WM_MODEMNOTIFY:
  412. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  413. TRACE_MSG(TF_BUFFER, "Received WM_MODEMNOTIFY");
  414. GetInput(hwnd);
  415. // Kick the script processing
  416. //
  417. PostProcessScript(hwnd);
  418. return TRUE;
  419. case WM_PROCESSSCRIPT:
  420. {
  421. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  422. TRACE_MSG(TF_BUFFER, "Received WM_PROCESSSCRIPT");
  423. if (!ptd->fContinue)
  424. {
  425. // We are not allowed to process a new command yet
  426. //
  427. return TRUE;
  428. };
  429. Terminal_NextCommand(ptd, hwnd);
  430. // If we are done or halt, show the status
  431. //
  432. if (Astexec_IsDone(&ptd->astexec) ||
  433. Astexec_IsHalted(&ptd->astexec))
  434. {
  435. BOOL bHalted = Astexec_IsHalted(&ptd->astexec);
  436. // Update the title
  437. //
  438. UpdateTerminalCaption(ptd, bHalted ? IDS_HALT : IDS_COMPLETE);
  439. // If the script completes successfully, continue the connection
  440. //
  441. if (!bHalted)
  442. {
  443. // Terminate the script successfully
  444. //
  445. TerminateTerminal(hwnd, IDOK);
  446. }
  447. else
  448. {
  449. // We are halted, need the user's attention.
  450. //
  451. if (IsIconic(hwnd))
  452. {
  453. ShowWindow(hwnd, SW_RESTORE);
  454. };
  455. SetForegroundWindow(hwnd);
  456. };
  457. };
  458. return TRUE;
  459. }
  460. case WM_TIMER: {
  461. HWND hwndNotify;
  462. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  463. TRACE_MSG(TF_GENERAL, "Killing timer");
  464. Astexec_ClearPause(&ptd->astexec);
  465. KillTimer(hwnd, TIMER_DELAY);
  466. // Did we time out on a 'wait..until' statement?
  467. if (Astexec_IsWaitUntil(&ptd->astexec))
  468. {
  469. // Yes; we need to finish processing the 'wait' statement
  470. // before we step to the next command
  471. Astexec_SetStopWaiting(&ptd->astexec);
  472. Astexec_ClearWaitUntil(&ptd->astexec);
  473. hwndNotify = hwnd;
  474. ASSERT(TRUE == ptd->fContinue);
  475. }
  476. else
  477. {
  478. if (IS_TEST_SCRIPT(ptd))
  479. {
  480. // Do not start processing the next one yet
  481. //
  482. ptd->fContinue = FALSE;
  483. hwndNotify = ptd->hwndDbg;
  484. }
  485. else
  486. {
  487. hwndNotify = hwnd;
  488. ASSERT(TRUE == ptd->fContinue);
  489. }
  490. }
  491. PostProcessScript(hwndNotify);
  492. }
  493. return TRUE;
  494. case WM_COMMAND:
  495. // Handle the control activities
  496. //
  497. return OnCommand(hwnd, wMsg, wParam, lParam);
  498. case WM_DESTROY:
  499. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  500. SetWindowLongPtr( GetDlgItem(hwnd, CID_T_EB_SCREEN), GWLP_WNDPROC,
  501. (ULONG_PTR)ptd->WndprocOldTerminalScreen );
  502. break;
  503. case WM_SIZE:
  504. AdjustTerminal(hwnd, (int)LOWORD(lParam), (int)HIWORD(lParam));
  505. break;
  506. case WM_GETMINMAXINFO:
  507. {
  508. MINMAXINFO FAR* lpMinMaxInfo = (MINMAXINFO FAR*)lParam;
  509. DWORD dwUnit = GetDialogBaseUnits();
  510. lpMinMaxInfo->ptMinTrackSize.x = (MIN_X*LOWORD(dwUnit))/4;
  511. lpMinMaxInfo->ptMinTrackSize.y = (MIN_Y*LOWORD(dwUnit))/4;
  512. break;
  513. };
  514. case WM_HELP:
  515. case WM_CONTEXTMENU:
  516. ContextHelp(gaTerminal, wMsg, wParam, lParam);
  517. break;
  518. };
  519. return 0;
  520. }
  521. /*----------------------------------------------------------------------------
  522. ** Terminal Screen Subclasses Window Procedure
  523. **----------------------------------------------------------------------------
  524. */
  525. LRESULT FAR PASCAL TerminalScreenWndProc(HWND hwnd,
  526. UINT wMsg,
  527. WPARAM wParam,
  528. LPARAM lParam )
  529. {
  530. HWND hwndParent;
  531. PTERMDLG pTerminaldialog;
  532. hwndParent = GetParent(hwnd);
  533. pTerminaldialog = (PTERMDLG)GetWindowLongPtr(hwndParent, DWLP_USER);
  534. if (wMsg == WM_EOLFROMDEVICE)
  535. {
  536. /* Remove the first line if the next line exceeds the maximum line
  537. */
  538. if (SendMessage(hwnd, EM_GETLINECOUNT, 0, 0L) == MAXTERMLINE)
  539. {
  540. SendMessage(hwnd, EM_SETSEL, 0,
  541. SendMessage(hwnd, EM_LINEINDEX, 1, 0L));
  542. SendMessage(hwnd, EM_REPLACESEL, 0, (LPARAM)(LPSTR)"");
  543. SendMessage(hwnd, EM_SETSEL, 32767, 32767);
  544. SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
  545. };
  546. /* An end-of-line in the device input was received. Send a linefeed
  547. ** character to the window.
  548. */
  549. wParam = '\n';
  550. wMsg = WM_CHAR;
  551. }
  552. else
  553. {
  554. BOOL fCtrlKeyDown = (GetKeyState( VK_CONTROL ) < 0);
  555. BOOL fShiftKeyDown = (GetKeyState( VK_SHIFT ) < 0);
  556. if (wMsg == WM_KEYDOWN)
  557. {
  558. /* The key was pressed by the user.
  559. */
  560. if (wParam == VK_RETURN && !fCtrlKeyDown && !fShiftKeyDown)
  561. {
  562. /* Enter key pressed without Shift or Ctrl is discarded. This
  563. ** prevents Enter from being interpreted as "press default
  564. ** button" when pressed in the edit box.
  565. */
  566. return 0;
  567. }
  568. if (fCtrlKeyDown && wParam == VK_TAB)
  569. {
  570. /* Ctrl+Tab pressed. Send a tab character to the device.
  571. ** Pass tab thru to let the edit box handle the visuals.
  572. ** Ctrl+Tab doesn't generate a WM_CHAR.
  573. */
  574. if (pTerminaldialog->fInputEnabled)
  575. {
  576. SendByte(hwndParent, (BYTE)VK_TAB);
  577. };
  578. }
  579. if (GetKeyState( VK_MENU ) < 0)
  580. {
  581. return (CallWindowProc(pTerminaldialog->WndprocOldTerminalScreen, hwnd, wMsg, wParam, lParam ));
  582. };
  583. }
  584. else if (wMsg == WM_CHAR)
  585. {
  586. /* The character was typed by the user.
  587. */
  588. if (wParam == VK_TAB)
  589. {
  590. /* Ignore tabs...Windows sends this message when Tab (leave
  591. ** field) is pressed but not when Ctrl+Tab (insert a TAB
  592. ** character) is pressed...weird.
  593. */
  594. return 0;
  595. }
  596. if (pTerminaldialog->fInputEnabled)
  597. {
  598. SendByte(hwndParent, (BYTE)wParam);
  599. };
  600. return 0;
  601. }
  602. }
  603. /* Call the previous window procedure for everything else.
  604. */
  605. return (CallWindowProc(pTerminaldialog->WndprocOldTerminalScreen, hwnd, wMsg, wParam, lParam ));
  606. }
  607. /*----------------------------------------------------------------------------
  608. ** Terminal Window's Control Handler
  609. **----------------------------------------------------------------------------
  610. */
  611. BOOL NEAR PASCAL OnCommand (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  612. {
  613. switch (LOWORD(wParam))
  614. {
  615. case CID_T_EB_SCREEN:
  616. {
  617. switch (HIWORD(wParam))
  618. {
  619. case EN_SETFOCUS:
  620. {
  621. /* Turn off the default button whenever the terminal
  622. ** window has the focus. Pressing [Return] in the
  623. ** terminal acts like a normal terminal.
  624. */
  625. SendDlgItemMessage(hwnd, CID_T_PB_ENTER, BM_SETSTYLE,
  626. (WPARAM)BS_DEFPUSHBUTTON, TRUE);
  627. /* Don't select the entire string on entry.
  628. */
  629. SendDlgItemMessage(hwnd, CID_T_EB_SCREEN, EM_SETSEL,
  630. 32767, 32767);
  631. SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
  632. break;
  633. };
  634. };
  635. break;
  636. };
  637. case CID_T_CB_INPUT:
  638. {
  639. PTERMDLG ptd;
  640. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  641. ptd->fInputEnabled = IsDlgButtonChecked(hwnd, CID_T_CB_INPUT);
  642. InvalidateRect(hwnd, NULL, FALSE);
  643. SetFocus(GetDlgItem(hwnd, CID_T_EB_SCREEN));
  644. break;
  645. }
  646. case IDOK:
  647. case IDCANCEL:
  648. TerminateTerminal(hwnd, LOWORD(wParam));
  649. break;
  650. };
  651. return 0;
  652. }
  653. /*----------------------------------------------------------------------------
  654. ** Initialize Terminal window
  655. **----------------------------------------------------------------------------
  656. */
  657. LRESULT NEAR PASCAL InitTerminalDlg(HWND hwnd)
  658. {
  659. HWND hwndScrn;
  660. RECT rect;
  661. PTERMDLG ptd;
  662. WINDOWPLACEMENT wp;
  663. BOOL fRet;
  664. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  665. // Install subclassed WndProcs.
  666. //
  667. hwndScrn = GetDlgItem(hwnd, CID_T_EB_SCREEN);
  668. ptd->WndprocOldTerminalScreen =
  669. (WNDPROC)SetWindowLongPtr( hwndScrn, GWLP_WNDPROC,
  670. (ULONG_PTR)TerminalScreenWndProc );
  671. // Set the terminal screen font
  672. //
  673. SendMessage(hwndScrn, WM_SETFONT, (WPARAM)ptd->hfontTerminal,
  674. 0L);
  675. // Get the recorded window placement
  676. //
  677. if ((fRet = GetSetTerminalPlacement(ptd->pscanner->psci->szEntryName,
  678. &wp, TRUE)) &&
  679. (wp.length >= sizeof(wp)))
  680. {
  681. // We have one, set it
  682. //
  683. SetWindowPlacement(hwnd, &wp);
  684. }
  685. else
  686. {
  687. // If nothing was specified at all, default to minimized
  688. // otherwise use the state set by scripter
  689. //
  690. if (!fRet)
  691. {
  692. wp.showCmd = SW_SHOWMINNOACTIVE;
  693. };
  694. // Start with minimized window
  695. //
  696. ShowWindow(hwnd, wp.showCmd);
  697. };
  698. // Adjust the dimension
  699. //
  700. GetClientRect(hwnd, &rect);
  701. AdjustTerminal(hwnd, rect.right-rect.left, rect.bottom-rect.top);
  702. // Adjust window activation
  703. //
  704. if (!IsIconic(hwnd))
  705. {
  706. SetForegroundWindow(hwnd);
  707. }
  708. else
  709. {
  710. CheckDlgButton(hwnd, CID_T_CB_MIN, BST_CHECKED);
  711. // if we are in debug mode, just bring it up
  712. //
  713. if (IS_TEST_SCRIPT(ptd) || ptd->fStartRestored)
  714. {
  715. ShowWindow(hwnd, SW_NORMAL);
  716. SetForegroundWindow(hwnd);
  717. };
  718. };
  719. // Initialize the input enable
  720. //
  721. CheckDlgButton(hwnd, CID_T_CB_INPUT,
  722. ptd->fInputEnabled ? BST_CHECKED : BST_UNCHECKED);
  723. // Set the window icon
  724. //
  725. SendMessage(hwnd, WM_SETICON, TRUE,
  726. (LPARAM)LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SCRIPT)));
  727. // Set the input focus to the screen
  728. //
  729. UpdateTerminalCaption(ptd, IDS_RUN);
  730. // Display the script window
  731. //
  732. if (IS_TEST_SCRIPT(ptd))
  733. {
  734. // Do not start until the debug window says so
  735. //
  736. ptd->fContinue = FALSE;
  737. // Start the debug window
  738. //
  739. if (!DisplayScript(ptd))
  740. {
  741. // Cannot start the debug window, switch to normal mode
  742. //
  743. ptd->fContinue = TRUE;
  744. ptd->script.uMode = NORMAL_MODE;
  745. };
  746. }
  747. else
  748. {
  749. // Start immediately
  750. //
  751. ptd->fContinue = TRUE;
  752. ptd->hwndDbg = NULL;
  753. };
  754. // Start receiving from the port
  755. //
  756. PostMessage(hwnd, WM_MODEMNOTIFY, 0, 0);
  757. return 0;
  758. }
  759. /*----------------------------------------------------------------------------
  760. ** Terminal window termination
  761. **----------------------------------------------------------------------------
  762. */
  763. void NEAR PASCAL TerminateTerminal(HWND hwnd, UINT id)
  764. {
  765. PTERMDLG ptd;
  766. WINDOWPLACEMENT wp;
  767. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  768. // Get the current window placement and record it
  769. //
  770. wp.length = sizeof(wp);
  771. if (GetWindowPlacement(hwnd, &wp))
  772. {
  773. // If user specifies start-minimized, remember it
  774. //
  775. if (IsDlgButtonChecked(hwnd, CID_T_CB_MIN) == BST_CHECKED)
  776. {
  777. wp.showCmd = SW_SHOWMINNOACTIVE;
  778. };
  779. // Recorded the window placement
  780. //
  781. GetSetTerminalPlacement(ptd->pscanner->psci->szEntryName, &wp, FALSE);
  782. };
  783. if (IS_TEST_SCRIPT(ptd))
  784. {
  785. // Destroy the script window here
  786. //
  787. DestroyWindow(ptd->hwndDbg);
  788. };
  789. // Terminate the window
  790. //
  791. #ifdef MODAL_DIALOG
  792. EndDialog(hwnd, id);
  793. #else
  794. PostQuitMessage(id);
  795. #endif // MODAL_DIALOG
  796. return;
  797. }
  798. /*----------------------------------------------------------------------------
  799. ** Terminal Input Handler
  800. **----------------------------------------------------------------------------
  801. */
  802. #ifdef DEBUG
  803. /*----------------------------------------------------------
  804. Purpose: Dumps the read buffer
  805. Returns:
  806. Cond: --
  807. */
  808. void PRIVATE DumpBuffer(
  809. PTERMDLG ptd)
  810. {
  811. #define IS_PRINTABLE(ch) InRange(ch, 32, 126)
  812. if (IsFlagSet(g_dwDumpFlags, DF_READBUFFER))
  813. {
  814. UINT ib;
  815. UINT cb;
  816. UINT cbMax = ptd->cbReceiveMax;
  817. char szBuf[SIZE_ReceiveBuf+1];
  818. LPSTR psz = szBuf;
  819. ASSERT(ptd->ibCurRead >= ptd->ibCurFind);
  820. ASSERT(SIZE_ReceiveBuf > ptd->ibCurFind);
  821. ASSERT(SIZE_ReceiveBuf > ptd->ibCurRead);
  822. ASSERT(SIZE_ReceiveBuf >= cbMax);
  823. *szBuf = 0;
  824. for (ib = ptd->ibCurFind, cb = 0;
  825. cb < cbMax;
  826. ib = (ib + 1) % SIZE_ReceiveBuf, cb++)
  827. {
  828. char ch = ptd->pbReceiveBuf[ib];
  829. if (IS_PRINTABLE(ch))
  830. *psz++ = ch;
  831. else
  832. *psz++ = '.';
  833. }
  834. *psz = 0; // add null terminator
  835. TRACE_MSG(TF_ALWAYS, "Read buffer: {%s}", (LPSTR)szBuf);
  836. }
  837. }
  838. #endif // DEBUG
  839. /*----------------------------------------------------------
  840. Purpose: Creates a find format handle. This function should
  841. be called to get a handle to use with FindFormat.
  842. Returns: RES_OK
  843. RES_E_OUTOFMEMORY
  844. Cond: --
  845. */
  846. RES PUBLIC CreateFindFormat(
  847. PHANDLE phFindFmt)
  848. {
  849. RES res = RES_OK;
  850. HSA hsa;
  851. ASSERT(phFindFmt);
  852. if ( !SACreate(&hsa, sizeof(FINDFMT), 8) )
  853. res = RES_E_OUTOFMEMORY;
  854. *phFindFmt = (HANDLE)hsa;
  855. return res;
  856. }
  857. /*----------------------------------------------------------
  858. Purpose: Adds a formatted search string to the list.
  859. Returns: RES_OK
  860. Cond: --
  861. */
  862. RES PUBLIC AddFindFormat(
  863. HANDLE hFindFmt,
  864. LPCSTR pszFindFmt,
  865. DWORD dwFlags, // FFF_*
  866. LPSTR pszBuf, // May be NULL
  867. DWORD cbBuf)
  868. {
  869. RES res = RES_OK;
  870. FINDFMT ff;
  871. HSA hsa = (HSA)hFindFmt;
  872. ZeroInit(&ff, FINDFMT);
  873. if (GSetString(&ff.pszFindFmt, pszFindFmt))
  874. {
  875. ff.pszBuf = pszBuf;
  876. ff.cbBuf = cbBuf;
  877. ff.dwFlags = dwFlags;
  878. if ( !SAInsertItem(hsa, SA_APPEND, &ff) )
  879. res = RES_E_OUTOFMEMORY;
  880. }
  881. else
  882. res = RES_E_OUTOFMEMORY;
  883. return res;
  884. }
  885. /*----------------------------------------------------------
  886. Purpose: Free a find format item
  887. Returns: --
  888. Cond: --
  889. */
  890. void CALLBACK FreeSAFindFormat(
  891. PVOID pv,
  892. LPARAM lParam)
  893. {
  894. PFINDFMT pff = (PFINDFMT)pv;
  895. if (pff->pszFindFmt)
  896. GSetString(&pff->pszFindFmt, NULL); // free
  897. }
  898. /*----------------------------------------------------------
  899. Purpose: Destroys a find format handle.
  900. Returns: RES_OK
  901. RES_E_INVALIDPARAM
  902. Cond: --
  903. */
  904. RES PUBLIC DestroyFindFormat(
  905. HANDLE hFindFmt)
  906. {
  907. RES res;
  908. HSA hsa = (HSA)hFindFmt;
  909. if (hsa)
  910. {
  911. SADestroyEx(hsa, FreeSAFindFormat, 0);
  912. res = RES_OK;
  913. }
  914. else
  915. res = RES_E_INVALIDPARAM;
  916. return res;
  917. }
  918. BOOL PRIVATE ChrCmp(WORD w1, WORD wMatch);
  919. BOOL PRIVATE ChrCmpI(WORD w1, WORD wMatch);
  920. /*----------------------------------------------------------
  921. Purpose: Compares the given character with the current format
  922. string. If the character matches the expected format,
  923. the function returns RES_OK.
  924. If the current character does not match the expected
  925. format, but the minimum sequence of characters for
  926. the format has been fulfilled, this function will
  927. increment *ppszFind to the next appropriate character
  928. or escape sequence, and check for the next expected
  929. format match.
  930. If the end of the format string is reached, RES_HALT
  931. is returned.
  932. Returns: RES_OK (if the character compares)
  933. RES_HALT (to stop immediately)
  934. RES_FALSE (if the character does not compare)
  935. Cond: --
  936. */
  937. RES PRIVATE CompareFormat(
  938. PFINDFMT pff,
  939. LPCSTR * ppszFind,
  940. char chRec)
  941. {
  942. RES res = RES_FALSE;
  943. LPCSTR pszFind = *ppszFind;
  944. LPCSTR pszNext;
  945. DWORD dwFlags = 0;
  946. char ch;
  947. char chNext;
  948. #define IS_ESCAPE(ch) ('%' == (ch))
  949. pszNext = MyNextChar(pszFind, &ch, &dwFlags);
  950. // Is this a DBCS trailing byte?
  951. if (IsFlagSet(dwFlags, MNC_ISTAILBYTE))
  952. {
  953. // Yes; handle this normally
  954. goto CompareNormal;
  955. }
  956. else
  957. {
  958. // No; check for special formatting characters first
  959. switch (ch)
  960. {
  961. case '%':
  962. chNext = *pszNext;
  963. if ('u' == chNext)
  964. {
  965. // Look for unsigned digits
  966. if (IS_DIGIT(chRec))
  967. {
  968. res = RES_OK;
  969. SetFlag(pff->dwFlags, FFF_MATCHEDONCE);
  970. }
  971. else
  972. {
  973. // Have we already found some digits?
  974. if (IsFlagSet(pff->dwFlags, FFF_MATCHEDONCE))
  975. {
  976. // Yes; then move on to the next thing to find
  977. ClearFlag(pff->dwFlags, FFF_MATCHEDONCE);
  978. pszNext = CharNext(pszNext);
  979. res = CompareFormat(pff, &pszNext, chRec);
  980. if (RES_FALSE != res)
  981. *ppszFind = pszNext;
  982. }
  983. else
  984. {
  985. // No
  986. res = RES_FALSE;
  987. }
  988. }
  989. }
  990. else if (IS_ESCAPE(chNext))
  991. {
  992. // Looking for a single '%'
  993. res = (chNext == chRec) ? RES_OK : RES_FALSE;
  994. if (RES_OK == res)
  995. *ppszFind = CharNext(pszNext);
  996. }
  997. else
  998. {
  999. goto CompareNormal;
  1000. }
  1001. break;
  1002. case 0: // null terminator
  1003. res = RES_HALT;
  1004. break;
  1005. default: {
  1006. BOOL bMatch;
  1007. CompareNormal:
  1008. // (The ChrCmp* functions return FALSE if they match)
  1009. if (IsFlagSet(pff->dwFlags, FFF_MATCHCASE))
  1010. bMatch = !ChrCmp(ch, chRec);
  1011. else
  1012. bMatch = !ChrCmpI(ch, chRec);
  1013. if (bMatch)
  1014. {
  1015. res = RES_OK;
  1016. *ppszFind = pszNext;
  1017. }
  1018. else
  1019. res = RES_FALSE;
  1020. }
  1021. break;
  1022. }
  1023. }
  1024. return res;
  1025. }
  1026. /*----------------------------------------------------------
  1027. Purpose: Scans for the specific find format string.
  1028. The function returns two indexes and the count of
  1029. matched bytes. Both indexes refer to the read
  1030. buffer. *pibMark indexes the first character
  1031. of a substring candidate. *pib indexes the
  1032. character that was compared last.
  1033. If the sequence of characters completely match the
  1034. requested string, the function returns RES_OK. The
  1035. matching sequence of characters is copied into
  1036. the collection buffer (if one is given).
  1037. If there is no complete match, the function returns
  1038. RES_FALSE. The caller should read in more data
  1039. before calling this function with the same requested
  1040. string.
  1041. If some trailing string in the read buffer matches
  1042. the requested string,
  1043. If the collection buffer becomes full before a
  1044. complete match is found, this function returns
  1045. RES_E_MOREDATA.
  1046. Returns: see above
  1047. Cond: --
  1048. */
  1049. RES PRIVATE ScanFormat(
  1050. PFINDFMT pff,
  1051. UINT ibCurFind,
  1052. LPCSTR pszRecBuf,
  1053. UINT cbRecMax,
  1054. LPUINT pibMark,
  1055. LPUINT pib,
  1056. LPINT pcbMatched)
  1057. {
  1058. // The trick is keeping track of when we've found a partial
  1059. // string across read boundaries. Consider the search string
  1060. // "ababc". We need to find it in the following cases (the
  1061. // character '|' indicates read boundary, '.' is an arbitrary
  1062. // byte):
  1063. //
  1064. // |...ababc..|
  1065. // |.abababc..|
  1066. // |......abab|c.........|
  1067. // |......abab|abc.......|
  1068. //
  1069. // This assumes the read buffer is larger than the search
  1070. // string. In order to do this, the read buffer must be
  1071. // a circular buffer, always retaining that portion of the
  1072. // possible string match from the last read.
  1073. //
  1074. // 1st read: |...ababc..| -> match found
  1075. //
  1076. // 1st read: |.abababc..| -> match found
  1077. //
  1078. // 1st read: |......abab| -> possible match (retain "abab")
  1079. // 2nd read: |ababc.....| -> match found
  1080. //
  1081. // 1st read: |......abab| -> possible match (retain "abab")
  1082. // 2nd read: |abababc...| -> match found
  1083. //
  1084. #define NOT_MARKED ((UINT)-1)
  1085. RES res = RES_FALSE; // assume the string is not here
  1086. LPSTR psz;
  1087. LPSTR pszBuf;
  1088. UINT ib;
  1089. UINT ibMark = NOT_MARKED;
  1090. UINT cb;
  1091. int cbMatched = 0;
  1092. int cbThrowAway = 0;
  1093. UINT cbBuf;
  1094. pszBuf = pff->pszBuf;
  1095. if (pszBuf)
  1096. {
  1097. TRACE_MSG(TF_GENERAL, "FindFormat: current buffer is {%s}", pszBuf);
  1098. cbBuf = pff->cbBuf;
  1099. if (cbBuf == pff->cbBuf)
  1100. {
  1101. ASSERT(0 < cbBuf);
  1102. cbBuf--; // save space for null terminator (first time only)
  1103. }
  1104. }
  1105. // Search for the (formatted) string in the receive
  1106. // buffer. Optionally store the matching received
  1107. // characters in the findfmt buffer.
  1108. for (psz = pff->pszFindFmt, ib = ibCurFind, cb = 0;
  1109. *psz && cb < cbRecMax;
  1110. ib = (ib + 1) % SIZE_ReceiveBuf, cb++)
  1111. {
  1112. // Match?
  1113. RES resT = CompareFormat(pff, &psz, pszRecBuf[ib]);
  1114. if (RES_OK == resT)
  1115. {
  1116. // Yes
  1117. if (NOT_MARKED == ibMark)
  1118. {
  1119. ibMark = ib; // Mark starting position
  1120. cbMatched = 0;
  1121. }
  1122. cbMatched++;
  1123. if (pszBuf)
  1124. {
  1125. if (0 == cbBuf)
  1126. {
  1127. res = RES_E_MOREDATA;
  1128. break;
  1129. }
  1130. *pszBuf++ = pszRecBuf[ib]; // Copy character to buffer
  1131. cbBuf--;
  1132. }
  1133. }
  1134. else if (RES_HALT == resT)
  1135. {
  1136. ASSERT(0 == *psz);
  1137. res = RES_HALT;
  1138. break;
  1139. }
  1140. else
  1141. {
  1142. // No; add this to our throw away count
  1143. cbThrowAway++;
  1144. // Are we in a partial find?
  1145. if (NOT_MARKED != ibMark)
  1146. {
  1147. // Yes; go back to where we thought the string might
  1148. // have started. The loop will increment one
  1149. // position and then resume search.
  1150. cb -= cbMatched;
  1151. ib = ibMark;
  1152. ibMark = NOT_MARKED;
  1153. psz = pff->pszFindFmt;
  1154. if (pszBuf)
  1155. {
  1156. pszBuf = pff->pszBuf;
  1157. cbBuf += cbMatched;
  1158. }
  1159. }
  1160. }
  1161. }
  1162. ASSERT(RES_FALSE == res || RES_HALT == res || RES_E_MOREDATA == res);
  1163. if (0 == *psz)
  1164. res = RES_OK;
  1165. if (pszBuf)
  1166. *pszBuf = 0; // add null terminator
  1167. ASSERT(RES_FALSE == res || RES_OK == res || RES_E_MOREDATA == res);
  1168. *pib = ib;
  1169. *pibMark = ibMark;
  1170. *pcbMatched = cbMatched;
  1171. if (RES_OK == res)
  1172. {
  1173. // Include any junk characters that preceded the matched string.
  1174. *pcbMatched += cbThrowAway;
  1175. }
  1176. else if (RES_FALSE == res)
  1177. {
  1178. // Should be at the end of the read buffer.
  1179. ASSERT(cb == cbRecMax);
  1180. }
  1181. return res;
  1182. }
  1183. /*----------------------------------------------------------
  1184. Purpose: This function attempts to find a formatted string in
  1185. the read buffer. See description of ScanFormat.
  1186. Returns: RES_OK (if complete string is found)
  1187. RES_FALSE (otherwise)
  1188. RES_E_MOREDATA (if no string found and pszBuf is full)
  1189. Cond: --
  1190. */
  1191. RES PUBLIC FindFormat(
  1192. HWND hwnd,
  1193. HANDLE hFindFmt,
  1194. LPDWORD piFound)
  1195. {
  1196. RES res = RES_E_FAIL;
  1197. #ifndef WINNT_RAS
  1198. //
  1199. // On NT, the 'hwnd' parameter is actually a pointer to the SCRIPTDATA
  1200. // for the current script, hence the #if-#else.
  1201. //
  1202. PTERMDLG ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1203. #else // !WINNT_RAS
  1204. SCRIPTDATA* ptd = (SCRIPTDATA*)hwnd;
  1205. #endif // !WINNT_RAS
  1206. HSA hsa = (HSA)hFindFmt;
  1207. UINT ib = NOT_MARKED;
  1208. UINT ibMark = NOT_MARKED;
  1209. int cbMatched = -1;
  1210. DWORD iff;
  1211. DWORD cff;
  1212. ASSERT(hsa);
  1213. DBG_ENTER(FindFormat);
  1214. DEBUG_CODE( DumpBuffer(ptd); )
  1215. // Consider each of the requested strings separately. If
  1216. // there are multiple candidate matches, choose the one
  1217. // that has the most matched characters.
  1218. cff = SAGetCount(hsa);
  1219. for (iff = 0; iff < cff; iff++)
  1220. {
  1221. PFINDFMT pff;
  1222. RES resT;
  1223. UINT ibMarkT;
  1224. UINT ibT;
  1225. int cbMatchedT;
  1226. SAGetItemPtr(hsa, iff, &pff);
  1227. resT = ScanFormat(pff, ptd->ibCurFind, ptd->pbReceiveBuf,
  1228. ptd->cbReceiveMax, &ibMarkT, &ibT, &cbMatchedT);
  1229. // Did this string match?
  1230. switch (resT)
  1231. {
  1232. case RES_OK:
  1233. // Yes; stop right now
  1234. ibMark = ibMarkT;
  1235. ib = ibT;
  1236. cbMatched = cbMatchedT;
  1237. *piFound = iff;
  1238. // Fall thru
  1239. case RES_E_MOREDATA:
  1240. res = resT;
  1241. goto GetOut;
  1242. case RES_FALSE:
  1243. if (cbMatchedT > cbMatched)
  1244. {
  1245. res = resT;
  1246. ibMark = ibMarkT;
  1247. ib = ibT;
  1248. cbMatched = cbMatchedT;
  1249. }
  1250. break;
  1251. default:
  1252. ASSERT(0);
  1253. break;
  1254. }
  1255. }
  1256. GetOut:
  1257. // Update the read buffer pointers to preserve any trailing
  1258. // substring that may have matched.
  1259. if (RES_OK == res)
  1260. {
  1261. // Found string!
  1262. TRACE_MSG(TF_BUFFER, "Found string in buffer");
  1263. // It is okay to have characters following the matched string
  1264. // that have not been scanned yet. However, it is not okay
  1265. // to still think there are characters preceding the matched
  1266. // string that need scanning.
  1267. ASSERT((UINT)cbMatched == ptd->cbReceiveMax && ib == ptd->ibCurRead ||
  1268. (UINT)cbMatched <= ptd->cbReceiveMax);
  1269. ptd->ibCurFind = ib;
  1270. ptd->cbReceiveMax -= cbMatched;
  1271. }
  1272. else if (RES_E_MOREDATA == res)
  1273. {
  1274. // Throw away whatever is in the receive buffer
  1275. TRACE_MSG(TF_BUFFER, "String too long in buffer");
  1276. ptd->ibCurFind = ptd->ibCurRead;
  1277. ptd->cbReceiveMax = 0;
  1278. }
  1279. else
  1280. {
  1281. ASSERT(RES_FALSE == res);
  1282. // End of receive buffer; did we even find a potential substring?
  1283. if (NOT_MARKED == ibMark)
  1284. {
  1285. // No; throw away whatever is in the receive buffer
  1286. TRACE_MSG(TF_BUFFER, "String not found in buffer");
  1287. ptd->ibCurFind = ptd->ibCurRead;
  1288. ptd->cbReceiveMax = 0;
  1289. }
  1290. else
  1291. {
  1292. // Yes; keep the substring part
  1293. TRACE_MSG(TF_BUFFER, "Partial string found in buffer");
  1294. ASSERT(ibMark >= ptd->ibCurFind);
  1295. ptd->ibCurFind = ibMark;
  1296. ptd->cbReceiveMax = cbMatched;
  1297. }
  1298. }
  1299. DBG_EXIT_RES(FindFormat, res);
  1300. return res;
  1301. }
  1302. #ifdef OLD_FINDFORMAT
  1303. /*----------------------------------------------------------
  1304. Purpose: This function attempts to find a formatted string in
  1305. the read buffer. If a sequence of characters match
  1306. the complete string, the function returns TRUE. The
  1307. matching sequence of characters is copied into pszBuf.
  1308. If a portion of the string (ie, from the beginning
  1309. of pszFindFmt to some middle of pszFindFmt) is found
  1310. in the buffer, the buffer is marked so the next read will
  1311. not overwrite the possible substring match. This
  1312. function then returns FALSE. The caller should
  1313. read in more data before calling this function again.
  1314. The formatted string may have the following characters:
  1315. %u - expect a number (to first non-digit)
  1316. ^M - expect a carriage-return
  1317. <cr> - expect a carriage-return
  1318. <lf> - expect a line-feed
  1319. All other characters are taken literally.
  1320. If pszBuf becomes full before a delimiter is
  1321. encountered, this function returns RES_E_MOREDATA.
  1322. Returns: RES_OK (if complete string is found)
  1323. RES_FALSE (otherwise)
  1324. RES_E_MOREDATA (if no delimiter encountered and pszBuf is full)
  1325. Cond: --
  1326. */
  1327. RES PUBLIC FindFormat(
  1328. HWND hwnd,
  1329. HANDLE hFindFmt)
  1330. {
  1331. // The trick is keeping track of when we've found a partial
  1332. // string across read boundaries. Consider the search string
  1333. // "ababc". We need to find it in the following cases (the
  1334. // character '|' indicates read boundary, '.' is an arbitrary
  1335. // byte):
  1336. //
  1337. // |...ababc..|
  1338. // |.abababc..|
  1339. // |......abab|c.........|
  1340. // |......abab|abc.......|
  1341. //
  1342. // This assumes the read buffer is larger than the search
  1343. // string. In order to do this, the read buffer must be
  1344. // a circular buffer, always retaining that portion of the
  1345. // possible string match from the last read.
  1346. //
  1347. // 1st read: |...ababc..| -> match found
  1348. //
  1349. // 1st read: |.abababc..| -> match found
  1350. //
  1351. // 1st read: |......abab| -> possible match (retain "abab")
  1352. // 2nd read: |ababc.....| -> match found
  1353. //
  1354. // 1st read: |......abab| -> possible match (retain "abab")
  1355. // 2nd read: |abababc...| -> match found
  1356. //
  1357. #define NOT_MARKED ((UINT)-1)
  1358. RES res = RES_FALSE;
  1359. PTERMDLG ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1360. PFINDFMT pff = (PFINDFMT)hFindFmt;
  1361. LPCSTR psz;
  1362. LPSTR pszBuf;
  1363. LPSTR pszRecBuf = ptd->pbReceiveBuf;
  1364. UINT ib;
  1365. UINT ibMark = NOT_MARKED;
  1366. UINT cb;
  1367. UINT cbMatched = 0;
  1368. UINT cbRecMax = ptd->cbReceiveMax;
  1369. UINT cbBuf;
  1370. ASSERT(pff);
  1371. DBG_ENTER_SZ(FindFormat, pff->pszFindFmt);
  1372. DEBUG_CODE( DumpBuffer(ptd); )
  1373. pszBuf = pff->pszBuf;
  1374. if (pszBuf)
  1375. {
  1376. TRACE_MSG(TF_GENERAL, "FindFormat: current buffer is {%s}", pff->pszBuf);
  1377. cbBuf = pff->cbBuf;
  1378. if (cbBuf == pff->cbBuf)
  1379. {
  1380. ASSERT(0 < cbBuf);
  1381. cbBuf--; // save space for null terminator (first time only)
  1382. }
  1383. }
  1384. // Search for the (formatted) string in the receive
  1385. // buffer. Optionally store the matching received
  1386. // characters in the findfmt buffer.
  1387. for (psz = pff->pszFindFmt, ib = ptd->ibCurFind, cb = 0;
  1388. *psz && cb < cbRecMax;
  1389. ib = (ib + 1) % SIZE_ReceiveBuf, cb++)
  1390. {
  1391. // Match?
  1392. res = CompareFormat(pff, &psz, pszRecBuf[ib]);
  1393. if (RES_OK == res)
  1394. {
  1395. // Yes
  1396. if (NOT_MARKED == ibMark)
  1397. {
  1398. ibMark = ib; // Mark starting position
  1399. cbMatched = 0;
  1400. }
  1401. cbMatched++;
  1402. if (pszBuf)
  1403. {
  1404. if (0 == cbBuf)
  1405. {
  1406. res = RES_E_MOREDATA;
  1407. break;
  1408. }
  1409. *pszBuf++ = pszRecBuf[ib]; // Copy character to buffer
  1410. cbBuf--;
  1411. }
  1412. }
  1413. else if (RES_HALT == res)
  1414. {
  1415. ASSERT(0 == *psz);
  1416. break;
  1417. }
  1418. else if (NOT_MARKED != ibMark)
  1419. {
  1420. // No; go back to where we thought the string might
  1421. // have started. The loop will increment one
  1422. // position and then resume search.
  1423. cb -= cbMatched;
  1424. ib = ibMark;
  1425. ibMark = NOT_MARKED;
  1426. psz = pff->pszFindFmt;
  1427. if (pszBuf)
  1428. {
  1429. pszBuf = pff->pszBuf;
  1430. cbBuf += cbMatched;
  1431. }
  1432. }
  1433. }
  1434. if (pszBuf)
  1435. *pszBuf = 0; // add null terminator
  1436. if ( !*psz )
  1437. {
  1438. // Found string!
  1439. TRACE_MSG(TF_BUFFER, "Found string in buffer");
  1440. ASSERT(cbMatched <= ptd->cbReceiveMax);
  1441. ptd->ibCurFind = ib;
  1442. ptd->cbReceiveMax -= cbMatched;
  1443. res = RES_OK;
  1444. }
  1445. else if (RES_E_MOREDATA == res)
  1446. {
  1447. // Throw away whatever is in the receive buffer
  1448. TRACE_MSG(TF_BUFFER, "String too long in buffer");
  1449. ptd->ibCurFind = ptd->ibCurRead;
  1450. ptd->cbReceiveMax = 0;
  1451. }
  1452. else
  1453. {
  1454. // End of receive buffer; did we even find a potential substring?
  1455. ASSERT(cb == cbRecMax);
  1456. if (NOT_MARKED == ibMark)
  1457. {
  1458. // No; throw away whatever is in the receive buffer
  1459. TRACE_MSG(TF_BUFFER, "String not found in buffer");
  1460. ptd->ibCurFind = ptd->ibCurRead;
  1461. ptd->cbReceiveMax = 0;
  1462. }
  1463. else
  1464. {
  1465. // Yes; keep the substring part
  1466. TRACE_MSG(TF_BUFFER, "Partial string found in buffer");
  1467. ASSERT(ibMark >= ptd->ibCurFind);
  1468. ptd->ibCurFind = ibMark;
  1469. ptd->cbReceiveMax = cbMatched;
  1470. }
  1471. res = RES_FALSE;
  1472. }
  1473. DBG_EXIT_RES(FindFormat, res);
  1474. return res;
  1475. }
  1476. #endif
  1477. #ifdef COPYTODELIM
  1478. /*----------------------------------------------------------
  1479. Purpose: Sets or clears the list of delimiters
  1480. Returns: --
  1481. Cond: --
  1482. */
  1483. void PRIVATE SetDelimiters(
  1484. PTERMDLG ptd,
  1485. LPCSTR pszTok,
  1486. BOOL bSet)
  1487. {
  1488. PBOOL rgbDelim = ptd->rgbDelim;
  1489. LPCSTR psz;
  1490. char ch;
  1491. for (psz = pszTok; *psz; )
  1492. {
  1493. psz = MyNextChar(psz, &ch);
  1494. ASSERT(InRange(ch, 0, CE_DELIM-1));
  1495. rgbDelim[ch] = bSet;
  1496. }
  1497. }
  1498. /*----------------------------------------------------------
  1499. Purpose: This function reads to one of the given token
  1500. delimiters. All characters in the read buffer (to
  1501. the delimiter) are copied into pszBuf, not including
  1502. the delimiter.
  1503. Any token delimiters that are encountered before the
  1504. first non-token delimiter are skipped, and the function
  1505. starts at the first non-token delimiter.
  1506. If a token delimiter is found, the function
  1507. returns RES_OK.
  1508. If a token delimiter is not found in the current
  1509. read buffer, the function returns RES_FALSE and the
  1510. characters that were read are still copied into
  1511. pszBuf. The caller should read in more data before
  1512. calling this function again.
  1513. If pszBuf becomes full before a delimiter is
  1514. encountered, this function returns RES_E_MOREDATA.
  1515. The string returned in pszBuf is null terminated.
  1516. Returns: RES_OK
  1517. RES_FALSE (if no delimiter encountered this time)
  1518. RES_E_MOREDATA (if no delimiter encountered and pszBuf is full)
  1519. Cond: --
  1520. */
  1521. RES PUBLIC CopyToDelimiter(
  1522. HWND hwnd,
  1523. LPSTR pszBuf,
  1524. UINT cbBuf,
  1525. LPCSTR pszTok)
  1526. {
  1527. RES res;
  1528. PTERMDLG ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1529. PBOOL rgbDelim = ptd->rgbDelim;
  1530. LPSTR pszReadBuf = ptd->pbReceiveBuf;
  1531. LPSTR psz;
  1532. UINT ib;
  1533. UINT cb;
  1534. UINT cbMax = ptd->cbReceiveMax;
  1535. DBG_ENTER_SZ(CopyToDelimiter, pszTok);
  1536. DEBUG_CODE( DumpBuffer(ptd); )
  1537. #ifdef DEBUG
  1538. for (ib = 0; ib < CE_DELIM; ib++)
  1539. ASSERT(FALSE == rgbDelim[ib]);
  1540. #endif
  1541. // Initialize the delimiters
  1542. SetDelimiters(ptd, pszTok, TRUE);
  1543. cbBuf--; // save space for terminator
  1544. // Skip to the first non-delimiter
  1545. for (ib = ptd->ibCurFind, cb = 0;
  1546. cb < cbMax;
  1547. ib = (ib + 1) % SIZE_ReceiveBuf, cb++)
  1548. {
  1549. char ch = pszReadBuf[ib];
  1550. ASSERT(InRange(ch, 0, CE_DELIM-1));
  1551. // Is this one of the delimiters?
  1552. if ( !rgbDelim[ch] )
  1553. {
  1554. // No; stop
  1555. break;
  1556. }
  1557. }
  1558. if (cb < cbMax || 0 == cbMax)
  1559. res = RES_FALSE; // assume no delimiter in this pass
  1560. else
  1561. res = RES_OK;
  1562. // Copy to the first delimiter encountered
  1563. for (psz = pszBuf;
  1564. 0 < cbBuf && cb < cbMax;
  1565. psz++, ib = (ib + 1) % SIZE_ReceiveBuf, cb++, cbBuf--)
  1566. {
  1567. char ch = pszReadBuf[ib];
  1568. ASSERT(InRange(ch, 0, CE_DELIM-1));
  1569. // Is this one of the delimiters?
  1570. if (rgbDelim[ch])
  1571. {
  1572. // Yes; we're done
  1573. res = RES_OK;
  1574. break;
  1575. }
  1576. else
  1577. {
  1578. // No;
  1579. *psz = ch;
  1580. }
  1581. }
  1582. *psz = 0; // add null terminator
  1583. ptd->ibCurFind = ib;
  1584. ptd->cbReceiveMax -= cb;
  1585. if (RES_FALSE == res)
  1586. {
  1587. res = (0 == cbBuf) ? RES_E_MOREDATA : RES_FALSE;
  1588. }
  1589. else
  1590. {
  1591. TRACE_MSG(TF_BUFFER, "Copied to delimiter %#02x", pszReadBuf[ib]);
  1592. }
  1593. // Deinitialize the delimiters
  1594. SetDelimiters(ptd, pszTok, FALSE);
  1595. DBG_EXIT_RES(CopyToDelimiter, res);
  1596. return res;
  1597. }
  1598. #endif // COPYTODELIM
  1599. /*----------------------------------------------------------
  1600. Purpose: Reads from the comm port into the circular buffer.
  1601. This functions sets *ppbBuf to the location of
  1602. the first new character read.
  1603. there is a potential but rare bug
  1604. that can occur with Internet providers that send
  1605. DBCS over the wire. If the last character in the
  1606. buffer is a DBCS lead-byte, and it is being thrown
  1607. away, then the next byte might be matched with an
  1608. existing character. The entire string following
  1609. this must match for us to find a false match.
  1610. Returns: TRUE (if a character was read successfully)
  1611. FALSE (otherwise)
  1612. Cond: --
  1613. */
  1614. BOOL PRIVATE ReadIntoBuffer(
  1615. #ifndef WINNT_RAS
  1616. //
  1617. // On NT, we use a SCRIPTDATA pointer to access the circular-buffer.
  1618. //
  1619. PTERMDLG ptd,
  1620. #else // !WINNT_RAS
  1621. SCRIPTDATA *ptd,
  1622. #endif // !WINNT_RAS
  1623. PDWORD pibStart, // start of newly read characters
  1624. PDWORD pcbRead) // count of newly read characters
  1625. {
  1626. BOOL bRet;
  1627. OVERLAPPED ov;
  1628. DWORD cb;
  1629. DWORD cbRead;
  1630. DWORD ib;
  1631. DBG_ENTER(ReadIntoBuffer);
  1632. ASSERT(pibStart);
  1633. ASSERT(pcbRead);
  1634. ov.Internal = 0;
  1635. ov.InternalHigh = 0;
  1636. ov.Offset = 0;
  1637. ov.OffsetHigh = 0;
  1638. ov.hEvent = NULL;
  1639. *pcbRead = 0;
  1640. // This is a circular buffer, so at most do two reads
  1641. ASSERT(ptd->ibCurRead >= ptd->ibCurFind);
  1642. ASSERT(SIZE_ReceiveBuf > ptd->ibCurFind);
  1643. ASSERT(SIZE_ReceiveBuf > ptd->ibCurRead);
  1644. *pcbRead = 0;
  1645. *pibStart = ptd->ibCurRead;
  1646. // (*pibStart can be different from ptd->ibCurFind)
  1647. ib = ptd->ibCurRead;
  1648. cb = SIZE_ReceiveBuf - ib;
  1649. do
  1650. {
  1651. DWORD ibNew;
  1652. ASSERT(SIZE_ReceiveBuf > *pcbRead);
  1653. #ifndef WINNT_RAS
  1654. //
  1655. // In order to read data on NT, we go through RxReadFile
  1656. // which reads from the buffer which was filled by RASMAN.
  1657. //
  1658. bRet = ReadFile(ptd->hport, &ptd->pbReceiveBuf[ib], cb, &cbRead, &ov);
  1659. SetEvent(ptd->hEvent[READ_EVENT]);
  1660. #else // !WINNT_RAS
  1661. bRet = RxReadFile(
  1662. ptd->hscript, &ptd->pbReceiveBuf[ib], cb, &cbRead
  1663. );
  1664. #endif // !WINNT_RAS
  1665. ptd->cbReceiveMax += cbRead;
  1666. *pcbRead += cbRead;
  1667. // Is this going to wrap around?
  1668. ibNew = (ib + cbRead) % SIZE_ReceiveBuf;
  1669. if (ibNew > ib)
  1670. cb -= cbRead; // No
  1671. else
  1672. cb = ptd->ibCurFind; // Yes
  1673. ib = ibNew;
  1674. } while (bRet && 0 != cbRead && SIZE_ReceiveBuf > *pcbRead);
  1675. ptd->ibCurRead = (ptd->ibCurRead + *pcbRead) % SIZE_ReceiveBuf;
  1676. DEBUG_CODE( DumpBuffer(ptd); )
  1677. DBG_EXIT_BOOL(ReadIntoBuffer, bRet);
  1678. return bRet;
  1679. }
  1680. /*----------------------------------------------------------
  1681. Purpose: Get input from the com port
  1682. Returns: TRUE
  1683. Cond: --
  1684. */
  1685. BOOL NEAR PASCAL GetInput(
  1686. HWND hwnd)
  1687. {
  1688. BOOL bRet = TRUE;
  1689. PTERMDLG ptd;
  1690. DWORD cbRead;
  1691. DWORD ibStart;
  1692. DBG_ENTER(GetInput);
  1693. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1694. #ifndef WINNT_RAS
  1695. //
  1696. // On NT, the information for the script is stored in a SCRIPTDATA.
  1697. // The code below exists only to allow this file to compile;
  1698. // "GetInput" is not called at all on NT.
  1699. //
  1700. if (ReadIntoBuffer(ptd, &ibStart, &cbRead) && 0 < cbRead)
  1701. #else // !WINNT_RAS
  1702. if (ReadIntoBuffer((SCRIPTDATA *)ptd, &ibStart, &cbRead) && 0 < cbRead)
  1703. #endif // !WINNT_RAS
  1704. {
  1705. char szBuf[SIZE_ReceiveBuf + 1];
  1706. LPSTR pch = szBuf;
  1707. UINT ib;
  1708. UINT cb;
  1709. HWND hwndScrn = GetDlgItem(hwnd, CID_T_EB_SCREEN);
  1710. for (ib = ibStart, cb = 0; cb < cbRead; cb++, ib = (ib + 1) % SIZE_ReceiveBuf)
  1711. {
  1712. char ch = ptd->pbReceiveBuf[ib];
  1713. /* Formatting: Converts CRs to LFs (there seems to be no VK_
  1714. ** for LF) and throws away LFs. This prevents the user from
  1715. ** exiting the dialog when they press Enter (CR) in the
  1716. ** terminal screen. LF looks like CRLF in the edit box. Also,
  1717. ** throw away TABs because otherwise they change focus to the
  1718. ** next control.
  1719. */
  1720. if (ch == VK_RETURN)
  1721. {
  1722. /* Must send whenever end-of-line is encountered because
  1723. ** EM_REPLACESEL doesn't handle VK_RETURN characters well
  1724. ** (prints garbage).
  1725. */
  1726. *pch = '\0';
  1727. /* Turn off current selection, if any, and replace the null
  1728. ** selection with the current buffer. This has the effect
  1729. ** of adding the buffer at the caret. Finally, send the
  1730. ** EOL to the window which (unlike EM_REPLACESEL) handles
  1731. ** it correctly.
  1732. */
  1733. SendMessage(hwndScrn, WM_SETREDRAW, (WPARAM )FALSE, 0);
  1734. SendMessage(hwndScrn, EM_SETSEL, 32767, 32767 );
  1735. SendMessage(hwndScrn, EM_REPLACESEL, 0, (LPARAM )szBuf );
  1736. SendMessage(hwndScrn, WM_EOLFROMDEVICE, 0, 0 );
  1737. SendMessage(hwndScrn, WM_SETREDRAW, (WPARAM )TRUE, 0);
  1738. SendMessage(hwndScrn, EM_SCROLLCARET, 0, 0);
  1739. InvalidateRect(hwndScrn, NULL, FALSE);
  1740. /* Start afresh on the output buffer.
  1741. */
  1742. pch = szBuf;
  1743. continue;
  1744. }
  1745. else if (ch == '\n' || ch == VK_TAB)
  1746. continue;
  1747. *pch++ = ch;
  1748. }
  1749. *pch = '\0';
  1750. if (pch != szBuf)
  1751. {
  1752. /* Send the last remnant of the line.
  1753. */
  1754. SendMessage(hwndScrn, EM_SETSEL, 32767, 32767);
  1755. SendMessage(hwndScrn, EM_REPLACESEL, 0, (LPARAM)szBuf );
  1756. SendMessage(hwndScrn, EM_SCROLLCARET, 0, 0);
  1757. }
  1758. }
  1759. DBG_EXIT_BOOL(GetInput, bRet);
  1760. return bRet;
  1761. }
  1762. /*----------------------------------------------------------------------------
  1763. ** Terminal Output Handler
  1764. **----------------------------------------------------------------------------
  1765. */
  1766. /*----------------------------------------------------------
  1767. Purpose: Send a byte to the device.
  1768. Returns: --
  1769. Cond: --
  1770. */
  1771. void PUBLIC SendByte(
  1772. HWND hwnd,
  1773. BYTE byte)
  1774. {
  1775. #ifndef WINNT_RAS
  1776. //
  1777. // On NT. we use a SCRIPTDATA structure to hold information about the script.
  1778. //
  1779. PTERMDLG ptd;
  1780. #else // !WINNT_RAS
  1781. SCRIPTDATA* ptd;
  1782. #endif // !WINNT_RAS
  1783. DWORD cbWrite;
  1784. OVERLAPPED ov;
  1785. DBG_ENTER(SendByte);
  1786. #ifndef WINNT_RAS
  1787. //
  1788. // On NT, the "hwnd" argument is actually a pointer to a SCRIPTDATA structure
  1789. // for the script being parsed.
  1790. //
  1791. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1792. #else // !WINNT_RAS
  1793. ptd = (SCRIPTDATA *)hwnd;
  1794. #endif // !WINNT_RAS
  1795. /* Send the character to the device. It is not passed thru
  1796. ** because the device will echo it.
  1797. */
  1798. ptd->pbSendBuf[0] = (BYTE)byte;
  1799. /* Make sure we still have the comm port
  1800. */
  1801. ov.Internal = 0;
  1802. ov.InternalHigh = 0;
  1803. ov.Offset = 0;
  1804. ov.OffsetHigh = 0;
  1805. ov.hEvent = NULL;
  1806. cbWrite = 0;
  1807. #ifndef WINNT_RAS
  1808. //
  1809. // On NT, we output data on the COM port by calling RxWriteFile
  1810. // which in turn passes the data to RasPortSend
  1811. //
  1812. WriteFile(ptd->hport, ptd->pbSendBuf, SIZE_SendBuf, &cbWrite, &ov);
  1813. #else // !WINNT_RAS
  1814. RxWriteFile(ptd->hscript, ptd->pbSendBuf, SIZE_SendBuf, &cbWrite);
  1815. #endif // !WINNT_RAS
  1816. TRACE_MSG(TF_BUFFER, "Sent byte %#02x", byte);
  1817. DBG_EXIT(SendByte);
  1818. }
  1819. /*----------------------------------------------------------------------------
  1820. ** Terminal Appearance Adjuster
  1821. **----------------------------------------------------------------------------
  1822. */
  1823. VOID NEAR PASCAL AdjustTerminal (HWND hwnd, int wWidth, int wHeight)
  1824. {
  1825. HWND hwndCtrl;
  1826. RECT rect;
  1827. SIZE sizeButton;
  1828. POINT ptPos;
  1829. DWORD dwUnit;
  1830. // Get the sizes of the push buttons
  1831. //
  1832. dwUnit = GetDialogBaseUnits();
  1833. hwndCtrl = GetDlgItem(hwnd, IDOK);
  1834. GetWindowRect(hwndCtrl, &rect);
  1835. sizeButton.cx = rect.right - rect.left;
  1836. sizeButton.cy = rect.bottom - rect.top;
  1837. ptPos.x = wWidth/2 - ((X_SPACING*LOWORD(dwUnit))/4)/2 - sizeButton.cx;
  1838. ptPos.y = wHeight - (sizeButton.cy+((Y_MARGIN*HIWORD(dwUnit))/4));
  1839. // Move the push buttons
  1840. MoveWindow(hwndCtrl, ptPos.x, ptPos.y, sizeButton.cx, sizeButton.cy, TRUE);
  1841. ptPos.x += ((X_SPACING*LOWORD(dwUnit))/4) + sizeButton.cx;
  1842. MoveWindow(GetDlgItem(hwnd, IDCANCEL), ptPos.x, ptPos.y,
  1843. sizeButton.cx, sizeButton.cy, TRUE);
  1844. // Move the input enable box
  1845. hwndCtrl = GetDlgItem(hwnd, CID_T_CB_MIN);
  1846. GetWindowRect(hwndCtrl, &rect);
  1847. sizeButton.cx = rect.right - rect.left;
  1848. sizeButton.cy = rect.bottom - rect.top;
  1849. ptPos.y -= (sizeButton.cy + ((Y_MARGIN*HIWORD(dwUnit))/4));
  1850. ScreenToClient(hwnd, (LPPOINT)&rect);
  1851. MoveWindow(hwndCtrl, rect.left, ptPos.y,
  1852. sizeButton.cx,
  1853. sizeButton.cy,
  1854. TRUE);
  1855. // Move the start-minimized box
  1856. hwndCtrl = GetDlgItem(hwnd, CID_T_CB_INPUT);
  1857. GetWindowRect(hwndCtrl, &rect);
  1858. sizeButton.cx = rect.right - rect.left;
  1859. sizeButton.cy = rect.bottom - rect.top;
  1860. ptPos.y -= (sizeButton.cy + ((Y_SMALL_MARGIN*HIWORD(dwUnit))/4));
  1861. ScreenToClient(hwnd, (LPPOINT)&rect);
  1862. MoveWindow(hwndCtrl, rect.left, ptPos.y,
  1863. sizeButton.cx,
  1864. sizeButton.cy,
  1865. TRUE);
  1866. // Get the current position of the terminal screen
  1867. hwndCtrl = GetDlgItem(hwnd, CID_T_EB_SCREEN);
  1868. GetWindowRect(hwndCtrl, &rect);
  1869. ScreenToClient(hwnd, (LPPOINT)&rect);
  1870. MoveWindow(hwndCtrl, rect.left, rect.top,
  1871. wWidth - 2*rect.left,
  1872. ptPos.y - rect.top - ((Y_SMALL_MARGIN*HIWORD(dwUnit))/4),
  1873. TRUE);
  1874. InvalidateRect(hwnd, NULL, TRUE);
  1875. return;
  1876. }
  1877. /*----------------------------------------------------------------------------
  1878. ** Terminal caption update
  1879. **----------------------------------------------------------------------------
  1880. */
  1881. void NEAR PASCAL UpdateTerminalCaption(PTERMDLG ptd, UINT ids)
  1882. {
  1883. LPSTR szTitle, szFmt;
  1884. UINT iRet;
  1885. // If we are not running a script, do not update the caption
  1886. //
  1887. if (*ptd->script.szPath == '\0')
  1888. return;
  1889. // Allocate buffer
  1890. //
  1891. if ((szFmt = (LPSTR)LocalAlloc(LMEM_FIXED, 2*MAX_PATH)) != NULL)
  1892. {
  1893. // Load the display format
  1894. //
  1895. if((iRet = LoadString(g_hinst, ids, szFmt, MAX_PATH)))
  1896. {
  1897. // Get the title buffer
  1898. //
  1899. szTitle = szFmt+iRet+1;
  1900. {
  1901. // Build up the title
  1902. //
  1903. wsprintf(szTitle, szFmt, ptd->script.szPath);
  1904. SetWindowText(ptd->hwnd, szTitle);
  1905. };
  1906. };
  1907. LocalFree((HLOCAL)szFmt);
  1908. };
  1909. return;
  1910. }
  1911. /*----------------------------------------------------------------------------
  1912. ** Terminal read-notification thread
  1913. **----------------------------------------------------------------------------
  1914. */
  1915. void WINAPI TerminalThread (PTERMDLG ptd)
  1916. {
  1917. DWORD dwEvent;
  1918. DWORD dwMask;
  1919. while((dwEvent = WaitForMultipleObjects(MAX_EVENT, ptd->hEvent,
  1920. FALSE, INFINITE))
  1921. < WAIT_OBJECT_0+MAX_EVENT)
  1922. {
  1923. switch (dwEvent)
  1924. {
  1925. case READ_EVENT:
  1926. // Are we stopped?
  1927. if (WAIT_TIMEOUT == WaitForSingleObject(ptd->hEvent[STOP_EVENT], 0))
  1928. {
  1929. // No; wait for next character
  1930. dwMask = 0;
  1931. TRACE_MSG(TF_BUFFER, "Waiting for comm traffic...");
  1932. WaitCommEvent(ptd->hport, &dwMask, NULL);
  1933. if ((dwMask & EV_RXCHAR) && (ptd->hwnd != NULL))
  1934. {
  1935. TRACE_MSG(TF_BUFFER, "...EV_RXCHAR incoming");
  1936. PostMessage(ptd->hwnd, WM_MODEMNOTIFY, 0, 0);
  1937. }
  1938. else
  1939. {
  1940. TRACE_MSG(TF_BUFFER, "...EV_other (%#08lx) incoming", dwMask);
  1941. }
  1942. }
  1943. else
  1944. {
  1945. // Yes; just get out of here
  1946. ExitThread(ERROR_SUCCESS);
  1947. }
  1948. break;
  1949. case STOP_EVENT:
  1950. ExitThread(ERROR_SUCCESS);
  1951. break;
  1952. default:
  1953. ASSERT(0);
  1954. break;
  1955. }
  1956. }
  1957. }
  1958. /*----------------------------------------------------------------------------
  1959. ** Set IP address
  1960. **----------------------------------------------------------------------------
  1961. */
  1962. DWORD NEAR PASCAL TerminalSetIP(HWND hwnd, LPCSTR pszIPAddr)
  1963. {
  1964. PTERMDLG ptd;
  1965. DWORD dwRet;
  1966. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1967. if (IS_TEST_SCRIPT(ptd))
  1968. {
  1969. // We are testing, just display the ip address
  1970. //
  1971. MsgBox(g_hinst,
  1972. hwnd,
  1973. MAKEINTRESOURCE(IDS_IP_Address),
  1974. MAKEINTRESOURCE(IDS_CAP_Script),
  1975. NULL,
  1976. MB_OK | MB_ICONINFORMATION,
  1977. pszIPAddr);
  1978. };
  1979. // Set the IP address permanently
  1980. //
  1981. dwRet = AssignIPAddress(ptd->pscanner->psci->szEntryName,
  1982. pszIPAddr);
  1983. return dwRet;
  1984. }
  1985. /*----------------------------------------------------------------------------
  1986. ** Terminal Input settings
  1987. **----------------------------------------------------------------------------
  1988. */
  1989. void NEAR PASCAL TerminalSetInput(HWND hwnd, BOOL fEnable)
  1990. {
  1991. PTERMDLG ptd;
  1992. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  1993. // If the status is not changed, do nothing
  1994. //
  1995. if ((ptd->fInputEnabled && !fEnable) ||
  1996. (!ptd->fInputEnabled && fEnable))
  1997. {
  1998. // Mark the input enabling flag
  1999. //
  2000. ptd->fInputEnabled = fEnable;
  2001. // Check the control properly
  2002. //
  2003. CheckDlgButton(hwnd, CID_T_CB_INPUT,
  2004. fEnable ? BST_CHECKED : BST_UNCHECKED);
  2005. // Repaint the terminal screen
  2006. //
  2007. InvalidateRect(hwnd, NULL, FALSE);
  2008. // If enable and the window is iconic, restore it
  2009. //
  2010. if (fEnable)
  2011. {
  2012. if (IsIconic(hwnd))
  2013. {
  2014. ShowWindow(hwnd, SW_RESTORE);
  2015. };
  2016. SetFocus(GetDlgItem(hwnd, CID_T_EB_SCREEN));
  2017. };
  2018. };
  2019. }
  2020. /*----------------------------------------------------------------------------
  2021. ** Dump the script window
  2022. **----------------------------------------------------------------------------
  2023. */
  2024. BOOL NEAR PASCAL DisplayScript (PTERMDLG ptd)
  2025. {
  2026. // Create the debug script window
  2027. //
  2028. ptd->hwndDbg = CreateDialogParam(g_hinst,
  2029. MAKEINTRESOURCE(IDD_TERMINALTESTDLG),
  2030. NULL,
  2031. DbgScriptDlgProc,
  2032. (LPARAM)ptd);
  2033. // Did we have the debug window?
  2034. //
  2035. if (!IsWindow(ptd->hwndDbg))
  2036. {
  2037. ptd->hwndDbg = NULL;
  2038. return FALSE;
  2039. };
  2040. return TRUE;
  2041. }
  2042. /*----------------------------------------------------------------------------
  2043. ** Script debug window procedure
  2044. **----------------------------------------------------------------------------
  2045. */
  2046. LRESULT FAR PASCAL DbgScriptDlgProc(HWND hwnd,
  2047. UINT wMsg,
  2048. WPARAM wParam,
  2049. LPARAM lParam )
  2050. {
  2051. PTERMDLG ptd;
  2052. switch (wMsg)
  2053. {
  2054. case WM_INITDIALOG:
  2055. {
  2056. HMENU hMenuSys;
  2057. ptd = (PTERMDLG)lParam;
  2058. SetWindowLongPtr(hwnd, DWLP_USER, (ULONG_PTR)lParam);
  2059. ptd->hwndDbg = hwnd;
  2060. // Show its own icon
  2061. //
  2062. SendMessage(hwnd, WM_SETICON, TRUE,
  2063. (LPARAM)LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SCRIPT)));
  2064. // Always gray out size and maximize command
  2065. //
  2066. hMenuSys = GetSystemMenu(hwnd, FALSE);
  2067. EnableMenuItem(hMenuSys, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
  2068. return (InitDebugWindow(hwnd));
  2069. }
  2070. case WM_PROCESSSCRIPT:
  2071. {
  2072. HWND hCtrl;
  2073. //
  2074. // The main window notifies that it is done with the current line
  2075. //
  2076. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  2077. // Make sure we do not continue processing the script
  2078. //
  2079. ptd->fContinue = FALSE;
  2080. hCtrl = GetDlgItem(hwnd, CID_T_PB_STEP);
  2081. EnableWindow(hCtrl, TRUE);
  2082. SetFocus(hCtrl);
  2083. TrackScriptLine(ptd, Astexec_GetCurLine(&ptd->astexec)-1);
  2084. break;
  2085. }
  2086. case WM_COMMAND:
  2087. switch (LOWORD(wParam))
  2088. {
  2089. case CID_T_EB_SCRIPT:
  2090. {
  2091. HWND hCtrl = GET_WM_COMMAND_HWND(wParam, lParam);
  2092. if (GET_WM_COMMAND_CMD(wParam, lParam)==EN_SETFOCUS)
  2093. {
  2094. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  2095. TrackScriptLine(ptd, ptd->iMarkLine);
  2096. };
  2097. break;
  2098. }
  2099. case CID_T_PB_STEP:
  2100. {
  2101. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  2102. // Allow the next step
  2103. //
  2104. EnableWindow(GET_WM_COMMAND_HWND(wParam, lParam), FALSE);
  2105. // Tell the main window to process the next script line
  2106. //
  2107. ptd->fContinue = TRUE;
  2108. PostProcessScript(ptd->hwnd);
  2109. break;
  2110. }
  2111. };
  2112. break;
  2113. case WM_HELP:
  2114. case WM_CONTEXTMENU:
  2115. ContextHelp(gaDebug, wMsg, wParam, lParam);
  2116. break;
  2117. };
  2118. return 0;
  2119. }
  2120. /*----------------------------------------------------------------------------
  2121. ** Init script debug window
  2122. **----------------------------------------------------------------------------
  2123. */
  2124. BOOL NEAR PASCAL InitDebugWindow (HWND hwnd)
  2125. {
  2126. PTERMDLG ptd;
  2127. HANDLE hFile;
  2128. LPBYTE lpBuffer;
  2129. DWORD cbRead;
  2130. HWND hCtrl;
  2131. UINT iLine, cLine, iMark;
  2132. ptd = (PTERMDLG)GetWindowLongPtr(hwnd, DWLP_USER);
  2133. ASSERT(IS_TEST_SCRIPT(ptd));
  2134. // Do not start the script yet
  2135. //
  2136. ptd->fContinue = FALSE;
  2137. // Allocate the read buffer
  2138. //
  2139. if ((lpBuffer = (LPBYTE)LocalAlloc(LMEM_FIXED, SIZE_ReceiveBuf)) == NULL)
  2140. return FALSE;
  2141. hCtrl = GetDlgItem(hwnd, CID_T_EB_SCRIPT);
  2142. hFile = CreateFile(ptd->script.szPath, GENERIC_READ, FILE_SHARE_READ,
  2143. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  2144. if (INVALID_HANDLE_VALUE != hFile)
  2145. {
  2146. while(ReadFile(hFile, lpBuffer, sizeof(SIZE_ReceiveBuf), &cbRead, NULL) &&
  2147. (cbRead != 0))
  2148. {
  2149. // Clean up the end garbage
  2150. //
  2151. if (cbRead < SIZE_ReceiveBuf)
  2152. lpBuffer[cbRead] = '\0';
  2153. SendMessage(hCtrl, EM_SETSEL, 32767, 32767 );
  2154. SendMessage(hCtrl, EM_REPLACESEL, 0, (LPARAM)lpBuffer );
  2155. };
  2156. CloseHandle(hFile);
  2157. };
  2158. // Display the file name
  2159. //
  2160. cbRead = GetDlgItemText(hwnd, CID_T_ST_FILE, lpBuffer, SIZE_ReceiveBuf) + 1;
  2161. if(SIZE_ReceiveBuf >= (cbRead + (lstrlen(ptd->script.szPath) * sizeof(TCHAR))))
  2162. {
  2163. wsprintf(lpBuffer+cbRead, lpBuffer, ptd->script.szPath);
  2164. SetDlgItemText(hwnd, CID_T_ST_FILE, lpBuffer+cbRead);
  2165. }
  2166. else
  2167. {
  2168. ASSERT(FALSE);
  2169. SetDlgItemText(hwnd, CID_T_ST_FILE, ptd->script.szPath);
  2170. }
  2171. LocalFree(lpBuffer);
  2172. // Init script track
  2173. //
  2174. for (iLine = 0, cLine = Edit_GetLineCount(hCtrl);
  2175. iLine < cLine; iLine++)
  2176. {
  2177. iMark = Edit_LineIndex(hCtrl, iLine);
  2178. Edit_SetSel(hCtrl, iMark, iMark);
  2179. Edit_ReplaceSel(hCtrl, TRACE_UNMARK);
  2180. };
  2181. // Initialize script tracking
  2182. //
  2183. PostProcessScript(hwnd);
  2184. return TRUE;
  2185. }
  2186. /*----------------------------------------------------------------------------
  2187. ** Trace the script
  2188. **----------------------------------------------------------------------------
  2189. */
  2190. void NEAR PASCAL TrackScriptLine(PTERMDLG ptd, DWORD iLine)
  2191. {
  2192. HWND hCtrl;
  2193. UINT iMark, iRange;
  2194. #pragma data_seg(DATASEG_READONLY)
  2195. const static char c_szLastline[] = {0x0d, 0x0a, ' '};
  2196. #pragma data_seg()
  2197. ASSERT(0 <= iLine || INVALID_SCRIPT_LINE == iLine);
  2198. hCtrl = GetDlgItem(ptd->hwndDbg, CID_T_EB_SCRIPT);
  2199. // Do not update the screen until we are done
  2200. //
  2201. SendMessage(hCtrl, WM_SETREDRAW, (WPARAM )FALSE, 0);
  2202. if ((ptd->iMarkLine != iLine) || (iLine == INVALID_SCRIPT_LINE))
  2203. {
  2204. // Remove the old mark
  2205. //
  2206. iMark = Edit_LineIndex(hCtrl, ptd->iMarkLine);
  2207. Edit_SetSel(hCtrl, iMark, iMark+sizeof(TRACE_MARK)-1);
  2208. Edit_ReplaceSel(hCtrl, TRACE_UNMARK);
  2209. // If this is the last line, make a dummy line
  2210. //
  2211. if (iLine == INVALID_SCRIPT_LINE)
  2212. {
  2213. Edit_SetSel(hCtrl, 32767, 32767);
  2214. Edit_ReplaceSel(hCtrl, c_szLastline);
  2215. iLine = Edit_GetLineCount(hCtrl);
  2216. EnableWindow(GetDlgItem(ptd->hwndDbg, CID_T_PB_STEP), FALSE);
  2217. #ifdef PROMPT_AT_COMPLETION
  2218. // Prompt the user to continue
  2219. //
  2220. SetFocus(GetDlgItem(ptd->hwnd, IDOK));
  2221. #else
  2222. // We are done processing the script, continue automatically
  2223. //
  2224. ptd->fContinue = TRUE;
  2225. PostProcessScript(ptd->hwnd);
  2226. #endif // PROMPT_AT_COMPLETION
  2227. };
  2228. // Mark the current line
  2229. //
  2230. iMark = Edit_LineIndex(hCtrl, iLine);
  2231. Edit_SetSel(hCtrl, iMark, iMark+sizeof(TRACE_UNMARK)-1);
  2232. Edit_ReplaceSel(hCtrl, TRACE_MARK);
  2233. ptd->iMarkLine = iLine;
  2234. }
  2235. else
  2236. {
  2237. iMark = Edit_LineIndex(hCtrl, iLine);
  2238. };
  2239. // Select the current line
  2240. //
  2241. iRange = Edit_LineLength(hCtrl, iMark)+1;
  2242. Edit_SetSel(hCtrl, iMark, iMark+iRange);
  2243. // Update the screen now
  2244. //
  2245. SendMessage(hCtrl, WM_SETREDRAW, (WPARAM )TRUE, 0);
  2246. InvalidateRect(hCtrl, NULL, FALSE);
  2247. Edit_ScrollCaret(hCtrl);
  2248. return;
  2249. };