home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume36 / ldb / part03 < prev    next >
Encoding:
Text File  |  1993-04-10  |  56.1 KB  |  1,741 lines

  1. Newsgroups: comp.sources.misc
  2. From: ross@teserv.den.mmc.com (Perry R. Ross)
  3. Subject: v36i100:  ldb - Play backgammon by e-mail, v1.3, Part03/12
  4. Message-ID: <1993Apr11.232938.17939@sparky.imd.sterling.com>
  5. X-Md4-Signature: 017ff1967eb19dbceb9b35798d5ea2d6
  6. Date: Sun, 11 Apr 1993 23:29:38 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ross@teserv.den.mmc.com (Perry R. Ross)
  10. Posting-number: Volume 36, Issue 100
  11. Archive-name: ldb/part03
  12. Environment: UNIX, C, VMS, VAXC, CURSES, 32BIT
  13. Supersedes: ldb: Volume 28, Issue 93-97
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 3 (of 12)."
  22. # Contents:  check.c flist_unix.c rcvop.c readmail.c
  23. # Wrapped by ross@teserv.den.mmc.com on Tue Apr  6 14:52:18 1993
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'check.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'check.c'\"
  27. else
  28. echo shar: Extracting \"'check.c'\" \(11760 characters\)
  29. sed "s/^X//" >'check.c' <<'END_OF_FILE'
  30. X/*    check.c        10/29/91
  31. X *
  32. X * Copyright 1991  Perry R. Ross
  33. X *
  34. X * Permission to use, copy, modify, and distribute this software and its
  35. X * documentation without fee is hereby granted, subject to the restrictions
  36. X * detailed in the README file, which is included here by reference.
  37. X * Any other use requires written permission from the author.  This software
  38. X * is distributed "as is" without any warranty, including any implied
  39. X * warranties of merchantability or fitness for a particular purpose.
  40. X * The author shall not be liable for any damages resulting from the
  41. X * use of this software.  By using this software, the user agrees
  42. X * to these terms.
  43. X */
  44. X
  45. X#include "ldb.h"
  46. X
  47. X/*======================================================================
  48. X * This file contains the functions that check for unused moves.
  49. X *======================================================================
  50. X */
  51. X
  52. X
  53. XPRIVATE int maxused, hiused, nlegal;
  54. X
  55. Xstruct legal *rmlegal();
  56. X
  57. X/*----------------------------------------------------------------------
  58. X *    legalmoves -- calculate all legal moves for a game
  59. X *
  60. X * This function is called after the dice have been rolled to search
  61. X * all possible combinations of moves to see which ones are legal.
  62. X * While this function is working, it keeps a list of legal moves,
  63. X * but this list is deleted before returning.  For the purposes
  64. X * of checking a move, it suffices to only keep the number of usable
  65. X * dice and the highest numbered usable dice.  Any move that uses as
  66. X * many dice as can be used, and uses the highest usable dice, is
  67. X * legal.  These values are stored in the maxused and hiused fields
  68. X * of the game structure.
  69. X *
  70. X * legalmoves performs an exhaustive search of all possible move
  71. X * combinations, storing the combinations that are not rejected
  72. X * by apply() in an instance of struct legal, which is linked into
  73. X * the doubly-linked list headed by lhead.  Note that, at this point,
  74. X * this list may contain many combinations that are not legal because
  75. X * they do not use all possible dice, or they use a smaller dice when
  76. X * a larger one is usable.  These illegal combinations are trimmed from
  77. X * the list by trimunused() and trimlowused().  Finally, duplicate combinations
  78. X * are removed from the list by trimequal().  Duplicate moves are those that
  79. X * use the same rolls to move from the same points, but possibly in a
  80. X * different order, for example (3/8 1/6) and (1/6 3/8).  This allows
  81. X * detection of the case where there is only one legal move, in which
  82. X * case this move is automatically applied (if rc.automove is set).
  83. X * If the list is empty, there are no legal moves.  Otherwise, the
  84. X * highest roll used in the list is stored in g->hiused, and the largest
  85. X * number of rolls used in the list is stored in g->maxused.  This information
  86. X * is used by check().
  87. X *----------------------------------------------------------------------
  88. X */
  89. X
  90. Xlegalmoves(g)
  91. Xstruct game *g;
  92. X{
  93. Xstruct game tmp;
  94. Xint i;
  95. Xstruct legal *l;
  96. X
  97. Xtmp = *g;            /* we don't want to change actual game */
  98. Xfor (i = 0; i < 4; tmp.mvs[i++].pt = -1);    /* mark all unused */
  99. Xmaxused = -1;            /* init to not all used */
  100. Xhiused = 0;            /* init to 0 */
  101. Xnlegal = 0;            /* init to no legal moves*/
  102. Xlhead = NULL;
  103. Xltail = NULL;
  104. Xif (tmp.mvs[0].roll == tmp.mvs[1].roll)
  105. X    scanmvs(&tmp,0,3);    /* there is only one ordering */
  106. Xelse {
  107. X    scanmvs(&tmp,0,1);        /* scan for one ordering */
  108. X    i = tmp.mvs[0].roll;    /* reverse rolls */
  109. X    tmp.mvs[0].roll = tmp.mvs[1].roll;
  110. X    tmp.mvs[1].roll = i;
  111. X    for (i = 0; i < 4; tmp.mvs[i++].pt = -1);    /* mark all unused */
  112. X    scanmvs(&tmp,0,1);        /* scan for other ordering */
  113. X    }
  114. Xtrimunused();        /* zap combinations that leave usable rolls unused */
  115. Xtrimlowused();        /* zap combinations that use the wrong die */
  116. Xtrimequal();        /* zap duplicates */
  117. Xg->maxused = maxused;    /* store maxused and hiused into the game structure */
  118. Xg->hiused = hiused;
  119. Xif (nlegal == 0) {    /* check for no legal moves */
  120. X    if (g->dispmsg != NULL)
  121. X        free(g->dispmsg);
  122. X    g->dispmsg = save("You don't have any legal moves.");
  123. X    }
  124. Xelse if (nlegal == 1) {    /* check for only one legal move */
  125. X    if (g->dispmsg != NULL)
  126. X        free(g->dispmsg);
  127. X    g->dispmsg = save("You only have one legal move.");
  128. X    if (*rc.automove == 'y') {    /* you want the move applied? */
  129. X        if ( (lhead->nmove==0) && (lhead->mvs[0].roll!=g->mvs[0].roll))
  130. X            g->mvs[1] = g->mvs[0];
  131. X        for (i = 0; i <= lhead->nmove; i++) {
  132. X            g->mvs[i] = lhead->mvs[i];
  133. X            apply(g,WHO_ME,i,0,NULL);
  134. X            }
  135. X        }
  136. X    }
  137. X
  138. X    /* we have no use for the legal moves list, so free it */
  139. X    /* maybe in the future we will have a use for it */
  140. X
  141. Xfor (l = lhead; l != NULL; l = l->next)
  142. X    free(l);
  143. Xlhead = NULL;
  144. Xltail = NULL;
  145. X}
  146. X
  147. X
  148. X/*----------------------------------------------------------------------
  149. X *    scanmvs -- search for all possible combinations of a move
  150. X *
  151. X * This function takes a single die and tries to use it on every point
  152. X * of the board.  For every point it is successful, it adds the
  153. X * combination to the legal moves list and performs a recursive descent
  154. X * searching for all legal combinations of the remaining dice.
  155. X *----------------------------------------------------------------------
  156. X */
  157. Xscanmvs(g,mn,max)
  158. Xstruct game *g;
  159. Xint mn, max;
  160. X{
  161. Xint i;
  162. Xboard sv;
  163. X
  164. Xcopyboard(g->board,sv);        /* save the board */
  165. Xfor (i = 0; i <= 24; i++) {
  166. X    if (i == 0)
  167. X        g->mvs[mn].pt = BARPT(g->mydir);    /* use correct barpt */
  168. X    else
  169. X        g->mvs[mn].pt = i;
  170. X    if (apply(g,WHO_ME,mn,0,NULL) < 0)    /* can't move from this pt */
  171. X        continue;
  172. X    addlegal(mn,g->mvs[mn].roll,g->mvs[mn].pt);    /* add to list */
  173. X    if (mn < max)
  174. X        scanmvs(g,mn+1,max);        /* try all remaining comb's */
  175. X    copyboard(sv,g->board);            /* restore board */
  176. X    }
  177. X}
  178. X
  179. X
  180. X/*----------------------------------------------------------------------
  181. X *    addlegal -- add a move combination to the list
  182. X *
  183. X * This function adds an instance of struct legal to the legal moves list.
  184. X * The arguments to addlegal only specify the usage of one particular
  185. X * die, by specifying its "move number".  For example, for a roll of
  186. X * 5 2, the 5 is move number 0 and the 2 is move number 1.  The usage
  187. X * of all lower numbered rolls is copied from the previous entry in the
  188. X * moves list (since that entry was added by the higher level scanmvs
  189. X * before the current scanmvs was called), and all higher-numbered rolls
  190. X * are marked unused.
  191. X *----------------------------------------------------------------------
  192. X */
  193. X
  194. Xaddlegal(mn,r,pt)
  195. Xint mn, r, pt;
  196. X{
  197. Xint i;
  198. Xstruct legal *n;
  199. X
  200. Xif ( (n = (struct legal *) calloc(sizeof(struct legal),1)) == NULL)
  201. X    fatal("Out of memory!");
  202. Xclearmvs(n->mvs);
  203. Xif (ltail != NULL)
  204. X    for (i = 0; i < mn; i++)        /* copy prefix from prev move */
  205. X        n->mvs[i] = ltail->mvs[i];
  206. Xn->mvs[mn].roll = r;        /* copy in this move */
  207. Xn->mvs[mn].pt = pt;
  208. Xn->next = NULL;            /* this is end of list */
  209. Xif (lhead == NULL) {        /* link into list */
  210. X    n->prev = NULL;
  211. X    lhead = n;
  212. X    ltail = n;
  213. X    }
  214. Xelse {
  215. X    n->prev = ltail;
  216. X    ltail->next = n;
  217. X    ltail = n;
  218. X    }
  219. Xn->himove = 0;
  220. Xfor (i = 0; i <= mn; i++)    /* search for highest used move */
  221. X    if (n->mvs[i].roll > n->himove)
  222. X        n->himove = n->mvs[i].roll;
  223. Xif (mn > maxused)        /* keep track of whether it is possible */
  224. X    maxused = mn;    /* to use all of the rolls */
  225. Xnlegal++;
  226. Xn->nmove = mn;        /* store number of moves used by this entry */
  227. X}
  228. X
  229. X
  230. X/*----------------------------------------------------------------------
  231. X *    trimunused -- remove moves that do not use all possible rolls
  232. X *
  233. X * This function scans the move list and deletes combinations that
  234. X * leave usable rolls unused.
  235. X *----------------------------------------------------------------------
  236. X */
  237. X
  238. Xtrimunused()
  239. X{
  240. Xstruct legal *l;
  241. X
  242. Xl = lhead;
  243. Xwhile (l != NULL) {
  244. X    if (l->nmove < maxused)
  245. X        l = rmlegal(l);
  246. X    else
  247. X        l = l->next;
  248. X    }
  249. X}
  250. X
  251. X
  252. X/*----------------------------------------------------------------------
  253. X *    trimlowused -- remove moves that do not use the largest possible roll
  254. X *
  255. X * This function scans the move list and deletes combinations that
  256. X * do not use the highest usable roll.
  257. X *----------------------------------------------------------------------
  258. X */
  259. X
  260. Xtrimlowused()
  261. X{
  262. Xstruct legal *l;
  263. X
  264. Xl = lhead;
  265. Xwhile (l != NULL) {
  266. X    if (l->himove < hiused)
  267. X        l = rmlegal(l);
  268. X    else
  269. X        l = l->next;
  270. X    }
  271. X}
  272. X
  273. X
  274. X/*----------------------------------------------------------------------
  275. X *    trimequal -- remove duplicate moves
  276. X *
  277. X * This function scans the move list and deletes combinations that
  278. X * are duplicates.
  279. X *----------------------------------------------------------------------
  280. X */
  281. X
  282. Xtrimequal()
  283. X{
  284. Xstruct legal *l, *p;
  285. Xstruct mv m1[4], m2[4];
  286. Xint i, n;
  287. X
  288. Xfor (l = lhead; l != NULL; l = l->next) {
  289. X    extractmvs(l,m1);
  290. X    n = l->nmove;
  291. X    p = l->next;
  292. X    while (p != NULL) {
  293. X        if (p->nmove != n) {
  294. X            p = p->next;
  295. X            continue;
  296. X            }
  297. X        extractmvs(p,m2);
  298. X        for (i = 0; i <= n; i++)
  299. X            if ((m1[i].roll != m2[i].roll)||(m1[i].pt != m2[i].pt))
  300. X                break;
  301. X        if (i <= n)
  302. X            p = p->next;
  303. X        else
  304. X            p = rmlegal(p);
  305. X        }
  306. X    }
  307. X}
  308. X
  309. X
  310. X/*----------------------------------------------------------------------
  311. X *    rmlegal -- remove a struct legal from the move list
  312. X *
  313. X * This function unlinks an entry from the move list and free's it.
  314. X *----------------------------------------------------------------------
  315. X */
  316. X
  317. Xstruct legal *rmlegal(l)
  318. Xstruct legal *l;
  319. X{
  320. Xstruct legal *t;
  321. X
  322. Xt = l;
  323. Xif (l == lhead) {
  324. X    lhead = l->next;
  325. X    l = lhead;
  326. X    }
  327. Xelse {
  328. X    if ( (l->prev->next = l->next) != NULL)
  329. X        l->next->prev = l->prev;
  330. X    l = l->next;
  331. X    }
  332. Xfree(t);
  333. Xnlegal--;
  334. Xreturn(l);
  335. X}
  336. X
  337. X
  338. X/*----------------------------------------------------------------------
  339. X *    extractmvs -- extract a struct legal into a struct mv
  340. X *
  341. X * This function copies the information from a move list entry into
  342. X * an instance of struct mv, and then sorts the rolls in the struct mv
  343. X * by increasing dice value (and by increasing point number for equal
  344. X * rolls).  Sorting the rolls makes it easier for trimequal to check
  345. X * for duplicate entries, it has no value as far as the game is concerned.
  346. X *----------------------------------------------------------------------
  347. X */
  348. X
  349. Xextractmvs(l,m)
  350. Xstruct legal *l;
  351. Xstruct mv *m;
  352. X{
  353. Xint i, n, s;
  354. Xstruct mv tmp;
  355. X
  356. Xclearmvs(m);
  357. Xfor (i = 0; i <= l->nmove; i++)        /* extract the moves */
  358. X    m[i] = l->mvs[i];
  359. Xn = l->nmove;
  360. Xdo {            /* sort by increasing roll then increasing point */
  361. X    s = 0;
  362. X    for (i = 0; i < n; i++) {        /* long live bubblesort */
  363. X        if (m[i].roll < m[i+1].roll)
  364. X            continue;
  365. X        else if ( (m[i].roll == m[i+1].roll) && (m[i].pt < m[i+1].pt) )
  366. X            continue;
  367. X        tmp = m[i];
  368. X        m[i] = m[i+1];
  369. X        m[i+1] = tmp;
  370. X        s = 1;
  371. X        }
  372. X    n--;
  373. X    } while (s);
  374. X}
  375. X
  376. X
  377. X/*----------------------------------------------------------------------
  378. X *    checkused -- check that a move uses the correct rolls
  379. X *
  380. X * This function is called just before a move is sent to the
  381. X * opponent.  It uses the values stored in g->maxused and g->hiused
  382. X * to make sure that the move uses all usable rolls, and that it
  383. X * uses the highest usable roll.  As a special case, it considers
  384. X * to be legal any move where all pieces are borne off.  This
  385. X * takes care of the special case where there is one piece
  386. X * left to bear off, and two rolls, one of which is too small
  387. X * to bear off.  Normally, ldb would insist that the smaller
  388. X * roll be used, then the larger one, so that both rolls
  389. X * would be used.  If one roll is large enough to bear the last
  390. X * man off, though, there is no need to force the other roll to be used.
  391. X *----------------------------------------------------------------------
  392. X */
  393. X
  394. Xcheckused(g)
  395. Xstruct game *g;
  396. X{
  397. Xint h, i;
  398. X
  399. Xif (g->board[OFFPT(g->mydir)].qty == 15)    /* special case, if all pcs */
  400. X    return(0);        /* are off, then all rolls have been used */
  401. Xh = 0;
  402. Xfor (i = 0; i <= g->maxused; i++) {
  403. X    if (g->mvs[i].pt < 0) {
  404. X        FeMessage("You left a roll unused.");
  405. X        return(1);
  406. X        }
  407. X    if (h < g->mvs[i].roll)
  408. X        h = g->mvs[i].roll;
  409. X    }
  410. Xif (g->hiused > h) {
  411. X    FeMessage("You can use the higher roll.");
  412. X    return(1);
  413. X    }
  414. Xreturn(0);
  415. X}
  416. END_OF_FILE
  417. if test 11760 -ne `wc -c <'check.c'`; then
  418.     echo shar: \"'check.c'\" unpacked with wrong size!
  419. fi
  420. # end of 'check.c'
  421. fi
  422. if test -f 'flist_unix.c' -a "${1}" != "-c" ; then 
  423.   echo shar: Will not clobber existing file \"'flist_unix.c'\"
  424. else
  425. echo shar: Extracting \"'flist_unix.c'\" \(13758 characters\)
  426. sed "s/^X//" >'flist_unix.c' <<'END_OF_FILE'
  427. X/*    flist_unix.c        4/24/91
  428. X *
  429. X * Copyright 1991  Perry R. Ross
  430. X *
  431. X * Permission to use, copy, modify, and distribute this software and its
  432. X * documentation without fee is hereby granted, subject to the restrictions
  433. X * detailed in the README file, which is included here by reference.
  434. X * Any other use requires written permission from the author.  This software
  435. X * is distributed "as is" without any warranty, including any implied
  436. X * warranties of merchantability or fitness for a particular purpose.
  437. X * The author shall not be liable for any damages resulting from the
  438. X * use of this software.  By using this software, the user agrees
  439. X * to these terms.
  440. X */
  441. X
  442. X/* This file uses the "regex-glob" routines, written by John Kercheval, and
  443. X * posted to comp.sources.misc.  The code appears at the end of this
  444. X * file with all original comments.
  445. X */
  446. X
  447. X#include "ldb.h"
  448. X
  449. X
  450. X/*----------------------------------------------------------------------
  451. X *    filelist -- generate a list of all matching files.
  452. X *
  453. X * This function generates a list of all files that match a pattern.
  454. X * Each file is stored in an instance of struct flist, which just
  455. X * links the names in a linked list.  A pointer to the beginning of
  456. X * the list is returned.  It is the callers responsibility to free
  457. X * the list when it is no longer needed.
  458. X *
  459. X * This function will only recognize wildcards in file names, NOT
  460. X * the directories leading up to them.  For example, nuts/ldb*.txt is
  461. X * fine, but *ts/ldb*.txt is not.
  462. X *
  463. X * This function uses the "new" directory routines (i.e. opendir,
  464. X * readdir, et. al).  If these are not on your system, you should
  465. X * have defined NEED_READDIR in your Makefile.
  466. X *----------------------------------------------------------------------
  467. X */
  468. X
  469. Xstruct flist *filelist(ptn)
  470. Xchar *ptn;
  471. X{
  472. Xstruct flist *head, *tail, *cur;
  473. XDIR *dp;
  474. Xchar *s;
  475. Xchar *dn, *pn;
  476. Xstruct direct *p;
  477. X
  478. Xhead = NULL;
  479. Xif (is_pattern(ptn) == 0) {    /* no wildcards, just a file name */
  480. X    if ( (cur = (struct flist *) calloc(sizeof(struct flist),1)) == NULL)
  481. X        fatal("Out of memory!");
  482. X    head = cur;
  483. X    tail = cur;
  484. X    cur->name = save(ptn);
  485. X    return(cur);
  486. X    }
  487. Xif ( (s = strrchr(ptn,'/')) != NULL) {    /* strip off directory name */
  488. X    *s = '\0';
  489. X    dn = save(ptn);        /* dir = everything before last /  */
  490. X    pn = save(s+1);        /* pattern = everything after last /  */
  491. X    *s = '/';
  492. X    }
  493. Xelse {
  494. X    dn = save(".");
  495. X    pn = save(ptn);
  496. X    }
  497. Xif ( (dp = opendir(dn)) == NULL) {
  498. X    free(dn);
  499. X    free(pn);
  500. X    return(NULL);
  501. X    }
  502. Xwhile ( (p = readdir(dp)) != NULL) {
  503. X    if ( (strcmp(p->d_name,".") == 0) || (strcmp(p->d_name,"..") == 0) )
  504. X        continue;
  505. X    if (match(pn,p->d_name) == 0)
  506. X        continue;
  507. X    if ( (cur = (struct flist *) calloc(sizeof(struct flist),1)) == NULL)
  508. X        fatal("Out of memory!");
  509. X    if (head == NULL) {
  510. X        head = cur;
  511. X        tail = cur;
  512. X        }
  513. X    else {
  514. X        tail->next = cur;
  515. X        tail = cur;
  516. X        }
  517. X    if (strcmp(dn,".") == 0)    /* file in current dir */
  518. X        cur->name = save(p->d_name);    /* just save name */
  519. X    else {                /* include directory name */
  520. X        cur->name = (char *) malloc(strlen(dn)+strlen(p->d_name)+2);
  521. X        if (cur->name == NULL)
  522. X            fatal("Out of memory!");
  523. X        sprintf(cur->name,"%s/%s",dn,p->d_name);
  524. X        }
  525. X    }
  526. Xclosedir(dp);
  527. Xfree(dn);
  528. Xfree(pn);
  529. Xreturn(head);
  530. X}
  531. X
  532. X
  533. X/* regex-glob code follows: (de-ansified by P. Ross -- sorry) */
  534. X
  535. X/*
  536. X EPSHeader
  537. X
  538. X   File: match.c
  539. X   Author: J. Kercheval
  540. X   Created: Sat, 01/05/1991  22:21:49
  541. X*/
  542. X/*
  543. X EPSRevision History
  544. X
  545. X   J. Kercheval  Wed, 02/20/1991  22:29:01  Released to Public Domain
  546. X*/
  547. X
  548. X/*
  549. X   Wildcard Pattern Matching
  550. X*/
  551. X
  552. X
  553. X/* #include "match.h" -- match.h included here for simplicity  P. Ross */
  554. X
  555. X/*
  556. X EPSHeader
  557. X
  558. X   File: match.h
  559. X   Author: J. Kercheval
  560. X   Created: Sat, 01/05/1991  22:27:18
  561. X*/
  562. X/*
  563. X EPSRevision History
  564. X
  565. X   J. Kercheval  Wed, 02/20/1991  22:28:37  Released to Public Domain
  566. X*/
  567. X
  568. X/*
  569. X   Wildcard Pattern Matching
  570. X*/
  571. X
  572. X#ifndef BOOLEAN
  573. X# define BOOLEAN int
  574. X#undef TRUE
  575. X#undef FALSE
  576. X# define TRUE 1
  577. X# define FALSE 0
  578. X#endif
  579. X
  580. X/*----------------------------------------------------------------------------
  581. X*
  582. X*  Match the pattern PATTERN against the string TEXT;
  583. X*  return TRUE if it matches, FALSE otherwise.
  584. X*
  585. X*  A match means the entire string TEXT is used up in matching.
  586. X*
  587. X*  In the pattern string:
  588. X*       `*' matches any sequence of characters
  589. X*       `?' matches any character
  590. X*       [SET] matches any character in the specified set,
  591. X*       [!SET] or [^SET] matches any character not in the specified set.
  592. X*
  593. X*  Note: the standard regex character '+' (one or more) should by
  594. X*        simulated by using "?*" which is equivelant here.
  595. X*
  596. X*  A set is composed of characters or ranges; a range looks like
  597. X*  character hyphen character (as in 0-9 or A-Z).
  598. X*  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  599. X*  Any other character in the pattern must be matched exactly.
  600. X*
  601. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  602. X*  and match the character exactly, precede it with a `\'.
  603. X*
  604. X----------------------------------------------------------------------------*/
  605. X
  606. XBOOLEAN match ( /* char *pattern, char *text */ );
  607. X
  608. X/*----------------------------------------------------------------------------
  609. X*
  610. X* Return TRUE if PATTERN has any special wildcard characters
  611. X*
  612. X----------------------------------------------------------------------------*/
  613. X
  614. XBOOLEAN is_pattern ( /* char *pattern */ );
  615. X
  616. X/* -- end of match.h  P. Ross -- */
  617. X
  618. X#define ABORT 2     /* end of search indicator */
  619. X
  620. XBOOLEAN regex_match_after_star ( /* char *pattern, char *text */ );
  621. X
  622. X/*----------------------------------------------------------------------------
  623. X*
  624. X* Return TRUE if PATTERN has any special wildcard characters
  625. X*
  626. X----------------------------------------------------------------------------*/
  627. X
  628. XBOOLEAN is_pattern (p)
  629. Xchar *p;
  630. X{
  631. X    while ( *p ) {
  632. X        switch ( *p++ ) {
  633. X            case '?':
  634. X            case '*':
  635. X            case '[':
  636. X                return TRUE;
  637. X            case '\\':
  638. X                if ( !*p++ ) return FALSE;
  639. X        }
  640. X    }
  641. X    return FALSE;
  642. X}
  643. X
  644. X
  645. X/*----------------------------------------------------------------------------
  646. X*
  647. X*  Match the pattern PATTERN against the string TEXT;
  648. X*  return TRUE if it matches, FALSE otherwise.
  649. X*
  650. X*  A match means the entire string TEXT is used up in matching.
  651. X*
  652. X*  In the pattern string:
  653. X*       `*' matches any sequence of characters
  654. X*       `?' matches any character
  655. X*       [SET] matches any character in the specified set,
  656. X*       [!SET] or [^SET] matches any character not in the specified set.
  657. X*
  658. X*  Note: the standard regex character '+' (one or more) should by
  659. X*        simulated by using "?*" which is equivelant here.
  660. X*
  661. X*  A set is composed of characters or ranges; a range looks like
  662. X*  character hyphen character (as in 0-9 or A-Z).
  663. X*  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  664. X*  Any other character in the pattern must be matched exactly.
  665. X*
  666. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  667. X*  and match the character exactly, precede it with a `\'.
  668. X*
  669. X----------------------------------------------------------------------------*/
  670. X
  671. XBOOLEAN regex_match (p, t)
  672. Xregister char *p;
  673. Xregister char *t;
  674. X{
  675. X    register char range_start, range_end;  /* start and end in range */
  676. X
  677. X    BOOLEAN invert;             /* is this [..] or [!..] */
  678. X    BOOLEAN member_match;       /* have I matched the [..] construct? */
  679. X    BOOLEAN loop;               /* should I terminate? */
  680. X
  681. X    for ( ; *p; p++, t++ ) {
  682. X
  683. X        /* if this is the end of the text then this is the end of the match */
  684. X        if (!*t) {
  685. X            return ( *p == '*' && *++p == '\0' ) ? TRUE : ABORT;
  686. X        }
  687. X
  688. X        /* determine and react to pattern type */
  689. X        switch ( *p ) {
  690. X
  691. X            /* single any character match */
  692. X            case '?':
  693. X                break;
  694. X
  695. X            /* multiple any character match */
  696. X            case '*':
  697. X                return regex_match_after_star (p, t);
  698. X
  699. X            /* [..] construct, single member/exclusion character match */
  700. X            case '[': {
  701. X
  702. X                /* move to beginning of range */
  703. X                p++;
  704. X
  705. X                /* check if this is a member match or exclusion match */
  706. X                invert = FALSE;
  707. X                if ( *p == '!' || *p == '^') {
  708. X                    invert = TRUE;
  709. X                    p++;
  710. X                }
  711. X
  712. X                /* if closing bracket here or at range start then we have a
  713. X                   malformed pattern */
  714. X                if ( *p == ']' ) {
  715. X                    return ABORT;
  716. X                }
  717. X
  718. X                member_match = FALSE;
  719. X                loop = TRUE;
  720. X
  721. X                while ( loop ) {
  722. X
  723. X                    /* if end of construct then loop is done */
  724. X                    if (*p == ']') {
  725. X                        loop = FALSE;
  726. X                        continue;
  727. X                    }
  728. X
  729. X                    /* matching a '!', '^', '-', '\' or a ']' */
  730. X                    if ( *p == '\\' ) {
  731. X                        range_start = range_end = *++p;
  732. X                    }
  733. X                    else {
  734. X                        range_start = range_end = *p;
  735. X                    }
  736. X
  737. X                    /* if end of pattern then bad pattern (Missing ']') */
  738. X                    if (!range_start)
  739. X                        return ABORT;
  740. X
  741. X                    /* move to next pattern char */
  742. X                    p++;
  743. X
  744. X                    /* check for range bar */
  745. X                    if (*p == '-') {
  746. X
  747. X                        /* get the range end */
  748. X                        range_end = *++p;
  749. X
  750. X                        /* special character range end */
  751. X                        if (range_end == '\\')
  752. X                            range_end = *++p;
  753. X
  754. X                        /* if end of pattern or construct then bad pattern */
  755. X                        if (range_end == '\0' || range_end == ']')
  756. X                            return ABORT;
  757. X                    }
  758. X
  759. X                    /* if the text character is in range then match found.
  760. X                       make sure the range letters have the proper
  761. X                       relationship to one another before comparison */
  762. X                    if ( range_start < range_end  ) {
  763. X                        if (*t >= range_start && *t <= range_end) {
  764. X                            member_match = TRUE;
  765. X                            loop = FALSE;
  766. X                        }
  767. X                    }
  768. X                    else {
  769. X                        if (*t >= range_end && *t <= range_start) {
  770. X                            member_match = TRUE;
  771. X                            loop = FALSE;
  772. X                        }
  773. X                    }
  774. X                }
  775. X
  776. X                /* if there was a match in an exclusion set then no match */
  777. X                /* if there was no match in a member set then no match */
  778. X                if ((invert && member_match) ||
  779. X                   !(invert || member_match))
  780. X                    return FALSE;
  781. X
  782. X                /* if this is not an exclusion then skip the rest of the [...]
  783. X                    construct that already matched. */
  784. X                if (member_match) {
  785. X                    while (*p != ']') {
  786. X
  787. X                        /* bad pattern (Missing ']') */
  788. X                        if (!*p)
  789. X                            return ABORT;
  790. X
  791. X                        /* skip exact match */
  792. X                        if (*p == '\\') {
  793. X                            p++;
  794. X                        }
  795. X
  796. X                        /* move to next pattern char */
  797. X                        p++;
  798. X                    }
  799. X                }
  800. X
  801. X                break;
  802. X            }
  803. X
  804. X            /* next character is quoted and must match exactly */
  805. X            case '\\':
  806. X
  807. X                /* move pattern pointer to quoted char and fall through */
  808. X                p++;
  809. X
  810. X            /* must match this character exactly */
  811. X            default:
  812. X                if (*p != *t)
  813. X                    return FALSE;
  814. X        }
  815. X    }
  816. X
  817. X    /* if end of text not reached then the pattern fails */
  818. X    return !*t;
  819. X}
  820. X
  821. X
  822. X/*----------------------------------------------------------------------------
  823. X*
  824. X* recursively call regex_match with final segment of PATTERN and of TEXT.
  825. X*
  826. X----------------------------------------------------------------------------*/
  827. X
  828. XBOOLEAN regex_match_after_star (p, t)
  829. Xregister char *p;
  830. Xregister char *t;
  831. X{
  832. X    register BOOLEAN match;
  833. X    register nextp;
  834. X
  835. X    /* pass over existing ? and * in pattern */
  836. X    while ( *p == '?' || *p == '*' ) {
  837. X
  838. X        /* take one char for each ? */
  839. X        if ( *p == '?' ) {
  840. X
  841. X            /* if end of text then no match */
  842. X            if ( !*t++ ) {
  843. X                return ABORT;
  844. X            }
  845. X        }
  846. X
  847. X        /* move to next char in pattern */
  848. X        p++;
  849. X    }
  850. X
  851. X    /* if end of pattern we have matched regardless of text left */
  852. X    if ( !*p ) {
  853. X        return TRUE;
  854. X    }
  855. X
  856. X    /* get the next character to match which must be a literal or '[' */
  857. X    nextp = *p;
  858. X    if ( nextp == '\\' )
  859. X        nextp = p[1];
  860. X
  861. X    /* Continue until we run out of text or definite result seen */
  862. X    match = FALSE;
  863. X    while ( match == FALSE ) {
  864. X
  865. X        /* a precondition for matching is that the next character
  866. X           in the pattern match the next character in the text or that
  867. X           the next pattern is the beginning of a range.  Increment text
  868. X           pointer as we go here */
  869. X        if ( *p == *t || nextp == '[' ) {
  870. X            match = regex_match(p, t);
  871. X        }
  872. X
  873. X        /* if the end of text is reached then no match */
  874. X        if ( !*t++ ) match = ABORT;
  875. X    }
  876. X
  877. X    /* return result */
  878. X    return match;
  879. X}
  880. X
  881. X/*----------------------------------------------------------------------------
  882. X*
  883. X* This is a shell to regex_match to return only a true BOOLEAN value
  884. X*
  885. X----------------------------------------------------------------------------*/
  886. X
  887. XBOOLEAN match(p, t)
  888. Xchar *p;
  889. Xchar *t;
  890. X{
  891. X    return ( regex_match(p,t) == TRUE ) ? TRUE : FALSE;
  892. X}
  893. END_OF_FILE
  894. if test 13758 -ne `wc -c <'flist_unix.c'`; then
  895.     echo shar: \"'flist_unix.c'\" unpacked with wrong size!
  896. fi
  897. # end of 'flist_unix.c'
  898. fi
  899. if test -f 'rcvop.c' -a "${1}" != "-c" ; then 
  900.   echo shar: Will not clobber existing file \"'rcvop.c'\"
  901. else
  902. echo shar: Extracting \"'rcvop.c'\" \(15063 characters\)
  903. sed "s/^X//" >'rcvop.c' <<'END_OF_FILE'
  904. X/*    rcvop.c        8/6/91
  905. X *
  906. X * Copyright 1991  Perry R. Ross
  907. X *
  908. X * Permission to use, copy, modify, and distribute this software and its
  909. X * documentation without fee is hereby granted, subject to the restrictions
  910. X * detailed in the README file, which is included here by reference.
  911. X * Any other use requires written permission from the author.  This software
  912. X * is distributed "as is" without any warranty, including any implied
  913. X * warranties of merchantability or fitness for a particular purpose.
  914. X * The author shall not be liable for any damages resulting from the
  915. X * use of this software.  By using this software, the user agrees
  916. X * to these terms.
  917. X */
  918. X
  919. X#include "ldb.h"
  920. X
  921. X/*===========================================================================
  922. X * This file contains the functions which make up the receive state
  923. X * machine.  These are called through the func[][] matrix, which takes
  924. X * the current state and a received operation and calls the appropriate
  925. X * handler function.  These functions will typically change the game
  926. X * state into one requiring user input at this host (ST_MY*), and return.
  927. X *===========================================================================
  928. X */
  929. X
  930. X
  931. X/*---------------------------------------------------------------------------
  932. X *    start -- initiate a game
  933. X *
  934. X * This function is called when a remote user starts a game with us.
  935. X * We store his personal information (opaddr & opname), roll 1 die,
  936. X * and compare it to the one he sent.  If we won the roll, the roll
  937. X * is stored in mvs[] and state is set to MYMOVE.  If we lost the roll,
  938. X * both dice are sent back in a USTART line.  If the roll was a tie,
  939. X * a TIE packet is sent back .  The originator will re-roll and send
  940. X * us a RESTART packet, which will repeat the opening roll.
  941. X *---------------------------------------------------------------------------
  942. X */
  943. X
  944. Xstart(g)
  945. Xstruct game *g;
  946. X{
  947. Xint mydie;
  948. Xchar c1, c2;
  949. X
  950. Xg->opaddr = P.addr;    /* save mail address of opponent */
  951. Xg->gameid = P.gameid;        /* copy game id */
  952. Xg->opver = P.version;        /* save opponent's ldb version */
  953. Xg->mycolor = P.colors[1];    /* copy out colors */
  954. Xg->opcolor = P.colors[0];
  955. Xif (isupper(*P.dir))
  956. X    *P.dir = tolower(*P.dir);
  957. Xg->mydir = (*P.dir == 'u') ? 1 : -1;    /* copy out directions */
  958. Xg->opdir = REV(g->mydir);
  959. Xg->gameval = 1;            /* no doubles yet */
  960. Xg->flags = 0;
  961. Xg->seq = 2;            /* we rcvd 1 pkt already, init to 2 */
  962. Xif ( (g->ppl = findppl(g->opaddr,P_ADDR)) == NULL) { /* know this guy? */
  963. X    g->myaddr = save(rc.myaddr);    /* nope, create a new ppl record */
  964. X    newppl(g);
  965. X    }
  966. Xelse
  967. X    g->myaddr = save(g->ppl->myaddr);
  968. Xg->ppl->opver = P.version;    /* save opponent's ldb version in ppl file */
  969. Xg->starttime = P.timestamp;    /* store timestamp from start packet */
  970. Xg->lastacc = P.timestamp;    /* set last access time to start time */
  971. Xif (P.autodbl == NULL)        /* set admax to MIN(my count, op's count) */
  972. X    g->admax = 0;
  973. Xelse
  974. X    g->admax = atoi(P.autodbl);
  975. Xif (rc.autodouble < g->admax)
  976. X    g->admax = rc.autodouble;
  977. Xg->adcnt = 0;            /* no autodoubles yet */
  978. Xclearmvs(g->mvs);
  979. Xclearmvs(g->opmvs);
  980. Xif (g->mydir > 0) {
  981. X    c1 = g->mycolor;    /* upbound color is mine */
  982. X    c2 = g->opcolor;    /* downbound color is opponent's */
  983. X    }
  984. Xelse {
  985. X    c1 = g->opcolor;    /* upbound color is opponent's */
  986. X    c2 = g->mycolor;    /* downbound color is mine */
  987. X    }
  988. Xnewboard(g->opbd,c1,c2);        /* set up boards for new game */
  989. Xnewboard(g->mybd,c1,c2);
  990. Xnewboard(g->board,c1,c2);
  991. Xif (P.jacoby != NULL)
  992. X    g->flags |= F_JACOBY;
  993. Xif (P.crawford != NULL)
  994. X    g->flags |= F_CRAWFORD;
  995. Xif (P.european != NULL)
  996. X    g->flags |= F_EUROPE;
  997. Xif (P.perm != NULL)
  998. X    g->flags |= F_PERM;
  999. Xif (P.match != NULL)
  1000. X    g->mtotal = atoi(P.match);
  1001. Xg->curbd = boardnums[*rc.initboard - 'a'];    /* display initial board */
  1002. Xmydie = Rolldie();
  1003. Xif (P.mvs[0].roll == mydie) {        /* a !#$%&@ tie */
  1004. X    if (g->adcnt < g->admax)    /* do an autodouble */
  1005. X        g->gameval = 1 << ++(g->adcnt);
  1006. X    sendpkt(g,TIE);
  1007. X    message("Tie on initial roll with %s (%s).\n",g->opname,g->opaddr);
  1008. X    return;            /* opponent will send RESTART */
  1009. X    }
  1010. Xif (mydie > (int) P.mvs[0].roll) {        /* we won the initial roll */
  1011. X    g->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  1012. X    g->mvs[1].roll = mydie;
  1013. X    g->mvs[0].pt = -1;    /* mark both rolls unused */
  1014. X    g->mvs[1].pt = -1;
  1015. X    g->state = ST_MYMOVE;    /* set state so we make a move */
  1016. X    legalmoves(g);        /* calculate legal moves for these rolls */
  1017. X    g->rolls[g->mvs[0].roll - 1]++;    /* count the rolls we got */
  1018. X    g->rolls[g->mvs[1].roll - 1]++;
  1019. X    }
  1020. Xelse {                /* we lost, tell the opponent to start */
  1021. X    g->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  1022. X    g->mvs[1].roll = mydie;    /* store so sendpkt can find it */
  1023. X    g->state = ST_OPTURN;
  1024. X    sendpkt(g,USTART);
  1025. X    message("Started game with %s (%s).\n",g->opname,g->opaddr);
  1026. X    }
  1027. X}
  1028. X
  1029. X
  1030. X/*---------------------------------------------------------------------------
  1031. X *    istart -- I won the opening toss
  1032. X *
  1033. X * This function is called when a USTART packet is received.  Both rolls
  1034. X * are copied into the game structure and the state is set to MYMOVE,
  1035. X * allowing us to use the roll but not to double.
  1036. X *---------------------------------------------------------------------------
  1037. X */
  1038. X
  1039. Xistart(g)
  1040. Xstruct game *g;
  1041. X{
  1042. X
  1043. Xg->mvs[0].roll = P.mvs[0].roll;    /* copy rolls from packet */
  1044. Xg->mvs[1].roll = P.mvs[1].roll;
  1045. Xg->mvs[0].pt = -1;    /* mark both rolls unused */
  1046. Xg->mvs[1].pt = -1;
  1047. Xg->state = ST_MYMOVE;    /* set state so we make a move */
  1048. Xlegalmoves(g);        /* calculate legal moves */
  1049. Xg->rolls[g->mvs[0].roll - 1]++;    /* count the rolls we got */
  1050. Xg->rolls[g->mvs[1].roll - 1]++;
  1051. X}
  1052. X
  1053. X
  1054. X/*---------------------------------------------------------------------------
  1055. X *    tie -- The opening toss was a tie, try again
  1056. X *
  1057. X * This function is called when we receive a TIE packet.  We reroll
  1058. X * one die and send a RESTART packet.  If the autodbl field in
  1059. X * the received packet is > 0, the game value is set to 2 ** autodbl.
  1060. X *---------------------------------------------------------------------------
  1061. X */
  1062. X
  1063. Xtie(g)
  1064. Xstruct game *g;
  1065. X{
  1066. X
  1067. Xclearmvs(g->mvs);
  1068. Xg->mvs[0].roll = Rolldie();
  1069. Xif (P.autodbl != NULL)
  1070. X    g->gameval = 1 << (g->adcnt = atoi(P.autodbl));
  1071. Xsendpkt(g,RESTART);
  1072. Xmessage("Tie on initial roll with %s (%s).\n",g->opname,g->opaddr);
  1073. X}
  1074. X
  1075. X
  1076. X/*---------------------------------------------------------------------------
  1077. X *    restart -- restart after opening tie
  1078. X *
  1079. X * This function is called when we receive a RESTART packet.  It is
  1080. X * mostly the same as start().
  1081. X *---------------------------------------------------------------------------
  1082. X */
  1083. X
  1084. Xrestart(g)
  1085. Xstruct game *g;
  1086. X{
  1087. Xint mydie;
  1088. X
  1089. Xclearmvs(g->mvs);
  1090. Xclearmvs(g->opmvs);
  1091. Xmydie = Rolldie();
  1092. Xif (P.mvs[0].roll == mydie) {        /* a !#$%&@ tie */
  1093. X    if (g->adcnt < g->admax)    /* do an autodouble */
  1094. X        g->gameval = 1 << ++(g->adcnt);
  1095. X    sendpkt(g,TIE);
  1096. X    message("Tie on initial roll with %s (%s).\n",g->opname,g->opaddr);
  1097. X    return;            /* opponent will send RESTART */
  1098. X    }
  1099. Xg->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  1100. Xg->mvs[1].roll = mydie;    /* store so sendpkt can find it */
  1101. Xif (mydie > (int) P.mvs[0].roll) {        /* we won the initial roll */
  1102. X    g->state = ST_MYMOVE;    /* set state so we make a move */
  1103. X    legalmoves(g);        /* calculate legal moves for these rolls */
  1104. X    g->rolls[g->mvs[0].roll - 1]++;    /* count the rolls we got */
  1105. X    g->rolls[g->mvs[1].roll - 1]++;
  1106. X    }
  1107. Xelse {                /* we lost, tell the opponent to start */
  1108. X    g->state = ST_OPTURN;
  1109. X    sendpkt(g,USTART);
  1110. X    message("Started game with %s (%s).\n",g->opname,g->opaddr);
  1111. X    }
  1112. X}
  1113. X
  1114. X
  1115. X/*---------------------------------------------------------------------------
  1116. X *    mstart -- start next game of match
  1117. X *
  1118. X * This function is called when we receive an MSTART packet.  It is
  1119. X * similar to restart, except that it also reinitializes the game
  1120. X * structure for the next game of the match.  All games of a match
  1121. X * use the same gameid and game structure.  Mstart also checks to
  1122. X * see if this is a Crawford rule game, and if so, sets F_CRGAME in g->flags.
  1123. X *---------------------------------------------------------------------------
  1124. X */
  1125. X
  1126. Xmstart(g)
  1127. Xstruct game *g;
  1128. X{
  1129. Xint mydie;
  1130. Xint i;
  1131. Xchar c1, c2;
  1132. X
  1133. Xg->state = ST_OPSTART;
  1134. Xg->gameval = 1;        /* reset for next game */
  1135. Xg->adcnt = 0;
  1136. Xg->flags &= ~F_IDOUBLED;
  1137. Xg->term = 0;
  1138. Xif (g->mydir > 0) {
  1139. X    c1 = g->mycolor;    /* upbound color is mine */
  1140. X    c2 = g->opcolor;    /* downbound color is opponent's */
  1141. X    }
  1142. Xelse {
  1143. X    c1 = g->opcolor;    /* upbound color is opponent's */
  1144. X    c2 = g->mycolor;    /* downbound color is mine */
  1145. X    }
  1146. Xnewboard(g->opbd,c1,c2);
  1147. Xnewboard(g->mybd,c1,c2);
  1148. Xnewboard(g->board,c1,c2);
  1149. Xfor (i = 0; i < 6; i++) {
  1150. X    g->rolls[i] = 0;
  1151. X    g->doubles[i] = 0;
  1152. X    g->oprolls[i] = 0;
  1153. X    g->opdoubles[i] = 0;
  1154. X    }
  1155. Xclearmvs(g->mvs);
  1156. Xclearmvs(g->opmvs);
  1157. Xcrawford_check(g);            /* is this the Crawford rule game? */
  1158. Xmydie = Rolldie();
  1159. Xif (P.mvs[0].roll == mydie) {        /* a !#$%&@ tie */
  1160. X    if (g->adcnt < g->admax)    /* do an autodouble */
  1161. X        g->gameval = 1 << ++(g->adcnt);
  1162. X    sendpkt(g,TIE);
  1163. X    message("Tie on initial roll with %s (%s).\n",g->opname,g->opaddr);
  1164. X    return;            /* opponent will send RESTART */
  1165. X    }
  1166. Xg->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  1167. Xg->mvs[1].roll = mydie;    /* store so sendpkt can find it */
  1168. Xif (mydie > (int) P.mvs[0].roll) {        /* we won the initial roll */
  1169. X    g->state = ST_MYMOVE;    /* set state so we make a move */
  1170. X    legalmoves(g);        /* calculate legal moves for these rolls */
  1171. X    g->rolls[g->mvs[0].roll - 1]++;    /* count the rolls we got */
  1172. X    g->rolls[g->mvs[1].roll - 1]++;
  1173. X    }
  1174. Xelse {                /* we lost, tell the opponent to start */
  1175. X    g->state = ST_OPTURN;
  1176. X    sendpkt(g,USTART);
  1177. X    message("Started game with %s (%s).\n",g->opname,g->opaddr);
  1178. X    }
  1179. X}
  1180. X
  1181. X
  1182. X/*---------------------------------------------------------------------------
  1183. X *    opmove -- opponent moved
  1184. X *
  1185. X * This function is called when we receive a MOVE packet.  The move is
  1186. X * copied into the opmvs field of the game structure and applied to the
  1187. X * board.  A copy of the board before the moves are applied is stored
  1188. X * in the opbd field, and a copy of the board after the moves are applied
  1189. X * is stored in the mybd field.  These two boards, along with the
  1190. X * current board in the board field, make up the three boards that can
  1191. X * be displayed with the "Board" command in the user menus in process.c
  1192. X *---------------------------------------------------------------------------
  1193. X */
  1194. X
  1195. Xopmove(g)
  1196. Xstruct game *g;
  1197. X{
  1198. Xint i, n, r;
  1199. Xstatic char buf[] = "Opponent move dated DDD MMM NN HH:MM:SS YYYY";
  1200. Xstruct game *pg;
  1201. X
  1202. Xcopyboard(g->board,g->opbd);    /* save board before opponent moved */
  1203. Xg->curbd = boardnums[*rc.initboard - 'a'];    /* display initial board */
  1204. Xclearmvs(g->opmvs);        /* clear old moves */
  1205. Xg->opmvs[0] = P.mvs[0];    /* extract opponent's moves */
  1206. Xg->opmvs[1] = P.mvs[1];
  1207. Xg->oprolls[g->opmvs[0].roll - 1]++;        /* count rolls opponent got */
  1208. Xg->oprolls[g->opmvs[1].roll - 1]++;
  1209. Xif (g->opmvs[0].roll == g->opmvs[1].roll) {
  1210. X    g->opmvs[2] = P.mvs[2];    /* he got doubles */
  1211. X    g->opmvs[3] = P.mvs[3];    /* extract 2 more moves */
  1212. X    g->opdoubles[g->opmvs[0].roll - 1]++;
  1213. X    n = 4;
  1214. X    }
  1215. Xelse
  1216. X    n = 2;
  1217. Xfor (i = 0; i < 4; i++)
  1218. X    g->blot[i] = 0;        /* clear all blot locations */
  1219. Xfor (i = 0; i < n; i++) {
  1220. X    if ( (r = apply(g,WHO_OPP,i,0,NULL)) < 0) {    /* err w/ op move */
  1221. X        copyboard(g->opbd,g->board);    /* restore board */
  1222. X        message("ERROR: Opponent move rejected, id=%s\n",
  1223. X            P.gameid);
  1224. X        message("       %s\n",rejmsg[-r]);
  1225. X        return;
  1226. X        }
  1227. X    else        /* if opponent hit our blot */
  1228. X        g->blot[i] = r;        /* save location */
  1229. X    }
  1230. Xcopyboard(g->board,g->mybd);        /* save board after op move */
  1231. Xif (g->board[OFFPT(g->opdir)].qty == 15)    /* opponent won */
  1232. X    ilose(g,T_ILOSE,1);
  1233. Xelse
  1234. X    g->state = ST_MYTURN;        /* opponent has moved, it's our turn */
  1235. Xclearmvs(g->mvs);        /* erase our previous move */
  1236. Xif (P.timestamp > 0) {        /* if we got a timestamp */
  1237. X    strncpy(&buf[20],ctime(&P.timestamp),24);    /* gen message */
  1238. X    g->dispmsg = save(buf);        /* save copy in game */
  1239. X    }
  1240. X}
  1241. X
  1242. X
  1243. X/*---------------------------------------------------------------------------
  1244. X *    opofr -- opponent offered to double
  1245. X *
  1246. X * This function is called when we receive an OFRDBL packet, indicating
  1247. X * the opponent wishes to double.  The game moves to state MYACCEPT,
  1248. X * where the user will decide to accept or decline the double.
  1249. X *---------------------------------------------------------------------------
  1250. X */
  1251. X
  1252. Xopofr(g)
  1253. Xstruct game *g;
  1254. X{
  1255. X
  1256. Xif (g->flags & F_CRGAME) {
  1257. X    message("ERROR: Opponent move rejected, id=%s\n",P.gameid);
  1258. X    message("       Invalid double (Crawford rule)\n");
  1259. X    return;
  1260. X    }
  1261. Xg->state = ST_MYACCEPT;            /* send us to an accept screen */
  1262. Xg->flags &= ~F_IDOUBLED;        /* I didn't double last */
  1263. Xcopyboard(g->board,g->opbd);
  1264. Xcopyboard(g->board,g->mybd);
  1265. Xg->curbd = BD_CUR;        /* display current board */
  1266. Xclearmvs(g->opmvs);        /* clear old moves */
  1267. Xclearmvs(g->mvs);        /* erase our previous move */
  1268. X}
  1269. X
  1270. X
  1271. X/*---------------------------------------------------------------------------
  1272. X *    opconc -- opponent conceded
  1273. X *
  1274. X * This function is called when we receive a CONCEDE packet, indicating
  1275. X * the opponent has given up.
  1276. X *---------------------------------------------------------------------------
  1277. X */
  1278. X
  1279. Xopconc(g)
  1280. Xstruct game *g;
  1281. X{
  1282. Xint gv;
  1283. X
  1284. Xiwin(g,T_OPCONCEDE,1);            /* wimp */
  1285. X}
  1286. X
  1287. X
  1288. X/*---------------------------------------------------------------------------
  1289. X *    opacpt -- opponent accepted double
  1290. X *
  1291. X * This function is called when we receive an ACPTDBL packet, indicating
  1292. X * the opponent has accepted our double.  The IDOUBLED flag is set to
  1293. X * prevent us from doubling again until the opponent doubles.  Since it
  1294. X * is now our turn, we go ahead and roll the dice and proceed to the
  1295. X * MYMOVE state.
  1296. X *---------------------------------------------------------------------------
  1297. X */
  1298. X
  1299. Xopacpt(g)
  1300. Xstruct game *g;
  1301. X{
  1302. X
  1303. Xg->gameval *= 2;        /* double game value */
  1304. Xg->flags |= F_IDOUBLED;        /* I doubled last */
  1305. Xg->state = ST_MYMOVE;        /* It is my move */
  1306. Xcopyboard(g->board,g->opbd);
  1307. Xcopyboard(g->board,g->mybd);
  1308. Xg->curbd = BD_CUR;        /* display current board */
  1309. Xclearmvs(g->opmvs);        /* clear old moves */
  1310. Xclearmvs(g->mvs);        /* erase our previous move */
  1311. Xrolldice(g);            /* go ahead and roll, I can't double */
  1312. Xg->dispmsg = save("Opponent has accepted your double.");/* notify user */
  1313. X}
  1314. X
  1315. X
  1316. X/*---------------------------------------------------------------------------
  1317. X *    opdec -- opponent declined double
  1318. X *
  1319. X * This function is called when a DECDBL packet is received.  This
  1320. X * indicates that the opponent has declined our double, and the game is over.
  1321. X *---------------------------------------------------------------------------
  1322. X */
  1323. X
  1324. Xopdec(g)
  1325. Xstruct game *g;
  1326. X{
  1327. Xint gv;
  1328. X
  1329. Xiwin(g,T_OPDECLINE,1);
  1330. Xcopyboard(g->board,g->opbd);
  1331. Xcopyboard(g->board,g->mybd);
  1332. Xg->curbd = BD_CUR;        /* display current board */
  1333. Xclearmvs(g->opmvs);        /* clear old moves */
  1334. Xclearmvs(g->mvs);        /* erase our previous move */
  1335. X}
  1336. X
  1337. X
  1338. X/*---------------------------------------------------------------------------
  1339. X *    smerr -- an illegal operation was received for this state
  1340. X *
  1341. X * This function is called when a packet is received that is invalid
  1342. X * for the game state.  An example of this would be receiving an
  1343. X * ACPTDBL packet when we did not send an OFRDBL.
  1344. X *---------------------------------------------------------------------------
  1345. X */
  1346. X
  1347. Xsmerr(g)
  1348. Xstruct game *g;
  1349. X{
  1350. X
  1351. Xmessage("ERROR: invalid operation (%d) for state (%d), id=%s\n",
  1352. X    P.opcode, g->state, g->gameid);
  1353. X}
  1354. END_OF_FILE
  1355. if test 15063 -ne `wc -c <'rcvop.c'`; then
  1356.     echo shar: \"'rcvop.c'\" unpacked with wrong size!
  1357. fi
  1358. # end of 'rcvop.c'
  1359. fi
  1360. if test -f 'readmail.c' -a "${1}" != "-c" ; then 
  1361.   echo shar: Will not clobber existing file \"'readmail.c'\"
  1362. else
  1363. echo shar: Extracting \"'readmail.c'\" \(12210 characters\)
  1364. sed "s/^X//" >'readmail.c' <<'END_OF_FILE'
  1365. X/*    readmail.c        8/7/91
  1366. X *
  1367. X * Copyright 1991  Perry R. Ross
  1368. X *
  1369. X * Permission to use, copy, modify, and distribute this software and its
  1370. X * documentation without fee is hereby granted, subject to the restrictions
  1371. X * detailed in the README file, which is included here by reference.
  1372. X * Any other use requires written permission from the author.  This software
  1373. X * is distributed "as is" without any warranty, including any implied
  1374. X * warranties of merchantability or fitness for a particular purpose.
  1375. X * The author shall not be liable for any damages resulting from the
  1376. X * use of this software.  By using this software, the user agrees
  1377. X * to these terms.
  1378. X */
  1379. X
  1380. X#include "ldb.h"
  1381. X
  1382. X/*----------------------------------------------------------------------
  1383. X *    readmail -- read the incoming mail and process it
  1384. X *
  1385. X * This function extracts each packet from the mail file and applies it
  1386. X * to the appropriate game structure.  Most packets are processed by
  1387. X * calling the handler found in the func array, which is a 2-dimensional
  1388. X * array indexed by the current game state and the received opcode.
  1389. X * The handlers are responsible for transforming the game state as
  1390. X * necessary.  The START and RSTART opcodes receive special processing,
  1391. X * since they apply to games that do not exist and thus have no state.
  1392. X * START packets result in the creation of a game, whose state is set
  1393. X * such that the correct handler will be called.  The RSTART opcode
  1394. X * is processed in the same way as the -start command line argument;
  1395. X * the packet is then discarded.
  1396. X *
  1397. X * Note that the file argument can be actually be a pattern, causing
  1398. X * all matching files to be scanned.  On UNIX systems, patterns are
  1399. X * interpreted in the same manner as the shell.  On VMS, they
  1400. X * are interpreted in the same manner as DCL.
  1401. X *----------------------------------------------------------------------
  1402. X */
  1403. X
  1404. Xreadmail(file)
  1405. Xchar *file;
  1406. X{
  1407. Xstruct flist *fl, *t;
  1408. X
  1409. Xfl = filelist(file);        /* generate a list of all matching files */
  1410. Xwhile (fl != NULL) {        /* for each matching file found */
  1411. X    readfile(fl->name);    /* scan it */
  1412. X    free(fl->name);        /* free the string */
  1413. X    t = fl;            /* keep a pointer to the struct */
  1414. X    fl = fl->next;        /* advance fl pointer */
  1415. X    free(t);        /* free the previous struct */
  1416. X    }
  1417. X}
  1418. X
  1419. X
  1420. X/*----------------------------------------------------------------------
  1421. X *    readfile -- scan a file looking for incoming messages
  1422. X *
  1423. X * This function is called by readmail to scan a file that matches
  1424. X * the pattern.
  1425. X *----------------------------------------------------------------------
  1426. X */
  1427. X
  1428. Xreadfile(name)
  1429. Xchar *name;
  1430. X{
  1431. XFILE *fp;
  1432. Xint d, c1, c2;
  1433. Xint flags = 0, match = 0;
  1434. Xstruct people *p;
  1435. Xstruct game *g;
  1436. X
  1437. Xif ( (fp = fopen(name,"r")) == NULL)
  1438. X    return;
  1439. Xif (rc.debug & DB_READFILE)
  1440. X    message("DB-readfile: scanning %s\n",name);
  1441. Xwhile (getpkt(fp) > 0) {    /* as long as we found a valid packet */
  1442. X    if (P.gameptr == NULL) {
  1443. X        if (rc.debug & DB_READFILE)
  1444. X            message("DB-readfile: found packet for <unknown>\n");
  1445. X        if (P.opcode == START) {
  1446. X            if ( ((p = findppl(P.addr,P_ADDR)) != NULL) &&
  1447. X                 (p->fence >= P.timestamp) )
  1448. X                continue;    /* ignore old start packets */
  1449. X            P.gameptr = addgame(); /* init later in start() */
  1450. X            P.gameptr->ppl = p;
  1451. X            P.gameptr->gameid = P.gameid;
  1452. X            P.gameptr->state = ST_OPSTART;
  1453. X            }
  1454. X        else if (P.opcode == RSTART) {    /* remote start packet */
  1455. X            if (rc.debug & DB_RSTART)
  1456. X                message("DB-readfile: remstart to %s\n",P.addr);
  1457. X            if ( (p = findppl(P.addr,P_ADDR)) != NULL)
  1458. X                P.addr = p->addr;    /* use real addr */
  1459. X            for (g = ghead; g != NULL; g = g->next)
  1460. X                if ( (P.timestamp == g->starttime) &&
  1461. X                     (strcmp(P.addr,g->opaddr) == 0) )
  1462. X                    break;    /* already seen this packet */
  1463. X            if (g != NULL)
  1464. X                continue;
  1465. X            if ( (p != NULL) && (p->fence >= P.timestamp) )
  1466. X                continue;    /* this game already played */
  1467. X            if (rc.debug & DB_RSTART)
  1468. X                message("DB-readfile: address is %s\n",P.addr);
  1469. X            if (P.dir == NULL)    /* if no direction was given */
  1470. X                d = cr_mydir;    /* use my default */
  1471. X            else            /* dir was given, grab it */
  1472. X                d = (*P.dir == 'u') ? 1 : -1;
  1473. X            if (P.colors == NULL) {    /* if no colors were given */
  1474. X                c1 = cr_mycolor;    /* use my defaults */
  1475. X                c2 = cr_opcolor;
  1476. X                }
  1477. X            else {                /* colors were given */
  1478. X                c1 = *P.colors;        /* use them */
  1479. X                c2 = P.colors[1];
  1480. X                }
  1481. X            if (P.jacoby != NULL)
  1482. X                flags |= F_JACOBY;
  1483. X            if (P.crawford != NULL)
  1484. X                flags |= F_CRAWFORD;
  1485. X            if (P.european != NULL)
  1486. X                flags |= F_EUROPE;
  1487. X            if (P.perm != NULL)
  1488. X                flags |= F_PERM;
  1489. X            if (P.match != NULL)
  1490. X                match = atoi(P.match);
  1491. X            notify = P.notify;    /* store notify address */
  1492. X            startgame(P.addr,d,c1,c2,flags,match,P.timestamp);
  1493. X            continue;    /* game started, discard this packet */
  1494. X            }
  1495. X        else {
  1496. X            message("ERROR: no such gameid: %s (ignored)\n",
  1497. X                P.gameid);
  1498. X            continue;
  1499. X            }
  1500. X        }
  1501. X    if (rc.debug & DB_READFILE)
  1502. X        message("DB-readfile: found packet for %s\n",
  1503. X            P.gameptr->gameid);
  1504. X    if (P.gameptr->state >= OPSTATES) {    /* hey, it's still my turn */
  1505. X        message("ERROR: move out of turn: %s (ignored)\n",P.gameid);
  1506. X        continue;
  1507. X        }
  1508. X    if (P.name != NULL) {        /* snarf opponent's name */
  1509. X        P.gameptr->opname = P.name;
  1510. X        if (P.gameptr->ppl != NULL) {
  1511. X            if (P.gameptr->ppl->name != NULL)
  1512. X                free(P.gameptr->ppl->name);
  1513. X            P.gameptr->ppl->name = save(P.name);
  1514. X            }
  1515. X        }
  1516. X    if (P.notify != NULL)
  1517. X        P.gameptr->notify = P.notify;
  1518. X    (*func[P.gameptr->state][P.opcode])(P.gameptr);    /* call handler */
  1519. X    }
  1520. Xfclose(fp);
  1521. Xif ( ((*rc.delmail == 'y') || (*rc.delmail == 'Y'))
  1522. X#ifndef VMS
  1523. X        && (*name != '/')    /* absolute paths not deleted */
  1524. X#endif
  1525. X    ) {
  1526. X    if (rc.debug & DB_READFILE)
  1527. X        message("DB-readfile: deleting mail file %s\n",name);
  1528. X    unlink(name);
  1529. X    }
  1530. X}
  1531. X
  1532. X
  1533. X/*---------------------------------------------------------------------------
  1534. X *    getpkt -- read one packet from a file
  1535. X *
  1536. X * This function reads the next packet from the specified file.
  1537. X * Getpkt() is passed a open file pointer to the file it is to scan.
  1538. X * Lines are read and discarded until a line is found that contains only:
  1539. X *        <<<===LDB===>>>
  1540. X * Subsequent lines should contain name/value pairs as specified
  1541. X * in nv_packet.  The packet ends with end of file or a line beginning
  1542. X * with "end=".  Getpkt reads from the input file until one
  1543. X * packet has been found and processed, then returns.  Subsequent calls
  1544. X * to getpkt with the same file pointer will process additional packets.
  1545. X * Getpkt returns 1 if a valid packet was read, 0 if EOF was encountered.
  1546. X * Getpkt ignores incoming packets with the incorrect sequence number.
  1547. X *
  1548. X * As a compatibility hook with old versions, getpkt checks that the
  1549. X * version field on the incoming packet is high enough to support
  1550. X * the following features if they are enabled in the game:
  1551. X *    feature        need at least version
  1552. X *    -------------------------------------
  1553. X *    match play        1.1
  1554. X *    jacoby rule        1.1
  1555. X *    crawford rule        1.1
  1556. X *    european rule        1.1
  1557. X *    permanent games        1.1
  1558. X * If the incoming packet indicates a feature is not supported by the
  1559. X * remote ldb, it is disabled and the game continues as if it had
  1560. X * never been set.  The Crawford rule contained a bug in pre-1.3 games,
  1561. X * so 1.3 will print a warning if an older version tries to start
  1562. X * a game with the crawford rule enabled.
  1563. X *
  1564. X * Getpkt handles RESEND packets itself, performing a resend and
  1565. X * discarding the packet.
  1566. X *---------------------------------------------------------------------------
  1567. X */
  1568. X
  1569. Xgetpkt(fp)
  1570. XFILE *fp;
  1571. X{
  1572. Xstatic char buf[128];
  1573. Xchar *oldaddr;
  1574. Xstruct game *g;
  1575. Xstruct people *p;
  1576. Xint i;
  1577. X
  1578. Xwhile (fgets(buf,sizeof(buf),fp) != NULL) {
  1579. X    if (strcmp(buf,"<<<===LDB===>>>\n"))/* skip all other lines */
  1580. X        continue;
  1581. X    P.gameid = NULL;    /* init P structure */
  1582. X    P.version = 100;    /* default to oldest version */
  1583. X    P.timestamp = 0L;
  1584. X    P.opcode = -1;
  1585. X    P.name = NULL;
  1586. X    P.addr = NULL;
  1587. X    P.comment = NULL;
  1588. X    P.comment2 = NULL;
  1589. X    P.seq = -1;
  1590. X    P.autodbl = NULL;
  1591. X    P.jacoby = NULL;    /* jacoby is off by default */
  1592. X    P.crawford = NULL;    /* so is crawford */
  1593. X    P.perm = NULL;        /* so is permanent option */
  1594. X    P.european = NULL;    /* so is european rule */
  1595. X    P.match = NULL;        /* so is match play */
  1596. X    clearmvs(P.mvs);
  1597. X    P.gameptr = NULL;
  1598. X    P.notify = NULL;
  1599. X    nvscan(fp,nv_packet,&P);    /* scan the packet into P */
  1600. X    if (P.gameid == NULL) {        /* didn't get a gameid */
  1601. X        message("ERROR: missing gameid in packet -- ignored\n");
  1602. X        continue;
  1603. X        }
  1604. X    if (P.version > 100) {        /* versions after 1.0 rot13 comments */
  1605. X        if (P.comment != NULL)
  1606. X            rotate(P.comment);
  1607. X        if (P.comment2 != NULL)
  1608. X            rotate(P.comment2);
  1609. X        }
  1610. X    if ( (P.gameptr = findgame(P.gameid)) == NULL) {/* doesn't exist */
  1611. X        if ( (P.opcode != START) && (P.opcode != RSTART) )
  1612. X            continue;    /* ignore pkts for dead games */
  1613. X        i = 1;            /* initial seq == 1 */
  1614. X        }
  1615. X    else {
  1616. X        if (P.opcode == RESEND) {    /* resend request */
  1617. X            if ((P.seq + 1) != P.gameptr->seq)
  1618. X                continue;    /* old resend request, ignore */
  1619. X            if (P.timestamp < P.gameptr->lastacc)
  1620. X                continue;    /* old resend request, ignore */
  1621. X            message(
  1622. X                "Resend requested for game %s -- sending...\n",
  1623. X                P.gameid);
  1624. X            resendpkt(P.gameptr);    /* resend */
  1625. X            P.gameptr->lastacc = time( (long *) 0);    /*set lastacc*/
  1626. X            if (P.timestamp > P.gameptr->lastacc)
  1627. X                P.gameptr->lastacc = P.timestamp;
  1628. X            continue;        /* and ignore packet */
  1629. X            }
  1630. X        i = P.gameptr->seq+1;    /* get current seq */
  1631. X        }
  1632. X    if (P.seq != i) {        /* sequence number is wrong */
  1633. X        if (P.seq > i)        /* rec'd seq # is too big */
  1634. X            message(        /* shouldn't happen */
  1635. X            "WARNING: game %s, seq no. is %d, s/b %d -- ignored.\n"
  1636. X            ,P.gameid,P.seq,i);
  1637. X        continue;        /* ignore pkts with bad sequence #s */
  1638. X        }
  1639. X    if ( (P.opcode < 0) || (P.opcode >= NOP) ) {    /* bad opcode */
  1640. X        message("ERROR: bad opcode for game %s: %d -- ignored.\n",
  1641. X            P.gameid,P.opcode);
  1642. X        continue;
  1643. X        }
  1644. X
  1645. X    if (P.gameptr == NULL)        /* everything after here needs ptr */
  1646. X        return(1);        /* to game structure */
  1647. X
  1648. X    P.gameptr->seq += 2;    /* bump sequence number */
  1649. X    P.gameptr->lastacc = time( (long *) 0);    /*set lastacc*/
  1650. X    if (P.timestamp > P.gameptr->lastacc)
  1651. X        P.gameptr->lastacc = P.timestamp;
  1652. X    if (P.gameptr->opcmt != NULL)
  1653. X        free(P.gameptr->opcmt);    /* discard old comment */
  1654. X    P.gameptr->opcmt = P.comment;    /* copy new comment */
  1655. X    if (P.gameptr->opcmt2 != NULL)
  1656. X        free(P.gameptr->opcmt2);/* discard old comment */
  1657. X    P.gameptr->opcmt2 = P.comment2;    /* copy new comment */
  1658. X    P.gameptr->opver = P.version;    /*in case he changed versions*/
  1659. X    P.gameptr->ppl->opver = P.version;
  1660. X
  1661. X    if (P.addr != NULL) {    /* opponent's address changed */
  1662. X        oldaddr = P.gameptr->ppl->addr;
  1663. X        for (g = ghead; g != NULL; g = g->next)    /* all games with */
  1664. X            if (strcmp(g->ppl->addr,oldaddr) == 0) {/* this opp */
  1665. X                free(g->opaddr);    /* free old addr */
  1666. X                g->opaddr = save(P.addr);    /* copy new */
  1667. X                }
  1668. X        for (p = phead; p != NULL; p = p->next)    /* look for dangling */
  1669. X            if (p->equiv != NULL)        /* equiv records */
  1670. X                if (strcmp(oldaddr,p->equiv) == 0) {
  1671. X                    free(p->equiv);
  1672. X                    p->equiv = save(P.addr);
  1673. X                    }
  1674. X        free(P.gameptr->ppl->addr);
  1675. X        P.gameptr->ppl->addr = save(P.addr);
  1676. X        message("Address change for %s: %s\n",
  1677. X            P.gameptr->opname,P.gameptr->opaddr);
  1678. X        }
  1679. X    
  1680. X    if (P.gameptr->ppl->newaddr == NA_SENT) {/* newaddr pending this op */
  1681. X        P.gameptr->ppl->newaddr = NA_NONE;    /* cancel it */
  1682. X        message("newaddr: %s notified.\n",P.gameptr->ppl->addr);
  1683. X        }
  1684. X
  1685. X            /* any 1.1 features used with 1.0? */
  1686. X    if ( (P.version < 110) && ( (P.gameptr->mtotal > 0) ||
  1687. X         (P.gameptr->flags & (F_JACOBY|F_CRAWFORD|F_PERM|F_EUROPE))) ) {
  1688. X        message("Warning: in game %s:\n",P.gameid);
  1689. X        message(
  1690. X"The following features are not supported by your opponent's version of ldb:\n"
  1691. X        );
  1692. X        if (P.gameptr->flags & F_JACOBY)
  1693. X            message("\tJacoby rule.\n");
  1694. X        if (P.gameptr->flags & F_CRAWFORD)
  1695. X            message("\tCrawford rule.\n");
  1696. X        if (P.gameptr->flags & F_EUROPE)
  1697. X            message("\tEuropean rule.\n");
  1698. X        if (P.gameptr->flags & F_PERM)
  1699. X            message("\tPermanent games.\n");
  1700. X        if (P.gameptr->mtotal > 0)
  1701. X            message("\tMatch play.\n");
  1702. X        P.gameptr->flags &=
  1703. X            ~(F_CRAWFORD|F_JACOBY|F_PERM|F_EUROPE);
  1704. X        P.gameptr->mtotal = 0;
  1705. X        message(
  1706. X"This game will continue as if those features had not been used.\n");
  1707. X        }
  1708. X    if ( (P.version < 130) && (P.gameptr->flags & F_CRAWFORD) )
  1709. X        message(
  1710. X"Warning: opponent using pre-1.3 ldb -- using Crawford rule not recommended!\n"
  1711. X        );
  1712. X    return(1);            /* return success */
  1713. X    }
  1714. Xreturn(0);        /* return this to mean end of file */
  1715. X}
  1716. END_OF_FILE
  1717. if test 12210 -ne `wc -c <'readmail.c'`; then
  1718.     echo shar: \"'readmail.c'\" unpacked with wrong size!
  1719. fi
  1720. # end of 'readmail.c'
  1721. fi
  1722. echo shar: End of archive 3 \(of 12\).
  1723. cp /dev/null ark3isdone
  1724. MISSING=""
  1725. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1726.     if test ! -f ark${I}isdone ; then
  1727.     MISSING="${MISSING} ${I}"
  1728.     fi
  1729. done
  1730. if test "${MISSING}" = "" ; then
  1731.     echo You have unpacked all 12 archives.
  1732.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1733. else
  1734.     echo You still need to unpack the following archives:
  1735.     echo "        " ${MISSING}
  1736. fi
  1737. ##  End of shell archive.
  1738. exit 0
  1739.  
  1740. exit 0 # Just in case...
  1741.