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

  1. Newsgroups: comp.sources.misc
  2. From: ross@teserv.den.mmc.com (Perry R. Ross)
  3. Subject: v36i102:  ldb - Play backgammon by e-mail, v1.3, Part05/12
  4. Message-ID: <1993Apr11.233020.18191@sparky.imd.sterling.com>
  5. X-Md4-Signature: 17bf1b9bdc7b9cdbbd741864a927e6aa
  6. Date: Sun, 11 Apr 1993 23:30:20 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 102
  11. Archive-name: ldb/part05
  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 5 (of 12)."
  22. # Contents:  misc.c save.c
  23. # Wrapped by ross@teserv.den.mmc.com on Tue Apr  6 14:52:20 1993
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'misc.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'misc.c'\"
  27. else
  28. echo shar: Extracting \"'misc.c'\" \(18769 characters\)
  29. sed "s/^X//" >'misc.c' <<'END_OF_FILE'
  30. X/*    misc.c        8/8/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/*----------------------------------------------------------------------
  49. X *    rolldice -- roll two dice
  50. X *
  51. X * This function calls Rolldie twice and fills in the game structure
  52. X * with the resulting values.  If the two calls to Rolldie return the
  53. X * same number, the mvs field is filled in so that the user has 4 rolls
  54. X * to use, otherwise the rolls are stored in the first two elements
  55. X * of the mvs field and the last two are marked unused.
  56. X *----------------------------------------------------------------------
  57. X */
  58. X
  59. Xrolldice(g)
  60. Xstruct game *g;
  61. X{
  62. X
  63. Xclearmvs(g->mvs);            /* clear old stuff */
  64. Xg->mvs[0].roll = Rolldie();             /* roll the dice */
  65. Xg->mvs[1].roll = Rolldie();
  66. Xg->rolls[g->mvs[0].roll - 1]++;        /* keep count of what rolls we got */
  67. Xg->rolls[g->mvs[1].roll - 1]++;
  68. Xif (g->mvs[0].roll == g->mvs[1].roll) {    /* hot damn, we got doubles */
  69. X    g->mvs[2].roll = g->mvs[0].roll;    /* copy roll into two */
  70. X    g->mvs[3].roll = g->mvs[0].roll;    /* more moves */
  71. X    g->doubles[g->mvs[1].roll - 1]++;    /* keep track of doubles */
  72. X    }
  73. Xlegalmoves(g);            /* calculate the # of moves & hi roll */
  74. X}
  75. X
  76. X
  77. X
  78. X/*----------------------------------------------------------------------
  79. X *    sendpkt -- send a packet to the opponent
  80. X *
  81. X * This function fills in the fields of a packet and passes that
  82. X * packet to the transport using TSendPacket.  It also stores the
  83. X * opcode sent in the lastop field of the game, so the packet
  84. X * can be regenerated if necessary.  Sendpkt returns 1 if the
  85. X * packet was sent, and 0 if an error occurred.
  86. X *----------------------------------------------------------------------
  87. X */
  88. X
  89. Xsendpkt(g,op)
  90. Xstruct game *g;
  91. Xchar op;
  92. X{
  93. Xstatic char colors[4], adbl[10], mch[10];
  94. Xchar cmt[60], cmt2[60], *sendaddr;
  95. Xint i, status;
  96. X
  97. Xif (FeIsActive)
  98. X    FeMessage("Sending...");
  99. Xsendaddr = g->opaddr;        /* this is overridden for NOTIFY */
  100. Xif ( (op != RESEND) && (op != NOTIFY) )
  101. X    g->lastop = op;            /* save last op for resend */
  102. Xfor (i = 0; i < 4; i++)
  103. X    g->blot[i] = 0;            /* clear blots hit */
  104. Xif (*rc.chkpt == 'y') {
  105. X    writegames(rc.gfile,rc.gbackup,rc.pfile);
  106. X    rc.gbackup = NULL;    /* only backup old file once */
  107. X    }
  108. XP.version = LDB_VER;            /* these fields go in all packets */
  109. XP.gameid = g->gameid;
  110. XP.opcode = op;
  111. XP.seq = g->seq;
  112. XP.jacoby = NULL;
  113. XP.crawford = NULL;
  114. XP.european = NULL;
  115. XP.perm = NULL;
  116. XP.match = NULL;
  117. Xif (g->opver > 100) {    /* versions after 1.0 rot13 comments */
  118. X    if (g->mycmt != NULL) {
  119. X        strncpy(cmt,g->mycmt,sizeof(cmt));    /* make copy */
  120. X        cmt[sizeof(cmt)-1] = '\0';        /* null term */
  121. X        P.comment = cmt;            /* save pointer */
  122. X        rotate(P.comment);            /* rot13 the copy */
  123. X        }
  124. X    else
  125. X        P.comment = NULL;
  126. X    if (g->mycmt2 != NULL) {
  127. X        strncpy(cmt2,g->mycmt2,sizeof(cmt2));    /* make copy */
  128. X        cmt2[sizeof(cmt2)-1] = '\0';        /* null term */
  129. X        P.comment2 = cmt2;            /* save pointer */
  130. X        rotate(P.comment2);            /* rot13 the copy */
  131. X        }
  132. X    else
  133. X        P.comment2 = NULL;
  134. X    }
  135. Xelse {                /* version 1.0 sends comments as cleartext */
  136. X    P.comment = g->mycmt;
  137. X    P.comment2 = g->mycmt2;
  138. X    }
  139. Xif (g->flags & F_SENTNAME) {
  140. X    P.name = NULL;
  141. X    P.notify = NULL;
  142. X    }
  143. Xelse {
  144. X    P.name = rc.myname;
  145. X    P.notify = g->notify;
  146. X    g->flags |= F_SENTNAME;
  147. X    }
  148. Xif (g->ppl->newaddr != NA_NONE) {    /* our address has changed */
  149. X    P.addr = g->ppl->myaddr;    /* notify this opponent */
  150. X    g->ppl->newaddr = NA_SENT;    /* remember that we did it */
  151. X    }
  152. Xelse
  153. X    P.addr = NULL;
  154. XP.colors = NULL;
  155. XP.dir = NULL;
  156. XP.autodbl = NULL;            /* used by START and TIE */
  157. XP.timestamp = time((long *) 0);        /* attach timestamp */
  158. Xif (g->lastacc < P.timestamp)        /* update last access time */
  159. X    g->lastacc = P.timestamp;    /* but don't let time go backwards */
  160. Xclearmvs(P.mvs);
  161. Xswitch (op) {                /* now do operation-specific stuff */
  162. Xcase START:
  163. X    P.addr = g->myaddr;        /* send opponent my email address */
  164. X    P.mvs[0].roll = g->mvs[0].roll;    /* send initial die roll */
  165. X    sprintf(colors,"%c%c",g->mycolor,g->opcolor);
  166. X    P.colors = colors;
  167. X    P.dir = (g->mydir > 0) ? "down" : "up";
  168. X    sprintf(adbl,"%d",rc.autodouble);
  169. X    P.autodbl = adbl;
  170. X    if (g->flags & F_JACOBY)    /* enable jacoby */
  171. X        P.jacoby = "yes";
  172. X    if (g->flags & F_CRAWFORD)    /* enable crawford */
  173. X        P.crawford = "yes";
  174. X    if (g->flags & F_EUROPE)    /* enable european rule */
  175. X        P.european = "yes";
  176. X    if (g->flags & F_PERM)        /* game is permanent */
  177. X        P.perm = "yes";
  178. X    if (g->mtotal > 0) {        /* enable match play */
  179. X        sprintf(mch,"%d",g->mtotal);
  180. X        P.match = mch;
  181. X        }
  182. X    break;
  183. Xcase USTART:
  184. X    P.mvs[0].roll = g->mvs[0].roll;    /* send both initial dice */
  185. X    P.mvs[1].roll = g->mvs[1].roll;
  186. X    break;
  187. Xcase MSTART:
  188. Xcase RESTART:                /* retry initial roll */
  189. X    P.mvs[0].roll = g->mvs[0].roll;    /* send new roll */
  190. X    break;
  191. Xcase TIE:
  192. X    if (g->adcnt > 0) {        /* send current autodouble count */
  193. X        sprintf(adbl,"%d",g->adcnt);
  194. X        P.autodbl = adbl;
  195. X        }
  196. X    break;
  197. Xcase MOVE:
  198. X    for (i = 0; i < 4; i++)
  199. X        P.mvs[i] = g->mvs[i];
  200. X    break;
  201. Xcase NOTIFY:
  202. X    P.addr = g->myaddr;
  203. X    P.seq = 0;            /* no sequences for notify packets */
  204. X    P.notify = NULL;
  205. X    P.name = rc.myname;
  206. X    i = gvalue(g,&status);
  207. X    sprintf(cmt,"%d %d %d",g->term,status,i);
  208. X    P.comment = cmt;    /* send term code, game val, & bg flag */
  209. X    P.comment2 = g->opaddr;    /* send opponent address */
  210. X    if ( (sendaddr = g->notify) == NULL)
  211. X        return;        /* shouldn't happen */
  212. X    }
  213. Xstatus = TSendPacket(&P,sendaddr);        /* send the packet */
  214. Xif (FeIsActive)                /* clear "Sending..." from mesg line */
  215. X    FeMessage(NULL);
  216. Xreturn(status);
  217. X}
  218. X
  219. X
  220. X
  221. X/*----------------------------------------------------------------------
  222. X *    resendpkt -- resend the last packet
  223. X *
  224. X * This function takes a game structure and causes the last packet sent
  225. X * in that game to be resent.
  226. X *
  227. X * The F_SENTNAME flag is cleared before resending, causing our personal
  228. X * name to be resent.  This is because the packet being resent might have
  229. X * been the one that sent the personal name (and set the F_SENTNAME flag),
  230. X * and since the F_SENTNAME flag is now set, the personal name would not
  231. X * be included in the resent packet.
  232. X *----------------------------------------------------------------------
  233. X */
  234. X
  235. Xresendpkt(g)
  236. Xstruct game *g;
  237. X{
  238. X
  239. Xg->flags &= ~F_SENTNAME;
  240. Xsendpkt(g,g->lastop);
  241. X}
  242. X
  243. X
  244. X
  245. X/*----------------------------------------------------------------------
  246. X *    str2mv -- decode move string to struct mv
  247. X *
  248. X * This function takes a string representation of a move, decodes it,
  249. X * and places the information into a mv structure.  This format is:
  250. X *
  251. X *    roll/move
  252. X *
  253. X * where roll is the die value used in the move, and is a single
  254. X * digit in [1..6], and move is one of the following:
  255. X *
  256. X *    a 1 or 2 digit number in [1..24]
  257. X *        This designates the point the move originates from.
  258. X *        This number is stored in the "pt" field of the move
  259. X *        structure without modification.
  260. X *    the string "BAR"
  261. X *        This means the piece is coming off the bar.
  262. X *        Zero is stored in the "pt" field of the move structure,
  263. X *        regardless of whether the player's bar point is 0 or 25.
  264. X *        Apply() and FeDrawMove understand this and convert 0 to
  265. X *        the appropriate bar point before using it.
  266. X *    the string "UNUSED"
  267. X *        This means the roll is unused.  -1 is stored in the
  268. X *        "pt" field of the mv structure.
  269. X *----------------------------------------------------------------------
  270. X */
  271. X
  272. Xstr2mv(s,m)
  273. Xchar *s;
  274. Xstruct mv *m;
  275. X{
  276. Xchar *p, *strchr();
  277. X
  278. Xif ( (p = strchr(s,'/')) == NULL) {
  279. X    message("ERROR: malformed move: %s\n",s);
  280. X    return;
  281. X    }
  282. Xif ( ( (m->roll = atoi(s)) < 0) || (m->roll > 6) ) {
  283. X    message("ERROR: invalid roll: %d\n",m->roll);
  284. X    return;
  285. X    }
  286. Xp++;
  287. Xif ( (m->roll == 0) || (*p == 'U') || (*p == 'u') )
  288. X    m->pt = -1;        /* this roll is unused */
  289. Xelse if ( (*p == 'B') || (*p == 'b') )
  290. X    m->pt = 0;        /* move from bar */
  291. Xelse if ( ( (m->pt = atoi(p)) < 0) || (m->pt > 25) ) {
  292. X    message("ERROR: invalid point: %d\n",m->pt);
  293. X    return;
  294. X    }
  295. X}
  296. X
  297. X
  298. X/*----------------------------------------------------------------------
  299. X *    mv2str -- encode move string from struct mv
  300. X *
  301. X * This function forms a string representation of a move based on
  302. X * the information in a mv structure.  This format is:
  303. X *
  304. X *    roll/move
  305. X *
  306. X * where roll is the die value stored in mv->roll, and move is:
  307. X *
  308. X *    mv->pt if mv->pt is in [1..24]
  309. X *    the string "BAR", if mv->pt is 0 or 25
  310. X *    the string "UNUSED", if mv->pt is < 0
  311. X *----------------------------------------------------------------------
  312. X */
  313. X
  314. Xmv2str(m,s)
  315. Xstruct mv *m;
  316. Xchar *s;
  317. X{
  318. X
  319. Xif (m->roll <= 0) {        /* non-existant roll */
  320. X    strcpy(s,"0/0");    /* should be skipped by nvwrite */
  321. X    return;            /* so we should never get here */
  322. X    }
  323. Xif (m->pt < 0)
  324. X    sprintf(s,"%d/UNUSED",m->roll);
  325. Xelse if ( (m->pt == DOWNBAR) || (m->pt == UPBAR) )
  326. X    sprintf(s,"%d/BAR",m->roll);
  327. Xelse
  328. X    sprintf(s,"%d/%d",m->roll,m->pt);
  329. X}
  330. X
  331. X
  332. X/*----------------------------------------------------------------------
  333. X *    clearmvs -- mark all entries in a mv array empty
  334. X *
  335. X * This function marks all elements of a mv array as being unused.
  336. X *----------------------------------------------------------------------
  337. X */
  338. X
  339. X
  340. Xclearmvs(m)
  341. Xstruct mv *m;
  342. X{
  343. Xint i;
  344. X
  345. Xfor (i = 0; i < 4; i++) {
  346. X    m[i].roll = -1;
  347. X    m[i].pt = -1;
  348. X    }
  349. X}
  350. X
  351. X
  352. X
  353. X/*----------------------------------------------------------------------
  354. X *    canmove -- see if a roll is usable in a range of points
  355. X *
  356. X * This function trys to use a roll over a range of points.  If it
  357. X * finds a point where a roll could be used, it returns 1, otherwise
  358. X * it returns 0.  The board is not changed.
  359. X *----------------------------------------------------------------------
  360. X */
  361. X
  362. Xcanmove(g,r,p1,p2)
  363. Xstruct game *g;        /* the game structure */
  364. Xint r;            /* which element of g->mvs */
  365. Xint p1, p2;        /* the range of points to try */
  366. X{
  367. Xint i, op;
  368. X
  369. Xif (p1 > p2) {
  370. X    i = p1;
  371. X    p1 = p2;
  372. X    p2 = i;
  373. X    }
  374. Xop = g->mvs[r].pt;        /* so we can restore it */
  375. Xfor (i = p1; i <= p2; i++) {
  376. X    g->mvs[r].pt = i;        /* try from this point */
  377. X    if (apply(g,WHO_ME,r,A_CHKONLY,NULL) >= 0) {
  378. X        g->mvs[r].pt = op;
  379. X        return(1);
  380. X        }
  381. X    }
  382. Xg->mvs[r].pt = op;
  383. Xreturn(0);
  384. X}
  385. X
  386. X
  387. X/*----------------------------------------------------------------------
  388. X *    rotate -- rot13 a buffer
  389. X *
  390. X * This function performs the popular "rot13" conversion to a buffer.
  391. X * The buffer is modified in place.  This conversion makes a
  392. X * string unreadable by adding 13 to letters in [A-Ma-m] and subtracting
  393. X * 13 from letters in [N-Zn-z].  All other characters are unchanged.
  394. X * Applying the rot13 transformation again returns the buffer to normal.
  395. X *----------------------------------------------------------------------
  396. X */
  397. X
  398. Xrotate(buf)
  399. Xchar *buf;
  400. X{
  401. Xregister char *s;
  402. X
  403. Xfor (s = buf; *s; s++) {
  404. X    if (! isalpha(*s))
  405. X        continue;
  406. X    if ( ((*s >= 'A') && (*s <= 'M')) || ((*s >= 'a') && (*s <= 'm')) )
  407. X        *s += (char) 13;
  408. X    else
  409. X        *s -= (char) 13;
  410. X    }
  411. X}
  412. X
  413. X
  414. X/*----------------------------------------------------------------------
  415. X *    message -- print a message in proper way
  416. X *
  417. X * This function checks to see if the front end is active.  If
  418. X * so, it calls FeMessage, otherwise it prints on stderr and
  419. X * sets the FeWaitInit flag so that when FeInitialize is called,
  420. X * it will wait for return to be pressed before clearing the screen.
  421. X *----------------------------------------------------------------------
  422. X */
  423. X
  424. Xmessage(s,a1,a2,a3)
  425. Xchar *s;
  426. Xint a1, a2, a3;
  427. X{
  428. Xchar buf[80];
  429. X
  430. Xif (FeIsActive) {
  431. X    sprintf(buf, s, a1, a2, a3);
  432. X    FeMessage(buf);
  433. X    }
  434. Xelse {
  435. X    fprintf(stderr,s,a1,a2,a3);
  436. X    FeWaitInit++;
  437. X    if (FeWaitInit > 22) {
  438. X        fprintf(stderr,"Press <return> to continue...");
  439. X        fgets(buf,sizeof(buf),stdin);
  440. X        FeWaitInit = 0;
  441. X        }
  442. X    }
  443. X}
  444. X
  445. X
  446. X/*----------------------------------------------------------------------
  447. X *    fatal -- terminate program with error message
  448. X *
  449. X * This function prints a message to stderr and exits.
  450. X *----------------------------------------------------------------------
  451. X */
  452. X
  453. Xfatal(msg)
  454. Xchar *msg;
  455. X{
  456. X
  457. XFeFinishSession();
  458. XTFinishSession();
  459. Xfprintf(stderr,"%s\n",msg);
  460. Xldbexit(STAT_ABORT);
  461. X}
  462. X
  463. X
  464. X/*----------------------------------------------------------------------
  465. X *    ldbexit -- terminate program in an orderly fashion
  466. X *
  467. X * This function can be called at any time, and ensures that the
  468. X * front end and the transport are closed down cleanly before
  469. X * exiting.
  470. X *----------------------------------------------------------------------
  471. X */
  472. X
  473. Xldbexit(code)
  474. Xint code;
  475. X{
  476. X
  477. XFeFinishSession();
  478. XTFinishSession();
  479. Xrelease_lock(rc.lockfile);
  480. Xfflush(stdout);
  481. Xfflush(stderr);
  482. Xexit(code);
  483. X}
  484. X
  485. X
  486. X/*----------------------------------------------------------------------
  487. X *    pipcount -- calculate a pip count for both players
  488. X *
  489. X * This function takes a board and a pointer to a game structure
  490. X * and calculates a pip count for both players.  These are returned
  491. X * in *mp and *op.
  492. X *----------------------------------------------------------------------
  493. X */
  494. X
  495. Xpipcount(b,g,mp,op)
  496. Xboard b;            /* the board */
  497. Xstruct game *g;            /* the game */
  498. Xint *mp;            /* where to store my pip count */
  499. Xint *op;            /* where to store opponent's pip count */
  500. X{
  501. Xint direction[2];
  502. Xint player;            /* me = 0; op = 1 */
  503. Xint PIP[2];            /* mypip = 0; oppip = 1 */
  504. Xint i;                /* counter */
  505. X
  506. XPIP[WHO_ME] = 0;            /* initialize my PIP count */
  507. XPIP[WHO_OPP] = 0;            /* initialize op PIP count */
  508. Xdirection[WHO_ME] = g->mydir;        /* my direction of travel */
  509. Xdirection[WHO_OPP] = g->opdir;        /* op direction of travel */
  510. X
  511. Xfor (i = 0; i <= DOWNBAR; i++) {    /* for each point on the board */
  512. X
  513. X    if (b[i].qty > 0) {
  514. X        player = (b[i].color == g->mycolor) ? WHO_ME : WHO_OPP;
  515. X        if ((i == UPBAR) || (i == DOWNBAR))
  516. X            PIP[player] += b[i].qty * 25;
  517. X        else {
  518. X            if (direction[player] == 1)
  519. X                PIP[player] += b[i].qty * (25 - i);
  520. X            else
  521. X                PIP[player] += b[i].qty * i;
  522. X            }
  523. X        }
  524. X    }
  525. X*mp = PIP[WHO_ME];        /* return my pip count */
  526. X*op = PIP[WHO_OPP];        /* return opponent's pip count */
  527. X}
  528. X
  529. X
  530. X/*----------------------------------------------------------------------
  531. X *    gvalue -- calculate the game value
  532. X *
  533. X * This function takes a completed game and calculates its value,
  534. X * taking into account gammons, backgammons, and the Jacoby and
  535. X * European rules.  These are:
  536. X *    - Gammon (when the loser does not bear off any pieces)
  537. X *      counts as double the game value shown on the cube.
  538. X *    - Backgammon (when the loser does not bear off any pieces
  539. X *      and has pieces in the winner's home board) counts as
  540. X *      triple the game value shown on the cube.
  541. X *    - Jacoby rule (if enabled) states that Gammons and Backgammons
  542. X *      count as single games if neither player doubled during the game.
  543. X *    - European rule (if enabled) states the Backgammons count double,
  544. X *      not triple, the game value shown on the cube.
  545. X * The game value is returned in *vp; the return value is 1 for a gammon,
  546. X * 2 for a backgammon, and 0 for neither.
  547. X *
  548. X * If the game was conceded, gvalue() scores a gammon if the loser has
  549. X * not borne off any pieces, and a backgammon if the loser has any pieces
  550. X * in the winners inner table.  This prevents players from conceding to
  551. X * avoid a gammon/backgammon.
  552. X *----------------------------------------------------------------------
  553. X */
  554. X
  555. Xgvalue(g,vp)
  556. Xstruct game *g;
  557. Xint *vp;
  558. X{
  559. Xint bf;
  560. Xint p1, p2;
  561. X
  562. Xbf = 0;                /* init to no gammon/backgammon */
  563. X*vp = g->gameval;        /* init to game value on cube */
  564. Xif ( (g->term == T_ILOSE) || (g->term == T_ICONCEDE) ) {
  565. X    if (g->board[OFFPT(g->mydir)].qty == 0) {
  566. X        p1 = (g->opdir > 0) ? 19 : 0;/* check op's inner tbl*/
  567. X        p2 = (g->opdir > 0) ? 25 : 6;    /* for my pieces */
  568. X        if (addpcs(g->board,g->mycolor,p1,p2) > 0)
  569. X            bf = 2;            /* flag a backgammon */
  570. X        else
  571. X            bf = 1;            /* flag a gammon */
  572. X        }
  573. X    }
  574. Xelse if ( (g->term == T_IWIN) || (g->term == T_OPCONCEDE) ) {
  575. X    if (g->board[OFFPT(g->opdir)].qty == 0) {
  576. X        p1 = (g->mydir > 0) ? 19 : 0;    /* check my inner tbl*/
  577. X        p2 = (g->mydir > 0) ? 25 : 6;    /* for op's pieces */
  578. X        if (addpcs(g->board,g->opcolor,p1,p2) > 0)
  579. X            bf = 2;            /* flag a backgammon */
  580. X        else
  581. X            bf = 1;            /* flag a gammon */
  582. X        }
  583. X    }
  584. Xif ( (g->flags & F_JACOBY) && (g->gameval == (1 << g->adcnt)) )
  585. X    return(bf);    /* jacoby enabled & no doubles, don't mult game val */
  586. Xif ( (g->flags & F_EUROPE) && (bf == 2) ) {
  587. X    *vp *= 2;    /* european rule enabled, bg = 2 multiplier */
  588. X    return(bf);
  589. X    }
  590. Xif (bf == 2)
  591. X    *vp *= 3;    /* backgammon = 3 multiplier */
  592. Xelse if (bf == 1)
  593. X    *vp *= 2;    /* gammon = 2 multipler */
  594. Xreturn(bf);        /* return gammon/backgammon/neither flag */
  595. X}
  596. X
  597. X
  598. X/*----------------------------------------------------------------------
  599. X *    iscontact -- determine if contact is possible for a game
  600. X *
  601. X * This function returns 1 if it is possible for any piece to hit
  602. X * any other piece, 0 if both players have moved past each other
  603. X * and the game is a race.
  604. X *----------------------------------------------------------------------
  605. X */
  606. X
  607. Xiscontact(g)
  608. Xstruct game *g;
  609. X{
  610. Xint dc, dn, i;
  611. X
  612. Xif (g->mydir > 0)        /* which color is down-bound? */
  613. X    dc = g->opcolor;
  614. Xelse
  615. X    dc = g->mycolor;
  616. Xdn = g->board[DOWNOFF].qty;        /* start with # pcs borne off */
  617. Xfor (i = 0; (i <= 24) && (dn < 15); i++) {
  618. X    if (g->board[i].qty == 0)
  619. X        continue;
  620. X    if (g->board[i].color != dc)    /* found some upbound pieces */
  621. X        return(1);        /* that are in hitting range */
  622. X    dn += g->board[i].qty;        /* keep count of down pcs found */
  623. X    }
  624. Xif (dn >= 15)                /* found all down pcs */
  625. X    return(0);            /* no more contact for this game */
  626. Xreturn(1);
  627. X}
  628. X
  629. X
  630. X/*----------------------------------------------------------------------
  631. X *    crawford_check -- is this the crawford rule game for a match?
  632. X *
  633. X * This function sets the F_CRGAME and F_CRDONE flags for a match.
  634. X * F_CRGAME is set if this is the crawford rule game for the match.
  635. X * F_CRDONE is set after the crawford rule game has been played.
  636. X *
  637. X * F_CRGAME is set if:
  638. X *    F_CRAWFORD is set    AND
  639. X *    F_CRDONE is not set    AND
  640. X *    a player is within one point of winning the match.
  641. X * otherwise F_CRGAME is cleared.
  642. X *
  643. X * F_CRDONE is set if:
  644. X *    F_CRAWFORD is set    AND
  645. X *    F_CRGAME was set (before the above)
  646. X * otherwise F_CRDONE is not changed.
  647. X *----------------------------------------------------------------------
  648. X */
  649. X
  650. Xcrawford_check(g)
  651. Xstruct game *g;
  652. X{
  653. Xint old_crgame;
  654. X
  655. Xold_crgame = g->flags & F_CRGAME;        /* save F_CRGAME */
  656. Xif ( ((g->flags & (F_CRAWFORD|F_CRDONE)) == F_CRAWFORD) &&
  657. X     ( (g->mcurrent[WHO_ME] == g->mtotal-1) ||
  658. X       (g->mcurrent[WHO_OPP] == g->mtotal-1) ) )
  659. X    g->flags |= F_CRGAME;        /* this is the crawford rule game */
  660. Xelse
  661. X    g->flags &= ~F_CRGAME;        /* not the crawford game, clear flag */
  662. Xif ( (g->flags & F_CRAWFORD) && old_crgame)
  663. X    g->flags |= F_CRDONE;        /* crawford rule game is over */
  664. X}
  665. END_OF_FILE
  666. if test 18769 -ne `wc -c <'misc.c'`; then
  667.     echo shar: \"'misc.c'\" unpacked with wrong size!
  668. fi
  669. # end of 'misc.c'
  670. fi
  671. if test -f 'save.c' -a "${1}" != "-c" ; then 
  672.   echo shar: Will not clobber existing file \"'save.c'\"
  673. else
  674. echo shar: Extracting \"'save.c'\" \(21975 characters\)
  675. sed "s/^X//" >'save.c' <<'END_OF_FILE'
  676. X/*    save.c        8/4/91
  677. X *
  678. X * Copyright 1991  Perry R. Ross
  679. X *
  680. X * Permission to use, copy, modify, and distribute this software and its
  681. X * documentation without fee is hereby granted, subject to the restrictions
  682. X * detailed in the README file, which is included here by reference.
  683. X * Any other use requires written permission from the author.  This software
  684. X * is distributed "as is" without any warranty, including any implied
  685. X * warranties of merchantability or fitness for a particular purpose.
  686. X * The author shall not be liable for any damages resulting from the
  687. X * use of this software.  By using this software, the user agrees
  688. X * to these terms.
  689. X */
  690. X
  691. X#include "ldb.h"
  692. X
  693. X/*===========================================================================
  694. X * This file contains miscellaneous functions that save and load things.
  695. X *===========================================================================
  696. X */
  697. X
  698. X/*---------------------------------------------------------------------------
  699. X *    save -- make a copy of a string
  700. X *
  701. X * This function makes a copy of a string in malloc memory, and returns
  702. X * a pointer to the copy.
  703. X *---------------------------------------------------------------------------
  704. X */
  705. X
  706. Xchar *save(s)
  707. Xchar *s;
  708. X{
  709. Xchar *n;
  710. X
  711. Xif (s == NULL) {
  712. X    return(NULL);
  713. X    }
  714. Xelse {
  715. X    if ( (n = calloc(strlen(s)+1,1)) == NULL)
  716. X        fatal("Out of memory!");
  717. X    strcpy(n,s);
  718. X    return(n);
  719. X    }
  720. X}
  721. X
  722. X
  723. X/*---------------------------------------------------------------------------
  724. X *    readldbrc -- read in the .ldbrc file
  725. X *
  726. X * This function reads the .ldbrc file, which contains the setup info
  727. X * for this user.  If the HOME environment variable is set, we chdir to it.
  728. X * If the LDBRC environment variable is set, it is used as the file to
  729. X * read instead of .ldbrc.
  730. X *---------------------------------------------------------------------------
  731. X */
  732. X
  733. Xreadldbrc()
  734. X{
  735. XFILE *fp;
  736. Xchar *s, *n, *getenv();
  737. Xchar buf[80];
  738. X
  739. Xif ( (s = getenv("HOME")) != NULL)    /* if we have a home */
  740. X    chdir(s);            /* go there */
  741. Xif ( (s = getenv("LDBRC")) == NULL)    /* LDBRC not set */
  742. X#ifdef VMS
  743. X    s = "ldb.rc";            /* use default file name */
  744. X#else
  745. X    s = ".ldbrc";            /* use default file name */
  746. X#endif
  747. Xif ( (fp = fopen(s,"r")) == NULL) {
  748. X    printf("'%s' does not exist.  Do you want to create it?",s);
  749. X    if ( (fgets(buf,sizeof(buf),stdin) == NULL)
  750. X         || ( (*buf != 'y') && (*buf != 'Y') ) )
  751. X        fatal("ldb aborted.");
  752. X    if ( (fp = fopen(s,"w")) == NULL) {
  753. X        printf("Sorry, could not create %s.\n",s);
  754. X        ldbexit(STAT_ABORT);
  755. X        }
  756. X    printf("Please enter your personal name: ");
  757. X    if (fgets(buf,sizeof(buf),stdin) == NULL) {
  758. X        fclose(fp);
  759. X        unlink(s);
  760. X        printf("ldb aborted.\n");
  761. X        ldbexit(STAT_ABORT);
  762. X        }
  763. X    buf[strlen(buf)-1] = '\0';    /* clobber the newline char */
  764. X    fprintf(fp,"myname=%s\n",buf);
  765. X    printf("Please enter your e-mail address: ");
  766. X    if (fgets(buf,sizeof(buf),stdin) == NULL) {
  767. X        fclose(fp);
  768. X        unlink(s);
  769. X        printf("ldb aborted.\n");
  770. X        ldbexit(STAT_ABORT);
  771. X        }
  772. X    buf[strlen(buf)-1] = '\0';    /* clobber the newline char */
  773. X    fprintf(fp,"myaddr=%s\n",buf);
  774. X    fprintf(fp,"mailfile=ldb.rcv\n");
  775. X#ifdef VMS
  776. X    fprintf(fp,"sendcmd=mail/subject=\"$s\" $f IN%%\"\"\"$a\"\"\"\n");
  777. X    fprintf(fp,"gamefile=ldb.data\n");
  778. X    fprintf(fp,"backupfile=ldb.olddata\n");
  779. X    fprintf(fp,"peoplefile=ldb.people\n");
  780. X    fprintf(fp,"lockfile=ldb.lock\n");
  781. X    fprintf(fp,"supercmd=\n");
  782. X#else
  783. X    fprintf(fp,"sendcmd=mail -s '$s' $a < $f\n");
  784. X    fprintf(fp,"gamefile=.ldbdata\n");
  785. X    fprintf(fp,"backupfile=.oldldbdata\n");
  786. X    fprintf(fp,"peoplefile=.ldbpeople\n");
  787. X    fprintf(fp,"lockfile=.ldb_lock\n");
  788. X    fprintf(fp,"supercmd=sh\n");
  789. X#endif
  790. X    fprintf(fp,"delmail=no\n");
  791. X    fprintf(fp,"tempfile=ldb.tmp\n");
  792. X    fprintf(fp,"colors=rw\n");
  793. X    fprintf(fp,"direction=up\n");
  794. X    fprintf(fp,"initialboard=current\n");
  795. X    fprintf(fp,"autoroll=yes\n");
  796. X    fprintf(fp,"automove=no\n");
  797. X    fprintf(fp,"autodouble=0\n");
  798. X    fprintf(fp,"superkey=0\n");
  799. X    fprintf(fp,"checkpoint=yes\n");
  800. X    fprintf(fp,"timeout=7\n");
  801. X    fprintf(fp,"keepold=7\n");
  802. X    fprintf(fp,"debug=0\n");
  803. X    fclose(fp);
  804. X    printf("\nYour %s file was created.  You may want to read the\n",s);
  805. X    printf("manual for additional options available in this file.\n\n");
  806. X#ifdef VMS
  807. X    printf("VMS users should edit the sendcmd command in %s to use\n",s);
  808. X    printf("the appropriate mailer.  The IN%% mailer is assumed.\n\n");
  809. X#endif
  810. X    if ( (fp = fopen(s,"r")) == NULL) {
  811. X        printf("I can't re-open your %s file!\n",s);
  812. X        ldbexit(STAT_ABORT);
  813. X        }
  814. X    }
  815. Xrc.myname = NULL;        /* these fields are required */
  816. Xrc.myaddr = NULL;
  817. X#ifdef VMS
  818. Xrc.gfile = "ldb.data";    /* default game storage file */
  819. Xrc.pfile = "ldb.people";    /* default opponent file */
  820. Xrc.gbackup = "ldb.olddata";    /* game backup file */
  821. Xrc.sendcmd = "mail/subject=\"$s\" $f IN%%\"\"\"$a\"\"\""; /* dflt mail cmd */
  822. Xrc.lockfile = "ldb.lock";    /* default lock file */
  823. Xrc.supercmd = "";        /* command to run when we have company */
  824. X#else
  825. Xrc.gfile = ".ldbdata";    /* default game storage file */
  826. Xrc.pfile = ".ldbpeople";    /* default opponent file */
  827. Xrc.gbackup = ".oldldbdata";    /* game backup file */
  828. Xrc.sendcmd = "mail -s '$s' $a < $f";    /* default mail command */
  829. Xrc.lockfile = ".ldb_lock";    /* default lock file */
  830. Xrc.supercmd = "sh";        /* command to run when we have company */
  831. X#endif
  832. Xrc.mfile = "ldb.rcv";    /* default file for received mail */
  833. Xrc.delmail = "no";            /* don't delete mail by default */
  834. Xrc.tempfile = "ldb.tmp";        /* default temp file */
  835. Xrc.defclrs = "rw";            /* "default" default colors */
  836. Xrc.defdir = "u";            /* and direction */
  837. Xrc.initboard = "c";            /* show current board by default */
  838. Xrc.autoroll = "y";            /* enable autoroll by default */
  839. Xrc.automove = "n";        /* disabled by default (it's really annoying */
  840. Xrc.autodouble = 0;        /* disable autodouble by default */
  841. Xrc.superkey = 0;        /* key to activate supercmd (dflt=disabled) */
  842. Xrc.chkpt = "y";            /* checkpoint is enabled by default */
  843. Xrc.acctime = 7;            /* access timeout in 7 days by default */
  844. Xrc.keepold = 7;            /* keep dead games 7 days by default */
  845. Xrc.debug = 0;            /* default to no debug */
  846. Xif ( (n = nvscan(fp,nv_rcfile,&rc)) != NULL) {
  847. X    fprintf(stderr,"Invalid line in .ldbrc: %s\n",n);
  848. X    ldbexit(STAT_ABORT);
  849. X    }
  850. Xfclose(fp);
  851. Xif (rc.myname == NULL) {
  852. X    fprintf(stderr,"ERROR: missing 'myname' line in %s\n",s);
  853. X    ldbexit(STAT_ABORT);
  854. X    }
  855. Xif (rc.myaddr == NULL) {
  856. X    fprintf(stderr,"ERROR: missing 'myaddr' line in %s\n",s);
  857. X    ldbexit(STAT_ABORT);
  858. X    }
  859. Xif ( (strlen(rc.defclrs) != 2) || (! isalpha(rc.defclrs[0])) ||
  860. X     (! isalpha(rc.defclrs[1])) || (rc.defclrs[0] == rc.defclrs[1]) ) {
  861. X    fprintf(stderr,"ERROR: invalid color string in %s: %s",s,rc.defclrs);
  862. X    ldbexit(STAT_ABORT);
  863. X    }
  864. Xif (strchr("ud",*rc.defdir) == NULL) {
  865. X    fprintf(stderr,"ERROR: direction must be 'up' or 'down' in %s\n",s);
  866. X    ldbexit(STAT_ABORT);
  867. X    }
  868. Xif (strchr("bac",*rc.initboard) == NULL) {
  869. X    fprintf(stderr,
  870. X     "ERROR: initialboard must be 'before', 'after', or 'current' in %s\n"
  871. X     ,s);
  872. X    ldbexit(STAT_ABORT);
  873. X    }
  874. Xif (strchr("yn",*rc.autoroll) == NULL) {
  875. X    fprintf(stderr,"ERROR: autoroll must be 'yes' or 'no' in %s\n",s);
  876. X    ldbexit(STAT_ABORT);
  877. X    }
  878. Xif (strchr("yn",*rc.automove) == NULL) {
  879. X    fprintf(stderr,"ERROR: automove must be 'yes' or 'no' in %s\n",s);
  880. X    ldbexit(STAT_ABORT);
  881. X    }
  882. Xif (strchr("yn",*rc.chkpt) == NULL) {
  883. X    fprintf(stderr,"ERROR: checkpoint must be 'yes' or 'no' in %s\n",s);
  884. X    ldbexit(STAT_ABORT);
  885. X    }
  886. X}
  887. X
  888. X
  889. X/*---------------------------------------------------------------------------
  890. X *    readgames -- read in games in progress
  891. X *
  892. X * This function reads the games file specified in .ldbrc and loads
  893. X * the games into the games list (ghead/gtail).
  894. X *---------------------------------------------------------------------------
  895. X */
  896. X
  897. Xreadgames()
  898. X{
  899. XFILE *fp;
  900. Xchar c, *s;
  901. Xstruct game *g;
  902. Xlong old;
  903. X
  904. Xreadpeople();
  905. Xif ( (fp = fopen(rc.gfile,"r")) == NULL)
  906. X    return;                /* no games */
  907. Xold = time( (long *) 0);
  908. Xif (rc.keepold > 0)        /* if keeping old games is enabled */
  909. X    old -= (rc.keepold * 86400);    /* how old is too old? */
  910. Xwhile ( (c = getc(fp)) != EOF) {
  911. X    ungetc(c,fp);    /* put char back */
  912. X    g = addgame();        /* insert a new game */
  913. X    g->opaddr = NULL;
  914. X    g->opname = NULL;
  915. X    g->myaddr = NULL;
  916. X    g->opver = 100;            /* default to oldest ldb version */
  917. X    g->mycmt = NULL;
  918. X    g->mycmt2 = NULL;
  919. X    g->opcmt = NULL;
  920. X    g->opcmt2 = NULL;
  921. X    g->dispmsg = NULL;
  922. X    g->hiused = 0;
  923. X    g->maxused = 0;
  924. X    g->starttime = 0L;
  925. X    clearmvs(g->opmvs);
  926. X    clearmvs(g->mvs);
  927. X    if ( (s = nvscan(fp,nv_gfile,g)) != NULL) {    /* read game */
  928. X        FeFinishSession();    /* close down front-end */
  929. X        TFinishSession();    /* close down transport */
  930. X        fprintf(stderr,"ERROR: invalid line in %s: %s\n", rc.gfile, s);
  931. X        ldbexit(STAT_ABORT);
  932. X        }
  933. X    if (g->gameid == NULL) {        /* empty game (prob. EOF) */
  934. X        deletegame(g);
  935. X        continue;
  936. X        }
  937. X    if (g->myaddr == NULL)        /* no myaddr line */
  938. X        g->myaddr = save(rc.myaddr);    /* dflt to ldbrc setting */
  939. X    if ( (g->ppl = findppl(g->opaddr,P_ADDR)) == NULL)
  940. X        newppl(g);
  941. X    if (g->opver > g->ppl->opver)        /* (upgrade hook) */
  942. X        g->ppl->opver = g->opver;    /* copy op's ldb version */
  943. X    if (
  944. X       (g->state == ST_GAMEOVER)    &&
  945. X       (g->flags & F_DISPLAYED)    &&
  946. X       (g->lastacc < old) )
  947. X        g->flags |= F_DELETE;    /* game is old, mark it for deletion */
  948. X    if (rc.debug & DB_RWGAMES)
  949. X        message("DB-readgames: read game %s\n",g->gameid);
  950. X    }
  951. Xfclose(fp);
  952. X}
  953. X
  954. X
  955. X/*----------------------------------------------------------------------
  956. X *    writegames -- save the game list to a file
  957. X *
  958. X * This function writes each game in the game list to the specified
  959. X * file.  Games with F_DELETE set in their flags are skipped, and
  960. X * are thus effectively deleted.  If the bkup arg is not NULL and
  961. X * is not an empty string, the old file is renamed to the string
  962. X * specified in bkup before the new file is created.
  963. X *----------------------------------------------------------------------
  964. X */
  965. X
  966. Xwritegames(file,bkup,pfile)
  967. Xchar *file, *bkup, *pfile;
  968. X{
  969. XFILE *fp;
  970. Xstruct game *g;
  971. X
  972. Xwritepeople(pfile);            /* save the people file */
  973. Xif ( (bkup != NULL) && (*bkup != '\0') ) {
  974. X    unlink(bkup);        /* prevent multiple versions on VMS */
  975. X    rename(file,bkup);    /* save old game file */
  976. X    }
  977. Xelse
  978. X    unlink(file);        /* prevent multiple versions on VMS */
  979. Xif ( (fp = fopen(file,"w")) == NULL) {
  980. X    message("ERROR: can't save games in %s!\n",file);
  981. X    return;            /* should do something to save games... */
  982. X    }
  983. Xfor (g = ghead; g != NULL; g = g->next) {
  984. X    if (g->flags & F_DELETE)
  985. X        continue;            /* game is over, delete it */
  986. X    if (g->state == ST_GAMEOVER)
  987. X        continue;            /* skip complete games 4 now */
  988. X    nvwrite(fp,nv_gfile,g);    /* write the struct */
  989. X    if (rc.debug & DB_RWGAMES)
  990. X        message("DB-writegames: write game %s\n",g->gameid);
  991. X    }
  992. Xfor (g = ghead; g != NULL; g = g->next) {
  993. X    if (g->flags & F_DELETE)
  994. X        continue;        /* game is over, delete it */
  995. X    if (g->state != ST_GAMEOVER)
  996. X        continue;        /* put complete games @ end */
  997. X    nvwrite(fp,nv_gfile,g);    /* write the struct */
  998. X    if (rc.debug & DB_RWGAMES)
  999. X        message("DB-writegames: write game %s\n",g->gameid);
  1000. X    }
  1001. Xfclose(fp);
  1002. X}
  1003. X
  1004. X
  1005. X/*---------------------------------------------------------------------------
  1006. X *    readpeople -- read in the people file
  1007. X *
  1008. X * This function reads the "people" file, which is the list of all of the
  1009. X * opponents we have ever played.  The people file serves three purposes:
  1010. X *    1. It keeps a record of games/gammons/backgammons/matches won/lost
  1011. X *       to each person.
  1012. X *    2. It stores the mail address so you don't have to remember,
  1013. X *       and allows you to assign an alias to each person.  This
  1014. X *       alias can be used in the -start argument instead of the
  1015. X *       mail address.
  1016. X *    3. It stores the "fence" time, which is the time that the
  1017. X *       (newest game played with that person that has finished)
  1018. X *       was started.  (Read it again, it makes sense).
  1019. X *       This information allows us to discard START packets that
  1020. X *       have remained in our mail (that we should have deleted,
  1021. X *       but didn't) by discarding START packets that are not newer
  1022. X *       than the fence time.
  1023. X *---------------------------------------------------------------------------
  1024. X */
  1025. X
  1026. Xreadpeople()
  1027. X{
  1028. XFILE *fp;
  1029. Xchar c, *s;
  1030. Xstruct people *p;
  1031. X
  1032. Xif ( (fp = fopen(rc.pfile,"r")) == NULL)
  1033. X    return;                /* no people file */
  1034. Xwhile ( (c = getc(fp)) != EOF) {
  1035. X    ungetc(c,fp);    /* put char back */
  1036. X    p = addppl();        /* insert a new record */
  1037. X    p->name = NULL;        
  1038. X    p->addr = NULL;
  1039. X    p->alias = NULL;
  1040. X    p->myaddr = NULL;
  1041. X    p->equiv = NULL;
  1042. X    p->fence = 0L;
  1043. X    if ( (s = nvscan(fp,nv_pfile,p)) != NULL) {    /* read record */
  1044. X        FeFinishSession();    /* close down front-end */
  1045. X        TFinishSession();    /* close down transport */
  1046. X        fprintf(stderr,"ERROR: invalid line in %s: %s\n", rc.pfile, s);
  1047. X        ldbexit(STAT_ABORT);
  1048. X        }
  1049. X    if (p->equiv != NULL)        /* an equiv record */
  1050. X        continue;        /* no other fields */
  1051. X    if (p->myaddr == NULL)            /* if no myaddr line */
  1052. X        p->myaddr = save(rc.myaddr);    /* dflt to ldbrc setting */
  1053. X    if (rc.debug & DB_RWGAMES)
  1054. X        message("DB-readpeople: read '%s'\n",p->name);
  1055. X    }
  1056. Xfclose(fp);
  1057. X}
  1058. X
  1059. X
  1060. X/*----------------------------------------------------------------------
  1061. X *    writepeople -- save the people list to a file
  1062. X *
  1063. X * This function writes the people list to rc.pfile.  It attempts to
  1064. X * replace temporary people structures with permanent ones before
  1065. X * saving the people list.
  1066. X *----------------------------------------------------------------------
  1067. X */
  1068. X
  1069. Xwritepeople(file)
  1070. Xchar *file;
  1071. X{
  1072. XFILE *fp;
  1073. Xstruct people *p;
  1074. Xstruct game *g;
  1075. X
  1076. Xunlink(file);        /* prevent multiple versions on VMS */
  1077. Xif ( (fp = fopen(file,"w")) == NULL) {
  1078. X    message("ERROR: can't save people in %s!\n",file);
  1079. X    return;            /* should do something to save people list */
  1080. X    }
  1081. X
  1082. Xfor (g = ghead; g != NULL; g = g->next)    /* for all games */
  1083. X    if (
  1084. X        (g->ppl->score[0] < 0)         &&    /* temp record */
  1085. X        (g->opname != NULL)             &&    /* we have real */
  1086. X        (strcmp(g->opname,PPL_ANON) != 0) )        /* personal info now?*/
  1087. X        newppl(g);                /* make permanent */
  1088. X
  1089. Xfor (p = phead; p != NULL; p = p->next) {
  1090. X    if (p->equiv == NULL) {
  1091. X        nvwrite(fp,nv_pfile,p);    /* write the struct */
  1092. X        if (rc.debug & DB_RWGAMES)
  1093. X            message("DB-writepeople: write '%s'\n",p->name);
  1094. X        }
  1095. X    else if (strcmp(p->equiv,p->addr) != 0)    /* if doesn't refer to self */
  1096. X        nvwrite(fp,nv_pequiv,p);    /* write an equiv record */
  1097. X    }
  1098. Xfclose(fp);
  1099. X}
  1100. X
  1101. X
  1102. X
  1103. X/*----------------------------------------------------------------------
  1104. X *    boardstr -- generate an ascii representation of a board
  1105. X *
  1106. X * This function produces a visible representation of a board.  Each point
  1107. X * on the board takes two characters; the quantity is offset by 65,
  1108. X * putting it in the range [A-P], and the color is unchanged since it
  1109. X * is already a printable character.  This results in a string of
  1110. X * 28 character pairs, one for each point on the board.  These are
  1111. X * in the order:
  1112. X *    0:    BAR point for upbound player
  1113. X *    1-24:    board points
  1114. X *    25:    BAR point for downbound player
  1115. X *    26:    OFF point for upbound player
  1116. X *    27:    OFF point for downbound player
  1117. X *----------------------------------------------------------------------
  1118. X */
  1119. X
  1120. Xchar *boardstr(b)
  1121. Xboard b;
  1122. X{
  1123. Xstatic char buf[BOARDSIZE*2+1];
  1124. Xchar *s, c;
  1125. Xint i;
  1126. X
  1127. Xs = buf;
  1128. Xfor (i = 0; i < BOARDSIZE; i++) {
  1129. X    *s++ = b[i].qty + 'A';        /* offset qty into u.c. letters */
  1130. X    c = b[i].color;
  1131. X    if (! isalpha(c))
  1132. X        c = '-';        /* use printing chars */
  1133. X    *s++ = c;
  1134. X    }
  1135. X*s = '\0';
  1136. Xreturn(buf);
  1137. X}
  1138. X
  1139. X
  1140. X
  1141. X/*----------------------------------------------------------------------
  1142. X *    nvscan -- read name/value pairs from a file
  1143. X *
  1144. X * This function provides a generalized method for reading name/value
  1145. X * pairs.  The names and value types are specified in an array of
  1146. X * struct namevalue's, along with an offset which is used to store
  1147. X * the value.  The offset is added to a base pointer, passed as the
  1148. X * "st" argument, to form a pointer, which is then converted to the
  1149. X * type indicated by the "type" field of the namevalue table using
  1150. X * the "nvtypes" union, and used to store the value.  The legal
  1151. X * value types are defined in ldb.h as FT_*.  Name/value pairs are
  1152. X * expected to be in the form "name=value\n", with no spaces before
  1153. X * or after name, and with any spaces after the = or before the
  1154. X * newline being part of the value string.  Comments are indicated by
  1155. X * a # in column 1, which comments to the end of the line.
  1156. X *----------------------------------------------------------------------
  1157. X */
  1158. X
  1159. Xchar *nvscan(fp,t,st)
  1160. XFILE *fp;        /* file to scan */
  1161. Xstruct namevalue *t;    /* table of name/value pairs */
  1162. Xchar *st;        /* really a pointer to a structure */
  1163. X{
  1164. Xstatic char buf[128];
  1165. Xchar *s, **p;
  1166. Xint i, j;
  1167. Xunion nvtypes u;
  1168. Xlong atol();
  1169. X
  1170. Xwhile (fgets(buf,sizeof(buf),fp) != NULL) {
  1171. X    if (*buf == '#')            /* comment character */
  1172. X        continue;
  1173. X    buf[strlen(buf)-1] = '\0';        /* clobber the newline */
  1174. X    if ( (s = strchr(buf,'=')) == NULL)
  1175. X        return(buf);            /* bad line, return it */
  1176. X    *s++ = '\0';
  1177. X    for (i = 0; t[i].name != NULL; i++)
  1178. X        if (strcmp(t[i].name,buf) == 0)
  1179. X            break;
  1180. X    if (t[i].name == NULL)        /* got a name we don't recognize */
  1181. X        continue;        /* ignore it */
  1182. X    u.nvchar = st + t[i].offset;    /* put pointer into union */
  1183. X    switch (t[i].type) {
  1184. X    case FT_CHAR:            /* just store a single char */
  1185. X        *u.nvchar = atoi(s);    /* chars stored as ints in the file */
  1186. X        break;
  1187. X    case FT_INT:            /* store an int */
  1188. X        *u.nvint = atoi(s);
  1189. X        break;
  1190. X    case FT_STRING:            /* store a copy of a string */
  1191. X        *u.nvstring = save(s);
  1192. X        break;
  1193. X    case FT_MOVE:            /* store a struct mv */
  1194. X        str2mv(s,u.nvmove);
  1195. X        break;
  1196. X    case FT_BOARD:            /* store an entire board */
  1197. X        for (j = 0; j < BOARDSIZE; j++) {
  1198. X            u.nvboard[j].qty = *s++ - 'A';
  1199. X            u.nvboard[j].color = *s++;
  1200. X            }
  1201. X        break;
  1202. X    case FT_STRLKUP:        /* look up string & store index */
  1203. X        p = (char **) t[i].dflt;    /* unions are such a pain */
  1204. X        if (p == NULL)        /* choke... */
  1205. X            fatal("ERROR: NULL string table in nvscan.");
  1206. X        for (j = 0; *p; j++, p++)
  1207. X            if (strcmp(s,*p) == 0)
  1208. X                break;
  1209. X        if (*p == NULL) {
  1210. X            FeFinishSession();    /* close down front-end */
  1211. X            TFinishSession();    /* close down transport */
  1212. X            fprintf(stderr,"ERROR: unknown string: %s\n",s);
  1213. X            ldbexit(STAT_ABORT);    /* shouldn't do this... */
  1214. X            }
  1215. X        *u.nvint = j;    /* store integer opcode */
  1216. X        break;
  1217. X    case FT_TIME:            /* read in a timestamp */
  1218. X        *u.nvtime = atol(s);
  1219. X        break;
  1220. X    case FT_INTARRAY:        /* array of integers */
  1221. X        for (j = 0; j < t[i].dflt; j++) {
  1222. X            while (isspace(*s))
  1223. X                s++;
  1224. X            (u.nvint)[j] = atoi(s);        /* store an int */
  1225. X            while (isdigit(*s))    /* skip to end of int */
  1226. X                s++;
  1227. X            }
  1228. X        break;
  1229. X    case FT_END:            /* we hit the end marker */
  1230. X        return(NULL);        /* return success */
  1231. X    default:            /* we have a bad nv table */
  1232. X        *--s = '=';        /* restore the buffer */
  1233. X        return(buf);        /* return bad line */
  1234. X        }
  1235. X    }
  1236. Xreturn(NULL);
  1237. X}
  1238. X
  1239. X
  1240. X
  1241. X/*----------------------------------------------------------------------
  1242. X *    nvwrite -- write name/value pairs into a file
  1243. X *
  1244. X * This function writes name/value pairs to a file in the same format
  1245. X * used by nvscan.  Nvwrite is merely the inverse of nvscan, taking values
  1246. X * out of the structure in the same manner nvscan used to store them
  1247. X * there, and generating "name=value" lines.  One line is generated for
  1248. X * each element in the namevalue table, except that elements of type
  1249. X * FT_MOVE whose "roll" field is <= 0 are skipped, as are elements of
  1250. X * type FT_STRING that are equal to NULL.
  1251. X *----------------------------------------------------------------------
  1252. X */
  1253. X
  1254. Xnvwrite(fp,t,st)
  1255. XFILE *fp;            /* file to write to */
  1256. Xstruct namevalue *t;        /* table of name/value pairs */
  1257. Xchar *st;            /* really a pointer to a structure */
  1258. X{
  1259. Xstruct namevalue *n;
  1260. Xstatic char buf[128];
  1261. Xint nstr;
  1262. Xunion nvtypes u;
  1263. Xchar c, **stbl;
  1264. Xint j;
  1265. X
  1266. Xfor (n = t; n->name != NULL; n++) {
  1267. X    u.nvchar = st + n->offset;
  1268. X    switch (n->type) {
  1269. X    case FT_CHAR:            /* just store a single char */
  1270. X        fprintf(fp,"%s=%d\n",n->name,*u.nvchar);
  1271. X        break;
  1272. X    case FT_INT:            /* store an int */
  1273. X        fprintf(fp,"%s=%d\n",n->name,*u.nvint);
  1274. X        break;
  1275. X    case FT_STRING:            /* store a copy of a string */
  1276. X        if (*u.nvstring != NULL)/* skip NULL strings */
  1277. X            fprintf(fp,"%s=%s\n",n->name,*u.nvstring);
  1278. X        break;
  1279. X    case FT_MOVE:            /* store a struct mv */
  1280. X        if (u.nvmove->roll > 0) {
  1281. X            mv2str(u.nvmove,buf);
  1282. X            fprintf(fp,"%s=%s\n",n->name,buf);
  1283. X            }
  1284. X        break;
  1285. X    case FT_BOARD:            /* store an entire board */
  1286. X        fprintf(fp,"%s=%s\n",n->name,boardstr(u.nvboard));
  1287. X        break;
  1288. X    case FT_STRLKUP:        /* look up string & store index */
  1289. X        stbl = (char **) n->dflt;    /* hope char** fits in int */
  1290. X        if (stbl == NULL)
  1291. X            fatal("ERROR: NULL string table in nvwrite.");
  1292. X        for (nstr = 0; stbl[nstr]; nstr++); /* # strings */
  1293. X        if ( (*u.nvint < 0) || (*u.nvint >= nstr) ) {
  1294. X            FeFinishSession();    /* close down front-end */
  1295. X            TFinishSession();    /* close down transport */
  1296. X            fprintf(stderr,"ERROR: invalid index: %s=%d\n",
  1297. X                n->name,*u.nvint);
  1298. X            ldbexit(STAT_ABORT);    /* shouldn't do this... */
  1299. X            }
  1300. X        fprintf(fp,"%s=%s\n",n->name,stbl[*u.nvint]);
  1301. X        break;
  1302. X    case FT_TIME:            /* generate a timestamp */
  1303. X        fprintf(fp,"%s=%lu\n",n->name,*u.nvtime);
  1304. X        break;
  1305. X    case FT_INTARRAY:        /* generate an integer array */
  1306. X        fprintf(fp,"%s",n->name);
  1307. X        c = '=';
  1308. X        for (j = 0; j < n->dflt; j++) {
  1309. X            fprintf(fp,"%c%d",c,(u.nvint)[j]);
  1310. X            c = ' ';
  1311. X            }
  1312. X        fprintf(fp,"\n");
  1313. X        break;
  1314. X    case FT_END:            /* we hit the end marker */
  1315. X        fprintf(fp,"%s=end\n",n->name);
  1316. X        break;
  1317. X    default:            /* we have a bad nv table */
  1318. X        FeFinishSession();    /* close down front-end */
  1319. X        TFinishSession();    /* close down transport */
  1320. X        fprintf(stderr,"ERROR: bad namevalue type: %s (%d)\n",
  1321. X            n->name,n->type);
  1322. X        ldbexit(STAT_ABORT);        /* should have saved games? */
  1323. X        }
  1324. X    }
  1325. X}
  1326. X
  1327. X
  1328. X/*---------------------------------------------------------------------------
  1329. X *    check_timeout -- see if a game has been accessed recently
  1330. X *
  1331. X * This function checks the access time of a game.  If it is less than
  1332. X * rc.acctime (default 7 days), and the game is waiting for the opponent,
  1333. X * an automatic timeout is performed.  If rc.acctime is <= 0,
  1334. X * automatic timeouts are disabled.
  1335. X *---------------------------------------------------------------------------
  1336. X */
  1337. X
  1338. Xcheck_timeout(g)
  1339. Xstruct game *g;
  1340. X{
  1341. Xlong old;
  1342. X
  1343. Xif (rc.acctime <= 0)
  1344. X    return;
  1345. Xold = time((long *) 0) - ((long) (rc.acctime*86400)); /* look for idle games */
  1346. Xif ( (g->lastacc < old) && (g->state < OPSTATES) ) {
  1347. X    message("Access timeout (%d days) with %s -- resending...\n",
  1348. X        rc.acctime,g->opaddr);
  1349. X    g->lastacc = time( (long *) 0);
  1350. X    resendpkt(g);    /* auto resend */
  1351. X    }
  1352. X}
  1353. END_OF_FILE
  1354. if test 21975 -ne `wc -c <'save.c'`; then
  1355.     echo shar: \"'save.c'\" unpacked with wrong size!
  1356. fi
  1357. # end of 'save.c'
  1358. fi
  1359. echo shar: End of archive 5 \(of 12\).
  1360. cp /dev/null ark5isdone
  1361. MISSING=""
  1362. for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
  1363.     if test ! -f ark${I}isdone ; then
  1364.     MISSING="${MISSING} ${I}"
  1365.     fi
  1366. done
  1367. if test "${MISSING}" = "" ; then
  1368.     echo You have unpacked all 12 archives.
  1369.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1370. else
  1371.     echo You still need to unpack the following archives:
  1372.     echo "        " ${MISSING}
  1373. fi
  1374. ##  End of shell archive.
  1375. exit 0
  1376.  
  1377. exit 0 # Just in case...
  1378.