Source code of Windows XP (NT5)
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.

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