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.

695 lines
17 KiB

  1. /* File: D:\WACKER\comwsock\comnvt.c (Created: 14-Feb-1996)
  2. *
  3. * Copyright 1996 by Hilgraeve Inc. -- Monroe, MI
  4. * All rights reserved
  5. *
  6. * $Revision: 6 $
  7. * $Date: 3/26/02 5:05p $
  8. */
  9. //#define DEBUGSTR
  10. #include <windows.h>
  11. #pragma hdrstop
  12. #include <tdll\stdtyp.h>
  13. #if defined (INCL_WINSOCK)
  14. #include <tdll\session.h>
  15. #include <tdll\com.h>
  16. #include <tdll\comdev.h>
  17. #include <comstd\comstd.hh>
  18. #include "comwsock.hh"
  19. #include <tdll\assert.h>
  20. #include <tdll\htchar.h>
  21. #include <emu\emu.h>
  22. static PSTOPT LookupOption( ST_STDCOM *hhDriver, ECHAR mc );
  23. // This is the "Network Virtual Terminal" emulation, i.e., the code
  24. // that handles Telnet option negotiations. WinSockNetworkVirtualTerminal
  25. // is called to check incoming data to see if there is
  26. // a Telnet command in there.
  27. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  28. * FUNCTION:
  29. * WinSockCreateNVT
  30. *
  31. * DESCRIPTION:
  32. * This function is called to create the necessary hooks and stuff to create
  33. * a Telnet NVT(network virtual terminal).
  34. *
  35. * PARAMETERS:
  36. * hhDriver -- private connection handle
  37. *
  38. * RETURNS:
  39. * Nothing.
  40. *
  41. * AUTHOR
  42. * mcc 01/09/96 (Ported from NPORT)
  43. */
  44. VOID WinSockCreateNVT(ST_STDCOM * hhDriver)
  45. {
  46. int ix;
  47. DbgOutStr("WinSockCreateNVT\r\n", 0,0,0,0,0);
  48. hhDriver->NVTstate = NVT_THRU;
  49. hhDriver->stMode[ECHO_MODE].option = TELOPT_ECHO;
  50. hhDriver->stMode[SGA_MODE].option = TELOPT_SGA;
  51. hhDriver->stMode[TTYPE_MODE].option = TELOPT_TTYPE;
  52. hhDriver->stMode[BINARY_MODE].option = TELOPT_BINARY;
  53. hhDriver->stMode[NAWS_MODE].option = TELOPT_NAWS;
  54. for (ix = 0; ix < MODE_MAX; ++ix) //jkh 6/18/98
  55. {
  56. hhDriver->stMode[ix].us = hhDriver->stMode[ix].him = NO;
  57. hhDriver->stMode[ix].usq = hhDriver->stMode[ix].himq = EMPTY;
  58. }
  59. }
  60. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  61. * FUNCTION:
  62. * WinSockReleaseNVT
  63. *
  64. * DESCRIPTION:
  65. * This function is currently a stub
  66. *
  67. * PARAMETERS:
  68. * hhDriver -- private connection handle
  69. *
  70. * RETURNS:
  71. * Nothing.
  72. */
  73. VOID WinSockReleaseNVT(ST_STDCOM * hhDriver)
  74. {
  75. DbgOutStr("WS releaseNVT\r\n", 0,0,0,0,0);
  76. }
  77. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  78. * FUNCTION:
  79. * WinSockGotDO
  80. *
  81. * DESCRIPTION:
  82. * Handles the case of us agreeing that the other side should enable an option
  83. *
  84. * PARAMETERS:
  85. * hhDriver -- private handle for this connection driver
  86. * pstO -- Telnet options data structure
  87. *
  88. * RETURNS:
  89. * nothing
  90. *
  91. * AUTHOR:
  92. * mcc 01/09/96 (Ported from NPORT)
  93. */
  94. VOID WinSockGotDO (ST_STDCOM * hhDriver, const PSTOPT pstO)
  95. {
  96. DbgOutStr("Got DO: %lx\r\n", pstO->option, 0,0,0,0);
  97. switch (pstO->us)
  98. {
  99. case NO:
  100. // We were off, but server want's us on so we agree and respond
  101. pstO->us = YES;
  102. WinSockSendMessage(hhDriver, WILL, pstO->option);
  103. break;
  104. case YES:
  105. // Ignore, we're already enabled
  106. break;
  107. case WANTNO:
  108. // This is an error,we had sent a WON'T and they responded with DO
  109. if (pstO->usq == EMPTY)
  110. pstO->us = NO; // leave option as WE wanted it
  111. else if (pstO->usq == OPPOSITE) // we were going to enable anyway so turn us on
  112. pstO->us = YES;
  113. pstO->usq = EMPTY;
  114. break;
  115. case WANTYES:
  116. // They're agreeing with our earlier WILL
  117. if (pstO->usq == EMPTY)
  118. {
  119. pstO->us = YES; // all done negotiating
  120. }
  121. else if (pstO->usq == OPPOSITE)
  122. {
  123. // we changed our mind while negotiating, renegotiate for WONT
  124. pstO->us = WANTNO;
  125. pstO->usq = EMPTY;
  126. WinSockSendMessage(hhDriver, WONT, pstO->option);
  127. }
  128. break;
  129. default:
  130. assert(FALSE);
  131. break;
  132. }
  133. // If the NAWS option was just turned on, we must respond with our terminal size
  134. // right away. (The WinsockSendNAWS function will check whether the option is now
  135. // on or off).
  136. if ( pstO->option == TELOPT_NAWS )
  137. WinSockSendNAWS( hhDriver );
  138. }
  139. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  140. * FUNCTION:
  141. * WinSockGotWILL
  142. *
  143. * DESCRIPTION:
  144. * Handles the case of getting a WILL response from the remote Telnet,
  145. * indicating that an option will be enabled
  146. *
  147. * PARAMETERS:
  148. * hhDriver -- private handle for this connection driver
  149. * pstO -- Telnet options data structure
  150. *
  151. * RETURNS:
  152. * nothing
  153. *
  154. * AUTHOR:
  155. * mcc 01/09/96 (Ported from NPORT)
  156. */
  157. VOID WinSockGotWILL(ST_STDCOM * hhDriver, const PSTOPT pstO)
  158. {
  159. DbgOutStr("Got WILL: %lx\r\n", pstO->option, 0,0,0,0);
  160. switch(pstO->him)
  161. {
  162. case NO:
  163. // He was off but want's to be on so agree and respond
  164. pstO->him = YES;
  165. WinSockSendMessage(hhDriver, DO, pstO->option);
  166. break;
  167. case YES:
  168. // He was already on so do nothing
  169. break;
  170. case WANTNO:
  171. // Error: he responded to our DONT with a WILL
  172. if (pstO->himq == EMPTY)
  173. pstO->him = NO;
  174. else if (pstO->himq == OPPOSITE)
  175. pstO->him = YES;
  176. pstO->himq = EMPTY;
  177. break;
  178. case WANTYES:
  179. // He responded to our DO with a WILL (life is good!)
  180. if (pstO->himq == EMPTY)
  181. {
  182. pstO->him = YES;
  183. }
  184. else if (pstO->himq == OPPOSITE)
  185. {
  186. // He agreed to our DO, but we changed our mind -- renegotiate
  187. pstO->him = WANTNO;
  188. pstO->himq = EMPTY;
  189. WinSockSendMessage(hhDriver, DONT, pstO->option);
  190. }
  191. break;
  192. default:
  193. assert(FALSE);
  194. break;
  195. }
  196. }
  197. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  198. * FUNCTION:
  199. * WinSockGotDONT
  200. *
  201. * DESCRIPTION:
  202. * Handles the case of getting a DONT option from the remote Telnet,
  203. * indicating a request not to implement a particular option
  204. *
  205. * PARAMETERS:
  206. * hhDriver Private driver handle
  207. * pstO
  208. *
  209. * RETURNS:
  210. * nothing
  211. */
  212. VOID WinSockGotDONT(ST_STDCOM * hhDriver, const PSTOPT pstO)
  213. {
  214. DbgOutStr("Got DONT: %lx\r\n", pstO->option, 0,0,0,0);
  215. switch (pstO->us)
  216. {
  217. case NO:
  218. // Got a DONT while we were already off, just ignore
  219. break;
  220. case YES:
  221. // Got a DONT while we were on, agree and respond
  222. pstO->us = NO;
  223. WinSockSendMessage(hhDriver, WONT, pstO->option);
  224. break;
  225. case WANTNO:
  226. // He responded to our WONT with a DONT (how appropriate)
  227. if (pstO->usq == EMPTY)
  228. {
  229. pstO->us = NO;
  230. }
  231. else if (pstO->usq == OPPOSITE)
  232. {
  233. // He agreed to our earlier WONT but we changed our mind
  234. pstO->us = WANTYES;
  235. pstO->usq = EMPTY;
  236. WinSockSendMessage(hhDriver, WILL, pstO->option);
  237. }
  238. break;
  239. case WANTYES:
  240. // He responded to our WILL with a DONT, so leave it off
  241. if (pstO->usq == EMPTY)
  242. {
  243. pstO->us = NO;
  244. }
  245. else if (pstO->usq == OPPOSITE)
  246. {
  247. // If he'd agreed to our WILL, we'd have immediately asked for WONT
  248. // but since he didn't agree, we already got what we wanted
  249. pstO->us = NO;
  250. pstO->usq = EMPTY;
  251. }
  252. break;
  253. default:
  254. assert(FALSE);
  255. break;
  256. }
  257. }
  258. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  259. * FUNCTION:
  260. *
  261. * DESCRIPTION:
  262. *
  263. * PARAMETERS:
  264. *
  265. * RETURNS:
  266. */
  267. VOID WinSockGotWONT(ST_STDCOM * hhDriver, const PSTOPT pstO)
  268. {
  269. DbgOutStr("Got WONT: %lx\r\n", pstO->option, 0,0,0,0);
  270. switch (pstO->him)
  271. {
  272. case NO:
  273. // Got a WONT while he was already off, just ignore
  274. break;
  275. case YES:
  276. // He wants to change from on to off, agree and respond
  277. pstO->him = NO;
  278. WinSockSendMessage(hhDriver, DONT, pstO->option);
  279. break;
  280. case WANTNO:
  281. // He responded to our DONT with a WONT (how agreeable of him)
  282. if (pstO->himq == EMPTY)
  283. {
  284. pstO->him = NO;
  285. }
  286. else if (pstO->himq == OPPOSITE)
  287. {
  288. // He agreed to our DONT but we changed our mind while waiting
  289. pstO->him = WANTYES;
  290. pstO->himq = EMPTY;
  291. WinSockSendMessage(hhDriver, DO, pstO->option);
  292. }
  293. break;
  294. case WANTYES:
  295. // He responded to our DO with a WONT -- let the wimp have his way
  296. if (pstO->himq == EMPTY)
  297. {
  298. pstO->him = NO;
  299. }
  300. else if (pstO->himq == OPPOSITE)
  301. {
  302. // If he'd agreed to our DO, we'd have asked for a DONT so
  303. // now we're happy anyway
  304. pstO->him = NO;
  305. pstO->himq = EMPTY;
  306. }
  307. break;
  308. default:
  309. assert(FALSE);
  310. break;
  311. }
  312. }
  313. /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  314. * FUNCTION:
  315. * WinSockNetworkVirtualTerminal
  316. *
  317. * DESCRIPTION:
  318. * called from CLoop to handle Telnet option negotiation
  319. *
  320. * PARAMETERS:
  321. * mc The current character being processed
  322. * pD Pointer to Winsock connection driver private handle
  323. *
  324. * RETURNS:
  325. * NVT_DISCARD if mc is to be discarded
  326. * NVT_KEEP if mc is to be processed further
  327. *
  328. * AUTHOR
  329. * mcc 01/09/96 (mostly from NPORT)
  330. */
  331. int FAR PASCAL WinSockNetworkVirtualTerminal(ECHAR mc, void *pD)
  332. {
  333. ST_STDCOM * hhDriver = (ST_STDCOM *)pD;
  334. #ifdef INCL_USER_DEFINED_BACKSPACE_AND_TELNET_TERMINAL_ID
  335. STEMUSET stEmuSet;
  336. #else
  337. int nTtype;
  338. #endif
  339. LPSTR pszPtr;
  340. UCHAR acTerm[64];
  341. HEMU hEmu;
  342. HSESSION hSession;
  343. PSTOPT pstTelnetOpt;
  344. assert(hhDriver);
  345. //DbgOutStr("NVT %d %c(0x%x = %d)\n", hhDriver->NVTstate,
  346. // ((mc == 0)? ' ': mc), mc, mc,0);
  347. switch (hhDriver->NVTstate)
  348. {
  349. case NVT_THRU:
  350. if (mc == IAC)
  351. {
  352. hhDriver->NVTstate = NVT_IAC;
  353. return NVT_DISCARD ;
  354. }
  355. return NVT_KEEP ;
  356. case NVT_IAC:
  357. switch (mc)
  358. {
  359. case IAC:
  360. hhDriver->NVTstate = NVT_THRU; // Got a doubled IAC, keep one
  361. return NVT_KEEP ;
  362. case DONT:
  363. hhDriver->NVTstate = NVT_DONT;
  364. return NVT_DISCARD ;
  365. case DO:
  366. hhDriver->NVTstate = NVT_DO;
  367. return NVT_DISCARD ;
  368. case WONT:
  369. hhDriver->NVTstate = NVT_WONT;
  370. return NVT_DISCARD ;
  371. case WILL:
  372. hhDriver->NVTstate = NVT_WILL;
  373. return NVT_DISCARD ;
  374. case SB:
  375. hhDriver->NVTstate = NVT_SB;
  376. return NVT_DISCARD ;
  377. case GA:
  378. case EL:
  379. case EC:
  380. case AYT:
  381. case AO:
  382. case IP:
  383. case BREAK:
  384. case DM:
  385. case SE:
  386. //mscMessageBeep((UINT)-1);
  387. hhDriver->NVTstate = NVT_THRU;
  388. return NVT_DISCARD ; // ignore all these
  389. case NOP:
  390. default:
  391. hhDriver->NVTstate = NVT_THRU;
  392. return NVT_KEEP;
  393. }
  394. case NVT_WILL:
  395. pstTelnetOpt = LookupOption( hhDriver, mc );
  396. if ( pstTelnetOpt )
  397. WinSockGotWILL( hhDriver, pstTelnetOpt ); // We support the option, negotiate
  398. else
  399. WinSockSendMessage( hhDriver, DONT, mc ); // We don't support it, decline
  400. hhDriver->NVTstate = NVT_THRU;
  401. return NVT_DISCARD ;
  402. case NVT_WONT:
  403. pstTelnetOpt = LookupOption( hhDriver, mc );
  404. if ( pstTelnetOpt )
  405. WinSockGotWONT( hhDriver, pstTelnetOpt ); // We support the option, negotiate
  406. // Since we don't support this option, it is always off, and we never respond
  407. // when the other side tries to set a state that already exists
  408. hhDriver->NVTstate = NVT_THRU;
  409. return NVT_DISCARD ;
  410. case NVT_DO:
  411. pstTelnetOpt = LookupOption( hhDriver, mc );
  412. if ( pstTelnetOpt )
  413. WinSockGotDO( hhDriver, pstTelnetOpt ); // We support the option, negotiate
  414. else
  415. WinSockSendMessage( hhDriver, WONT, mc ); // We don't support it, decline
  416. hhDriver->NVTstate = NVT_THRU;
  417. return NVT_DISCARD ;
  418. case NVT_DONT:
  419. pstTelnetOpt = LookupOption( hhDriver, mc );
  420. if ( pstTelnetOpt )
  421. WinSockGotDONT( hhDriver, pstTelnetOpt ); // We support the option, negotiate
  422. // Since we don't support this option, it is always off, and we never respond
  423. // when the other side tries to set a state that already exists
  424. hhDriver->NVTstate = NVT_THRU;
  425. return NVT_DISCARD ;
  426. case NVT_SB:
  427. /* At this time we only handle one sub-negotiation */
  428. switch (mc)
  429. {
  430. case TELOPT_TTYPE:
  431. hhDriver->NVTstate = NVT_SB_TT;
  432. return NVT_DISCARD ;
  433. default:
  434. break;
  435. }
  436. hhDriver->NVTstate = NVT_THRU;
  437. return NVT_KEEP;
  438. case NVT_SB_TT:
  439. switch (mc)
  440. {
  441. case TELQUAL_SEND:
  442. hhDriver->NVTstate = NVT_SB_TT_S;
  443. return NVT_DISCARD ;
  444. default:
  445. break;
  446. }
  447. hhDriver->NVTstate = NVT_THRU;
  448. return NVT_KEEP;
  449. case NVT_SB_TT_S:
  450. switch (mc)
  451. {
  452. case IAC:
  453. hhDriver->NVTstate = NVT_SB_TT_S_I;
  454. return NVT_DISCARD ;
  455. default:
  456. break;
  457. }
  458. hhDriver->NVTstate = NVT_THRU;
  459. return NVT_KEEP;
  460. case NVT_SB_TT_S_I:
  461. switch (mc)
  462. {
  463. case SE:
  464. memset(acTerm, 0, sizeof(acTerm));
  465. pszPtr = (LPSTR)acTerm;
  466. *pszPtr++ = (UCHAR)IAC;
  467. *pszPtr++ = (UCHAR)SB;
  468. *pszPtr++ = (UCHAR)TELOPT_TTYPE;
  469. *pszPtr++ = (UCHAR)TELQUAL_IS;
  470. ComGetSession(hhDriver->hCom, &hSession);
  471. assert(hSession);
  472. hEmu = sessQueryEmuHdl(hSession);
  473. assert(hEmu);
  474. #ifdef INCL_USER_DEFINED_BACKSPACE_AND_TELNET_TERMINAL_ID
  475. // The telnet terminal ids are no longer hard-coded. We
  476. // are now using the terminal id that is supplied by the
  477. // user in the "Settings" properties page. - cab:11/18/96
  478. //
  479. emuQuerySettings(hEmu, &stEmuSet);
  480. strcpy(pszPtr, stEmuSet.acTelnetId);
  481. #else
  482. nTtype = emuQueryEmulatorId(hEmu);
  483. switch (nTtype)
  484. {
  485. case EMU_ANSI:
  486. strcpy(pszPtr, "ANSI");
  487. break;
  488. case EMU_TTY:
  489. strcpy(pszPtr, "TELETYPE-33");
  490. break;
  491. case EMU_VT52:
  492. strcpy(pszPtr, "DEC-VT52");
  493. break;
  494. case EMU_VT100:
  495. // strcpy(pszPtr, "VT100");
  496. strcpy(pszPtr, "DEC-VT100");
  497. break;
  498. #if defined(INCL_VT220)
  499. case EMU_VT220:
  500. // strcpy(pszPtr, "VT220");
  501. strcpy(pszPtr, "DEC-VT220");
  502. break;
  503. #endif
  504. #if defined(INCL_VT320)
  505. case EMU_VT220:
  506. // strcpy(pszPtr, "VT320");
  507. strcpy(pszPtr, "DEC-VT320");
  508. break;
  509. #endif
  510. #if defined(INCL_VT100PLUS)
  511. case EMU_VT100PLUS:
  512. // strcpy(pszPtr, "VT100");
  513. strcpy(pszPtr, "DEC-VT100");
  514. break;
  515. #endif
  516. #if defined(INCL_VTUTF8)
  517. case EMU_VTUTF8:
  518. strcpy(pszPtr, "VT-UTF8");
  519. break;
  520. #endif
  521. default:
  522. strcpy(pszPtr, "DEC-VT100"); // "UNKNOWN");
  523. break;
  524. }
  525. #endif
  526. DbgOutStr("NVT: Terminal=%s", pszPtr, 0,0,0,0);
  527. pszPtr = pszPtr + strlen(pszPtr);
  528. *pszPtr++ = (UCHAR)IAC;
  529. *pszPtr++ = (UCHAR)SE;
  530. WinSockSendBuffer(hhDriver,
  531. (INT)(pszPtr - (LPSTR)acTerm),
  532. (LPSTR)acTerm);
  533. hhDriver->NVTstate = NVT_THRU;
  534. return NVT_DISCARD ;
  535. default:
  536. break;
  537. }
  538. hhDriver->NVTstate = NVT_THRU;
  539. return NVT_KEEP;
  540. default:
  541. hhDriver->NVTstate = NVT_THRU;
  542. return NVT_KEEP;
  543. }
  544. }
  545. //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  546. // FUNCTION: WinSockSendNAWS
  547. //
  548. // DESCRIPTION: Sends our terminal dimensions according to the Telnet NAWS option
  549. // specification. NAWS stands for Negotiate About Window Size. It is
  550. // defined in RFC 1073, "Telnet Window Size Option". If a telnet server
  551. // enables this capability by sending us an IAC DO NAWS sequence, we
  552. // will agree to it by responding with IAC WILL NAWS and then sending
  553. // the number of rows and columns in a sub-option negotiation sequence
  554. // as implemented here. We also send the sub-option sequence whenever
  555. // our terminal size changes.
  556. //
  557. // ARGUMENTS: hhDriver -- pointer to our com driver
  558. //
  559. // RETURNS: void
  560. //
  561. // AUTHOR: John Hile, 6/17/98
  562. //
  563. VOID WinSockSendNAWS( ST_STDCOM *hhDriver )
  564. {
  565. HEMU hEmu;
  566. HSESSION hSession;
  567. int iRows;
  568. int iCols;
  569. UCHAR achOutput[9]; // exact size
  570. // We've been asked to send our terminal size to the server. We're only
  571. // allowed to do so if we have successfully enabled the NAWS option with
  572. // the server.
  573. if ( hhDriver->stMode[NAWS_MODE].us == YES)
  574. {
  575. // OK, option has been turned on. Send
  576. // "IAC SB NAWS WIDTH[1] WIDTH[0] HEIGHT[1] HEIGHT[0] IAC SE" to server
  577. // Get actual terminal size (not menu settings) from emulator
  578. ComGetSession(hhDriver->hCom, &hSession);
  579. assert(hSession);
  580. hEmu = sessQueryEmuHdl(hSession);
  581. assert(hEmu);
  582. emuQueryRowsCols( hEmu, &iRows, &iCols );
  583. achOutput[0] = (UCHAR)IAC;
  584. achOutput[1] = (UCHAR)SB;
  585. achOutput[2] = (UCHAR)TELOPT_NAWS;
  586. achOutput[3] = (UCHAR)(iCols / 0xFF);
  587. achOutput[4] = (UCHAR)(iCols % 0xFF);
  588. achOutput[5] = (UCHAR)(iRows / 0xFF);
  589. achOutput[6] = (UCHAR)(iRows % 0xFF);
  590. achOutput[7] = (UCHAR)IAC;
  591. achOutput[8] = (UCHAR)SE;
  592. WinSockSendBuffer(hhDriver, sizeof(achOutput), (LPSTR)achOutput);
  593. }
  594. }
  595. //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  596. // FUNCTION: LookupTelnetOption
  597. //
  598. // DESCRIPTION: Searches our table of telnet option management structures to
  599. // see whether we support the option coded by character mc.
  600. //
  601. // ARGUMENTS: hhDriver -- our comm driver handle
  602. // mc -- the character that defines the option we're looking up
  603. //
  604. // RETURNS: Pointer to the option management structure if found or NULL otherwise
  605. //
  606. // AUTHOR: John Hile, 6/17/98
  607. //
  608. static PSTOPT LookupOption( ST_STDCOM *hhDriver, ECHAR mc )
  609. {
  610. int ix;
  611. for (ix = 0; ix < MODE_MAX; ix++)
  612. if (hhDriver->stMode[ix].option == mc)
  613. return &hhDriver->stMode[ix];
  614. return (PSTOPT)0;
  615. }
  616. #endif