home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / games / volume14 / okbrdge2 / part08 < prev    next >
Encoding:
Internet Message Format  |  1993-01-26  |  53.7 KB

  1. Path: uunet!zephyr.ens.tek.com!master!saab!billr
  2. From: billr@saab.CNA.TEK.COM (Bill Randle)
  3. Newsgroups: comp.sources.games
  4. Subject: v14i086:  okbridge2 - computer-mediated bridge game, Part08/14
  5. Message-ID: <3525@master.CNA.TEK.COM>
  6. Date: 7 Sep 92 21:42:11 GMT
  7. Sender: news@master.CNA.TEK.COM
  8. Lines: 2023
  9. Approved: billr@saab.CNA.TEK.COM
  10.  
  11. Submitted-by: mclegg@cs.UCSD.EDU (Matthew Clegg)
  12. Posting-number: Volume 14, Issue 86
  13. Archive-name: okbridge2/Part08
  14. Supersedes: okbridge: Volume 13, Issue 16-22
  15. Environment: BSD-derived Unix, NeXT, curses, sockets
  16.  
  17.  
  18.  
  19. #! /bin/sh
  20. # This is a shell archive.  Remove anything before this line, then unpack
  21. # it by saving it into a file and typing "sh file".  To overwrite existing
  22. # files, type "sh file -c".  You can also feed this as standard input via
  23. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  24. # will see the following message at the end:
  25. #        "End of archive 8 (of 14)."
  26. # Contents:  gps.c input.c
  27. # Wrapped by billr@saab on Mon Sep  7 14:33:37 1992
  28. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  29. if test -f 'gps.c' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'gps.c'\"
  31. else
  32. echo shar: Extracting \"'gps.c'\" \(20883 characters\)
  33. sed "s/^X//" >'gps.c' <<'END_OF_FILE'
  34. X/* GPS.c -- global playing service
  35. X
  36. X   Copyright (C) 1990-1992 by Matthew Clegg.  All Rights Reserved
  37. X
  38. X   OKbridge is made available as a free service to the Internet.
  39. X   Accordingly, the following restrictions are placed on its use:
  40. X
  41. X   1.  OKbridge may not be modified in any way without the explicit 
  42. X       permission of Matthew Clegg.  
  43. X
  44. X   2.  OKbridge may not be used in any way for commercial advantage.
  45. X       It may not be placed on for-profit networks or on for-profit
  46. X       computer systems.  It may not be bundled as part of a package
  47. X       or service provided by a for-profit organization.
  48. X
  49. X   If you have questions about restrictions on the use of OKbridge,
  50. X   write to mclegg@cs.ucsd.edu.
  51. X
  52. X   DISCLAIMER:  The user of OKbridge accepts full responsibility for any
  53. X   damage which may be caused by OKbridge.
  54. X
  55. X
  56. X   The global playing service is a global database of player information.
  57. X   In the initial implementation, it will contain only information
  58. X   about currently playing tables.  At a later date, we will add
  59. X   capabilities for downloading sets of boards and playing competitively.
  60. X
  61. X   Implementation Notes:
  62. X
  63. X   1. The GPS is implemented on a strictly request/response basis,
  64. X      similar in principle to a remote procedure call.  However,
  65. X      rather than using RPC, we use TCP streams.  This is done because
  66. X      I don't know if a standard implementation of RPC is available 
  67. X      on all BSD systems.  Also, some of the data we communicate doesn't
  68. X      seem to fit easily into the RPC model.
  69. X
  70. X   2. When we first start up, we will attempt to connect to the GPS
  71. X      and print out a message of the day along with a list of
  72. X      currently playing tables.  Thereafter, we will hold open the
  73. X      GPS connection.
  74. X
  75. X   3. When a GPS request is made, we may not even have the connection
  76. X      open.  Therefore, the request/response loop will have the 
  77. X      following structure:
  78. X
  79. X        A. Determine if the connection is still open.
  80. X           If not, attempt to connect.  If the attempt is unsuccessful,
  81. X           then exit with an error.
  82. X
  83. X        B. Transfer our request to the GPS.
  84. X
  85. X    C. Enter a wait-loop waiting for the response.  In this loop,
  86. X           we should monitor the other communication channels (e.g.,
  87. X           the keyboard and network) so as to allow a user interrupt
  88. X           if necessary.
  89. X
  90. X    D. Process the GPS response.
  91. X
  92. X   Unfortunately, this module is not re-entrant.  This can be a bit of a
  93. X   pain since the GPS_read_line procedure calls Wait_for_input in
  94. X   input.c, which in turn calls Wait_for_event in network.c.
  95. X
  96. X*/
  97. X
  98. X#include <stdio.h>
  99. X#include <string.h>
  100. X#include <errno.h>
  101. X#include <ctype.h>
  102. X#include <time.h>
  103. X
  104. X#define GPS
  105. X#include "types.h"
  106. X#include "state.h"
  107. X#include "display.h"
  108. X#include "parser.h"
  109. X#include "socket.h"
  110. X#include "terminal.h"
  111. X#include "conversation.h"
  112. X#include "input.h"
  113. X#include "gps_info.h"
  114. X#include "gps.h"
  115. X
  116. X#ifdef GCC
  117. Xextern void close ();
  118. Xextern int  getpid ();
  119. Xextern void srand ();
  120. Xextern sscanf ();
  121. Xextern fclose ();
  122. Xextern fprintf ();
  123. X#endif
  124. X
  125. Xextern int  No_connections ();
  126. Xextern char *strdup ();
  127. X
  128. Xextern char *sys_errlist[];
  129. Xextern int errno;
  130. X
  131. Xstatic char GPS_buf [100];
  132. Xstatic char GPS_error_buf [100];
  133. X
  134. X#ifdef LOGFILE
  135. Xextern FILE *net_log;
  136. X#endif
  137. X
  138. Xstatic void GPS_Comment (msg)
  139. X     char *msg;
  140. X{
  141. X  Display_Player_Comment (COMMENT_PRIVATE, "GPS", msg);
  142. X}
  143. X
  144. X
  145. Xstatic void GPS_transmit (buf)
  146. X     char *buf;
  147. X/* Transmits the buf to the GPS. */
  148. X{
  149. X  int n = strlen(buf);
  150. X
  151. X  if (n > 0)
  152. X    fd_writeln (GPS_socket, buf);
  153. X  else
  154. X    fd_writeln (GPS_socket, " ");
  155. X
  156. X#ifdef LOGFILE
  157. X  fprintf (net_log, "GPS> %s\n", buf);
  158. X  fflush (net_log);
  159. X#endif
  160. X}
  161. X
  162. Xstatic void Close_GPS_socket ()
  163. X/* If we are connected to the GPS, then closes the current connection. */
  164. X{
  165. X  if (GPS_socket > 0)
  166. X    close (GPS_socket);
  167. X
  168. X  GPS_socket = 0;
  169. X  GPS_request_in_progress = 0;
  170. X}
  171. X
  172. Xvoid GPS_Reset ()
  173. X{
  174. X  Close_GPS_socket ();
  175. X}
  176. X
  177. Xstatic int GPS_data_available_event ()
  178. X{
  179. X  return(Check_for_data (GPS_socket));
  180. X}
  181. X
  182. Xstatic int GPS_readline (buf, buflen)
  183. X     char *buf; int buflen;
  184. X/* Reads a line of data from the GPS without blocking.  Returns -1 if
  185. X   the "end-of-request" marker is received and -2 if an error occurs
  186. X   during the read.  Otherwise, returns the number of bytes transferred.
  187. X*/
  188. X{
  189. X  int status;
  190. X
  191. X  if (GPS_unavailable || !Use_GPS)
  192. X    return (-2);
  193. X
  194. X  Refresh_Input_Buffers ();
  195. X  Wait_for_event_at_game_level (GPS_data_available_event);
  196. X  status = Check_for_data (GPS_socket);
  197. X
  198. X  if (status < 0) {
  199. X    Close_GPS_socket ();
  200. X    return (-2);
  201. X  }
  202. X
  203. X  status = fd_readln (GPS_socket, buf, buflen);
  204. X
  205. X  if (status < 0) {
  206. X    if (status == 0)
  207. X      GPS_Comment ("Zero length transmission.");
  208. X    Close_GPS_socket ();
  209. X    return (-2);
  210. X  }
  211. X
  212. X  while ((status > 0) && isspace(buf[status-1]))
  213. X    buf[--status] = '\0';
  214. X
  215. X#ifdef LOGFILE
  216. X  fprintf (net_log, "GPS< %s\n", buf);
  217. X  fflush (net_log);
  218. X#endif
  219. X
  220. X  if(!strcmp(buf, GPS_RESPONSE_MARKER)) {
  221. X    GPS_request_in_progress = 0;
  222. X    return (-1);
  223. X  } else
  224. X    return (status);
  225. X}
  226. X
  227. Xstatic void GPS_Ask_for_response ()
  228. X{
  229. X  GPS_transmit (GPS_REQUEST_MARKER);
  230. X}
  231. X
  232. Xstatic void Conclude_GPS_request ()
  233. X/* Reads data from the GPS socket until the end of request is encountered. */
  234. X{
  235. X  while (GPS_request_in_progress)
  236. X    GPS_readline (GPS_buf, 100);
  237. X}
  238. X
  239. Xstatic void Check_GPS_connection ()
  240. X/* Checks if the connection to the GPS has been closed or if an
  241. X   interrupt occurred during the last transmit operation.  If so, then
  242. X   resets the connection to a state from which we can proceed. 
  243. X*/
  244. X{
  245. X  char buf[100];
  246. X  int status;
  247. X
  248. X  if (GPS_request_in_progress) {
  249. X    Close_GPS_socket ();
  250. X    return;
  251. X  }
  252. X
  253. X  if (GPS_socket == 0)
  254. X    return;
  255. X
  256. X  status = Check_for_data (GPS_socket);
  257. X  while (status) {
  258. X    if (status < 0) {
  259. X      Close_GPS_socket ();
  260. X      return;
  261. X    }
  262. X    status = fd_readln (GPS_socket, buf, 100);
  263. X    if (status <= 0) {
  264. X      Close_GPS_socket ();
  265. X      return;
  266. X    }
  267. X    status = Check_for_data (GPS_socket);
  268. X  }
  269. X}
  270. X
  271. Xstatic int Perform_GPS_handshake ()
  272. X/* Sends the handshake information to the GPS to test and also
  273. X   requests handshake information in return.  Verifies that we are
  274. X   running the same version as the server.  If the verification succeeds,
  275. X   then returns 0.  If we are running different versions, then returns 1.
  276. X   If some other error occurs, then returns -1.
  277. X*/
  278. X{
  279. X  char mbuf[100], kbuf[100];
  280. X
  281. X  sprintf (mbuf, "GPS %s", GPS_version);
  282. X  GPS_transmit (mbuf);
  283. X
  284. X  GPS_readline(kbuf, 100);
  285. X  if (strcmp(mbuf, kbuf)) {
  286. X    sprintf (GPS_error_buf, 
  287. X         "GPS SERVER IS INCOMPATIBLE -- SERVER REPORTS VERSION %s", kbuf);
  288. X    return (1);
  289. X  }
  290. X
  291. X  if (local_player_email != NULL)
  292. X    sprintf (mbuf, "%s%s %s %s %s %s", 
  293. X         major_revision_level, minor_revision_level,
  294. X         Host_IP, Registration, User_name, User_fullname);
  295. X  else
  296. X    sprintf (mbuf, "%s%s %s %s %s %s", 
  297. X         major_revision_level, minor_revision_level,
  298. X         Host_IP, Host_name, User_name, User_fullname);
  299. X
  300. X  GPS_transmit (mbuf);
  301. X  return (0);
  302. X}
  303. X
  304. Xstatic int Open_GPS_connection ()
  305. X/* Opens the connection to the GPS.  If an error occurs, then sets
  306. X   GPS_unavailable to 1 and exits with code -1.  If no error occurs,
  307. X   returns 0.
  308. X*/
  309. X{
  310. X  int status;
  311. X
  312. X  Check_GPS_connection ();
  313. X
  314. X  GPS_request_in_progress = 1;
  315. X  if (GPS_socket > 0)
  316. X    return (0);
  317. X
  318. X  Status ("CONNECTING TO GLOBAL PLAYER SERVICE ...");
  319. X  restore_cursor ();
  320. X  status = client_init (GPS_IP, GPS_port, 1);
  321. X  if (status < 0) {
  322. X    Status ("CONNECTING TO GLOBAL PLAYER SERVICE ... UNABLE TO CONNECT");
  323. X    GPS_unavailable = 1;
  324. X    GPS_request_in_progress = 0;
  325. X    return (-1);
  326. X  }
  327. X
  328. X  GPS_socket = status;
  329. X  status = Perform_GPS_handshake ();
  330. X  if (status < 0) {
  331. X    Status ("CONNECTING TO GLOBAL PLAYER SERVICE ... ERROR IN HANDSHAKE");
  332. X    restore_cursor ();
  333. X    Close_GPS_socket ();
  334. X    GPS_unavailable = 1;
  335. X    GPS_request_in_progress = 0;
  336. X    return (-1);
  337. X  } else if (status) {
  338. X    GPS_Comment (GPS_error_buf);
  339. X    GPS_Comment ("DO YOU NEED TO UPDATE YOUR COPY OF OKBRIDGE?");
  340. X    restore_cursor ();
  341. X    GPS_unavailable = 1;
  342. X    GPS_request_in_progress = 0;
  343. X    return (-1);
  344. X  }
  345. X
  346. X  Status ("CONNECTING TO GLOBAL PLAYER SERVICE ... CONNECTION ESTABLISHED");
  347. X  Refresh_Input_Buffers ();
  348. X  return (0);
  349. X}
  350. X
  351. X
  352. X#define CHECK_GPS(x)  if(GPS_check_status()) x
  353. X
  354. Xstatic int GPS_check_status ()
  355. X/* Checks the current status of our GPS connection.  If there is a
  356. X   reason that we cannot honor a request at the moment, we display
  357. X   an appropriate error message and return 1.  Otherwise, we return 0.
  358. X*/
  359. X{
  360. X  if (!Use_GPS) {
  361. X    Status ("WE ARE NOT USING THE GLOBAL PLAYER SERVICE NOW.");
  362. X    return (1);
  363. X  } else if (GPS_unavailable) {
  364. X    Status ("THE GLOBAL PLAYER SERVICE IS CURRENTLY UNAVAILABLE.");
  365. X    return (1);
  366. X  } else if (GPS_request_in_progress) {
  367. X#ifdef LOGFILE
  368. X    fprintf 
  369. X      (net_log, "GPS ** ERROR -- WE ARE ALREADY PROCESSING A GPS REQUEST.\n");
  370. X    fflush (net_log);
  371. X#endif
  372. X    Status ("ERROR -- WE ARE ALREADY PROCESSING A GPS REQUEST.");
  373. X    return (1);
  374. X  } else if (Open_GPS_connection()) {
  375. X    GPS_Comment ("UNABLE TO CONTACT GLOBAL PLAYER SERVICE.");
  376. X    return (1);
  377. X  }
  378. X  return (0);
  379. X
  380. X}
  381. X
  382. Xvoid GPS_Get_Message_of_the_Day ()
  383. X/* Contacts the GPS and prints out the "message of the day". */
  384. X{
  385. X  int status;
  386. X
  387. X  CHECK_GPS(return);
  388. X
  389. X  GPS_transmit ("MOTD");
  390. X  GPS_Ask_for_response ();
  391. X
  392. X  status = GPS_readline (GPS_buf, 100);
  393. X  while (status >= 0) {
  394. X    GPS_Comment (GPS_buf);
  395. X    status = GPS_readline (GPS_buf, 100);
  396. X  }
  397. X}
  398. X
  399. Xint GPS_Get_Server_IP (server_name, location, port)
  400. X     char *server_name, *location; int *port;
  401. X/* Searches the list of currently playing tables for a server whose name
  402. X   matches server_name.  If one is found, then copies the corresponding
  403. X   location and port to the buffers provided by the caller, and returns 0.
  404. X   If no match is found, returns 1.
  405. X*/
  406. X{
  407. X  int status;
  408. X
  409. X  CHECK_GPS(return(1));
  410. X
  411. X  GPS_transmit("FIND_TABLE");
  412. X  if (server_name != NULL)
  413. X    GPS_transmit(server_name);
  414. X  GPS_Ask_for_response ();
  415. X
  416. X  status = GPS_readline (GPS_buf, 100);
  417. X  if (status < 0)
  418. X    return (1);
  419. X
  420. X  sscanf (GPS_buf, "%s %d", location, port);
  421. X  Conclude_GPS_request ();
  422. X  return (0);
  423. X}
  424. X
  425. Xvoid GPS_Broadcast_Server ()
  426. X/* Sends a message to the global player service notifying it that we
  427. X   are serving a table.  Sends the names of each of the players at the
  428. X   table in the message.
  429. X*/
  430. X{
  431. X  Connection c;
  432. X
  433. X  CHECK_GPS(return);
  434. X
  435. X  GPS_transmit("SERVER");
  436. X  sprintf (GPS_buf, "%s %d %d %d %s",
  437. X       Host_IP, network_port, getpid (), No_connections (Local_table), 
  438. X       local_player_name);
  439. X  GPS_transmit (GPS_buf);
  440. X  GPS_transmit (Server_Start_time);
  441. X
  442. X/*
  443. X  sprintf (GPS_buf, "%9s %s %s", seat_names[local_player], local_player_name);
  444. X  GPS_transmit (GPS_buf);
  445. X*/
  446. X
  447. X  FOREACH_PLAYER (c, Local_table) {
  448. X    sprintf (GPS_buf, "%-9s %-8s %s", seat_names[c->seat], c->player_name,
  449. X         c->fullname);
  450. X    GPS_transmit (GPS_buf);
  451. X  }
  452. X  GPS_Ask_for_response ();
  453. X  Conclude_GPS_request ();
  454. X}
  455. X
  456. Xvoid GPS_Broadcast_Server_Silently ()
  457. X/* Same as GPS_Broadcast_Server but does not print an error message if
  458. X   we are not using the GPS or if the GPS was found to be unavailable
  459. X   in a previous call.
  460. X*/
  461. X{
  462. X  if (!Use_GPS || GPS_unavailable)
  463. X    return;
  464. X
  465. X  GPS_Broadcast_Server ();
  466. X}
  467. X
  468. Xvoid GPS_Advertise_Message (message)
  469. X     char *message;
  470. X/* If we are in server mode, then displays the given message along with
  471. X   our table announcement in the tables display. */
  472. X{
  473. X  CHECK_GPS(return);
  474. X
  475. X  GPS_transmit("MESSAGE");
  476. X  sprintf (GPS_buf, "%s %d", Host_IP, getpid ());
  477. X  GPS_transmit (GPS_buf);
  478. X
  479. X  if (message != NULL)
  480. X    GPS_transmit (message);
  481. X
  482. X  GPS_Ask_for_response ();
  483. X  Conclude_GPS_request ();
  484. X  
  485. X}
  486. X
  487. Xvoid GPS_List_Tables ()
  488. X/* Contacts the GPS for a list of the currently playing tables.
  489. X   Lists the tables at the terminal.
  490. X*/
  491. X{
  492. X  int status, no_tables, no_players, port;
  493. X  char name_buf[10], time_buf[40], out_buf[80], ip_buf[40];
  494. X
  495. X  CHECK_GPS(return);
  496. X
  497. X  GPS_transmit("TABLES");
  498. X  GPS_Ask_for_response ();
  499. X  
  500. X  no_tables = 0;
  501. X  status = GPS_readline(GPS_buf, 100);
  502. X  while (status > 0) {
  503. X    if ((no_tables == 0) && (display_mode == TALK_DISPLAY)) {
  504. X/*      GPS_Comment (" "); */
  505. X      if (terminal_lines < 25)
  506. X    Clear_Comment_Display ();
  507. X      GPS_Comment ("LIST OF CURRENTLY PLAYING TABLES");
  508. X      GPS_Comment (" ");
  509. X      GPS_Comment ("Name      IP Number        Port  Players   Start Time");
  510. X      GPS_Comment ("----      ---------        ----  -------   ----------");
  511. X    }
  512. X    status = GPS_readline (time_buf, 40);
  513. X    sscanf (GPS_buf, "%d %s %d %s", &no_players, ip_buf, &port, name_buf);
  514. X    sprintf (out_buf, "%-8s  %-16s %4d       %2d   %s", 
  515. X         name_buf, ip_buf, port, no_players, time_buf);
  516. X    GPS_Comment (out_buf);
  517. X    no_tables++;
  518. X    status = GPS_readline(GPS_buf, 100);
  519. X  }
  520. X
  521. X  if (no_tables == 0) {
  522. X    GPS_Comment ("THERE ARE NO CURRENTLY PLAYING TABLES.");
  523. X    if (server_mode)
  524. X      GPS_Comment 
  525. X    ("TYPE '/PUBLISH' TO PUBLISH THIS TABLE IN THE GPS DATABASE.");
  526. X    else
  527. X      GPS_Comment 
  528. X    ("TYPE '/SERVE' IF YOU WOULD LIKE TO SERVE A TABLE YOURSELF.");
  529. X  } else if (display_mode == TALK_DISPLAY) {
  530. X    GPS_Comment (" ");
  531. X    GPS_Comment ("TYPE '/JOIN name' TO JOIN THE TABLE SERVED BY name.");
  532. X    GPS_Comment 
  533. X      ("TYPE '/PLAYERS name' TO SEE WHO IS PLAYING AT name's TABLE.");
  534. X  }
  535. X  Conclude_GPS_request ();
  536. X}
  537. X
  538. X
  539. Xvoid GPS_List_Players (server)
  540. X     char *server;
  541. X/* Lists the players at the given table. */
  542. X{
  543. X  int status, no_players;
  544. X  int current_display_mode = display_mode;
  545. X  int current_input_mode   = input_mode;
  546. X  int lines_left_on_page;
  547. X  char buf[80];
  548. X
  549. X  CHECK_GPS(return);
  550. X
  551. X  GPS_transmit ("PLAYERS");
  552. X  GPS_transmit ((server == NULL)? "*ALL*": server);
  553. X  GPS_Ask_for_response ();
  554. X
  555. X  no_players = 0;
  556. X  if (server != NULL) {
  557. X    status = GPS_readline(GPS_buf, 100);
  558. X    while (status >= 0) {
  559. X      if ((no_players == 0) && (display_mode == TALK_DISPLAY)) {
  560. X    GPS_Comment (" ");
  561. X    sprintf (buf, "LIST OF PLAYERS AT %s's TABLE:", server);
  562. X    GPS_Comment (buf);
  563. X      }
  564. X      GPS_Comment (GPS_buf);
  565. X      status = GPS_readline(GPS_buf, 100);
  566. X      no_players += 1;
  567. X    }
  568. X    if (no_players == 0) {
  569. X      sprintf (buf, "%s IS NOT HOSTING A TABLE.", server);
  570. X      GPS_Comment (buf);
  571. X    }
  572. X  } else {
  573. X    status = GPS_readline(GPS_buf, 100);
  574. X    lines_left_on_page = 0;
  575. X    while (status >= 0) {
  576. X      if (lines_left_on_page <= 0) {
  577. X    if (no_players == 0) {
  578. X      Set_Display_Mode (TALK_DISPLAY);
  579. X      Set_Input_Mode (TALK_INPUT);
  580. X    } else
  581. X      Pause ("PRESS <ESC> TO SEE THE NEXT PAGE OF PLAYERS.");
  582. X    Clear_Comment_Display ();
  583. X    GPS_Comment (" ");
  584. X    sprintf (buf, "%-10s %-8s %s", "TABLE", "SEAT", "NAME");
  585. X    GPS_Comment (buf);
  586. X    sprintf (buf, "%-10s %-8s %s", "-----", "----", "----");
  587. X    GPS_Comment (buf);
  588. X    lines_left_on_page = terminal_lines - 9;
  589. X      }
  590. X      GPS_Comment (GPS_buf);
  591. X      status = GPS_readline(GPS_buf, 100);
  592. X      no_players += 1;
  593. X      lines_left_on_page -= 1;
  594. X    }
  595. X    if ((current_display_mode != TALK_DISPLAY) && (no_players > 0))
  596. X      Pause ("PRESS <ESC> TO CONTINUE ...");
  597. X    Set_Display_Mode (current_display_mode);
  598. X    Set_Input_Mode (current_input_mode);
  599. X    if (no_players == 0)
  600. X      GPS_Comment ("NO TABLES ARE CURRENTLY BEING SERVED.");
  601. X  }
  602. X
  603. X  GPS_request_in_progress = 0;
  604. X
  605. X}
  606. X
  607. Xvoid GPS_End_Server_Mode ()
  608. X{
  609. X  char buf[80];
  610. X
  611. X  CHECK_GPS(return);
  612. X
  613. X  GPS_transmit ("END_TABLE");
  614. X  sprintf (buf, "%s %d %d", Host_IP, getpid(), hands_played);
  615. X  GPS_transmit (buf);
  616. X  GPS_Ask_for_response ();
  617. X  Conclude_GPS_request ();
  618. X  hands_played = 0;
  619. X
  620. X}
  621. X
  622. Xvoid GPS_Directory ()
  623. X/* Returns a listing of the email duplicate files which are available. */
  624. X{
  625. X  int status;
  626. X
  627. X  CHECK_GPS(return);
  628. X
  629. X  GPS_transmit ("DIR");
  630. X  GPS_Ask_for_response ();
  631. X
  632. X  status = GPS_readline (GPS_buf, 100);
  633. X  while (status > 0) {
  634. X    GPS_Comment (GPS_buf);
  635. X    status = GPS_readline (GPS_buf, 100);
  636. X  }
  637. X}
  638. X
  639. Xvoid GPS_Download (download_file)
  640. X     char *download_file;
  641. X/* Downloads the specified file from the global player service. */
  642. X{
  643. X  int status;
  644. X  FILE *fp;
  645. X
  646. X  CHECK_GPS(return);
  647. X
  648. X  fp = fopen (download_file, "w");
  649. X  if (fp == NULL) {
  650. X    sprintf (GPS_buf, "ERROR OPENING LOCAL FILE %s: %s", download_file,
  651. X         sys_errlist[errno]);
  652. X    Moderator_Comment (GPS_buf);
  653. X    return;
  654. X  }
  655. X
  656. X  GPS_transmit ("DOWNLOAD");
  657. X  GPS_transmit (download_file);
  658. X  GPS_Ask_for_response ();
  659. X
  660. X  status = GPS_readline (GPS_buf, 100);
  661. X  if (status < 0) {
  662. X    Status ("ERROR IN ACCESSING DOWNLOAD FILE.");
  663. X    fclose (fp);
  664. X    return;
  665. X  }
  666. X
  667. X  if ((GPS_buf[0] == 'E') && (GPS_buf[1] == 'R') && (GPS_buf[2] == 'R')) {
  668. X    sprintf (GPS_buf, "THERE IS NO SUCH DUPLICATE FILE %s.", download_file);
  669. X    Status (GPS_buf);
  670. X    fclose (fp);
  671. X    return;
  672. X  }
  673. X
  674. X  Status ("DOWNLOADING FILE FROM GPS ...");
  675. X  restore_cursor ();
  676. X  while (status > 0) {
  677. X    fprintf (fp, "%s\n", GPS_buf);
  678. X    status = GPS_readline (GPS_buf, 100);
  679. X  }
  680. X  fclose (fp);
  681. X}
  682. X
  683. Xvoid GPS_Dup (scoring_mode)
  684. X     char *scoring_mode;
  685. X/* Initiates gps duplicate mode.  Scoring mode should be either 
  686. X * MP, IMP or END.
  687. X */
  688. X{
  689. X  CHECK_GPS(return);
  690. X
  691. X  GPS_transmit ("DUP");
  692. X  GPS_transmit (scoring_mode);
  693. X  GPS_Ask_for_response ();
  694. X  Conclude_GPS_request ();
  695. X}
  696. X
  697. Xstatic Board *GPS_Download_Board ()
  698. X/* Downloads a board from the GPS.  Returns NULL if no board available or
  699. X   if an error occurred.
  700. X */
  701. X{
  702. X  Board *b;
  703. X  Play_record *p;
  704. X  char buf1[100], buf2[100], buf3[100];
  705. X  int status;
  706. X
  707. X  status = GPS_readline (buf1, 100);
  708. X  if (status < 0)
  709. X    return (NULL);
  710. X
  711. X  GPS_readline (buf2, 100);
  712. X  b = Decode_board (buf1, buf2);
  713. X  if (b == NULL)
  714. X    return (NULL);
  715. X
  716. X  status = GPS_readline (buf1, 100);
  717. X  while ((status > 0) && (buf1[0] != '-')) {
  718. X    GPS_readline (buf2, 100);
  719. X    GPS_readline (buf3, 100);
  720. X    p = Decode_play_record (buf1, buf2, buf3);
  721. X    if (p == NULL)
  722. X      return (NULL);
  723. X    if (p->bidding_completed)
  724. X      Compute_contract (b, p);
  725. X    if (p->hand_completed)
  726. X      Compute_MIMP_points (b, p);
  727. X    p->next = b->play_records;
  728. X    b->play_records = p;
  729. X    status = GPS_readline (buf1, 100);
  730. X  }
  731. X  Compute_Intl_Matchpoints (b);
  732. X  Compute_Matchpoints (b);
  733. X
  734. X  if (b->scoring_mode == MP_SCORING)
  735. X    Sort_play_records_by_matchpoints (b);
  736. X  else if (b->scoring_mode == IMP_SCORING)
  737. X    Sort_play_records_by_imps (b);
  738. X
  739. X  return (b);
  740. X}
  741. X
  742. XBoard *GPS_Get_Next_Board ()
  743. X/* Downloads the next duplicate board from the GPS. */
  744. X{
  745. X  Board *b;
  746. X
  747. X  CHECK_GPS(return(NULL));
  748. X
  749. X  GPS_transmit ("BOARD");
  750. X  sprintf (GPS_buf, "%s %s %s %s",
  751. X       PLAYER_NAME(Local_table, PLAYER_NORTH),
  752. X       PLAYER_NAME(Local_table, PLAYER_EAST),
  753. X       PLAYER_NAME(Local_table, PLAYER_SOUTH),
  754. X       PLAYER_NAME(Local_table, PLAYER_WEST));
  755. X  GPS_transmit (GPS_buf);
  756. X  GPS_Ask_for_response ();
  757. X
  758. X  Status ("DOWNLOADING NEXT BOARD FROM GPS ...");
  759. X  restore_cursor ();
  760. X
  761. X  b = GPS_Download_Board ();
  762. X  Conclude_GPS_request ();
  763. X  Clear_Status ();
  764. X  return (b);
  765. X}
  766. X
  767. Xvoid GPS_Upload_Play_Record (p)
  768. X     Play_record *p;
  769. X/* Uploads the play record p for the current email board to the GPS. */
  770. X{
  771. X  char buf1[100], buf2[100], buf3[100];
  772. X
  773. X  CHECK_GPS(return);
  774. X
  775. X  GPS_transmit ("PLAYREC");
  776. X  Encode_play_record (p, buf1, buf2, buf3);
  777. X  GPS_transmit (buf1);
  778. X  GPS_transmit (buf2);
  779. X  GPS_transmit (buf3);
  780. X  GPS_Ask_for_response ();
  781. X  Conclude_GPS_request ();
  782. X}
  783. X
  784. XBoard *GPS_Download_Results (player_name)
  785. X     char *player_name;
  786. X/* Downloads the all of the recorded results in the GPS for the given 
  787. X * player.  Returns a list of boards or NULL if no results are available.
  788. X */
  789. X{
  790. X  Board *results_head, *results_tail;
  791. X
  792. X  CHECK_GPS (return(NULL));
  793. X
  794. X  GPS_transmit("RESULTS");
  795. X  GPS_transmit(player_name);
  796. X  GPS_Ask_for_response ();
  797. X
  798. X  results_head = results_tail = GPS_Download_Board ();
  799. X  while (results_tail != NULL) {
  800. X    results_tail->next = GPS_Download_Board ();
  801. X    results_tail = results_tail->next;
  802. X  }
  803. X  Conclude_GPS_request ();
  804. X  return (results_head);
  805. X}
  806. X
  807. X
  808. Xvoid  GPS_Send_Playing_Time (seconds)
  809. X     long int seconds;
  810. X/* Sends the playing time for the local player in seconds to the GPS. */
  811. X{
  812. X  CHECK_GPS (return);
  813. X
  814. X  GPS_transmit ("USAGE");
  815. X  sprintf (GPS_buf, "%s %s %8.2f", 
  816. X       Host_IP, User_name,
  817. X       ((float) seconds) / ((float) 60.0));
  818. X  GPS_transmit (GPS_buf);
  819. X  GPS_Ask_for_response ();
  820. X  Conclude_GPS_request ();
  821. X}
  822. X
  823. Xvoid GPS_Display_Scoreboard ()
  824. X/* Downloads the scoreboard from the GPS and displays it. */
  825. X{
  826. X  int line_no = 1;
  827. X  int status;
  828. X  int current_display_mode = display_mode;
  829. X  int current_input_mode = input_mode;
  830. X  char buf[80];
  831. X
  832. X  CHECK_GPS (return);
  833. X
  834. X  GPS_transmit ("SCOREBOARD");
  835. X  GPS_Ask_for_response ();
  836. X
  837. X  Set_Display_Mode (MANUAL_DISPLAY);
  838. X  Set_Input_Mode (TALK_INPUT);
  839. X
  840. X  status = GPS_readline (buf, 80);
  841. X  while (status >= 0) {
  842. X    print (line_no++, 1, buf);
  843. X    status = GPS_readline (buf, 80);
  844. X  }
  845. X
  846. X  Press_Return_to_Continue ("");
  847. X  Set_Display_Mode (current_display_mode);
  848. X  Set_Input_Mode (current_input_mode);
  849. X  Conclude_GPS_request ();
  850. X}
  851. X
  852. Xvoid GPS_Change_Name (old_name, new_name)
  853. X     char *old_name, *new_name;
  854. X{
  855. X  CHECK_GPS(return);
  856. X
  857. X  GPS_transmit("NEWNAME");
  858. X  GPS_transmit(old_name);
  859. X  GPS_transmit(new_name);
  860. X  GPS_Ask_for_response ();
  861. X  Conclude_GPS_request ();
  862. X}
  863. END_OF_FILE
  864. if test 20883 -ne `wc -c <'gps.c'`; then
  865.     echo shar: \"'gps.c'\" unpacked with wrong size!
  866. fi
  867. # end of 'gps.c'
  868. fi
  869. if test -f 'input.c' -a "${1}" != "-c" ; then 
  870.   echo shar: Will not clobber existing file \"'input.c'\"
  871. else
  872. echo shar: Extracting \"'input.c'\" \(29960 characters\)
  873. sed "s/^X//" >'input.c' <<'END_OF_FILE'
  874. X/* input.c -- Input driver for the bridge program.
  875. X *
  876. X ! Copyright (C) 1990-1992 by Matthew Clegg.  All Rights Reserved
  877. X ! 
  878. X ! OKbridge is made available as a free service to the Internet.
  879. X ! Accordingly, the following restrictions are placed on its use:
  880. X ! 
  881. X ! 1.  OKbridge may not be modified in any way without the explicit 
  882. X !     permission of Matthew Clegg.  
  883. X ! 
  884. X ! 2.  OKbridge may not be used in any way for commercial advantage.
  885. X !     It may not be placed on for-profit networks or on for-profit
  886. X !     computer systems.  It may not be bundled as part of a package
  887. X !     or service provided by a for-profit organization.
  888. X ! 
  889. X ! If you have questions about restrictions on the use of OKbridge,
  890. X ! write to mclegg@cs.ucsd.edu.
  891. X ! 
  892. X ! DISCLAIMER:  The user of OKbridge accepts full responsibility for any
  893. X ! damage which may be caused by OKbridge.
  894. X *
  895. X */
  896. X#define _INPUT_
  897. X#include <ctype.h>
  898. X#include <stdio.h>
  899. X#include <string.h>
  900. X/* #include <time.h> */
  901. X#include <sys/time.h>
  902. X#include "types.h"
  903. X#include "state.h"
  904. X#include "terminal.h"
  905. X#include "display.h"
  906. X#include "commands.h"
  907. X#include "conversation.h"
  908. X
  909. X#include "help.h"
  910. X#include "input.h"
  911. X#include "gps.h"
  912. X
  913. X#ifndef index
  914. Xextern char *index();
  915. X#endif
  916. X
  917. Xextern gettimeofday (), strcasecmp ();
  918. X
  919. Xextern int errno;
  920. Xextern char *sys_errlist[];
  921. Xextern char *strdup ();
  922. Xextern char *malloc ();
  923. X
  924. Xextern int Terminate_Program ();
  925. X
  926. X#define random(n) ((rand()/64) % (n))
  927. X
  928. X#define MAPUPPER(c) ((('a' <= (c)) && ((c) <= 'z'))? ((c) + 'A' - 'a') : (c))
  929. X
  930. X
  931. Xtypedef struct input_buffer_struct {
  932. X    char    buf[91];
  933. X    int    row, col, length;    /* aspects of the screen display. */
  934. X    int    pos;            /* cursor position in buffer. */
  935. X    int     defaulted;              /* true if input buffer contains the
  936. X                       default input. */
  937. X    char    default_input[80];      /* a default input response which
  938. X                       will be displayed if the user
  939. X                       presses return on a blank line. */
  940. X    int     active;                 /* true if this buffer is active. */
  941. X    int     focus;                  /* true if this is the focus buffer. */
  942. X    struct input_buffer_struct *next; /* next buffer to choose if we
  943. X                         rotate the focus. */
  944. X} *input_buffer;
  945. X
  946. Xstatic input_buffer talk_buffer    = NULL;
  947. Xstatic input_buffer play_buffer    = NULL;
  948. Xstatic input_buffer ask_buffer     = NULL;
  949. Xstatic input_buffer focus_buffer   = NULL;
  950. X
  951. Xint Escape_key = 27;            /* The key which will be recognized as
  952. X                   the <ESC> key for terminating pauses.
  953. X                */
  954. X
  955. Xint pause_mode = 0;             /* A boolean flag which if true indicates we
  956. X                   are waiting for the local player to press
  957. X                   <ESC> */
  958. X
  959. Xstatic int query_complete = 0;  /* During QUERY mode, this boolean flag
  960. X                   records whether or not the user has
  961. X                   given an answer. */
  962. X
  963. Xstatic int query_response = 0;  /* The user's response to a query.
  964. X                   1 = YES, 0 = NO. */
  965. X
  966. Xstatic struct timeval hand_start;  /* time at which the current hand
  967. X                      was begun. */
  968. X
  969. Xstatic struct timeval play_start;  /* time at which the current player
  970. X                      was notified of his turn. */
  971. X
  972. Xstatic int total_seconds_for_hand=0; /* the total number of seconds played
  973. X                    in the current hand. */
  974. X
  975. Xstatic int local_seconds_for_hand=0; /* the number of seconds used by the
  976. X                    local player. */
  977. X
  978. Xstatic int clock_is_running = 0;     /* true if we are currently timing
  979. X                    the local player. */
  980. X
  981. Xint timer_is_on = 1;                 /* true if we should display the
  982. X                    timer after each bid/play. */
  983. X
  984. X
  985. Xvoid Begin_timer_for_hand ()
  986. X{
  987. X  gettimeofday (&hand_start, NULL);
  988. X  total_seconds_for_hand = 0;
  989. X  local_seconds_for_hand = 0;
  990. X
  991. X  Display_Total_Time ("", "");
  992. X}
  993. X
  994. Xvoid End_timer_for_hand ()
  995. X{
  996. X  struct timeval hand_end;
  997. X
  998. X  gettimeofday (&hand_end, NULL);
  999. X  total_seconds_for_hand = hand_end.tv_sec - hand_start.tv_sec;
  1000. X}
  1001. X
  1002. Xvoid Display_timer ()
  1003. X{
  1004. X  char local_time_buf[20], total_time_buf[20];
  1005. X  struct timeval hand_end;
  1006. X  int secs;
  1007. X
  1008. X  if (clock_is_running) {
  1009. X    secs = play_start.tv_sec;
  1010. X    gettimeofday (&play_start, NULL);
  1011. X    local_seconds_for_hand += play_start.tv_sec - secs;
  1012. X  }
  1013. X
  1014. X  gettimeofday (&hand_end, NULL);
  1015. X  total_seconds_for_hand = hand_end.tv_sec - hand_start.tv_sec;
  1016. X  if (total_seconds_for_hand == 0)
  1017. X    total_seconds_for_hand = 1;
  1018. X
  1019. X  if (timer_is_on) {
  1020. X    sprintf (local_time_buf, "%2d:%02d", 
  1021. X         local_seconds_for_hand/60, local_seconds_for_hand % 60);
  1022. X    sprintf (total_time_buf, "%2d:%02d",
  1023. X         total_seconds_for_hand/60, total_seconds_for_hand % 60);
  1024. X    Display_Total_Time (total_time_buf, local_time_buf);
  1025. X  }
  1026. X}
  1027. X
  1028. Xvoid Update_and_Display_Total_Time ()
  1029. X{
  1030. X  char time_buf[80];
  1031. X  struct timeval hand_end;
  1032. X
  1033. X  gettimeofday (&hand_end, NULL);
  1034. X  total_seconds_for_hand = hand_end.tv_sec - hand_start.tv_sec;
  1035. X  Display_timer ();
  1036. X}
  1037. X
  1038. Xvoid Begin_timer_for_play ()
  1039. X{
  1040. X  gettimeofday (&play_start, NULL);
  1041. X  clock_is_running = 1;
  1042. X}
  1043. X
  1044. Xvoid End_timer_for_play ()
  1045. X{
  1046. X  struct timeval play_end;
  1047. X
  1048. X  if (!clock_is_running)
  1049. X    return;
  1050. X
  1051. X  clock_is_running = 0;
  1052. X  gettimeofday (&play_end, NULL);
  1053. X  total_seconds_for_hand = play_end.tv_sec - hand_start.tv_sec;
  1054. X  local_seconds_for_hand += play_end.tv_sec - play_start.tv_sec;
  1055. X  Display_timer ((char *) NULL);
  1056. X
  1057. X}
  1058. X
  1059. Xvoid Broadcast_Comment (c)
  1060. X    char *c;
  1061. X{
  1062. X    Send_comment (Local_table, c);
  1063. X}
  1064. X
  1065. Xvoid clear_input_buffer (ib)
  1066. X    input_buffer ib;
  1067. X/* Clears the screen display representing the input buffer, and
  1068. X * resets the cursor position.
  1069. X */
  1070. X{
  1071. X    int i;
  1072. X
  1073. X    for (i = 0; i < ib->length; i++)
  1074. X      ib->buf[i] = ' ';
  1075. X    ib->buf[ib->length+1] = '\0';
  1076. X    print (ib->row, ib->col, ib->buf);
  1077. X
  1078. X    set_cursor (ib->row, ib->col);
  1079. X    ib->defaulted = 0;
  1080. X    ib->buf[0]   = '\0';
  1081. X    ib->pos      =   0;
  1082. X }
  1083. X
  1084. Xstatic void Refresh_input_buffer (ib)
  1085. X     input_buffer ib;
  1086. X/* Re-displays the contents of the given buffer. */
  1087. X{
  1088. X  print (ib->row, ib->col, ib->buf);
  1089. X  set_cursor (ib->row, ib->col+ib->pos);
  1090. X}
  1091. X
  1092. Xvoid Refresh_Input_Buffers ()
  1093. X{
  1094. X  
  1095. X  if (talk_buffer->active)
  1096. X    Refresh_input_buffer (talk_buffer);
  1097. X  if (ask_buffer->active)
  1098. X    Refresh_input_buffer (ask_buffer);
  1099. X  if (play_buffer->active)
  1100. X    Refresh_input_buffer (play_buffer);
  1101. X
  1102. X  /* Perform an additional refresh on the focus buffer so that the
  1103. X     cursor will be positioned properly: */
  1104. X  if ((focus_buffer == NULL) || !focus_buffer->active)
  1105. X    focus_buffer = talk_buffer;
  1106. X  Refresh_input_buffer (focus_buffer);
  1107. X}
  1108. X
  1109. Xvoid Clear_Focus_Buffer ()
  1110. X/* Clears the focus buffer and places the cursor at the beginning of
  1111. X * the line.
  1112. X */
  1113. X{
  1114. X  clear_input_buffer (focus_buffer);
  1115. X}
  1116. X
  1117. Xint Talking ()
  1118. X/* Returns TRUE if the focus is currently on the talk buffer. */
  1119. X{
  1120. X  return (focus_buffer == talk_buffer);
  1121. X}
  1122. X
  1123. Xstatic void Rotate_Focus_Buffer ()
  1124. X/* If multiple input buffers are active, then rotates the focus to the
  1125. X   next active buffer. */
  1126. X{
  1127. X  do {
  1128. X    focus_buffer = focus_buffer->next;
  1129. X  } while (!focus_buffer->active);
  1130. X  Refresh_input_buffer (focus_buffer);
  1131. X  restore_cursor ();
  1132. X}
  1133. X
  1134. Xint update_input_buffer (ib, ch)
  1135. X        input_buffer ib; int ch;
  1136. X/* Adds the character ch to the input buffer, or if it is a control
  1137. X . character, then modifies the cursor position or buffer appropriately.
  1138. X . If the user has indicated he is through entering input, by entering
  1139. X . i.e. <^J> or <^M>, then TRUE is returned.  Otherwise, FALSE is
  1140. X . returned.
  1141. X */
  1142. X{
  1143. X  char chbuf[2], *def, message_buf[80];
  1144. X  
  1145. X  chbuf[1] = '\0';
  1146. X  if (ch == Escape_key)
  1147. X    pause_mode = 0;
  1148. X
  1149. X  if (isprint(ch) && (ib->pos < ib->length)) {
  1150. X    if (ib->defaulted)
  1151. X      clear_input_buffer (ib);
  1152. X    chbuf[0] = ch;
  1153. X    print (ib->row, ib->col+ib->pos, chbuf);
  1154. X    ib->buf[ib->pos++] = ch;
  1155. X    ib->buf[ib->pos] = '\0';
  1156. X  } else if ((ch == '\010') || (ch == '\177')) {
  1157. X    if (ib->pos > 0) {
  1158. X      chbuf[0] = ' ';
  1159. X      ib->buf[--ib->pos] = '\0';
  1160. X      print (ib->row, ib->col+ib->pos, chbuf);
  1161. X    }
  1162. X    ib->defaulted = 0;
  1163. X  } else if (ch == '\001') {              /* ^A - alert partner's bid */
  1164. X    if (IS_OBSERVER(local_player))
  1165. X      Status ("OBSERVERS MAY NOT USE THE ALERT COMMAND.");
  1166. X    else if (Local_table->game_mode != BIDDING_MODE)
  1167. X      Status ("YOU MAY ONLY ALERT DURING BIDDING MODE.");
  1168. X    else if (Index_of_Last_Bid (Local_board, Local_play, 
  1169. X                player_partner[local_player]) < 0)
  1170. X      Status ("YOUR PARTNER HAS NOT BID YET.");
  1171. X    else
  1172. X      Send_alert (Local_table, !FORMAL(Local_table));
  1173. X  } else if (ch == '\002') {              /* ^B - review bidding. */
  1174. X    Clear_Status ();
  1175. X    Review_Bidding ();
  1176. X    return (0);
  1177. X  } else if (ch == '\004') {              /* ^D - toggle default mode */
  1178. X    Clear_Status ();
  1179. X    default_plays = default_plays? 0: 1;
  1180. X    sprintf (message_buf, "DEFAULT INPUT MODE IS NOW %s",
  1181. X         default_plays? "ON": "OFF");
  1182. X    Status (message_buf);
  1183. X  } else if (ch == '\007') {              /* ^G - toggle bell. */
  1184. X    Clear_Status ();
  1185. X    bell_is_on = bell_is_on? 0: 1;
  1186. X    sprintf (message_buf, "THE BELL IS NOW %s", bell_is_on? "ON": "OFF");
  1187. X    Status (message_buf);
  1188. X    ring_bell ();
  1189. X  } else if (ch == '\011') {              /* ^I - rotate focus buffer */
  1190. X    Rotate_Focus_Buffer ();
  1191. X    return (0);
  1192. X  } else if (ch == '\020') {              /* ^P - toggle prompt mode */
  1193. X    Clear_Status ();
  1194. X    prompt_dummy = prompt_dummy? 0: 1;
  1195. X    sprintf (message_buf, "PROMPT MODE IS NOW %s",
  1196. X         prompt_dummy? "ON": "OFF");
  1197. X    Status (message_buf);
  1198. X  } else if (ch == '\022') {             /* ^R - refresh display. */
  1199. X    Refresh_Display ();
  1200. X    Refresh_Input_Buffers ();
  1201. X  } else if (ch == '\024') {             /* ^T - rotate focus buffer. */
  1202. X    Display_timer ();
  1203. X    Rotate_Focus_Buffer ();
  1204. X    return (0);
  1205. X  } else if (ch == '\025') {             /* ^U - erase input buffer. */
  1206. X    clear_input_buffer (ib);
  1207. X  } else if (ch == '\027') {             /* ^W - wake up partner. */
  1208. X    Wakeup_player ((char *) NULL);
  1209. X#ifdef DEBUG
  1210. X  } else if (ch == '\030') {             /* ^X - abort program. */
  1211. X    Continue_Comment_Display ();
  1212. X    Terminate_Program ("INTERRUPT RECEIVED -- TERMINATING PROGRAM");
  1213. X#endif
  1214. X  } else if (ch == '\0') {
  1215. X    print (ib->row, ib->col, ib->buf);
  1216. X  } else if ((ch == '\012') || (ch == '\015')) {
  1217. X    /* First, we strip trailing blanks from the input buffer ... */
  1218. X    while ((ib->pos > 0) && (ib->buf[ib->pos-1] == ' '))
  1219. X      ib->buf[--ib->pos] = '\0';
  1220. X
  1221. X    if (ib->pos > 0)
  1222. X      return (1);
  1223. X    else {
  1224. X      /* If the user presses return on an empty line, we check to see
  1225. X     if there is a default input available.  If so, we copy it to
  1226. X     the input buffer and display it. */
  1227. X      if (default_plays && (ib->default_input[0] != '\0')) {
  1228. X    for (def = ib->default_input; *def != '\0'; def++)
  1229. X      ib->buf[ib->pos++] = *def;
  1230. X    ib->buf[ib->pos] = '\0';
  1231. X    print (ib->row, ib->col, ib->default_input);
  1232. X    ib->defaulted = 1;
  1233. X      }
  1234. X    }
  1235. X  }
  1236. X  set_cursor (ib->row, ib->col+ib->pos);
  1237. X  return (0);
  1238. X}
  1239. Xstatic int Has_suit (h, s)
  1240. X     hand h; int s;
  1241. X/* Returns 1 if h contains a card of suit s, or 0 otherwise. */
  1242. X{
  1243. X  int i;
  1244. X
  1245. X  for (i = 0; i < 13; i++)
  1246. X    if (h[13*s + i])
  1247. X      return (1);
  1248. X
  1249. X  return (0);
  1250. X}
  1251. X
  1252. Xstatic int Unique_suit (h)
  1253. X     hand h;
  1254. X/* If all of the cards in h are from a single suit, then returns that suit.
  1255. X   Otherwise, returns -1. */
  1256. X{
  1257. X  int i, n, s;
  1258. X
  1259. X  s = -1;
  1260. X  for (i = n = 0; i < 4; i++)
  1261. X    if (Has_suit (h, i)) {
  1262. X      s = i; 
  1263. X      n++;
  1264. X    }
  1265. X
  1266. X  if (n == 1)
  1267. X    return (s);
  1268. X  else
  1269. X    return (-1);
  1270. X}
  1271. X
  1272. X
  1273. Xvoid Initialize_Input ()
  1274. X/* This routine should be called once when the program first begins,
  1275. X *  in order to set up the input buffers correctly.
  1276. X */
  1277. X{
  1278. X  talk_buffer=(input_buffer) malloc (sizeof(struct input_buffer_struct));
  1279. X  play_buffer=(input_buffer) malloc (sizeof(struct input_buffer_struct));
  1280. X  ask_buffer =(input_buffer) malloc (sizeof(struct input_buffer_struct));
  1281. X  
  1282. X  talk_buffer->row = TALK_ROW;
  1283. X  talk_buffer->col = TALK_COL + 6;
  1284. X  talk_buffer->length = TALK_LENGTH - 8;
  1285. X  talk_buffer->pos = 0;
  1286. X  talk_buffer->defaulted = 0;
  1287. X  talk_buffer->buf[0] = '\0';
  1288. X  
  1289. X  play_buffer->row = PLAY_ROW;
  1290. X  play_buffer->col = PLAY_COL + 6;
  1291. X  play_buffer->length = PLAY_LENGTH - 6;
  1292. X  play_buffer->pos = 0;
  1293. X  play_buffer->defaulted = 0;
  1294. X  play_buffer->buf[0] = '\0';
  1295. X  
  1296. X  ask_buffer->row = TALK_ROW + 1;
  1297. X  ask_buffer->col = 1;
  1298. X  ask_buffer->length = 5;
  1299. X  ask_buffer->pos = 0;
  1300. X  ask_buffer->defaulted = 0;
  1301. X  ask_buffer->buf[0] = '\0';
  1302. X
  1303. X  talk_buffer->next = ask_buffer;
  1304. X  ask_buffer->next = play_buffer;
  1305. X  play_buffer->next = talk_buffer;
  1306. X
  1307. X  Set_Input_Mode (TALK_INPUT);
  1308. X}
  1309. X
  1310. Xvoid Reinitialize_Input ()
  1311. X/* Clears all of the input buffers. */
  1312. X{
  1313. X  clear_input_buffer (talk_buffer);
  1314. X  clear_input_buffer (play_buffer);
  1315. X  clear_input_buffer (ask_buffer);
  1316. X  Refresh_Input_Buffers ();
  1317. X}
  1318. X
  1319. Xvoid Set_Input_Mode (new_mode)
  1320. X     int new_mode;
  1321. X/* Sets the input mode to the given mode.  Redisplays the talk and
  1322. X * query buffers, if appropriate.  If the new mode is BID_INPUT (resp.
  1323. X * PLAY_INPUT), the set of legal bids (resp. plays) is computed and
  1324. X * the default bid (play) is also computed.
  1325. X */
  1326. X{
  1327. X  if ((display_mode == TALK_DISPLAY) || (display_mode == HELP_DISPLAY)) {
  1328. X    talk_buffer->row = terminal_lines - 1;
  1329. X    ask_buffer->row  = terminal_lines;
  1330. X  } else {
  1331. X    talk_buffer->row = TALK_ROW;
  1332. X    ask_buffer->row  = TALK_ROW+1;
  1333. X  }
  1334. X
  1335. X  switch (new_mode) {
  1336. X  case TALK_INPUT:
  1337. X    play_buffer->active = ask_buffer->active = 0;
  1338. X    talk_buffer->active = 1;
  1339. X    focus_buffer = talk_buffer;
  1340. X    break;
  1341. X
  1342. X  case BID_INPUT:
  1343. X    play_buffer->active = 1;
  1344. X    ask_buffer->active = 0;
  1345. X    focus_buffer = play_buffer;
  1346. X    sprintf (play_buffer->default_input, "PASS");
  1347. X    break;
  1348. X
  1349. X  case PLAY_INPUT:
  1350. X    play_buffer->active = 1;
  1351. X    ask_buffer->active = 0;
  1352. X    play_buffer->default_input[0] = '\0';
  1353. X    break;
  1354. X
  1355. X  case QUERY_INPUT:
  1356. X    ask_buffer->active = 1;
  1357. X    focus_buffer = ask_buffer;
  1358. X    break;
  1359. X  }
  1360. X
  1361. X  Refresh_Input_Buffers ();
  1362. X  input_mode = new_mode;
  1363. X}
  1364. X
  1365. Xstatic void Process_help_input ()
  1366. X/* Processes the talk buffer when we are in help mode.  If the buffer is
  1367. X   non-empty, then we display the corresponding topic.  Otherwise,
  1368. X   we exit help mode.
  1369. X*/
  1370. X{
  1371. X  browse_help (talk_buffer->buf);
  1372. X}
  1373. X
  1374. Xint Reserved_message (message)
  1375. X     char *message;
  1376. X/* Compares the given message to the list of card and bid names.  If a
  1377. X   match is found, then returns true.  Otherwise, returns false.
  1378. X   The purpose of this routine is to discourage players from sending
  1379. X   talk messages which reveal intended bids or plays.
  1380. X*/
  1381. X{
  1382. X  char compare_buff[100], *ch, buf2[5];
  1383. X  int i;
  1384. X
  1385. X  if (!strlen(message))
  1386. X    return (1);
  1387. X
  1388. X  strcpy (compare_buff, message);
  1389. X  ch = compare_buff;
  1390. X  for (ch = compare_buff; *ch != '\0'; ch++)
  1391. X    *ch = MAPUPPER(*ch);
  1392. X
  1393. X  if (strlen(compare_buff) == 1)
  1394. X    if (index("23456789TJQKACDHS", compare_buff[0]) != NULL)
  1395. X      return (1);
  1396. X
  1397. X  for (i=0; i < 52; i++)
  1398. X    if (!strcmp(card_names[i], compare_buff))
  1399. X      return (1);
  1400. X
  1401. X  if (strlen(compare_buff) == 2) {
  1402. X    buf2[0] = compare_buff[1];
  1403. X    buf2[1] = compare_buff[0];
  1404. X    buf2[2] = '\0';
  1405. X    for (i=0; i < 52; i++)
  1406. X      if (!strcmp(card_names[i], buf2))
  1407. X    return (1);
  1408. X  }
  1409. X
  1410. X  if ((strlen(compare_buff) == 2) && (compare_buff[1] == 'N')){
  1411. X    compare_buff[2] = 'T';
  1412. X    compare_buff[3] = '\0';
  1413. X  }
  1414. X
  1415. X  for (i=0; i < 38; i++)
  1416. X    if (!strcmp(bid_names[i], compare_buff))
  1417. X      return (1);
  1418. X
  1419. X  return (0);
  1420. X  
  1421. X}
  1422. X
  1423. Xstatic void Process_talk_input ()
  1424. X/* Processes the talk buffer.  If the first character of the buffer is 
  1425. X   slash '/', then the buffer is interpreted as a command.  Otherwise,
  1426. X   it is sent as a message to the other players.
  1427. X */
  1428. X{
  1429. X  int f = IS_PLAYER(local_player) && FORMAL(Local_table);
  1430. X
  1431. X  if (talk_buffer->buf[0] == '\0')
  1432. X    return;
  1433. X
  1434. X  if (talk_buffer->buf[0] == '/') {
  1435. X    Parse_Input_Command (talk_buffer->buf);
  1436. X    return;
  1437. X  }
  1438. X  
  1439. X  if (talk_buffer->buf[0] == '%') {
  1440. X    if (client_mode)
  1441. X      Send_servereq (Local_table, talk_buffer->buf+1);
  1442. X    else
  1443. X      Status ("SERVER COMMAND IGNORED.");
  1444. X    return;
  1445. X  }
  1446. X
  1447. X  if (Reserved_message(talk_buffer->buf)) {
  1448. X    if ((Local_table->game_mode == BIDDING_MODE) &&
  1449. X    (Local_play->next_player == local_player))
  1450. X      Status ("PRESS <TAB> TO ENTER YOUR BID.");
  1451. X    else if (Local_table->game_mode == PLAYING_MODE)
  1452. X      Status ("PRESS <TAB> TO ENTER YOUR PLAY.");
  1453. X  } else if (!Reserved_message (talk_buffer->buf)) {
  1454. X    if (spectator_mode && !PRACTICE(Local_table))
  1455. X      Send_talk (Local_table, TALK_RCPT_SPEC, talk_buffer->buf);
  1456. X    else if ((Local_table->game_mode == BIDDING_MODE) ||
  1457. X        (Local_table->game_mode == PLAYING_MODE))
  1458. X      Send_talk (Local_table, f? TALK_RCPT_OPPS: TALK_RCPT_ALL, 
  1459. X         talk_buffer->buf);
  1460. X    else
  1461. X      Send_talk (Local_table, TALK_RCPT_ALL, talk_buffer->buf);
  1462. X  }
  1463. X  
  1464. X}
  1465. X
  1466. Xstatic int Parse_bid_input (b, level, alert)
  1467. X     char *b; int *level, *alert;
  1468. X/* Parses the string b, looking for a bid.  If the bid can be parsed
  1469. X   correctly, then sets the level and alert flags appropriately and
  1470. X   returns 0.  Otherwise, displays an error message in the status line
  1471. X   and returns 1.
  1472. X*/
  1473. X{
  1474. X  int i, n;
  1475. X  char bid_buf[80];
  1476. X
  1477. X  n = 0;
  1478. X  while ((b[n] != '\0') && isspace(b[n])) n++;
  1479. X
  1480. X  sprintf (bid_buf, "%s", b+n);
  1481. X  n = strlen(bid_buf);
  1482. X  while ((n > 0) && isspace(bid_buf[n-1])) n--;
  1483. X  if (n == 0) {
  1484. X    sprintf (bid_buf, "%s %s", 
  1485. X         "THE FORMAT OF A CORRECT BID IS <LEVEL> <TRUMPSUIT>", 
  1486. X         " OR P OR X OR XX");
  1487. X    Status (bid_buf);
  1488. X    return (1);
  1489. X  }
  1490. X
  1491. X  if ((n > 0) && (bid_buf[n-1]) == '!') {
  1492. X    *alert = 1;
  1493. X    bid_buf[--n] = '\0';
  1494. X  } else
  1495. X    *alert = 0;
  1496. X
  1497. X  while ((n > 0) && isspace(bid_buf[n-1])) n--;
  1498. X  if (n == 0) {
  1499. X    sprintf (bid_buf, "%s %s", 
  1500. X         "THE FORMAT OF A CORRECT BID IS <LEVEL> <TRUMPSUIT>", 
  1501. X         " OR P OR X OR XX");
  1502. X    Status (bid_buf);
  1503. X    return (1);
  1504. X  }
  1505. X
  1506. X  if (!strcasecmp (bid_buf, "P"))
  1507. X    *level = BID_PASS;
  1508. X  else if (!strcasecmp (bid_buf, "PASS"))
  1509. X    *level = BID_PASS;
  1510. X  else if (!strcasecmp (bid_buf, "X"))
  1511. X    *level = BID_DOUBLE;
  1512. X  else if (!strcasecmp (bid_buf, "DOUBLE"))
  1513. X    *level = BID_DOUBLE;
  1514. X  else if (!strcasecmp (bid_buf, "XX"))
  1515. X    *level = BID_REDOUBLE;
  1516. X  else if (!strcasecmp (bid_buf, "REDOUBLE"))
  1517. X    *level = BID_REDOUBLE;
  1518. X  else {
  1519. X    i = 0;
  1520. X    if ((strlen(bid_buf) == 2) && (MAPUPPER(bid_buf[1]) == 'N')) {
  1521. X      bid_buf[2] = 'T';
  1522. X      bid_buf[3] = '\0';
  1523. X    }
  1524. X    while ((bid_names[i] != NULL) && strcasecmp(bid_names[i], bid_buf))
  1525. X      i++;
  1526. X    if (bid_names[i] == NULL) {
  1527. X      sprintf (bid_buf, "%s %s", 
  1528. X           "THE FORMAT OF A CORRECT BID IS <LEVEL> <TRUMPSUIT>", 
  1529. X           " OR P OR X OR XX");
  1530. X      Status (bid_buf);
  1531. X      return (1);
  1532. X    }
  1533. X    *level = i;
  1534. X  }
  1535. X
  1536. X  return (0);
  1537. X}
  1538. X
  1539. Xstatic void display_valid_bids 
  1540. X  (player, minimum_bid, double_ok, redouble_ok)
  1541. X     int player;
  1542. X     int minimum_bid;
  1543. X     int double_ok;
  1544. X     int redouble_ok;
  1545. X{
  1546. X  char double_string[40], bid_string[80];
  1547. X
  1548. X  if (double_ok)
  1549. X    sprintf (double_string, "; DOUBLE IS OK");
  1550. X  else if (redouble_ok)
  1551. X    sprintf (double_string, "; REDOUBLE IS OK");
  1552. X  else
  1553. X    double_string[0] = '\0';
  1554. X  
  1555. X  if (bid_names[minimum_bid] == NULL) {
  1556. X    if (double_ok)
  1557. X      sprintf (bid_string, "ERROR -- THE ONLY LEGAL BIDS ARE PASS %s.",
  1558. X           "AND DOUBLE");
  1559. X    else if (redouble_ok)
  1560. X      sprintf (bid_string, "ERROR -- THE ONLY LEGAL BIDS ARE PASS %s.",
  1561. X           "AND REDOUBLE");
  1562. X    else
  1563. X      sprintf (bid_string, "ERROR -- THE ONLY LEGAL BID IS PASS.");
  1564. X  } else
  1565. X    sprintf (bid_string, "ERROR -- MINIMUM BID IS %s%s",
  1566. X         bid_names[minimum_bid], double_string);
  1567. X  Status (bid_string);
  1568. X}
  1569. X
  1570. Xstatic int legal_bid (bid, minimum_bid, double_ok, redouble_ok)
  1571. X     int bid;
  1572. X     int minimum_bid;
  1573. X     int double_ok;
  1574. X     int redouble_ok;
  1575. X/* Returns true if the given bid is legal in the current context. */
  1576. X{
  1577. X  if (bid < 0)
  1578. X    return (0);
  1579. X  else if (bid == BID_PASS)
  1580. X    return (1);
  1581. X  else if (bid == BID_DOUBLE)
  1582. X    return (double_ok);
  1583. X  else if (bid == BID_REDOUBLE)
  1584. X    return (redouble_ok);
  1585. X  else
  1586. X    return (minimum_bid <= bid);
  1587. X}
  1588. X
  1589. Xstatic void Process_bid_input ()
  1590. X{
  1591. X  int level, alert;
  1592. X  int minimum_bid, double_ok, redouble_ok;
  1593. X
  1594. X  if (play_buffer->buf[0] == '\0')
  1595. X    return;
  1596. X
  1597. X  if (play_buffer->buf[0] == '/') {
  1598. X    Parse_Input_Command (play_buffer->buf);
  1599. X    return;
  1600. X  }
  1601. X
  1602. X  if (play_buffer->buf[0] == '%') {
  1603. X    if (client_mode)
  1604. X      Send_servereq (Local_table, play_buffer->buf+1);
  1605. X    else
  1606. X      Status ("SERVER COMMAND IGNORED.");
  1607. X    return;
  1608. X  }
  1609. X
  1610. X  if ((Local_play->next_player != local_player) || pause_mode) {
  1611. X    Status ("IT IS NOT YOUR TURN TO BID.");
  1612. X    return;
  1613. X  }
  1614. X
  1615. X  if (Parse_bid_input (play_buffer->buf, &level, &alert))
  1616. X    return;
  1617. X
  1618. X  Generate_valid_bids (Local_play, local_player, &minimum_bid, &double_ok,
  1619. X               &redouble_ok);
  1620. X
  1621. X  if (local_player != Local_play->next_player) {
  1622. X    Status ("IT IS NOT YOUR TURN TO BID.");
  1623. X    return;
  1624. X  } else if (!legal_bid (level, minimum_bid, double_ok, redouble_ok)) {
  1625. X    display_valid_bids (local_player, minimum_bid, double_ok, redouble_ok);
  1626. X    return;
  1627. X  }
  1628. X
  1629. X  if (!strcasecmp(local_player_name, "worf"))
  1630. X    if (level == 15) {
  1631. X      alert = 1;
  1632. X      Broadcast_Comment ("PUTZ ALERT!");
  1633. X    }
  1634. X
  1635. X  Send_bid (Local_table, level, alert);
  1636. X}
  1637. X
  1638. Xstatic int Default_card (h, suit, rank)
  1639. X     hand h; int suit, rank;
  1640. X/* Determines the first card in the hand h which has rank
  1641. X . s and suit r, where either s or r can be specified as -1 indicating
  1642. X . to take the minimum value.  If a matching card was found, then returns
  1643. X . the index of that card.  Otherwise, returns -1 - the number of possible
  1644. X . cards.
  1645. X */
  1646. X{
  1647. X  int i, s, r, n, c;
  1648. X
  1649. X  /* First, we count how many cards match the input specifications: */
  1650. X  c = n = 0;
  1651. X  for (i = 0; i < 52; i++) {
  1652. X    s = suit_of(i);
  1653. X    r = rank_of(i);
  1654. X    if ((s == suit) || (suit == -1))
  1655. X      if ((r == rank) || (rank == -1))
  1656. X    if (h[i]) {
  1657. X      if (n == 0)
  1658. X        c = i; 
  1659. X      n++;
  1660. X    }
  1661. X  }
  1662. X
  1663. X  /* If exactly one card matched, or if multiple cards match but the suit
  1664. X     was specified, then we return the lowest ranking matching card. */
  1665. X
  1666. X  if (n == 1)
  1667. X    return (c);
  1668. X  else if ((suit != -1) && (n > 0))
  1669. X    return (c);
  1670. X
  1671. X  return (-1-n);
  1672. X}
  1673. X
  1674. Xvoid Compute_Legal_Plays (legal_plays)
  1675. X     card_type *legal_plays;
  1676. X/* Computes the set of legal plays for the local player, and places
  1677. X   them into the array legal_plays.
  1678. X*/
  1679. X{
  1680. X  int i, lead_index;
  1681. X
  1682. X  for (i = 0; i < 52; i++)
  1683. X    legal_plays[i] = 0;
  1684. X
  1685. X  if (Local_play->no_plays % 4 == 0)
  1686. X    Generate_valid_leads (Local_board, Local_play, 
  1687. X              Local_play->next_player, legal_plays);
  1688. X  else {
  1689. X    lead_index = Local_play->no_plays - (Local_play->no_plays % 4);
  1690. X    Generate_valid_follows (Local_board, Local_play, Local_play->next_player,
  1691. X                Local_play->play_list[lead_index], legal_plays);
  1692. X  }
  1693. X
  1694. X}
  1695. X
  1696. Xvoid Compute_Default_Play ()
  1697. X/* Computes the default play for the local player, based upon the informaion
  1698. X   contained in Local_board and Local_play. 
  1699. X*/
  1700. X{
  1701. X  hand legal_plays;
  1702. X  int card, suit;
  1703. X
  1704. X  play_buffer->default_input[0] = '\0';
  1705. X  play_buffer->defaulted = 0;
  1706. X
  1707. X  Compute_Legal_Plays (legal_plays);
  1708. X
  1709. X  card = Default_card (legal_plays, -1, -1);
  1710. X  if (card >= 0) {
  1711. X    sprintf (play_buffer->default_input, "%s", card_names[card]);
  1712. X    sprintf (play_buffer->buf, "%s", card_names[card]);
  1713. X    play_buffer->defaulted = 1;
  1714. X    play_buffer->pos = strlen(play_buffer->buf);
  1715. X    return;
  1716. X  }
  1717. X
  1718. X  suit = Unique_suit (legal_plays);
  1719. X  if (suit < 0)
  1720. X    return;
  1721. X
  1722. X  card = Default_card (legal_plays, suit, -1);
  1723. X  if (card < 0)
  1724. X    return;
  1725. X    
  1726. X  sprintf (play_buffer->default_input, "%s", card_names[card]);
  1727. X}
  1728. X
  1729. Xvoid Clear_Default_Play ()
  1730. X/* Clears a default play which may have been set earlier. */
  1731. X{
  1732. X  if (play_buffer->defaulted)
  1733. X    clear_input_buffer (play_buffer);
  1734. X
  1735. X  play_buffer->default_input[0] = '\0';
  1736. X}
  1737. X
  1738. Xstatic int Parse_play_input (p, c, legal_plays)
  1739. X     char *p; int *c; hand legal_plays;
  1740. X/* Parses the string p, looking for a card.  If the card can be parsed
  1741. X   correctly, then sets the variable c to the index of the card and
  1742. X   returns 0.  Otherwise, displays an error message in the status line
  1743. X   and returns 1.
  1744. X*/
  1745. X{
  1746. X  char *suit_string = "CDHS", *s;
  1747. X  char *rank_string = "23456789TJQKA", *r;
  1748. X  int rank, suit, card;
  1749. X
  1750. X  /* First we have to decode the card specified, to see if it is something
  1751. X     reasonable. */
  1752. X  if (strlen(p) > 2) {
  1753. X    Status ("ERROR -- THE FORMAT OF A PLAY IS <suit> <rank>");
  1754. X    return (1);
  1755. X  }
  1756. X
  1757. X  if (strlen(p) == 1) {
  1758. X    /* The player has omitted either the suit or the rank.  We must 
  1759. X       determine which. */
  1760. X    if ((s = index(suit_string, MAPUPPER(*p))) != NULL) {
  1761. X      suit = s - suit_string;
  1762. X      card = Default_card (legal_plays, suit, -1);
  1763. X      if (card < 0) {
  1764. X    Status ("ERROR -- YOU CANNOT PLAY A CARD FROM THAT SUIT");
  1765. X    return (1);
  1766. X      }
  1767. X    } else if ((r = index(rank_string, MAPUPPER(*p))) != NULL) {
  1768. X      rank = r - rank_string;
  1769. X      card = Default_card (legal_plays, -1, rank);
  1770. X      if (card < 0) {
  1771. X    if (card == -1)
  1772. X      Status ("ERROR -- YOU CANNOT PLAY A CARD OF THAT RANK.");
  1773. X    else
  1774. X      Status ("ERROR -- THAT DOES NOT SPECIFY A UNIQUE CARD.");
  1775. X    return (1);
  1776. X      }
  1777. X    } else {
  1778. X      Status ("ERROR -- THE FORMAT OF A PLAY IS <suit> <rank>");
  1779. X      return (1);
  1780. X    }
  1781. X  } else {
  1782. X    for (card = 0; (card_names[card] != NULL) 
  1783. X     && strcasecmp(card_names[card], p); card++);
  1784. X    if (card_names[card] == NULL) {
  1785. X      card = p[0]; p[0] = p[1]; p[1] = card;
  1786. X      for (card = 0; (card_names[card] != NULL) 
  1787. X       && strcasecmp(card_names[card], p); card++);
  1788. X    }
  1789. X    if (card_names[card] == NULL) {
  1790. X      Status ("ERROR -- THE FORMAT OF A PLAY IS <suit> <rank>");
  1791. X      return (1);
  1792. X    }
  1793. X  }
  1794. X
  1795. X  *c = card;
  1796. X  return (0);
  1797. X}
  1798. X
  1799. Xstatic void display_valid_plays (current_hand)
  1800. X     hand current_hand;
  1801. X{
  1802. X  char card_string [60], card_message[80];
  1803. X  int i, j, c;
  1804. X  
  1805. X  c = 0;
  1806. X  for (i = 0; i < 52; i++) {
  1807. X    if (current_hand[i]) {
  1808. X      for (j = 0; card_names[i][j] != '\0'; j++)
  1809. X    card_string[c++] = card_names[i][j];
  1810. X      card_string[c++] = ' ';
  1811. X    }
  1812. X  }
  1813. X  card_string[c++] = '\0';
  1814. X  sprintf (card_message,"ERROR -- VALID PLAYS ARE %s", card_string);
  1815. X  Status (card_message);
  1816. X}
  1817. X
  1818. X
  1819. Xstatic void Process_play_input ()
  1820. X{
  1821. X  int play;
  1822. X  hand legal_plays;
  1823. X
  1824. X  if (play_buffer->buf[0] == '\0')
  1825. X    return;
  1826. X
  1827. X  if (play_buffer->buf[0] == '/') {
  1828. X    Parse_Input_Command (play_buffer->buf);
  1829. X    return;
  1830. X  }
  1831. X
  1832. X  if (play_buffer->buf[0] == '%') {
  1833. X    if (client_mode)
  1834. X      Send_servereq (Local_table, play_buffer->buf+1);
  1835. X    else
  1836. X      Status ("SERVER COMMAND IGNORED.");
  1837. X    return;
  1838. X  }
  1839. X
  1840. X  if (Local_table->playing_mode != PRACTICE_PLAYING_MODE)
  1841. X    if ((Next_Player (Local_play) != local_player) || pause_mode) {
  1842. X      Status ("IT IS NOT YOUR TURN TO PLAY.");
  1843. X      return;
  1844. X    }
  1845. X
  1846. X  Compute_Legal_Plays (legal_plays);
  1847. X  if (Parse_play_input (play_buffer->buf, &play, legal_plays))
  1848. X    return;
  1849. X
  1850. X  if (!legal_plays [play]) {
  1851. X    display_valid_plays (legal_plays);
  1852. X    return;
  1853. X  }
  1854. X
  1855. X  Clear_Default_Play ();
  1856. X  if (Local_table->playing_mode == PRACTICE_PLAYING_MODE)
  1857. X    Send_playreq (Local_table, play, Local_play->no_plays);
  1858. X  else
  1859. X    Send_play (Local_table, play);
  1860. X
  1861. X  pause_mode = 1;  /* This is a kludge intended to prevent a player
  1862. X              from playing a card twice. */
  1863. X}
  1864. X
  1865. Xstatic void Process_ask_input ()
  1866. X{
  1867. X  int resp = ask_buffer->buf[0];
  1868. X
  1869. X  if (resp == '\0')
  1870. X    return;
  1871. X
  1872. X  if ((resp == 'y') || (resp == 'Y')) {
  1873. X    query_response = 1;
  1874. X    query_complete = 1;
  1875. X  } else if ((resp == 'n') || (resp == 'N')) {
  1876. X    query_response = 0;
  1877. X    query_complete = 1;
  1878. X  } else
  1879. X    ring_bell ();
  1880. X}
  1881. X
  1882. X
  1883. Xvoid Accept_Keyboard_Characters ()
  1884. X/* If any keyboard characters are available, then reads them and adds
  1885. X * them to the current focus buffer.  This may result in a change of
  1886. X * state of the program or in messages being transmitted to the other
  1887. X * players.
  1888. X */
  1889. X{
  1890. X  while (char_avail()) {
  1891. X    if (update_input_buffer (focus_buffer, input_char ())) {
  1892. X      Clear_Status ();
  1893. X      if (focus_buffer == talk_buffer) {
  1894. X    if (display_mode == HELP_DISPLAY)
  1895. X      Process_help_input ();
  1896. X    else
  1897. X      Process_talk_input ();
  1898. X      } else if (focus_buffer == ask_buffer)
  1899. X    Process_ask_input ();
  1900. X      else if (input_mode == BID_INPUT)
  1901. X    Process_bid_input ();
  1902. X      else if (input_mode == PLAY_INPUT)
  1903. X    Process_play_input ();
  1904. X        clear_input_buffer (focus_buffer);
  1905. X        update_input_buffer (focus_buffer, '\0');
  1906. X    }
  1907. X  }
  1908. X}
  1909. X
  1910. Xstatic int Pause_mode_exit_event ()
  1911. X{
  1912. X  return (!pause_mode);
  1913. X}
  1914. X
  1915. Xvoid Pause (pause_message)
  1916. X     char *pause_message;
  1917. X/* Displays the given message on the status line, and waits for the
  1918. X * user to press the escape key.  Returns after escape has been pressed.
  1919. X */
  1920. X{
  1921. X  int prev_pause = pause_mode;
  1922. X
  1923. X  if ((pause_message == NULL) || (strlen(pause_message) == 0))
  1924. X    Lock_Status ("PRESS <ESC> TO CONTINUE ...");
  1925. X  else
  1926. X    Lock_Status (pause_message);
  1927. X
  1928. X  pause_mode = 1;
  1929. X
  1930. X  ring_bell ();
  1931. X  Refresh_Input_Buffers ();
  1932. X  Wait_for_event_at_game_level (Pause_mode_exit_event);
  1933. X  Unlock_Status ();
  1934. X  pause_mode = prev_pause;
  1935. X}
  1936. X
  1937. Xstatic int Query_complete_event ()
  1938. X{
  1939. X  return (query_complete);
  1940. X}
  1941. X
  1942. Xint  Ask (question)
  1943. X     char *question;
  1944. X/* Presents the question to the player and asks for a response.
  1945. X * Returns 1 if 'y' was entered and '0' otherwise.
  1946. X */
  1947. X{
  1948. X  int prev_mode = input_mode;
  1949. X  input_buffer prev_focus = focus_buffer;
  1950. X
  1951. X  int prev_ask_col = ask_buffer->col;
  1952. X    /* We save the current location of the beginning of the ask buffer
  1953. X       in case this is a re-entrant call. */
  1954. X
  1955. X  ask_buffer->col = strlen(question) + 2;
  1956. X  clear_input_buffer (ask_buffer);
  1957. X  Clear_Status ();
  1958. X  Lock_Status (question);
  1959. X  ring_bell ();
  1960. X
  1961. X  Set_Input_Mode (QUERY_INPUT);
  1962. X  if (default_plays)
  1963. X    sprintf (ask_buffer->default_input, "NO");
  1964. X  else
  1965. X    ask_buffer->default_input[0] = '\0';
  1966. X
  1967. X  query_complete = 0;
  1968. X  Refresh_Input_Buffers ();
  1969. X  Wait_for_event_at_game_level (Query_complete_event);
  1970. X  Unlock_Status ();
  1971. X
  1972. X  focus_buffer = prev_focus;
  1973. X  ask_buffer->col = prev_ask_col;
  1974. X  Set_Input_Mode (prev_mode);
  1975. X  query_complete = 0;
  1976. X  return (query_response);
  1977. X}
  1978. X
  1979. Xvoid Press_Return_to_Continue (msg)
  1980. X     char *msg;
  1981. X/* Prints the message on the status line and then waits for the user
  1982. X   to press return.
  1983. X */
  1984. X{
  1985. X  char buf[100];
  1986. X  int ch;
  1987. X
  1988. X  if (strlen(msg) == 0)
  1989. X    Lock_Status ("PRESS RETURN TO CONTINUE ...");
  1990. X  else {
  1991. X    sprintf (buf, "%s -- PRESS RETURN TO CONTINUE ...", msg);
  1992. X    Lock_Status (buf);
  1993. X  }
  1994. X  ring_bell ();
  1995. X
  1996. X  while (1) {
  1997. X    Wait_for_event_at_game_level (char_avail);
  1998. X    ch = input_char ();
  1999. X    if ((ch == '\012') || (ch == '\015')) {
  2000. X      Unlock_Status ();
  2001. X      return;
  2002. X    }
  2003. X  }
  2004. X  
  2005. X}
  2006. END_OF_FILE
  2007. if test 29960 -ne `wc -c <'input.c'`; then
  2008.     echo shar: \"'input.c'\" unpacked with wrong size!
  2009. fi
  2010. # end of 'input.c'
  2011. fi
  2012. echo shar: End of archive 8 \(of 14\).
  2013. cp /dev/null ark8isdone
  2014. MISSING=""
  2015. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  2016.     if test ! -f ark${I}isdone ; then
  2017.     MISSING="${MISSING} ${I}"
  2018.     fi
  2019. done
  2020. if test "${MISSING}" = "" ; then
  2021.     echo You have unpacked all 14 archives.
  2022.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2023. else
  2024.     echo You still need to unpack the following archives:
  2025.     echo "        " ${MISSING}
  2026. fi
  2027. ##  End of shell archive.
  2028. exit 0
  2029.