home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / games / volume13 / okbridge / part03 < prev    next >
Encoding:
Internet Message Format  |  1992-01-12  |  53.6 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: v13i018:  okbridge - computer-mediated bridge game, Part03/07
  5. Message-ID: <2277@masterCNA.TEK.COM>
  6. Date: 10 Jan 92 16:44:42 GMT
  7. Sender: news@masterCNA.TEK.COM
  8. Lines: 1957
  9. Approved: billr@saab.CNA.TEK.COM
  10.  
  11. Submitted-by: mclegg@cs.UCSD.EDU (Matthew Clegg)
  12. Posting-number: Volume 13, Issue 18
  13. Archive-name: okbridge/Part03
  14. Environment: BSD-derived Unix, curses, sockets
  15.  
  16.  
  17.  
  18. #! /bin/sh
  19. # This is a shell archive.  Remove anything before this line, then unpack
  20. # it by saving it into a file and typing "sh file".  To overwrite existing
  21. # files, type "sh file -c".  You can also feed this as standard input via
  22. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  23. # will see the following message at the end:
  24. #        "End of archive 3 (of 7)."
  25. # Contents:  input.c.aa startup.c
  26. # Wrapped by billr@saab on Fri Jan 10 08:31:28 1992
  27. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  28. if test -f 'input.c.aa' -a "${1}" != "-c" ; then 
  29.   echo shar: Will not clobber existing file \"'input.c.aa'\"
  30. else
  31. echo shar: Extracting \"'input.c.aa'\" \(40095 characters\)
  32. sed "s/^X//" >'input.c.aa' <<'END_OF_FILE'
  33. X/* input.c -- Input driver for the bridge program.
  34. X ! 
  35. X ! Copyright (C) 1990,1991 by Matthew Clegg
  36. X ! 
  37. X ! This program may be copied and distributed freely.  Please do not
  38. X ! charge money for this program or for any program derived from it.
  39. X ! If you modify this program, then include a notice stating plainly
  40. X ! that your program is derived from the okbridge program and is not
  41. X ! the same as the official okbridge program.
  42. X !
  43. X ! I welcome any suggestions for improvement to okbridge, and 
  44. X ! I would be especially happy to receive improved source code.
  45. X ! If you have comments or suggestions, or if you would like to
  46. X ! join the okbridge mailing list, then write to
  47. X !
  48. X !   mclegg@cs.ucsd.edu
  49. X !
  50. X*/
  51. X#define _INPUT_
  52. X#ifdef __TURBOC__
  53. X#include <alloc.h>
  54. X#include <stdlib.h>
  55. X#else
  56. X  extern int errno;
  57. X  extern char *sys_errlist[];
  58. X  extern char *strdup();
  59. X#define random(n) ((rand()/64) % (n))
  60. X#endif
  61. X#include <ctype.h>
  62. X#include <stdio.h>
  63. X#include <string.h>
  64. X/* #include <time.h> */
  65. X#include <sys/time.h>
  66. X#include "globals.h"
  67. X#include "ps.h"
  68. X#include "network.h"
  69. X#include "terminal.h"
  70. X#include "display.h"
  71. X#include "help.h"
  72. X#include "input.h"
  73. X#include "email.h"
  74. X
  75. Xextern FILE *logfile;
  76. X
  77. Xextern int codefile_line_no;
  78. Xextern int server_mode;
  79. Xextern char *email_error_message;
  80. X
  81. Xstatic char *parsing_errmsg;
  82. Xstatic char *keyword_error = "ERROR IN COMMAND KEYWORD";
  83. Xstatic char *number_error  = "A NUMERIC ARGUMENT IS REQUIRED";
  84. Xstatic char *level_error   = "ERROR IN BIDDING LEVEL";
  85. Xstatic char *suit_error    = "ERROR IN SUIT";
  86. Xstatic char *rank_error    = "ERROR IN RANK";
  87. Xstatic char *deal_error    = "ERROR IN DEAL OF CARDS";
  88. Xstatic char *bid_error= 
  89. X  "THE FORMAT OF A CORRECT BID IS <LEVEL> <TRUMPSUIT> OR P OR X OR XX";
  90. Xstatic char *play_error    =
  91. X  "ERROR - THE FORMAT OF A CORRECT PLAY IS <SUIT> <RANK>";
  92. Xstatic char *ending_error  = "EXTRA JUNK AT END OF INPUT";
  93. Xstatic char *bell_error    = "FORMAT OF /BELL COMMAND IS /BELL [ON|OFF]";
  94. Xstatic char *prompt_error  = "FORMAT OF /PROMPT COMMAND IS /PROMPT [ON|OFF]";
  95. Xstatic char *echo_error       = "ERROR IN ECHO RESPONSE";
  96. Xstatic char *resp_error    = "ERROR IN RESP COMMAND";
  97. Xstatic char *score_error   = "ERROR IN SCORE COMMAND";
  98. Xstatic char *vuln_error    = "ERROR IN VULN COMMAND";
  99. Xstatic char *save_error    = "SAVE COMMAND REQUIRES A filename TO BE GIVEN";
  100. Xstatic char *load_error    = "LOAD COMMAND REQUIRES A filename TO BE GIVEN";
  101. Xstatic char *replay_error  = "REPLAY COMMAND REQUIRES A filename TO BE GIVEN";
  102. Xstatic char *default_error = "FORMAT OF /DEFAULT COMMAND IS /DEFAULT [ON|OFF]";
  103. X
  104. X/* Most of the commands the we receive from the other players
  105. X . we process immediately after receiving them.  However, the
  106. X . bids and plays we store in a queue until we are ready to
  107. X . accept them.  We implement this queue as a circular array.
  108. X . Since the queue for a given player is never expected to
  109. X . contain more than one item, we see no need to implement
  110. X . a dynamically allocated queue.
  111. X .
  112. X */
  113. X
  114. X
  115. X#define QUEUE_LENGTH 50
  116. Xtypedef struct player_command_struct player_queue [QUEUE_LENGTH];
  117. X
  118. Xstatic player_queue command_queue [4];
  119. Xstatic int          queue_in  [4] = {0, 0, 0, 0};
  120. Xstatic int          queue_out [4] = {0, 0, 0, 0};
  121. X
  122. X#define QUEUE_EMPTY(n) (queue_in[n] == queue_out[n])
  123. X#define QUEUE_FULL(n)  (((queue_in[n]+1) % QUEUE_LENGTH) == queue_out[n])
  124. X
  125. Xstatic char *local_player_names [] = {"NORTH", "EAST", "SOUTH", "WEST"};
  126. Xint         players_here        [] = {0, 0, 0, 0};
  127. X  /* players_here is used to record which players have given an
  128. X   . acknowledgment over the network that they are ready to play.
  129. X   */
  130. Xstatic parse_string command_string = NULL;
  131. Xstatic input_buffer talk_buffer    = NULL;
  132. Xstatic input_buffer play_buffer    = NULL;
  133. Xstatic input_buffer ask_buffer     = NULL;
  134. X
  135. Xchar *autoload_file = NULL;     /* The name of the file from which we
  136. X                   will initially try to load a sequence
  137. X                   of boards, if we are north in email mode. */
  138. Xchar *autosave_file = NULL;     /* The name of the file to which we will
  139. X                   automatically save the boards that we
  140. X                   have played. */
  141. X
  142. Xint ring_my_bell = 1;        /* A boolean variable that when true causes
  143. X                   the terminal bell to be rung before asking
  144. X                   for a bid or play. */
  145. X
  146. Xstatic char *default_input = NULL; /* The input which will be given to the
  147. X                      program if the user asks for default. */
  148. Xstatic int  default_suit = -1;  /* The default suit that will be used if a
  149. X                   player specifies only the rank of the
  150. X                   card to be played.  -1 == no default. */
  151. Xstatic long command_disabled;
  152. X                   /* We only allow the local player to
  153. X                    . use a subset of the commands which
  154. X                    . the internal interpreter handles --
  155. X                    . the remaining commands are used
  156. X                    . for internal communications. */
  157. X
  158. Xstatic int forced_talk_mode =0; /* True if the player has forced the program
  159. X                   into talk mode, even though a bid or
  160. X                   play is required. */
  161. X
  162. X#define disable(x)  command_disabled |= (x)
  163. X#define enable(x)   command_disabled  &= ~(x)
  164. X#define disabled(x) (command_disabled & (x))
  165. X#define enabled(x)  !disabled (x)
  166. X
  167. X
  168. Xstatic struct timeval ping_start;  /* time at which last ping command
  169. X                    . was issued. */
  170. X
  171. Xstatic int waiting_for_acknowledgment = 0;
  172. X                              /* true if we are waiting for the user
  173. X                 to press RETURN.  This mode can be
  174. X                 interrupted by a claim request. */
  175. X
  176. Xstatic int claim_responses;   /* number of responses received so far to
  177. X                 the most recent claim command. */
  178. Xstatic int claim_rejected;    /* true if the claim offer was rejected. */
  179. X
  180. Xstatic int scoring_mode_known = 0; /* true when we know what scoring mode
  181. X                      will be used. */
  182. X
  183. X
  184. Xstatic initialize_command_queue ()
  185. X{
  186. X    int i;
  187. X
  188. X    for (i = 0; i < 4; i++)
  189. X        queue_in[i] = queue_out[i] = 0;
  190. X};
  191. Xstatic int command_available (player_no)
  192. X    int player_no;
  193. X/* Returns true if there is a command available from the specified player. */
  194. X{
  195. X    return (!QUEUE_EMPTY(player_no));
  196. X};
  197. Xstatic copy_player_command (dest, source)
  198. X    player_command dest, source;
  199. X{
  200. X    memcpy (dest, source, sizeof(struct player_command_struct));
  201. X};
  202. X
  203. Xstatic enqueue_command (pc)
  204. X    player_command pc;
  205. X/* Adds the player_command pc to the queue for player_no. */
  206. X{
  207. X    player_command dest;
  208. X    int n;
  209. X
  210. X    if (QUEUE_FULL(pc->player_no)) {
  211. X        Display_Player_Comment ("INTERNAL ERROR!",
  212. X            "COMMAND QUEUE OVERFLOW");
  213. X    } else {
  214. X          n = pc->player_no;
  215. X          dest = &(command_queue[n][queue_in[n]]);
  216. X          copy_player_command (dest, pc);
  217. X        queue_in[n] = (queue_in[n] + 1) % QUEUE_LENGTH;
  218. X    }
  219. X};
  220. Xstatic dequeue_command (n, pc)
  221. X    int n; player_command pc;
  222. X/* Removes the command at the head of the queue for the given player. */
  223. X{
  224. X    player_command source;
  225. X
  226. X    if (QUEUE_EMPTY(n)) {
  227. X        Display_Player_Comment ("INTERNAL ERROR!",
  228. X            "COMMAND QUEUE IS EMPTY");
  229. X    } else {
  230. X          source = &(command_queue[n][queue_out[n]]);
  231. X          copy_player_command (pc, source);
  232. X        queue_out[n] = (queue_out[n] + 1) % QUEUE_LENGTH;
  233. X    }
  234. X};
  235. Xstatic skip_spaces (ps)
  236. X    parse_string ps;
  237. X/* Skips characters in the parse_string ps until a nonblank character
  238. X . is found.
  239. X */
  240. X{
  241. X    while (ps_scan(ps) == ' ')
  242. X        ps_next(ps);
  243. X};
  244. Xstatic int parse_natural (ps)
  245. X    parse_string ps;
  246. X/* Parses a natural number.  If successful, returns the non-negative
  247. X . integer which is parsed.  Otherwise, returns -1 and sets error
  248. X . message accordingly. 
  249. X */
  250. X{
  251. X    int ch, n, digits;
  252. X
  253. X    n = digits = 0;
  254. X        skip_spaces (ps);
  255. X    ch = ps_scan (ps);
  256. X    while (('0' <= ch) && (ch <= '9')) {
  257. X        ps_next (ps);
  258. X        digits++;
  259. X        n = 10*n + (ch - '0');
  260. X        ch = ps_scan (ps);
  261. X    };
  262. X    if (digits == 0) {
  263. X        parsing_errmsg = number_error;
  264. X        return (-1);
  265. X    } else
  266. X        return (n);
  267. X};
  268. X
  269. Xstatic int parse_level (ps)
  270. X    parse_string ps;
  271. X/* Parses the bidding level.
  272. X . <level> = 1 | 2 | 3 | 4 | 5 | 6 | 7
  273. X . If successful, returns the level as an integer.  Otherwise,
  274. X . returns -1, and sets error message accordingly.
  275. X */
  276. X{
  277. X    int n;
  278. X    n = parse_natural (ps);
  279. X    if ((1 <= n) && (n <= 7))
  280. X        return (n);
  281. X    else if (n >= 0)
  282. X        parsing_errmsg = level_error;
  283. X    return (-1);
  284. X    
  285. X};
  286. Xstatic int parse_suit (ps)
  287. X    parse_string ps;
  288. X/* Parses a suit name.
  289. X . <suit> = C | D | H | S | CLUB[S] | DIAMOND[S] | HEART[S] | SPADE[S]
  290. X . If successful, then returns the suit index.  Otherwise, returns
  291. X . -1 and sets parsing_error accordingly.
  292. X */
  293. X{
  294. X    skip_spaces (ps);
  295. X    if (ps_matches_ic(ps, "CLUB")) {
  296. X        ps_matches_ic(ps, "S");
  297. X        return (SUIT_CLUBS);
  298. X    } else if (ps_matches_ic(ps, "DIAMOND")) {
  299. X        ps_matches_ic(ps, "S");
  300. X        return (SUIT_DIAMONDS);
  301. X    } else if (ps_matches_ic(ps, "HEART")) {
  302. X        ps_matches_ic(ps, "S");
  303. X        return (SUIT_HEARTS);
  304. X    } else if (ps_matches_ic(ps, "SPADE")) {
  305. X        ps_matches_ic(ps, "S");
  306. X        return (SUIT_SPADES);
  307. X    } else if (ps_matches_ic(ps, "C"))
  308. X        return (SUIT_CLUBS);
  309. X    else if (ps_matches_ic(ps, "D"))
  310. X        return (SUIT_DIAMONDS);
  311. X    else if (ps_matches_ic(ps, "H"))
  312. X        return (SUIT_HEARTS);
  313. X    else if (ps_matches_ic(ps, "S"))
  314. X        return (SUIT_SPADES);
  315. X    parsing_errmsg = suit_error;
  316. X    return (-1);
  317. X};
  318. Xstatic int parse_rank (ps)
  319. X    parse_string ps;
  320. X/* Parses the rank of a card.
  321. X . <rank> = 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | T | J | Q | K | A
  322. X .            | JACK | QUEEN | KING | ACE
  323. X . Returns the rank of the card, where 2 has rank 0 and A has rank 12.
  324. X . If an error, sets parsing_errmsg accordingly and returns -1.
  325. X */
  326. X{
  327. X    skip_spaces (ps);
  328. X    if (ps_matches(ps, "2"))
  329. X        return (0);
  330. X    else if (ps_matches(ps, "3"))
  331. X        return (1);
  332. X    else if (ps_matches(ps, "4"))
  333. X        return (2);
  334. X    else if (ps_matches(ps, "5"))
  335. X        return (3);
  336. X    else if (ps_matches(ps, "6"))
  337. X        return (4);
  338. X    else if (ps_matches(ps, "7"))
  339. X        return (5);
  340. X    else if (ps_matches(ps, "8"))
  341. X        return (6);
  342. X    else if (ps_matches(ps, "9"))
  343. X        return (7);
  344. X    else if (ps_matches(ps, "10"))
  345. X        return (8);
  346. X    else if (ps_matches_ic(ps, "TEN"))
  347. X        return (8);
  348. X    else if (ps_matches_ic(ps, "JACK"))
  349. X        return (9);
  350. X    else if (ps_matches_ic(ps, "QUEEN"))
  351. X        return (10);
  352. X    else if (ps_matches_ic(ps, "KING"))
  353. X        return (11);
  354. X    else if (ps_matches_ic(ps, "ACE"))
  355. X        return (12);
  356. X    else if (ps_matches_ic(ps, "T"))
  357. X        return (8);
  358. X    else if (ps_matches_ic(ps, "J"))
  359. X        return (9);
  360. X    else if (ps_matches_ic(ps, "Q"))
  361. X        return (10);
  362. X    else if (ps_matches_ic(ps, "K"))
  363. X        return (11);
  364. X    else if (ps_matches_ic(ps, "A"))
  365. X        return (12);
  366. X    parsing_errmsg = rank_error;
  367. X    return (-1);
  368. X};
  369. Xstatic parse_rdeal_command (ps, pc)
  370. X    parse_string ps; player_command pc;
  371. X/* Parses a rdeal command, which is of the format:
  372. X . RDEAL <cards>
  373. X . where <cards> is the 'shuffled' deck of cards.  That is,
  374. X . cards is a string of 52 characters, each of which is a 0, 1, 2 or 3.
  375. X . The interpretation of this is that if the j'th character is i,
  376. X . then player i is dealt card j.
  377. X */
  378. X{
  379. X    int i, ch;
  380. X    skip_spaces (ps);
  381. X    pc->command = CMD_RDEAL;
  382. X    for (i = 0; i < 52; i++) {
  383. X        ch = ps_scan (ps);
  384. X        if (('0' <= ch) && (ch <= '3'))
  385. X            pc->data.deal[i] = ch - '0';
  386. X        else {
  387. X            pc->command = CMD_ERROR;
  388. X            parsing_errmsg = deal_error;
  389. X            return;
  390. X        };
  391. X        ps_next (ps);
  392. X    };
  393. X    return;
  394. X};
  395. Xstatic parse_bid_command (ps, pc)
  396. X    parse_string ps; player_command pc;
  397. X/* Parses a bid command, which is of the form:
  398. X * 2. BID  (<level> (<suit> | <notrump>)) | <pass> | <double> | <redouble>
  399. X */
  400. X{
  401. X    int lv, cd;
  402. X    pc->command = CMD_BID;
  403. X    skip_spaces (ps);
  404. X    if (ps_matches_ic (ps, "P")) {
  405. X        ps_matches_ic (ps, "ASS");
  406. X        pc->data.bid = bidding_code (BID_PASS, 0, 0);
  407. X        return;
  408. X    } else if (ps_matches_ic (ps, "XX")
  409. X           || ps_matches_ic (ps, "REDOUBLE")) {
  410. X        pc->data.bid = bidding_code (BID_REDOUBLE, 0, 0);
  411. X        return;
  412. X    } else if (ps_matches_ic (ps, "X")
  413. X           || ps_matches_ic (ps, "DOUBLE")) {
  414. X        pc->data.bid = bidding_code (BID_DOUBLE, 0, 0);
  415. X        return;
  416. X    } else {
  417. X        lv = parse_level (ps);
  418. X        if (lv == -1) {
  419. X            parsing_errmsg = bid_error;
  420. X            pc->command = CMD_ERROR;
  421. X            return;
  422. X        };
  423. X        skip_spaces (ps);
  424. X        if      (ps_matches_ic (ps, "NOTRUMP")) cd = SUIT_NOTRUMP;
  425. X        else if (ps_matches_ic (ps, "NT"))       cd = SUIT_NOTRUMP;
  426. X        else if (ps_matches_ic (ps, "N"))    cd = SUIT_NOTRUMP;
  427. X        else {
  428. X            cd = parse_suit (ps);
  429. X            if (cd == -1) {
  430. X                parsing_errmsg = bid_error;
  431. X                pc->command = CMD_ERROR;
  432. X                return;
  433. X            };
  434. X        };
  435. X        pc->data.bid = bidding_code (BID_SUIT, lv, cd);
  436. X        return;
  437. X    };
  438. X};
  439. Xstatic parse_play_command (ps, pc)
  440. X    parse_string ps; player_command pc;
  441. X/* Parses a play command:
  442. X . play <suit> <rank>
  443. X */
  444. X{
  445. X    int s, r;
  446. X    pc->command = CMD_PLAY;
  447. X    skip_spaces (ps);
  448. X    s = parse_suit (ps);
  449. X    if (s == -1) {
  450. X        parsing_errmsg = play_error;
  451. X        pc->command = CMD_ERROR;
  452. X        return;
  453. X    };
  454. X    r = parse_rank (ps);
  455. X    if (r == -1) {
  456. X        parsing_errmsg = play_error;
  457. X        pc->command = CMD_ERROR;
  458. X        return;
  459. X    };
  460. X    pc->data.card = card_code (s, r);
  461. X};
  462. X
  463. Xstatic parse_local_play_command (ps, pc)
  464. X    parse_string ps; player_command pc;
  465. X/* Parses a play command:
  466. X .   play [<suit>] <rank>
  467. X . If default_suit >= 0, then <suit> may be omitted.
  468. X */
  469. X{
  470. X    int s, r;
  471. X    pc->command = CMD_PLAY;
  472. X    skip_spaces (ps);
  473. X    s = parse_suit (ps);
  474. X    if ((s == -1) && (default_suit < 0)) {
  475. X        parsing_errmsg = play_error;
  476. X        pc->command = CMD_ERROR;
  477. X        return;
  478. X    } else if (s == -1)
  479. X            s = default_suit;
  480. X
  481. X    r = parse_rank (ps);
  482. X    if (r == -1) {
  483. X        parsing_errmsg = play_error;
  484. X        pc->command = CMD_ERROR;
  485. X        return;
  486. X    };
  487. X    pc->data.card = card_code (s, r);
  488. X};
  489. Xstatic parse_talk_command (ps, pc)
  490. X    parse_string ps; player_command pc;
  491. X/* The talk command is used to communicate messages between the players.
  492. X . The format of the talk command is:
  493. X .  talk <message>
  494. X . Where <message> is any sequence of (printable) characters.
  495. X */
  496. X{
  497. X    int ch, n;
  498. X    pc->command = CMD_TALK;
  499. X    skip_spaces (ps);
  500. X    n = 0;
  501. X    ch = ps_scan (ps);
  502. X    while (ch != '\0') {
  503. X            if (n < MESSAGE_LENGTH-1)
  504. X          pc->data.message[n++] = ch;
  505. X        ps_next (ps);
  506. X        ch = ps_scan (ps);
  507. X    };
  508. X    pc->data.message[n] = '\0';
  509. X};
  510. Xstatic parse_comment_command (ps, pc)
  511. X    parse_string ps; player_command pc;
  512. X/* The comment command is used to communicate a message to all of the
  513. X . players, where the origin of the message is the okbridge program. 
  514. X . The format of the comment command is:
  515. X .  comment <message>
  516. X . Where <message> is any sequence of (printable) characters.
  517. X */
  518. X{
  519. X    int ch, n;
  520. X    pc->command = CMD_COMMENT;
  521. X    skip_spaces (ps);
  522. X    n = 0;
  523. X    ch = ps_scan (ps);
  524. X    while (ch != '\0') {
  525. X            if (n < MESSAGE_LENGTH-1)
  526. X          pc->data.message[n++] = ch;
  527. X        ps_next (ps);
  528. X        ch = ps_scan (ps);
  529. X    };
  530. X    pc->data.message[n] = '\0';
  531. X};
  532. Xstatic parse_hello_command (ps, pc)
  533. X    parse_string ps; player_command pc;
  534. X/* The hello command is used for handshaking after when the program
  535. X . is first starting.  The program sends an initial 'hello' to indicate
  536. X . that it has joined the game.  The format of the 'hello' command is
  537. X .   HELLO <version> <name>
  538. X . where 
  539. X .   <version> is the major revision level of this program
  540. X .   <name>    is the name of the player
  541. X */
  542. X{
  543. X        int n, ch;
  544. X
  545. X    pc->command = CMD_HELLO;
  546. X    skip_spaces (ps);
  547. X    n = 0;
  548. X    ch = ps_scan (ps);
  549. X    while ((ch != '\0') && (ch != ' ') && (n < VERSION_LENGTH-1)) {
  550. X        pc->data.version_name[n++] = ch;
  551. X        ps_next (ps);
  552. X        ch = ps_scan (ps);
  553. X    };
  554. X    pc->data.version_name[n] = '\0';
  555. X    skip_spaces (ps);
  556. X    n = VERSION_LENGTH;
  557. X    ch = ps_scan (ps);
  558. X    while ((ch != '\0') && (ch != ' ')) {
  559. X      if (n < VERSION_NAME_LENGTH-1)
  560. X        pc->data.version_name[n++] = ch;
  561. X      ps_next (ps);
  562. X      ch = ps_scan (ps);
  563. X    };
  564. X    pc->data.version_name[n] = '\0';
  565. X};
  566. Xstatic parse_ack_command (ps, pc)
  567. X    parse_string ps; player_command pc;
  568. X/* The 'ack' command is used to acknowledge another player's 'hello'
  569. X . command.  Each time we receive a hello, we return an 'ack'.
  570. X . The format of the 'ack' command is
  571. X .   ACK <version> <name>
  572. X . where 
  573. X .   <version> is the major revision level of this program
  574. X .   <name>    is the name of the player
  575. X */
  576. X{
  577. X        int n, ch;
  578. X
  579. X    pc->command = CMD_ACK;
  580. X    skip_spaces (ps);
  581. X    n = 0;
  582. X    ch = ps_scan (ps);
  583. X    while ((ch != '\0') && (ch != ' ') && (n < VERSION_LENGTH-1)) {
  584. X        pc->data.version_name[n++] = ch;
  585. X        ps_next (ps);
  586. X        ch = ps_scan (ps);
  587. X    };
  588. X    pc->data.version_name[n] = '\0';
  589. X    skip_spaces (ps);
  590. X    n = VERSION_LENGTH;
  591. X    ch = ps_scan (ps);
  592. X    while ((ch != '\0') && (ch != ' ') && (n < VERSION_NAME_LENGTH-1)) {
  593. X        pc->data.version_name[n++] = ch;
  594. X        ps_next (ps);
  595. X        ch = ps_scan (ps);
  596. X    };
  597. X    pc->data.version_name[n] = '\0';
  598. X};
  599. Xstatic parse_quit_command (ps, pc)
  600. X    parse_string ps; player_command pc;
  601. X/* The 'quit' command indicates that a player has decided to quit the
  602. X . game.
  603. X */
  604. X{
  605. X    pc->command = CMD_QUIT;
  606. X};
  607. Xstatic parse_help_command (ps, pc)
  608. X    parse_string ps; player_command pc;
  609. X/* The help command requests information about the program.
  610. X . The format of the command is:
  611. X .  help <topic>
  612. X . where <topic> is an optional word identifying the name of a topic
  613. X . about which the player seeks help.
  614. X */
  615. X{
  616. X    int ch, n;
  617. X    pc->command = CMD_HELP;
  618. X    skip_spaces (ps);
  619. X    n = 0;
  620. X    ch = ps_scan (ps);
  621. X    while (ch != '\0') {
  622. X            if (n < TOPIC_LENGTH-1)
  623. X          pc->data.topic[n++] = ch;
  624. X        ps_next (ps);
  625. X        ch = ps_scan (ps);
  626. X    };
  627. X    pc->data.topic[n] = '\0';
  628. X};
  629. Xstatic parse_ping_command (ps, pc)
  630. X    parse_string ps; player_command pc;
  631. X/* The ping command tests the network connections.
  632. X . When the program receives a ping command, it automatically
  633. X . issues an "echo" command to the person who sent the "ping".
  634. X . The round-trip time is measured.  The ping command has no
  635. X . parameters.
  636. X */
  637. X{
  638. X    pc->command = CMD_PING;
  639. X};
  640. X
  641. Xstatic parse_echo_command (ps, pc)
  642. X    parse_string ps; player_command pc;
  643. X/* The echo command is issued in response to the ping command.
  644. X . The format of the echo command is:
  645. X .  echo PLAYER
  646. X . where PLAYER is the player who issued the ping command.
  647. X */
  648. X{
  649. X    int ch, n;
  650. X    skip_spaces (ps);
  651. X    pc->data.ping_source = -1;
  652. X    for (n = 0; n < 4; n++) 
  653. X        if (ps_matches_ic(ps, local_player_names[n]))
  654. X            pc->data.ping_source = n;
  655. X    if (pc->data.ping_source == -1)
  656. X        pc->command = CMD_ERROR;
  657. X    else
  658. X        pc->command = CMD_ECHO;
  659. X    parsing_errmsg = echo_error;
  660. X};
  661. Xstatic parse_bell_command (ps, pc)
  662. X/* The BELL commands controls whether or not the terminal's bell
  663. X . will be rung each time it is the local player's turn to enter
  664. X . a bid or play.  The format of the BELL command is
  665. X .   BELL [ON | OFF]
  666. X */
  667. X    parse_string ps; player_command pc;
  668. X{
  669. X    int ch;
  670. X
  671. X    pc->command = CMD_BELL;
  672. X    skip_spaces (ps);
  673. X    ch = ps_scan (ps);
  674. X    if (ch == '\0')
  675. X        pc->data.bell = -1;
  676. X    else if (ps_matches_ic (ps, "ON"))
  677. X        pc->data.bell = 1;
  678. X    else if (ps_matches_ic (ps, "OFF"))
  679. X        pc->data.bell = 0;
  680. X    else {
  681. X        parsing_errmsg = bell_error;
  682. X        pc->command = CMD_ERROR;
  683. X    };
  684. X};
  685. X
  686. Xstatic parse_default_command (ps, pc)
  687. X/* The DEFAULT command controls whether or not the program will automatically
  688. X . provide default inputs.  The format of the DEFAULT command is
  689. X .   DEFAULT [ON | OFF]
  690. X */ 
  691. X    parse_string ps; player_command pc;
  692. X{
  693. X    int ch;
  694. X
  695. X    pc->command = CMD_DEFAULT;
  696. X    skip_spaces (ps);
  697. X    ch = ps_scan (ps);
  698. X    if (ch == '\0')
  699. X        pc->data.defaalt = -1;
  700. X    else if (ps_matches_ic (ps, "ON"))
  701. X        pc->data.defaalt = 1;
  702. X    else if (ps_matches_ic (ps, "OFF"))
  703. X        pc->data.defaalt = 0;
  704. X    else {
  705. X        parsing_errmsg = default_error;
  706. X        pc->command = CMD_ERROR;
  707. X    };
  708. X};
  709. X
  710. Xstatic parse_review_command (ps, pc)
  711. X        parse_string ps; player_command pc;
  712. X/* If we are in the playing phase of the game, then this command displays
  713. X   the bids which were made in the bidding phase.  Otherwise, it does
  714. X   nothing.
  715. X */
  716. X{
  717. X  pc->command = CMD_REVIEW;
  718. X};
  719. X
  720. Xstatic parse_claim_command (ps, pc)
  721. X    parse_string ps; player_command pc;
  722. X/* The claim command is used by the declarer to claim a portion of
  723. X . the remaining tricks.  The defending players are asked if they
  724. X . agree with the claim, and if so, the hand is terminated early.
  725. X . The format of the claim command is:
  726. X .   CLAIM n
  727. X . where n is an integer in the range 0-13.
  728. X */
  729. X{ 
  730. X    int n;
  731. X
  732. X    pc->command = CMD_CLAIM;
  733. X    skip_spaces (ps);
  734. X    if (ps_scan(ps) == '\0')
  735. X      n = 14 - trick;
  736. X    else
  737. X      n = parse_natural (ps, pc);
  738. X
  739. X    if (n < 0)
  740. X      pc->command = CMD_ERROR;
  741. X    else
  742. X      pc->data.tricks = n;
  743. X};
  744. X
  745. Xstatic parse_respond_command (ps, pc)
  746. X    parse_string ps; player_command pc;
  747. X/* The respond command is used by the defending player to indicate
  748. X . his/her response to the declarer's claim.  The format of the
  749. X . respond command is
  750. X .   RESP [ACCEPT | REJECT]
  751. X */
  752. X{
  753. X    pc->command = CMD_RESP;
  754. X    skip_spaces (ps);
  755. X    if (ps_matches_ic (ps, "ACCEPT"))
  756. X        pc->data.response = 1;
  757. X    else if (ps_matches_ic (ps, "REJECT"))
  758. X        pc->data.response = 0;
  759. X    else {
  760. X        parsing_errmsg = resp_error;
  761. X        pc->command = CMD_ERROR;
  762. X    };
  763. X};
  764. X
  765. Xstatic parse_finish_command (ps, pc)
  766. X    parse_string ps; player_command pc;
  767. X/* The finish command brings the current hand to an early conclusion
  768. X . after the defenders have agreed to concede a number of tricks to the
  769. X . declarer.  The format of the finish command is:
  770. X .   FINISH n
  771. X . where n is the number of additional tricks conceded to the declarer.
  772. X */
  773. X{ 
  774. X    int n;
  775. X
  776. X    pc->command = CMD_FINISH; 
  777. X    n = parse_natural (ps, pc); 
  778. X    if (n < 0) 
  779. X        pc->command = CMD_ERROR;
  780. X    else
  781. X        pc->data.tricks = n;
  782. X};
  783. X
  784. X
  785. Xstatic parse_prompt_command (ps, pc)
  786. X    parse_string ps; player_command pc;
  787. X/* The prompt command controls the prompting of the local player when
  788. X . he/she is the dummy.  If prompting is on, then the local player
  789. X . is required to press RETURN at the end of each trick.  If prompting
  790. X . is off, then the local player may not see all of the cards that
  791. X . are played by the others.  The format of the PROMPT command is:
  792. X .   PROMPT [ON | OFF]
  793. X */
  794. X{
  795. X    int ch;
  796. X
  797. X    pc->command = CMD_PROMPT;
  798. X    skip_spaces (ps);
  799. X    ch = ps_scan (ps);
  800. X    if (ch == '\0')
  801. X        pc->data.prompt = -1;
  802. X    else if (ps_matches_ic (ps, "ON"))
  803. X        pc->data.prompt = 1;
  804. X    else if (ps_matches_ic (ps, "OFF"))
  805. X        pc->data.prompt = 0;
  806. X    else {
  807. X        parsing_errmsg = prompt_error;
  808. X        pc->command = CMD_ERROR;
  809. X    };
  810. X};
  811. X
  812. Xstatic parse_score_command (ps, pc)
  813. X    parse_string ps; player_command pc;
  814. X/* The score command is used at the beginning of the game in order
  815. X . to establish the scoring conventions for this game.  The format
  816. X . of the score command is
  817. X .  SCORE [RUBBER | CHICAGO | DUPLICATE | IMP]
  818. X */
  819. X{
  820. X    pc->command = CMD_SCORE;
  821. X    skip_spaces (ps);
  822. X    if (ps_matches_ic (ps, "RUBBER"))
  823. X        pc->data.scoring = RUBBER_SCORING;
  824. X    else if (ps_matches_ic (ps, "CHICAGO"))
  825. X        pc->data.scoring = CHICAGO_SCORING;
  826. X    else if (ps_matches_ic (ps, "DUPLICATE"))
  827. X            pc->data.scoring = DUPLICATE_SCORING;
  828. X    else if (ps_matches_ic (ps, "EMAIL"))
  829. X        pc->data.scoring = EMAIL_SCORING;
  830. X    else if (ps_matches_ic (ps, "IMP"))
  831. X                pc->data.scoring = IMP_SCORING;
  832. X        else {
  833. X        parsing_errmsg = score_error;
  834. X        pc->command = CMD_ERROR;
  835. X    };
  836. X};
  837. X
  838. Xstatic parse_vuln_command (ps, pc)
  839. X    parse_string ps; player_command pc;
  840. X/* The vuln command is used to indicate the vulnerabilities of
  841. X . each side.  It is used during email duplicate play.  The format
  842. X . of the vuln command is:
  843. X .  VULN [NONE | NS | EW | BOTH]
  844. X */
  845. X{
  846. X    pc->command = CMD_VULN;
  847. X    skip_spaces (ps);
  848. X    if (ps_matches_ic(ps, "NORTH"))
  849. X      pc->data.vulnerable = PLAYER_NORTH << 2;
  850. X    else if (ps_matches_ic(ps, "EAST"))
  851. X      pc->data.vulnerable = PLAYER_EAST << 2;
  852. X    else if (ps_matches_ic(ps, "SOUTH"))
  853. X      pc->data.vulnerable = PLAYER_SOUTH << 2;
  854. X    else if (ps_matches_ic(ps, "WEST"))
  855. X      pc->data.vulnerable = PLAYER_WEST << 2;
  856. X    else {
  857. X        parsing_errmsg = vuln_error;
  858. X        pc->command = CMD_ERROR;
  859. X    };
  860. X    skip_spaces (ps);
  861. X    if (ps_matches_ic(ps, "NONE"))
  862. X        pc->data.vulnerable |= 0;
  863. X    else if (ps_matches_ic(ps, "NS"))
  864. X        pc->data.vulnerable |= 1 << SIDE_NS;
  865. X    else if (ps_matches_ic(ps, "EW"))
  866. X        pc->data.vulnerable |= 1 << SIDE_EW;
  867. X    else if (ps_matches_ic(ps, "BOTH"))
  868. X        pc->data.vulnerable |= (1 << SIDE_NS) + (1 << SIDE_EW);
  869. X    else {
  870. X        parsing_errmsg = vuln_error;
  871. X        pc->command = CMD_ERROR;
  872. X    };
  873. X};
  874. X
  875. Xstatic parse_log_command (ps, pc)
  876. X     parse_string ps; player_command pc;
  877. X/* The log command establishes a log file to which the subsequent
  878. X . hands are written.  The format of the log command is
  879. X .  LOG <filename>
  880. X . If <filename> is empty, then logging is discontinued.
  881. X */
  882. X{
  883. X  int i, ch;
  884. X
  885. X  pc->command = CMD_LOG;
  886. X  i = 0;
  887. X  skip_spaces (ps);
  888. X  ch = ps_scan (ps);
  889. X  while ((ch != ' ') && (ch != '\0')) {
  890. X    if (i < FILENAME_LENGTH-1)
  891. X      pc->data.filename[i++] = ch;
  892. X    ps_next (ps);
  893. X    ch = ps_scan (ps);
  894. X  };
  895. X  pc->data.filename[i++] = '\0';
  896. X};
  897. X
  898. Xstatic parse_deal_command (ps, pc)
  899. X    parse_string ps; player_command pc;
  900. X/* The deal command is used by north in the email duplicate scoring
  901. X . option to have the program generate a series of random deals.
  902. X . The format of the command is
  903. X .   DEAL [n]
  904. X . where n is the number of hands which the computer should deal
  905. X . before requesting further action.  If n is omitted, then the
  906. X . computer deals continuously.
  907. X */
  908. X{ 
  909. X    int n;
  910. X
  911. X    pc->command = CMD_DEAL;
  912. X    skip_spaces (ps);
  913. X    if (ps_scan(ps) != '\0') {
  914. X        n = parse_natural (ps, pc);
  915. X        if (n < 0)
  916. X            pc->command = CMD_ERROR;
  917. X        else
  918. X            pc->data.tricks = n;
  919. X    } else
  920. X        n = -1;
  921. X    pc->data.nhands = n;
  922. X};
  923. X
  924. Xstatic parse_save_command (ps, pc)
  925. X     parse_string ps; player_command pc;
  926. X/* The save command writes a set of deals to an email duplicate
  927. X . file.  The format of the save command is:
  928. X .  SAVE <filename>
  929. X */
  930. X{
  931. X  int i, ch;
  932. X
  933. X  pc->command = CMD_SAVE;
  934. X  i = 0;
  935. X  skip_spaces (ps);
  936. X  ch = ps_scan (ps);
  937. X  while ((ch != ' ') && (ch != '\0')) {
  938. X    if (i < FILENAME_LENGTH-1)
  939. X      pc->data.filename[i++] = ch;
  940. X    ps_next (ps);
  941. X    ch = ps_scan (ps);
  942. X  };
  943. X  pc->data.filename[i] = '\0';
  944. X
  945. X  if (i == 0) {
  946. X    parsing_errmsg = save_error;
  947. X    pc->command = CMD_ERROR;
  948. X  };
  949. X};
  950. X
  951. Xstatic parse_load_command (ps, pc)
  952. X     parse_string ps; player_command pc;
  953. X/* The load command reads a set of deals from an email duplicate
  954. X . file.  The format of the load command is:
  955. X .  LOAD <filename>
  956. X */
  957. X{
  958. X  int i, ch;
  959. X
  960. X  pc->command = CMD_LOAD;
  961. X  i = 0;
  962. X  skip_spaces (ps);
  963. X  ch = ps_scan (ps);
  964. X  while ((ch != ' ') && (ch != '\0')) {
  965. X    if (i < FILENAME_LENGTH-1)
  966. X      pc->data.filename[i++] = ch;
  967. X    ps_next (ps);
  968. X    ch = ps_scan (ps);
  969. X  };
  970. X  pc->data.filename[i] = '\0';
  971. X
  972. X  if (i == 0) {
  973. X    parsing_errmsg = load_error;
  974. X    pc->command = CMD_ERROR;
  975. X  };
  976. X};
  977. X
  978. Xstatic parse_replay_command (ps, pc)
  979. X     parse_string ps; player_command pc;
  980. X/* The replay command reads a set of deals from an email duplicate
  981. X . file which will be automatically saved back to the file after the
  982. X . end of play.  The format of the load command is:
  983. X .  REPLAY <filename>
  984. X */
  985. X{
  986. X  int i, ch;
  987. X
  988. X  pc->command = CMD_REPLAY;
  989. X  i = 0;
  990. X  skip_spaces (ps);
  991. X  ch = ps_scan (ps);
  992. X  while ((ch != ' ') && (ch != '\0')) {
  993. X    if (i < FILENAME_LENGTH-1)
  994. X      pc->data.filename[i++] = ch;
  995. X    ps_next (ps);
  996. X    ch = ps_scan (ps);
  997. X  };
  998. X  pc->data.filename[i] = '\0';
  999. X
  1000. X  if (i == 0) {
  1001. X    parsing_errmsg = replay_error;
  1002. X    pc->command = CMD_ERROR;
  1003. X  };
  1004. X};
  1005. X
  1006. Xstatic parse_player_command (ps, pc)
  1007. X    parse_string ps; player_command pc;
  1008. X/* parse the sting ps into a player command, which is stored in pc.
  1009. X * If an error is encountered, then an error message is stored in the
  1010. X * global variable parsing_errmsg.  This procedure looks at the first
  1011. X * word of the command to decide how it should be processed, and then
  1012. X * calls a specialized parsing routine based upon this.  The called
  1013. X * parsing routine is expected to consume all of the input in the
  1014. X * parse_string ps.  If it does not, then an error message is generated.
  1015. X */
  1016. X{
  1017. X        char message_buf [100];
  1018. X
  1019. X    skip_spaces (ps);
  1020. X    if      (ps_matches_ic(ps, "RDEAL" )) parse_rdeal_command (ps, pc);
  1021. X    else if (ps_matches_ic(ps, "BID"  ))  parse_bid_command  (ps, pc);
  1022. X    else if (ps_matches_ic(ps, "RPLAY" )) parse_play_command (ps, pc);
  1023. X    else if (ps_matches_ic(ps, "PLAY"))   parse_local_play_command(ps,pc);
  1024. X    else if (ps_matches_ic(ps, "FINISH")) parse_finish_command (ps, pc);
  1025. X    else if (ps_matches_ic(ps, "TALK" ))  parse_talk_command (ps, pc);
  1026. X    else if (ps_matches_ic(ps, "HELLO"))  parse_hello_command(ps, pc);
  1027. X    else if (ps_matches_ic(ps, "ACK"  ))  parse_ack_command  (ps, pc);
  1028. X    else if (ps_matches_ic(ps, "QUIT" ))  parse_quit_command (ps, pc);
  1029. X    else if (ps_matches_ic(ps, "HELP" ))  parse_help_command (ps, pc);
  1030. X    else if (ps_matches_ic(ps, "BELL" ))  parse_bell_command (ps, pc);
  1031. X    else if (ps_matches_ic(ps, "DEFAULT"))parse_default_command (ps, pc);
  1032. X    else if (ps_matches_ic(ps, "REVIEW")) parse_review_command (ps, pc);
  1033. X    else if (ps_matches_ic(ps, "PROMPT")) parse_prompt_command (ps, pc);
  1034. X    else if (ps_matches_ic(ps, "PING" ))  parse_ping_command (ps, pc);
  1035. X    else if (ps_matches_ic(ps, "ECHO" ))  parse_echo_command (ps, pc);
  1036. X    else if (ps_matches_ic(ps, "CLAIM"))  parse_claim_command (ps, pc);
  1037. X    else if (ps_matches_ic(ps, "RESP"))   parse_respond_command (ps, pc);
  1038. X    else if (ps_matches_ic(ps, "SCORE"))  parse_score_command (ps, pc);
  1039. X    else if (ps_matches_ic(ps, "LOG"))    parse_log_command (ps, pc);
  1040. X    else if (ps_matches_ic(ps, "DEAL"))   parse_deal_command (ps, pc);
  1041. X    else if (ps_matches_ic(ps, "LOAD"))   parse_load_command (ps, pc);
  1042. X    else if (ps_matches_ic(ps, "REPLAY")) parse_replay_command (ps, pc);
  1043. X    else if (ps_matches_ic(ps, "SAVE"))   parse_save_command (ps, pc);
  1044. X    else if (ps_matches_ic(ps, "COMMENT"))parse_comment_command (ps, pc);
  1045. X    else if (ps_matches_ic(ps, "VULN"))   parse_vuln_command (ps, pc);
  1046. X    else if (ps_matches_ic(ps, "SEATERR")) {
  1047. X      reset_network ();
  1048. X      Display_Player_Comment ("NETWORK",
  1049. X        "THE SEAT YOU REQUESTED IS ALREADY TAKEN!");
  1050. X      Display_Player_Comment ("NETWORK", ps + PS_OFFSET + ps[PS_POS]);
  1051. X      Terminate_Program ("PROGRAM TERMINATING");
  1052. X    } else {
  1053. X        pc->command = CMD_ERROR;
  1054. X        parsing_errmsg = keyword_error;
  1055. X    };
  1056. X    skip_spaces (ps);
  1057. X    if ((ps_scan(ps) != '\0') && (pc->command != CMD_ERROR)) {
  1058. X        if (pc->command == CMD_BID)
  1059. X            parsing_errmsg = bid_error;
  1060. X        else if (pc->command == CMD_PLAY)
  1061. X            parsing_errmsg = play_error;
  1062. X        else
  1063. X            parsing_errmsg = ending_error;
  1064. X        pc->command = CMD_ERROR;
  1065. X    };
  1066. X};
  1067. Xclear_input_buffer (ib)
  1068. X    input_buffer ib;
  1069. X/* Clears the screen display representing the input buffer, and
  1070. X * resets the cursor position.
  1071. X */
  1072. X{
  1073. X    int i;
  1074. X    ib->buf[0]   = '\0';
  1075. X    ib->pos      =   0;
  1076. X    for (i = 0; i < ib->length; i++)
  1077. X        print (ib->row, ib->col + i, " ");
  1078. X    set_cursor (ib->row, ib->col);
  1079. X    ib->defaulted = 0;
  1080. X};
  1081. X
  1082. Xstatic Review_Bidding ()
  1083. X/* Generates a review of the bidding. */
  1084. X{
  1085. X  int i, passes, round, first_bid;
  1086. X  char msg_buf[80];
  1087. X
  1088. X  if ((game_mode == PLAYING_MODE) || (game_mode == REVIEW_MODE)) {
  1089. X    sprintf (msg_buf, "OPENED BY %s: ", player_names[dealer]);
  1090. X    i = 0; passes = round = -1; first_bid = 1;
  1091. X    while ((passes < 3) && (strlen(msg_buf) < 72)) {
  1092. X      if (i == dealer) round++;
  1093. X      if (round >= 0) {
  1094. X    sprintf (msg_buf + strlen(msg_buf), "%s%s",
  1095. X         first_bid? " ": "-",
  1096. X         bid_names[bids[i][round]]);
  1097. X    if (bids[i][round] == BID_PASS) passes++;
  1098. X    else passes = 0;
  1099. X    first_bid = 0;
  1100. X      };
  1101. X      i = player_next [i];
  1102. X    };
  1103. X    if (strlen(msg_buf) > 72)
  1104. X      sprintf (msg_buf+strlen(msg_buf), " ...");
  1105. X    Display_Status (msg_buf);
  1106. X  } else 
  1107. X    Display_Status 
  1108. X      ("THE BIDDING CANNOT BE REVIEWED NOW");
  1109. X};
  1110. X
  1111. Xint update_input_buffer (pib, ch)
  1112. X    input_buffer pib; int ch;
  1113. X/* Adds the character ch to the input buffer, or if it is a control
  1114. X . character, then modifies the cursor position or buffer appropriately.
  1115. X . If the user has indicated he is through entering input, by entering
  1116. X . i.e. <^J> or <^M>, then TRUE is returned.  Otherwise, FALSE is
  1117. X . returned.
  1118. X */
  1119. X{
  1120. X    char chbuf[2], *def, message_buf[80];
  1121. X    input_buffer ib;
  1122. X
  1123. X    ib = forced_talk_mode? talk_buffer: pib;
  1124. X     chbuf[1] = '\0';
  1125. X    if (isprint(ch) && (ib->pos < ib->length)) {
  1126. X            if (ib->defaulted)
  1127. X          clear_input_buffer (ib);
  1128. X        chbuf[0] = ch;
  1129. X        print (ib->row, ib->col+ib->pos, chbuf);
  1130. X        ib->buf[ib->pos++] = ch;
  1131. X        ib->buf[ib->pos] = '\0';
  1132. X    } else if ((ch == '\010') || (ch == '\177')) {
  1133. X            if (ib->pos > 0) {
  1134. X          chbuf[0] = ' ';
  1135. X          ib->buf[--ib->pos] = '\0';
  1136. X          print (ib->row, ib->col+ib->pos, chbuf);
  1137. X        };
  1138. X        ib->defaulted = 0;
  1139. X        } else if (ch == '\033') {              /* ESC - clear input buffer. */
  1140. X        clear_input_buffer (ib);
  1141. X        forced_talk_mode = 0;
  1142. X        ib = pib;
  1143. X        print (ib->row, ib->col, ib->buf);
  1144. X        } else if (ch == '\002') {              /* ^B - review bidding. */
  1145. X      Clear_Status_Display ();
  1146. X          Review_Bidding ();
  1147. X    } else if (ch == '\004') {              /* ^D - toggle default mode */
  1148. X      Clear_Status_Display ();
  1149. X      default_plays = 1 - default_plays;
  1150. X      sprintf (message_buf, "DEFAULT INPUT MODE IS NOW %s",
  1151. X           default_plays? "ON": "OFF");
  1152. X      Display_Status (message_buf);
  1153. X        } else if (ch == '\007') {              /* ^G - toggle bell. */
  1154. X      Clear_Status_Display ();
  1155. X      ring_my_bell = 1-ring_my_bell;
  1156. X      sprintf (message_buf, "THE BELL IS NOW %s",
  1157. X           ring_my_bell? "ON": "OFF");
  1158. X      Display_Status (message_buf);
  1159. X      if (ring_my_bell) ring_bell ();
  1160. X    } else if (ch == '\020') {              /* ^P - toggle prompt mode */
  1161. X      Clear_Status_Display ();
  1162. X      prompt_dummy = 1-prompt_dummy;
  1163. X      sprintf (message_buf, "PROMPT MODE IS NOW %s",
  1164. X           prompt_dummy? "ON": "OFF");
  1165. X      Display_Status (message_buf);
  1166. X    } else if (ch == '\022') {        /* ^R - refresh display. */
  1167. X        Refresh_Display ();
  1168. X        print (ib->row, ib->col, ib->buf);
  1169. X        } else if (ch == '\024') {             /* ^T - forced talk mode. */
  1170. X           if (ib != talk_buffer)
  1171. X         forced_talk_mode = 1;
  1172. X           ib = talk_buffer;
  1173. X    } else if (ch == '\030') {           /* ^X  - abort program. */
  1174. X            Terminate_Program 
  1175. X          ("INTERRUPT RECEIVED -- TERMINATING PROGRAM");
  1176. X    } else if (ch == '\0')
  1177. X        print (ib->row, ib->col, ib->buf);
  1178. X    else if ((ch == '\012') || (ch == '\015')) {
  1179. X            if ((ib->pos > 0) && !forced_talk_mode)
  1180. X                return (1);
  1181. X        else if (forced_talk_mode) {
  1182. X          forced_talk_mode = 0;
  1183. X          if (!Reserved_message (ib->buf)) {
  1184. X            send_message_talk (ib->buf);
  1185. X            Display_Player_Comment (player_names[local_player],
  1186. X                        ib->buf);
  1187. X          };
  1188. X          clear_input_buffer (ib);
  1189. X          ib = pib;
  1190. X          print (ib->row, ib->col, ib->buf);
  1191. X        } else {
  1192. X          if (waiting_for_acknowledgment && !forced_talk_mode) {
  1193. X            waiting_for_acknowledgment = 0;
  1194. X            return (1);
  1195. X          };
  1196. X          if (default_plays && (default_input != NULL)) {
  1197. X                for (def = default_input; *def != '\0'; def++)
  1198. X                     ib->buf[ib->pos++] = *def;
  1199. X            ib->buf[ib->pos] = '\0';
  1200. X            print (ib->row, ib->col, default_input);
  1201. X            ib->defaulted = 1;
  1202. X              };
  1203. X        };
  1204. X        };
  1205. X    set_cursor (ib->row, ib->col+ib->pos);
  1206. X    return (0);
  1207. X};
  1208. Xsend_player_message (message)
  1209. X    char *message;
  1210. X/* Transmits the message to the other players through the network. 
  1211. X * Prepends the name of the player to the message before transmitting.
  1212. X */
  1213. X{
  1214. X    char msg_buf [100];
  1215. X    sprintf (msg_buf,"%s %s",local_player_names[local_player], message);
  1216. X    send_message (msg_buf);
  1217. X};
  1218. Xstatic receive_player_message (player_no, pm)
  1219. X    int *player_no; parse_string pm;
  1220. X/* Receives a player message if one is available.  Returns with
  1221. X . player_no set to the player from whom the message was sent, and
  1222. X . pm containing the text of the message.  If there was an error in
  1223. X . the message, or if no message is available, then returns with
  1224. X . player_no set to -1.
  1225. X */
  1226. X{
  1227. X    int i;
  1228. X    char msg_buf [100], error_buf [120];
  1229. X
  1230. X    *player_no = -1;
  1231. X    if (!message_available()) return;
  1232. X
  1233. X    receive_message (msg_buf);
  1234. X    if (strlen(msg_buf) == 0) return;
  1235. X
  1236. X    ps_copy (pm, msg_buf);
  1237. X    for (i = 0; i < 4; i++) {
  1238. X        if (ps_matches_ic(pm, local_player_names[i])) {
  1239. X            *player_no = i;
  1240. X            i = 4;
  1241. X        };
  1242. X    };
  1243. X    if (*player_no == -1) {
  1244. X            sprintf (error_buf, "UNRECOGNIZED PLAYER: %s", msg_buf);
  1245. X        Display_Player_Comment ("NETWORK ERROR", error_buf);
  1246. X          };
  1247. X};
  1248. Xstatic send_message_rdeal (current_deal)
  1249. X    deal current_deal;
  1250. X/* Converts the deal 'current_deal' into a string and sends it as a
  1251. X . message to the other players.
  1252. X */
  1253. X{
  1254. X    char deal_string[53], deal_command[80];
  1255. X    int i;
  1256. X    for (i = 0; i < 52; i++) deal_string[i] = current_deal[i] + '0';
  1257. X    deal_string[52] = '\0';
  1258. X    sprintf (deal_command, "RDEAL %s", deal_string);
  1259. X    send_player_message (deal_command);
  1260. X};
  1261. Xstatic send_message_bid (bid)
  1262. X    int bid;
  1263. X{
  1264. X    char bid_command[40];
  1265. X    sprintf (bid_command, "BID %s", bid_names[bid]);
  1266. X    send_player_message (bid_command);
  1267. X};
  1268. Xstatic send_message_play (play)
  1269. X    int play;
  1270. X{
  1271. X    char play_command[40];
  1272. X    sprintf (play_command, "RPLAY %s", card_names[play]);
  1273. X    send_player_message (play_command);
  1274. X};
  1275. Xstatic send_message_talk (talk_message)
  1276. X    char *talk_message;
  1277. X{
  1278. X    char talk_command[100];
  1279. X    sprintf (talk_command, "TALK %s", talk_message);
  1280. X    send_player_message (talk_command);
  1281. X};
  1282. Xstatic send_message_hello ()
  1283. X{
  1284. X        char hello_command [80];
  1285. X
  1286. X    sprintf (hello_command, "HELLO %s %s", major_revision_level,
  1287. X         player_names[local_player]);
  1288. X    send_player_message (hello_command);
  1289. X};
  1290. Xstatic send_message_ack ()
  1291. X{
  1292. X        char ack_command [80];
  1293. X
  1294. X    sprintf (ack_command, "ACK %s %s", major_revision_level,
  1295. X         player_names[local_player]);
  1296. X    send_player_message (ack_command);
  1297. X};
  1298. Xstatic send_message_quit ()
  1299. X{
  1300. X    send_player_message ("QUIT");
  1301. X};
  1302. Xstatic send_message_ping ()
  1303. X{
  1304. X    send_player_message ("PING");
  1305. X};
  1306. X
  1307. Xstatic send_message_echo (player)
  1308. X    int player;
  1309. X{
  1310. X    char echobuf[20];
  1311. X
  1312. X    sprintf (echobuf, "ECHO %s", local_player_names[player]);
  1313. X    send_player_message (echobuf);
  1314. X};
  1315. X
  1316. Xstatic send_message_claim (k)
  1317. X     int k;
  1318. X{
  1319. X  char message_buf [40];
  1320. X
  1321. X  sprintf (message_buf, "CLAIM %d", k);
  1322. X  send_player_message (message_buf);
  1323. X};
  1324. X
  1325. Xstatic send_message_respond (r)
  1326. X     int r;
  1327. X{
  1328. X  if (r)
  1329. X    send_player_message ("RESP ACCEPT");
  1330. X  else
  1331. X    send_player_message ("RESP REJECT");
  1332. X};
  1333. X
  1334. Xstatic send_message_finish (n)
  1335. X     int n;
  1336. X{
  1337. X  char message_buf[40];
  1338. X
  1339. X  sprintf (message_buf, "FINISH %d", n);
  1340. X  send_player_message (message_buf);
  1341. X};
  1342. X
  1343. Xstatic send_message_score ()
  1344. X{
  1345. X  switch (scoring_mode) {
  1346. X  case RUBBER_SCORING:
  1347. X    send_player_message ("SCORE RUBBER");
  1348. X    break;
  1349. X  case CHICAGO_SCORING:
  1350. X    send_player_message ("SCORE CHICAGO");
  1351. X    break;
  1352. X  case DUPLICATE_SCORING:
  1353. X    send_player_message ("SCORE DUPLICATE");
  1354. X    break;
  1355. X  case EMAIL_SCORING:
  1356. X    send_player_message ("SCORE EMAIL");
  1357. X    break;
  1358. X  case IMP_SCORING:
  1359. X    send_player_message ("SCORE IMP");
  1360. X    break;
  1361. X  };
  1362. X};
  1363. X
  1364. Xstatic send_message_comment (c)
  1365. X    char *c;
  1366. X{
  1367. X    char comment_buf[90];
  1368. X
  1369. X    sprintf (comment_buf, "COMMENT %s", c);
  1370. X    send_player_message (comment_buf);
  1371. X};
  1372. X    
  1373. Xvoid Broadcast_Comment (c)
  1374. X    char *c;
  1375. X{
  1376. X    send_message_comment (c);
  1377. X    Display_Player_Comment ("MODERATOR", c);
  1378. X};
  1379. X
  1380. Xstatic send_message_vuln (d, ns, ew)
  1381. X    int d, ns, ew;
  1382. X{
  1383. X        char message_buf [80];
  1384. X
  1385. X    if (ns && ew)
  1386. X      sprintf (message_buf, "VULN %s BOTH", local_player_names[d]);
  1387. X    else if (ns)
  1388. X      sprintf (message_buf, "VULN %s NS", local_player_names[d]);
  1389. X    else if (ew)
  1390. X      sprintf (message_buf, "VULN %s EW", local_player_names[d]);
  1391. X    else
  1392. X      sprintf (message_buf, "VULN %s NONE", local_player_names[d]);
  1393. X    send_player_message (message_buf);
  1394. X};
  1395. X
  1396. Xstatic receive_player_command (pc)
  1397. X    player_command pc;
  1398. X/* Receives a message from the network.  Parses it into pc. */
  1399. X{
  1400. X    int player;
  1401. X    receive_player_message (&player, command_string);
  1402. X    pc->player_no = player;
  1403. X    if (player >= 0)
  1404. X      parse_player_command (command_string, pc);
  1405. X};
  1406. X
  1407. Xstatic int verify_compatibility (remote_player, remote_version)
  1408. X     int remote_player; char *remote_version;
  1409. X/* This procedure compares the revision level being used by
  1410. X . one of the remote players to the one being used by the local
  1411. X . player.  If there is a mismatch, then an error message is
  1412. X . generated.  If we are a client and we find that we are incompatible
  1413. X . with the server, then the program aborts.  If we detect an
  1414. X . incompatibility error in some other situation, we return 1.
  1415. X . Otherwise, we return 0.
  1416. X */
  1417. X{
  1418. X        char error_message [80];
  1419. X
  1420. X        if (!strcmp (major_revision_level, remote_version))
  1421. X      return;
  1422. X
  1423. X    sprintf (error_message, "INCOMPATIBILITY ERROR -- ");
  1424. X    if (*remote_version == 0) {
  1425. X      sprintf (error_message + strlen(error_message), 
  1426. X           "%s IS USING VERSION 1.0",
  1427. X           player_names[remote_player]);
  1428. X    } else {
  1429. X      sprintf (error_message + strlen(error_message), 
  1430. X           "%s IS USING VERSION %s",
  1431. X           player_names[remote_player], remote_version);
  1432. X    };
  1433. X    if (server_mode) {
  1434. X      Display_Player_Comment ("MODERATOR", error_message);
  1435. X      Close_Network_Connection (local_player_names[remote_player]);
  1436. X      players_here [remote_player] = 0;
  1437. X      sprintf (error_message, "%s (%s) HAS BEEN DISCONNECTED.",
  1438. X           player_names[remote_player],
  1439. X           local_player_names[remote_player]);
  1440. X      Broadcast_Comment (error_message);
  1441. X      return (1);
  1442. X    } else if (remote_player != local_player) {
  1443. X      Display_Player_Comment ("MODERATOR", error_message);
  1444. X      players_here [remote_player] = 0;
  1445. X      return (1);
  1446. X    } else
  1447. X      Terminate_Program (error_message);
  1448. X
  1449. X    return (0);
  1450. X};      
  1451. X
  1452. END_OF_FILE
  1453. if test 40095 -ne `wc -c <'input.c.aa'`; then
  1454.     echo shar: \"'input.c.aa'\" unpacked with wrong size!
  1455. fi
  1456. # end of 'input.c.aa'
  1457. fi
  1458. if test -f 'startup.c' -a "${1}" != "-c" ; then 
  1459.   echo shar: Will not clobber existing file \"'startup.c'\"
  1460. else
  1461. echo shar: Extracting \"'startup.c'\" \(10601 characters\)
  1462. sed "s/^X//" >'startup.c' <<'END_OF_FILE'
  1463. X/* startup.c
  1464. X ! 
  1465. X ! Copyright (C) 1991 by Matthew Clegg
  1466. X ! 
  1467. X ! This program may be copied and distributed freely.  Please do not
  1468. X ! charge money for this program or for any program derived from it.
  1469. X ! If you modify this program, then include a notice stating plainly
  1470. X ! that your program is derived from the okbridge program and is not
  1471. X ! the same as the official okbridge program.
  1472. X !
  1473. X ! I welcome any suggestions for improvement to okbridge, and 
  1474. X ! I would be especially happy to receive improved source code.
  1475. X ! If you have comments or suggestions, or if you would like to
  1476. X ! join the okbridge mailing list, then write to
  1477. X !
  1478. X !   mclegg@cs.ucsd.edu
  1479. X !
  1480. X *
  1481. X * This file contains procedures for reading the okbridge startup
  1482. X * file .okbridgerc.  Each line in this file is either a comment line
  1483. X * or a (field, value) pair.  Comment lines begin with the pound sign
  1484. X * '#' character.  Field, value pairs are of the format
  1485. X *    <Field-name>    <value>
  1486. X *
  1487. X * The fields which are currently recognized are as follows:
  1488. X *
  1489. X * BELL        ON | OFF
  1490. X *    When requesting input (a bid or a play), the terminal's
  1491. X *    bell is rung by default.  However, this can be disabled
  1492. X *    by specifying 'BELL OFF'.  This has the same effect as the
  1493. X *    '/BELL OFF' command.
  1494. X *
  1495. X * DEFAULT      ON | OFF
  1496. X *      This controls whether or not default inputs will be provided for
  1497. X *      bids, plays and questions.
  1498. X *
  1499. X * HELPFILE    <directory-name>
  1500. X *    This field specifies the directory to be used for reading
  1501. X *    the okbridge help files.
  1502. X *
  1503. X * LOAD         <email-duplicate-filename>
  1504. X *      This field is only valid if the position is north and the
  1505. X *      scoring mode is email duplicate.  In this case, okbridge will
  1506. X *      automatically read a set of email duplicate boards from the
  1507. X *      named file.
  1508. X *
  1509. X * LOG        <filename>
  1510. X *    If this statement is present in the startup file, then
  1511. X *    the hands will automatically be logged to the given filename.
  1512. X *      If the first character of <filename> is '+', then logs the
  1513. X *      hands to the end of the file rather than erasing the old file.
  1514. X *
  1515. X * NAME        <local-player-name>
  1516. X *    This field specifies the name that will be used to identify
  1517. X *    the local player to the other players.
  1518. X *
  1519. X * POSITION    NORTH | EAST | SOUTH | WEST
  1520. X *    This field specifies the local player's position.
  1521. X *
  1522. X * PORT        <positive-integer>
  1523. X *    This field specifies the internet port number that will be
  1524. X *    used for communications with the server.
  1525. X *
  1526. X * PROMPT     NO | YES
  1527. X *    The value of this field is only relevant in hands where the
  1528. X *    local player is the dummy.  In this case, the dummy is
  1529. X *    ordinarily prompted to press RETURN at the end of each trick.
  1530. X *    This allows the dummy to see the cards that are played as they
  1531. X *    are played.  However, if 'PROMPT NO' is specified, then the
  1532. X *    dummy will not be prompted.
  1533. X *
  1534. X * REPLAY       <email-duplicate-filename>
  1535. X *      This field is only valid if the position is north and the
  1536. X *      scoring mode is email duplicate.  In this case, a set of
  1537. X *      boards will automatically be read from the named file.
  1538. X *      After they have been played, the results will automatically
  1539. X *      be written back to the file from which the boards were read.
  1540. X *
  1541. X * SCORING    RUBBER | CHICAGO | DUPLICATE | EMAIL | IMP
  1542. X *    This field is only relevant if the local player is north.
  1543. X *    In this case, the SCORING field determines the type of scoring
  1544. X *    that will be used by default in the game.
  1545. X *
  1546. X * SERVER    ME | <internet-name-or-number>
  1547. X *    If the value of this field is 'ME', then the local player
  1548. X *    will assume the role of server.  If the value of this field
  1549. X *    is anything else, then it is interpreted as an internet name
  1550. X *    or number of the machine where the server is running.
  1551. X *
  1552. X*/
  1553. X
  1554. X#include <ctype.h>
  1555. X#include <stdio.h>
  1556. X#include <string.h>
  1557. X
  1558. X#include "globals.h"
  1559. X
  1560. Xextern char *getenv ();
  1561. Xextern char *strdup ();
  1562. X
  1563. X#ifdef HPUX
  1564. X#define index(X,Y) strchr(X,Y)
  1565. X#else 
  1566. X#ifndef index
  1567. Xextern char *index ();
  1568. X#endif
  1569. X#endif
  1570. X
  1571. X#define    MAX_LENGTH    100
  1572. X
  1573. Xtypedef void (*field_handler) ();
  1574. X
  1575. Xtypedef struct Field_Descriptor_struct {
  1576. X    char *field_name;
  1577. X    char *parameter_type;
  1578. X    field_handler    handler;
  1579. X} Field_Descriptor;
  1580. X
  1581. Xvoid Bell_Field (), Default_Field (), Helpfile_Field (), Load_Field (), 
  1582. X     Log_Field (), Name_Field (), Position_Field (), Port_Field (), 
  1583. X     Prompt_Field (), Replay_Field (), Scoring_Field (), Server_Field ();
  1584. X
  1585. Xstatic Field_Descriptor Fields [] = {
  1586. X    {"BELL",    "OFF,ON",            Bell_Field},
  1587. X    {"DEFAULT",    "OFF,ON",            Default_Field},
  1588. X    {"HELPFILE",    "*",                Helpfile_Field},
  1589. X    {"LOAD",        "*",                            Load_Field},
  1590. X    {"LOG",        "*",                Log_Field},
  1591. X    {"NAME",    "*",                Name_Field},
  1592. X    {"POSITION",    "NORTH,EAST,SOUTH,WEST",    Position_Field},
  1593. X    {"PORT",    "#",                Port_Field},
  1594. X    {"PROMPT",    "NO,YES",            Prompt_Field},
  1595. X        {"REPLAY",      "*",                            Replay_Field},
  1596. X    {"SCORING",    "RUBBER,CHICAGO,DUPLICATE,EMAIL,IMP",     Scoring_Field},
  1597. X    {"SERVER",    "*",                Server_Field},
  1598. X    {NULL,        NULL,                NULL}
  1599. X  };
  1600. X
  1601. Xstatic char line_buffer [MAX_LENGTH];
  1602. Xstatic int  current_char, last_pos;
  1603. Xstatic int  line_length;
  1604. Xstatic int  line_no;
  1605. Xstatic FILE *init_file;
  1606. Xstatic int  error_flag;
  1607. X
  1608. Xextern int errno;
  1609. Xextern char *sys_errlist[];
  1610. X
  1611. Xextern int ring_my_bell;
  1612. Xextern char *help_file_name;
  1613. Xextern FILE *logfile;
  1614. Xextern int local_player;
  1615. Xextern int network_port;
  1616. Xextern int server_mode;
  1617. Xextern char *server_name;
  1618. Xextern char *local_player_name;
  1619. Xextern char *autoload_file, *autosave_file;
  1620. X
  1621. Xstatic Field_Error (error_msg)
  1622. X    char *error_msg;
  1623. X/* Prints out the current line from the field file along with an error
  1624. X   message.
  1625. X*/
  1626. X{
  1627. X    int i;
  1628. X
  1629. X    fprintf (stderr, "line %2d: %s\n", line_no, line_buffer);
  1630. X    for (i = 0; i < last_pos + 9; i++) 
  1631. X        fprintf (stderr, " ");
  1632. X    fprintf (stderr, "^ %s\n", error_msg);
  1633. X    error_flag = 1;
  1634. X};
  1635. X
  1636. Xstatic void Bell_Field (i)
  1637. X    int i;
  1638. X{
  1639. X    ring_my_bell = i;
  1640. X};
  1641. X
  1642. Xstatic void Default_Field (i)
  1643. X     int i;
  1644. X{
  1645. X  default_plays = i;
  1646. X};
  1647. X
  1648. Xstatic void Helpfile_Field (s)
  1649. X    char *s;
  1650. X{
  1651. X    help_file_name = strdup (s);
  1652. X};
  1653. X
  1654. Xstatic void Load_Field (s)
  1655. X     char *s;
  1656. X{
  1657. X  autoload_file = strdup (s);
  1658. X};
  1659. X
  1660. Xstatic void Log_Field (s)
  1661. X    char *s;
  1662. X{
  1663. X    char error_buf [80];
  1664. X    char *filename;
  1665. X
  1666. X    if (s[0] == '+') {
  1667. X      filename = s + 1;
  1668. X      logfile = fopen (filename, "a");
  1669. X    } else {
  1670. X      filename = s;
  1671. X      logfile = fopen (filename, "w");
  1672. X    };
  1673. X    if (logfile == NULL) {
  1674. X        sprintf (error_buf, "Error opening %s: %s", filename,
  1675. X            sys_errlist[errno]);
  1676. X        Field_Error (error_buf);
  1677. X    };
  1678. X};
  1679. X
  1680. Xstatic void Name_Field (s)
  1681. X    char *s;
  1682. X{
  1683. X    local_player_name = strdup (s);
  1684. X};
  1685. X
  1686. Xstatic void Position_Field (i)
  1687. X    int i;
  1688. X{
  1689. X    local_player = i;
  1690. X};
  1691. X
  1692. Xstatic void Port_Field (i)
  1693. X    int i;
  1694. X{
  1695. X    network_port = i;
  1696. X};
  1697. X
  1698. Xstatic void Prompt_Field (i)
  1699. X    int i;
  1700. X{
  1701. X    prompt_dummy = i;
  1702. X};
  1703. X
  1704. Xstatic void Replay_Field (s)
  1705. X     char *s;
  1706. X{
  1707. X  autoload_file = strdup (s);
  1708. X  autosave_file = strdup (s);
  1709. X};
  1710. X
  1711. Xstatic void Scoring_Field (i)
  1712. X    int i;
  1713. X{
  1714. X    scoring_mode = i;
  1715. X};
  1716. X
  1717. Xstatic void Server_Field (s)
  1718. X    char *s;
  1719. X{
  1720. X    if (!strcasecmp(s, "ME"))
  1721. X        server_mode = 1;
  1722. X    else {
  1723. X        server_mode = 0;
  1724. X        server_name = strdup (s);
  1725. X    };
  1726. X};
  1727. X
  1728. Xstatic int Read_Field_Line ()
  1729. X/* Reads a line from initialization file init_file and copies it into
  1730. X   line_buffer.  Returns 1 if data is returned, or 0 if the end of file
  1731. X   is reached.  Skips comment lines.  Strips trailing blanks from the
  1732. X   end of the line.
  1733. X*/
  1734. X{
  1735. X    int ch;
  1736. X
  1737. X    do {
  1738. X        line_length = 0;
  1739. X        ch = getc (init_file);
  1740. X        while ((ch != EOF) && (ch != '\n')) {
  1741. X            if (line_length < MAX_LENGTH)
  1742. X                line_buffer [line_length++] = ch;
  1743. X            ch = getc(init_file);
  1744. X        };
  1745. X        if (ch == EOF)
  1746. X            return (0);
  1747. X        line_no++;
  1748. X        while (line_buffer[line_length-1] == ' ')
  1749. X            line_length--;
  1750. X    } while ((line_length == 0) || (line_buffer[0] == '#'));
  1751. X
  1752. X    current_char = 0;
  1753. X    line_buffer[line_length] = '\0';
  1754. X    return (1);
  1755. X};
  1756. X
  1757. Xstatic int Read_Keyword (buf, buflen)
  1758. X    char *buf; int buflen;
  1759. X/* Reads a whitespace delimited sequence of characters from the current
  1760. X   input line into the buffer.  Returns the number of characters
  1761. X   transferred.
  1762. X*/
  1763. X{
  1764. X    int i;
  1765. X
  1766. X    i = 0;
  1767. X    while ((current_char < line_length) && 
  1768. X       isspace (line_buffer[current_char])) 
  1769. X        current_char++;
  1770. X
  1771. X    last_pos = current_char;
  1772. X    while ((current_char < line_length) && 
  1773. X      !isspace(line_buffer[current_char])) {
  1774. X        if (i < buflen-1)
  1775. X            buf[i++] = line_buffer[current_char++];
  1776. X        else
  1777. X            current_char++;
  1778. X    };
  1779. X    buf[i] = '\0';
  1780. X    return (i);
  1781. X};
  1782. X
  1783. Xstatic int Lookup_Keyword (lookup_string, keyword)
  1784. X    char *lookup_string, *keyword;
  1785. X/* Assumes that lookup_string is a comma-delimited sequence of keywords.
  1786. X   Looks for the keyword in lookup_string which matches the given keyword.
  1787. X   If the keyword is found, then returns its index in lookup_string,
  1788. X   i.e.,  Lookup_Keyword ("OFF,ON", "OFF") = 0, 
  1789. X      Lookup_Keyword ("OFF,ON", "ON") = 1.
  1790. X   If the keyword is not found, then returns -1.
  1791. X*/
  1792. X{
  1793. X    int i, n;
  1794. X
  1795. X    i = 0;
  1796. X    n = strlen (keyword);
  1797. X    while (lookup_string != NULL) {
  1798. X        if (!strncasecmp(lookup_string, keyword, n)) {
  1799. X            if ((lookup_string[n] == '\0') || 
  1800. X                (lookup_string[n] == ','))
  1801. X                return (i);
  1802. X        };
  1803. X        i += 1;
  1804. X        lookup_string = index (lookup_string, ',');
  1805. X        if (lookup_string != NULL) lookup_string++;
  1806. X    };
  1807. X    return (-1);
  1808. X        
  1809. X};
  1810. X
  1811. Xstatic int all_digits (s)
  1812. X    char *s;
  1813. X{
  1814. X    while (*s)
  1815. X        if (!isdigit(*(s++)))
  1816. X            return (0);
  1817. X    return (1);
  1818. X};
  1819. X
  1820. Xvoid Read_Initialization_File ()
  1821. X{
  1822. X    char *home_dir, filename_buf[128];
  1823. X    char name [20], value [80], message_buf[80];
  1824. X    int i, j;
  1825. X
  1826. X    init_file = fopen (".okbridgerc", "r");
  1827. X    home_dir = getenv ("HOME");
  1828. X
  1829. X    if ((init_file == NULL) && (home_dir != NULL)) {
  1830. X        sprintf (filename_buf, "%s/.okbridgerc", home_dir);
  1831. X        init_file = fopen (filename_buf, "r");
  1832. X    };
  1833. X
  1834. X    if (init_file == NULL) {
  1835. X        /* couldn't find an initialization file. */
  1836. X        return;
  1837. X    };
  1838. X
  1839. X    error_flag = 0;
  1840. X    while (Read_Field_Line()) {
  1841. X        Read_Keyword (name, 20);
  1842. X        i = 0;
  1843. X        while ((Fields[i].field_name != NULL) &&
  1844. X           strcmp(Fields[i].field_name, name))
  1845. X            i++;
  1846. X        if (Fields[i].field_name == NULL)
  1847. X            Field_Error ("Error in field name");
  1848. X        else {
  1849. X          Read_Keyword (value, 80);
  1850. X          if (current_char < line_length)
  1851. X            Field_Error ("Extra data at end of line");
  1852. X          if (Fields[i].parameter_type[0] == '*')
  1853. X            Fields[i].handler (value);
  1854. X          else if (Fields[i].parameter_type[0] == '#') {
  1855. X            if (all_digits(value))
  1856. X                Fields[i].handler (atoi(value));
  1857. X            else
  1858. X                Field_Error ("Expected a positive integer");
  1859. X          } else {
  1860. X            j = Lookup_Keyword (Fields[i].parameter_type, value);
  1861. X            if (j < 0) {
  1862. X                sprintf (message_buf, "Expected keyword: %s",
  1863. X                    Fields[i].parameter_type);
  1864. X                Field_Error (message_buf);
  1865. X            } else
  1866. X                Fields[i].handler (j);
  1867. X          };
  1868. X        };
  1869. X    };
  1870. X
  1871. X    fclose (init_file);
  1872. X
  1873. X    if (error_flag) {
  1874. X        fprintf (stderr, "\n%s -- %s\n",
  1875. X            "Errors in .okbridgerc initialization file",
  1876. X            "Program terminating");
  1877. X        exit (1);
  1878. X    };
  1879. X    
  1880. X};
  1881. END_OF_FILE
  1882. if test 10601 -ne `wc -c <'startup.c'`; then
  1883.     echo shar: \"'startup.c'\" unpacked with wrong size!
  1884. fi
  1885. # end of 'startup.c'
  1886. fi
  1887. echo shar: End of archive 3 \(of 7\).
  1888. cp /dev/null ark3isdone
  1889. MISSING=""
  1890. for I in 1 2 3 4 5 6 7 ; do
  1891.     if test ! -f ark${I}isdone ; then
  1892.     MISSING="${MISSING} ${I}"
  1893.     fi
  1894. done
  1895. if test "${MISSING}" = "" ; then
  1896.     echo You have unpacked all 7 archives.
  1897.     rm -f ark[1-9]isdone
  1898.     echo creating input.c from input.c.aa and input.c.ab
  1899.     cat input.c.aa input.c.ab >input.c
  1900.     rm -f input.c.aa input.c.ab
  1901. else
  1902.     echo You still need to unpack the following archives:
  1903.     echo "        " ${MISSING}
  1904. fi
  1905. ##  End of shell archive.
  1906. exit 0
  1907.