home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume28 / ldb / part03 < prev    next >
Encoding:
Text File  |  1992-03-14  |  43.1 KB  |  1,370 lines

  1. Newsgroups: comp.sources.misc
  2. From: ROSS@emf780.den.mmc.com ("Perry R. Ross")
  3. Subject:  v28i095:  ldb - Play backgammon by e-mail, Part03/05
  4. Message-ID: <1992Mar13.035526.11801@sparky.imd.sterling.com>
  5. X-Md4-Signature: 951004bed299e37d5256129d7b91b9a7
  6. Date: Fri, 13 Mar 1992 03:55:26 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: ROSS@emf780.den.mmc.com (Perry R. Ross)
  10. Posting-number: Volume 28, Issue 95
  11. Archive-name: ldb/part03
  12. Environment: UNIX, C, VMS, VAXC, CURSES, 32BIT
  13.  
  14. #! /bin/sh
  15. # This is a shell archive.  Remove anything before this line, then unpack
  16. # it by saving it into a file and typing "sh file".  To overwrite existing
  17. # files, type "sh file -c".  You can also feed this as standard input via
  18. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  19. # will see the following message at the end:
  20. #        "End of archive 3 (of 5)."
  21. # Contents:  process.c rcvop.c save.c
  22. # Wrapped by ross@emf780 on Tue Mar 10 09:24:21 1992
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. if test -f 'process.c' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'process.c'\"
  26. else
  27. echo shar: Extracting \"'process.c'\" \(13996 characters\)
  28. sed "s/^X//" >'process.c' <<'END_OF_FILE'
  29. X/*    process.c        8/8/91
  30. X *
  31. X * Copyright 1991  Perry R. Ross
  32. X *
  33. X * Permission to use, copy, modify, and distribute this software and its
  34. X * documentation without fee is hereby granted, subject to the restrictions
  35. X * detailed in the README file, which is included here by reference.
  36. X * Any other use requires written permission from the author.  This software
  37. X * is distributed "as is" without any warranty, including any implied
  38. X * warranties of merchantability or fitness for a particular purpose.
  39. X * The author shall not be liable for any damages resulting from the
  40. X * use of this software.  By using this software, the user agrees
  41. X * to these terms.
  42. X */
  43. X
  44. X#include "ldb.h"
  45. X
  46. X/*===========================================================================
  47. X * This file contains the code to process user input.
  48. X *===========================================================================
  49. X */
  50. X
  51. X
  52. X
  53. X/*----------------------------------------------------------------------
  54. X *    process -- process a game while it needs local input
  55. X *
  56. X * This function calls the appropriate handler, according to what
  57. X * state the game is in, to prompt the user for input.  If a game
  58. X * does not need local input, it is skipped.  Process() returns 1
  59. X * if the game still needs local input, in which case the main program
  60. X * will re-call process with the same game.  When the game no longer
  61. X * needs local input, process will return 0, telling the main program
  62. X * to proceed to the next game.
  63. X *----------------------------------------------------------------------
  64. X */
  65. X
  66. Xprocess(g)
  67. Xstruct game *g;
  68. X{
  69. Xint i;
  70. X
  71. Xif (g->state < OPSTATES)    /* this game is waiting for the opponent */
  72. X    return(0);        /* skip it for now */
  73. XFeDrawGame(g);            /* draw the game */
  74. Xswitch (g->state) {
  75. Xcase ST_MYTURN:            /* my turn, I haven't rolled yet */
  76. X    if ( ((g->flags & F_IDOUBLED) == 0) || (*rc.autoroll == 'n') ) {
  77. X        i = myturn(g);    /* I didn't double last */
  78. X        break;
  79. X        }
  80. X    rolldice(g);        /* if I doubled last, go ahead and roll */
  81. X    g->state = ST_MYMOVE;    /* skip this state completely */
  82. X    if (*rc.chkpt == 'y') {    /* checkpoint games */
  83. X        writegames(rc.gfile,rc.gbackup);
  84. X        rc.gbackup = NULL;    /* only backup old file once */
  85. X        }
  86. X    for (i = 0; i < 4; i++)        /* draw my new roll */
  87. X        FeDrawMove(g,1,i);
  88. X    /**** fall through ****/
  89. Xcase ST_MYMOVE:            /* my turn, I have rolled */
  90. X    i = mymove(g);        /* user has rolled, must move */
  91. X    break;
  92. Xcase ST_MYACCEPT:
  93. X    i = myacpt(g);    /* user must accept or decline double */
  94. X    break;
  95. Xcase ST_GAMEOVER:
  96. X    i = gameover(g);        /* tell him the game is over */
  97. X    break;
  98. X    }
  99. Xif ( (i == 0) && (*rc.chkpt == 'y') ) {
  100. X    writegames(rc.gfile,rc.gbackup);
  101. X    rc.gbackup = NULL;    /* only backup old file once */
  102. X    }
  103. Xreturn(i);
  104. X}
  105. X
  106. X
  107. X/*----------------------------------------------------------------------
  108. X *    myturn -- allow user to roll or double
  109. X *
  110. X * This function is called to allow the user to choose between
  111. X * rolling the dice and doubling.  It also allows the user to cycle
  112. X * through the three board displays, to concede, and to go to the next game.
  113. X *----------------------------------------------------------------------
  114. X */
  115. X
  116. Xmyturn(g)
  117. Xstruct game *g;
  118. X{
  119. Xchar c;
  120. Xstatic char *m[]={"Roll","Double","Board","Next Game","Concede","Quit",NULL};
  121. X
  122. XFeDrawMenu(m);            /* display the menu */
  123. Xwhile (1) {
  124. X    c = FeMenu(m,0,0,"\n\r ");    /* get a menu choice */
  125. X    switch (c) {
  126. X    case 'R':            /* roll them dice */
  127. X    case '\n':
  128. X    case '\r':
  129. X    case ' ':
  130. X        g->curbd = BD_CUR;    /* bring up current board */
  131. X        rolldice(g);
  132. X        g->state = ST_MYMOVE;    /* I just entered a new state */
  133. X        if (*rc.chkpt == 'y') {    /* checkpoint games */
  134. X            writegames(rc.gfile,rc.gbackup);
  135. X            rc.gbackup = NULL;    /* only backup old file once */
  136. X            }
  137. X        return(1);        /* make sure process gets re-called */
  138. X    case 'D':            /* I want to double */
  139. X        if (g->flags & F_IDOUBLED) {    /* I doubled last */
  140. X            FeMessage("You doubled last.");
  141. X            break;        /* so I can't double now */
  142. X            }
  143. X        g->state = ST_OPACCEPT;    /* we are waiting for accept/decline */
  144. X        FeGetComment(g);    /* get message */
  145. X        sendpkt(g,OFRDBL);    /* send the double packet */
  146. X        return(0);        /* this game is done for now */
  147. X    case 'C':            /* I'm wimping out */
  148. X        g->state = ST_GAMEOVER;    /* this game is over */
  149. X        g->term = T_ICONCEDE;    /* this is why */
  150. X        FeGetComment(g);    /* get message */
  151. X        sendpkt(g,CONCEDE);    /* send the packet */
  152. X        return(1);        /* display the gameover screen */
  153. X    case 'B':            /* display different board */
  154. X        if (g->curbd++ >= BD_CUR)    /* go to next board */
  155. X            g->curbd = BD_BEFOP;    /* wrap around */
  156. X        return(1);        /* redraw & call us again */
  157. X    case 'N':            /* I don't want to decide right now */
  158. X        return(0);
  159. X    case 'Q':            /* I want to quit ldb */
  160. X        return(-1);
  161. X        }
  162. X    }
  163. X}
  164. X
  165. X
  166. X/*----------------------------------------------------------------------
  167. X *    mymove -- allow user to move
  168. X *
  169. X * This function is called to allow the user to use his roll.
  170. X * It also allows the user to cycle through the three board displays,
  171. X * to concede, and to go to the next game.
  172. X * Since the user has already rolled, doubling is not allowed here.
  173. X *----------------------------------------------------------------------
  174. X */
  175. X
  176. Xmymove(g)
  177. Xstruct game *g;
  178. X{
  179. Xchar c;
  180. Xint i, n;
  181. Xstatic char used[] = "That move is already used -- use Reset to start over";
  182. Xstruct mv tmp;
  183. Xstatic char *m[] = {"Reset","Send","Board","Next Game","Concede",
  184. X            "Point","Off","Quit",NULL};
  185. Xint lastpt = 99;        /* point last move started from */
  186. Xint lastd = 99;            /* point last move ended at */
  187. X
  188. XFeDrawMenu(m);
  189. Xwhile (1) {
  190. X    c = FeMenu(m,g->mvs[0].roll,g->mvs[1].roll,"\n\r ");
  191. X    switch (c) {
  192. X    case 'S':            /* send moves */
  193. X        if (checkused(g))    /* didn't use all our moves */
  194. X            break;
  195. X        FeGetComment(g);    /* see if we're sending a comment */
  196. X        sendpkt(g,MOVE);        /* send our move */
  197. X        if (g->board[OFFPT(g->mydir)].qty == 15) {    /* I win */
  198. X            g->state = ST_GAMEOVER;
  199. X            g->term = T_IWIN;    /* now return 1 so that */
  200. X            return(1);        /* gameover() gets called */
  201. X            }
  202. X        else {
  203. X            g->state = ST_OPTURN;
  204. X            return(0);
  205. X            }
  206. X    case 'R':            /* reset -- erase moves & start over */
  207. X        if (g->curbd != BD_CUR)    {    /* if we are not looking at */
  208. X            g->curbd = BD_CUR;    /* current board, switch */
  209. X            FeLabelBoard(bdlabels[BD_CUR]);
  210. X            }
  211. X        for (i = 0; i < 4; i++) {
  212. X            g->mvs[i].pt = -1;
  213. X            FeDrawMove(g,1,i);
  214. X            }
  215. X        copyboard(g->mybd,g->board);
  216. X        FeDrawBoard(g->board,NULL,g->mydir,0,g->flags & F_INVERT);
  217. X        break;
  218. X    case 'B':                /* display different board */
  219. X        if (g->curbd++ >= BD_CUR)    /* go to next board */
  220. X            g->curbd = BD_BEFOP;    /* wrap around */
  221. X        return(1);        /* redraw & call us again */
  222. X    case 'C':            /* I'm wimping out */
  223. X        g->state = ST_GAMEOVER;    /* this game is over */
  224. X        g->term = T_ICONCEDE;    /* this is why */
  225. X        FeGetComment(g);    /* get message */
  226. X        sendpkt(g,CONCEDE);    /* send the packet */
  227. X        return(1);        /* display the gameover screen */
  228. X    case 'N':            /* I don't want to decide right now */
  229. X        return(0);
  230. X    case 'Q':            /* I want to quit ldb */
  231. X        return(-1);
  232. X    case ' ':            /* continue last move */
  233. X        if (g->curbd != BD_CUR) {
  234. X            g->curbd = BD_CUR;
  235. X            FeDrawBoard(g->board,NULL,g->mydir,0,
  236. X                g->flags & F_INVERT);
  237. X            FeLabelBoard(bdlabels[BD_CUR]);
  238. X            }
  239. X        n = (g->mvs[0].roll == g->mvs[1].roll) ? 4 : 2;
  240. X        for (i = 0; i < n; i++)        /* search for an unused move */
  241. X            if (g->mvs[i].pt < 0)
  242. X                break;
  243. X        if (i >= n) {
  244. X            FeMessage("You don't have any moves left.");
  245. X            break;
  246. X            }
  247. X        if ( (lastd < 1) || (lastd > 24) ) {
  248. X            FeMessage(
  249. X               "No move to continue -- please select a roll.");
  250. X            break;
  251. X            }
  252. X        g->mvs[i].pt = lastd;
  253. X        n = apply(g,1,i,A_REDRAW,&lastd);
  254. X        if (n < 0) {    /* move rejected */
  255. X            g->mvs[i].pt = -1;
  256. X            FeMessage(rejmsg[-n]);
  257. X            lastd = 99;
  258. X            }
  259. X        else
  260. X            lastpt = g->mvs[i].pt;
  261. X        FeDrawMove(g,1,i);
  262. X        break;
  263. X    case '\n':            /* repeat last move */
  264. X    case '\r':
  265. X        if (g->curbd != BD_CUR) {
  266. X            g->curbd = BD_CUR;
  267. X            FeDrawBoard(g->board,NULL,g->mydir,0,
  268. X                g->flags & F_INVERT);
  269. X            FeLabelBoard(bdlabels[BD_CUR]);
  270. X            }
  271. X        n = (g->mvs[0].roll == g->mvs[1].roll) ? 4 : 2;
  272. X        for (i = 0; i < n; i++)        /* search for an unused move */
  273. X            if (g->mvs[i].pt < 0)
  274. X                break;
  275. X        if (i >= n) {
  276. X            FeMessage("You don't have any moves left.");
  277. X            break;
  278. X            }
  279. X        if ( (lastpt < 0) || (lastpt > 25) ) {
  280. X            FeMessage("No move to repeat -- please select a roll.");
  281. X            break;
  282. X            }
  283. X        g->mvs[i].pt = lastpt;
  284. X        n = apply(g,1,i,A_REDRAW,&lastd);
  285. X        if (n < 0) {    /* move rejected */
  286. X            g->mvs[i].pt = -1;
  287. X            FeMessage(rejmsg[-n]);
  288. X            lastpt = 99;
  289. X            }
  290. X        FeDrawMove(g,1,i);
  291. X        break;
  292. X    case 'P':                /* make point from last move */
  293. X        if (g->curbd != BD_CUR) {
  294. X            g->curbd = BD_CUR;
  295. X            FeDrawBoard(g->board,NULL,g->mydir,0,
  296. X                g->flags & F_INVERT);
  297. X            FeLabelBoard(bdlabels[BD_CUR]);
  298. X            }
  299. X        n = (g->mvs[0].roll == g->mvs[1].roll) ? 4 : 2;
  300. X        for (i = 0; i < n; i++)        /* search for an unused move */
  301. X            if (g->mvs[i].pt < 0)
  302. X                break;
  303. X        if (i >= n) {
  304. X            FeMessage("You don't have any moves left.");
  305. X            break;
  306. X            }
  307. X        if ( (lastpt < 0) || (lastpt > 25) ) {
  308. X            FeMessage("No point to make -- please select a roll.");
  309. X            break;
  310. X            }
  311. X        g->mvs[i].pt = lastd - g->mydir*g->mvs[i].roll;
  312. X        n = apply(g,1,i,A_REDRAW,&lastd);
  313. X        if (n < 0) {    /* move rejected */
  314. X            g->mvs[i].pt = -1;
  315. X            FeMessage(rejmsg[-n]);
  316. X            }
  317. X        FeDrawMove(g,1,i);
  318. X        break;
  319. X    case 'O':                /* bear off with next roll */
  320. X        if (g->curbd != BD_CUR) {
  321. X            g->curbd = BD_CUR;
  322. X            FeDrawBoard(g->board,NULL,g->mydir,0,
  323. X                g->flags & F_INVERT);
  324. X            FeLabelBoard(bdlabels[BD_CUR]);
  325. X            }
  326. X        n = (g->mvs[0].roll == g->mvs[1].roll) ? 4 : 2;
  327. X        for (i = 0; i < n; i++)        /* search for an unused move */
  328. X            if (g->mvs[i].pt < 0)
  329. X                break;
  330. X        if (i >= n) {
  331. X            FeMessage("You don't have any moves left.");
  332. X            break;
  333. X            }
  334. X        n = ( (g->mydir > 0) ? 25 : 0 ) - g->mydir*g->mvs[i].roll;
  335. X        while ( (n > 0) && (n < 25) && ( (g->board[n].qty <= 0) ||
  336. X           (g->board[n].color != g->mycolor) )  )
  337. X            n += g->mydir;        /* search for occupied point */
  338. X        if ( (n < 1) || (n > 24) ) {
  339. X            FeMessage("You cannot bear off with that roll.");
  340. X            break;
  341. X            }
  342. X        g->mvs[i].pt = n;
  343. X        n = apply(g,1,i,A_REDRAW,&lastd);
  344. X        if (n < 0) {    /* move rejected */
  345. X            g->mvs[i].pt = -1;
  346. X            FeMessage(rejmsg[-n]);
  347. X            }
  348. X        FeDrawMove(g,1,i);
  349. X        break;
  350. X    case '1':
  351. X    case '2':
  352. X    case '3':
  353. X    case '4':
  354. X    case '5':
  355. X    case '6':
  356. X        if (g->curbd != BD_CUR) {
  357. X            g->curbd = BD_CUR;
  358. X            FeDrawBoard(g->board,NULL,g->mydir,0,
  359. X                g->flags & F_INVERT);
  360. X            FeLabelBoard(bdlabels[BD_CUR]);
  361. X            }
  362. X        c -= '0';
  363. X        if ( (c == g->mvs[0].roll) && (c == g->mvs[1].roll) ) {
  364. X            for (i = 0; i < 4; i++)        /* doubles */
  365. X                if (g->mvs[i].pt < 0)
  366. X                    break;
  367. X            if (i == 4) {
  368. X                FeMessage(used);
  369. X                break;
  370. X                }
  371. X            }
  372. X        else if (c == g->mvs[0].roll) {        /* used 1st move */
  373. X            if (g->mvs[0].pt >= 0) {
  374. X                FeMessage(used);
  375. X                break;
  376. X                }
  377. X            i = 0;
  378. X            }
  379. X        else {
  380. X            if (g->mvs[0].pt < 0) {    /* used 2nd move 1st */
  381. X                tmp = g->mvs[0];    /* swap moves */
  382. X                g->mvs[0] = g->mvs[1];
  383. X                g->mvs[1] = tmp;
  384. X                FeDrawMove(g,1,0);
  385. X                FeDrawMove(g,1,1);
  386. X                i = 0;
  387. X                }
  388. X            else if (g->mvs[1].pt >= 0) {    /* this move used? */
  389. X                FeMessage(used);
  390. X                break;
  391. X                }
  392. X            else
  393. X                i = 1;
  394. X            }
  395. X        n = FeGetPoint(g,i,lastpt,lastd);
  396. X        if (n >= 0) {
  397. X            if (n > 25) {
  398. X                FeMessage("Invalid point number");
  399. X                FeDrawMove(g,1,i);
  400. X                break;
  401. X                }
  402. X            g->mvs[i].pt = n;
  403. X            n = apply(g,1,i,A_REDRAW,&lastd);
  404. X            if (n < 0) {    /* move rejected */
  405. X                g->mvs[i].pt = -1;
  406. X                FeMessage(rejmsg[-n]);
  407. X                }
  408. X            else
  409. X                lastpt = g->mvs[i].pt;
  410. X            }
  411. X        FeDrawMove(g,1,i);
  412. X        break;
  413. X    default:
  414. X        FeMessage("Invalid command.");
  415. X        break;
  416. X        }
  417. X    }
  418. X}
  419. X
  420. X
  421. X/*----------------------------------------------------------------------
  422. X *    myacpt -- allow user to accept or decline double.
  423. X *
  424. X * This function allows the user to decide whether he
  425. X * wants to accept or decline his opponent's double.
  426. X * It also allows the user to cycle through the three board displays,
  427. X * to concede, and to go to the next game.
  428. X * Rolling and doubling are not allowed here.
  429. X *----------------------------------------------------------------------
  430. X */
  431. X
  432. Xmyacpt(g)
  433. Xstruct game *g;
  434. X{
  435. Xchar c;
  436. Xstatic char *m[] = {"Accept","Decline","Board","Next Game","Quit",NULL};
  437. X
  438. XFeDrawMenu(m);
  439. Xwhile (1) {
  440. X    c = FeMenu(m,0,0,"");
  441. X    switch (c) {
  442. X    case 'A':                /* I accepted */
  443. X        g->gameval *= 2;        /* the game value is doubled */
  444. X        FeGetComment(g);        /* get message */
  445. X        sendpkt(g,ACPTDBL);        /* send accept packet */
  446. X        g->state = ST_OPTURN;        /* it's opponent's turn */
  447. X        return(0);            /* done w/ this game for now */
  448. X    case 'D':                /* I declined */
  449. X        g->state = ST_GAMEOVER;        /* game is dead */
  450. X        g->term = T_IDECLINE;        /* store the reason */
  451. X        FeGetComment(g);        /* get message */
  452. X        sendpkt(g,DECDBL);        /* tell the opponent */
  453. X        return(1);            /* call gameover() */
  454. X    case 'B':                /* display different board */
  455. X        if (g->curbd++ >= BD_CUR)    /* go to next board */
  456. X            g->curbd = BD_BEFOP;    /* wrap around */
  457. X        return(1);        /* redraw & call us again */
  458. X    case 'N':
  459. X        return(0);        /* I'm done with this game for now */
  460. X    case 'Q':            /* I want to quit ldb */
  461. X        return(-1);
  462. X    default:
  463. X        FeMessage("Invalid command.");
  464. X        break;
  465. X        }
  466. X    }
  467. X}
  468. X
  469. X
  470. X/*----------------------------------------------------------------------
  471. X *    gameover -- show game to user before it is deleted.
  472. X *
  473. X * This function displays a game that has just terminated.
  474. X * It displays the final board, the reason the game ended, and the
  475. X * number of points won or lost.  The game will be deleted by
  476. X * writegames() when ldb exits.
  477. X *----------------------------------------------------------------------
  478. X */
  479. X
  480. Xgameover(g)
  481. Xstruct game *g;
  482. X{
  483. Xchar c;
  484. Xstatic char *m[] = {"Board","Next Game","Quit",NULL};
  485. X
  486. XFeDrawMenu(m);
  487. Xwhile (1) {
  488. X    c = FeMenu(m,0,0,"\n\r ");
  489. X    switch (c) {
  490. X    case 'B':                /* display different board */
  491. X        if (g->curbd++ >= BD_CUR)    /* go to next board */
  492. X            g->curbd = BD_BEFOP;    /* wrap around */
  493. X        return(1);        /* redraw & call us again */
  494. X    case 'N':            /* delete game & go to next */
  495. X    case ' ':
  496. X    case '\r':
  497. X    case '\n':
  498. X        g->flags |= F_DELETE;    /* tell writegames to delete game */
  499. X        return(0);        /* I'm done looking at this game */
  500. X    case 'Q':            /* delete game & quit */
  501. X        g->flags |= F_DELETE;    /* tell writegames to delete game */
  502. X        return(-1);
  503. X    default:
  504. X        FeMessage("Invalid command.");
  505. X        break;
  506. X        }
  507. X    }
  508. X}
  509. END_OF_FILE
  510. if test 13996 -ne `wc -c <'process.c'`; then
  511.     echo shar: \"'process.c'\" unpacked with wrong size!
  512. fi
  513. chmod +x 'process.c'
  514. # end of 'process.c'
  515. fi
  516. if test -f 'rcvop.c' -a "${1}" != "-c" ; then 
  517.   echo shar: Will not clobber existing file \"'rcvop.c'\"
  518. else
  519. echo shar: Extracting \"'rcvop.c'\" \(11577 characters\)
  520. sed "s/^X//" >'rcvop.c' <<'END_OF_FILE'
  521. X/*    rcvop.c        8/6/91
  522. X *
  523. X * Copyright 1991  Perry R. Ross
  524. X *
  525. X * Permission to use, copy, modify, and distribute this software and its
  526. X * documentation without fee is hereby granted, subject to the restrictions
  527. X * detailed in the README file, which is included here by reference.
  528. X * Any other use requires written permission from the author.  This software
  529. X * is distributed "as is" without any warranty, including any implied
  530. X * warranties of merchantability or fitness for a particular purpose.
  531. X * The author shall not be liable for any damages resulting from the
  532. X * use of this software.  By using this software, the user agrees
  533. X * to these terms.
  534. X */
  535. X
  536. X#include "ldb.h"
  537. X
  538. X/*===========================================================================
  539. X * This file contains the functions which make up the receive state
  540. X * machine.  These are called through the func[][] matrix, which takes
  541. X * the current state and a received operation and calls the appropriate
  542. X * handler function.  These functions will typically change the game
  543. X * state into one requiring user input at this host (ST_MY*), and return.
  544. X *===========================================================================
  545. X */
  546. X
  547. X
  548. X/*---------------------------------------------------------------------------
  549. X *    start -- initiate a game
  550. X *
  551. X * This function is called when a remote user starts a game with us.
  552. X * We store his personal information (opaddr & opname), roll 1 die,
  553. X * and compare it to the one he sent.  If we won the roll, the roll
  554. X * is stored in mvs[] and state is set to MYMOVE.  If we lost the roll,
  555. X * both dice are sent back in a USTART line.  If the roll was a tie,
  556. X * a TIE packet is sent back .  The originator will re-roll and send
  557. X * us a RESTART packet, which will repeat the opening roll.
  558. X *---------------------------------------------------------------------------
  559. X */
  560. X
  561. Xstart(g)
  562. Xstruct game *g;
  563. X{
  564. Xint mydie;
  565. Xchar c1, c2;
  566. X
  567. Xg->opaddr = P.addr;    /* save mail address of opponent */
  568. Xg->gameid = P.gameid;        /* copy game id */
  569. Xg->mycolor = P.colors[1];    /* copy out colors */
  570. Xg->opcolor = P.colors[0];
  571. Xif (isupper(*P.dir))
  572. X    *P.dir = tolower(*P.dir);
  573. Xg->mydir = (*P.dir == 'u') ? 1 : -1;    /* copy out directions */
  574. Xg->opdir = REV(g->mydir);
  575. Xg->gameval = 1;            /* no doubles yet */
  576. Xg->flags = 0;
  577. Xg->seq = 2;            /* we rcvd 1 pkt already, init to 2 */
  578. Xif (P.autodbl == NULL)        /* set admax to MIN(my count, op's count) */
  579. X    g->admax = 0;
  580. Xelse
  581. X    g->admax = atoi(P.autodbl);
  582. Xif (rc.autodouble < g->admax)
  583. X    g->admax = rc.autodouble;
  584. Xg->adcnt = 0;            /* no autodoubles yet */
  585. Xclearmvs(g->mvs);
  586. Xclearmvs(g->opmvs);
  587. Xif (g->mydir > 0) {
  588. X    c1 = g->mycolor;    /* upbound color is mine */
  589. X    c2 = g->opcolor;    /* downbound color is opponent's */
  590. X    }
  591. Xelse {
  592. X    c1 = g->opcolor;    /* upbound color is opponent's */
  593. X    c2 = g->mycolor;    /* downbound color is mine */
  594. X    }
  595. Xnewboard(g->opbd,c1,c2);        /* set up boards for new game */
  596. Xnewboard(g->mybd,c1,c2);
  597. Xnewboard(g->board,c1,c2);
  598. Xmydie = Rolldie();
  599. Xif (P.mvs[0].roll == mydie) {        /* a !#$%&@ tie */
  600. X    if (g->adcnt < g->admax)    /* do an autodouble */
  601. X        g->gameval = 1 << ++(g->adcnt);
  602. X    sendpkt(g,TIE);
  603. X    printf("Tie on initial roll with %s (%s).\n",g->opaddr,g->opname);
  604. X    return;            /* opponent will send RESTART */
  605. X    }
  606. Xif (mydie > (int) P.mvs[0].roll) {        /* we won the initial roll */
  607. X    g->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  608. X    g->mvs[1].roll = mydie;
  609. X    g->mvs[0].pt = -1;    /* mark both rolls unused */
  610. X    g->mvs[1].pt = -1;
  611. X    g->state = ST_MYMOVE;    /* set state so we make a move */
  612. X    }
  613. Xelse {                /* we lost, tell the opponent to start */
  614. X    g->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  615. X    g->mvs[1].roll = mydie;    /* store so sendpkt can find it */
  616. X    g->state = ST_OPTURN;
  617. X    sendpkt(g,USTART);
  618. X    printf("Started game with %s (%s).\n",g->opaddr,g->opname);
  619. X    }
  620. X}
  621. X
  622. X
  623. X/*---------------------------------------------------------------------------
  624. X *    istart -- I won the opening toss
  625. X *
  626. X * This function is called when a USTART packet is received.  Both rolls
  627. X * are copied into the game structure and the state is set to MYMOVE,
  628. X * allowing us to use the roll but not to double.
  629. X *---------------------------------------------------------------------------
  630. X */
  631. X
  632. Xistart(g)
  633. Xstruct game *g;
  634. X{
  635. X
  636. Xg->mvs[0].roll = P.mvs[0].roll;    /* copy rolls from packet */
  637. Xg->mvs[1].roll = P.mvs[1].roll;
  638. Xg->mvs[0].pt = -1;    /* mark both rolls unused */
  639. Xg->mvs[1].pt = -1;
  640. Xg->state = ST_MYMOVE;    /* set state so we make a move */
  641. X}
  642. X
  643. X
  644. X/*---------------------------------------------------------------------------
  645. X *    tie -- The opening toss was a tie, try again
  646. X *
  647. X * This function is called when we receive a TIE packet.  We reroll
  648. X * one die and send a RESTART packet.  If the autodbl field in
  649. X * the received packet is > 0, the game value is set to 2 ** autodbl.
  650. X *---------------------------------------------------------------------------
  651. X */
  652. X
  653. Xtie(g)
  654. Xstruct game *g;
  655. X{
  656. X
  657. Xclearmvs(g->mvs);
  658. Xg->mvs[0].roll = Rolldie();
  659. Xif (P.autodbl != NULL)
  660. X    g->gameval = 1 << (g->adcnt = atoi(P.autodbl));
  661. Xsendpkt(g,RESTART);
  662. Xprintf("Tie on initial roll with %s (%s).\n",g->opaddr,g->opname);
  663. X}
  664. X
  665. X
  666. X/*---------------------------------------------------------------------------
  667. X *    restart -- restart after opening tie
  668. X *
  669. X * This function is called when we receive a RESTART packet.  It is
  670. X * mostly the same as start().
  671. X *---------------------------------------------------------------------------
  672. X */
  673. X
  674. Xrestart(g)
  675. Xstruct game *g;
  676. X{
  677. Xint mydie;
  678. X
  679. Xclearmvs(g->mvs);
  680. Xclearmvs(g->opmvs);
  681. Xmydie = Rolldie();
  682. Xif (P.mvs[0].roll == mydie) {        /* a !#$%&@ tie */
  683. X    if (g->adcnt < g->admax)    /* do an autodouble */
  684. X        g->gameval = 1 << ++(g->adcnt);
  685. X    sendpkt(g,TIE);
  686. X    printf("Tie on initial roll with %s (%s).\n",g->opaddr,g->opname);
  687. X    return;            /* opponent will send RESTART */
  688. X    }
  689. Xg->mvs[0].roll = P.mvs[0].roll;    /* copy initial roll */
  690. Xg->mvs[1].roll = mydie;    /* store so sendpkt can find it */
  691. Xif (mydie > (int) P.mvs[0].roll)        /* we won the initial roll */
  692. X    g->state = ST_MYMOVE;    /* set state so we make a move */
  693. Xelse {                /* we lost, tell the opponent to start */
  694. X    g->state = ST_OPTURN;
  695. X    sendpkt(g,USTART);
  696. X    printf("Started game with %s (%s).\n",g->opaddr,g->opname);
  697. X    }
  698. X}
  699. X
  700. X
  701. X/*---------------------------------------------------------------------------
  702. X *    opmove -- opponent moved
  703. X *
  704. X * This function is called when we receive a MOVE packet.  The move is
  705. X * copied into the opmvs field of the game structure and applied to the
  706. X * board.  A copy of the board before the moves are applied is stored
  707. X * in the opbd field, and a copy of the board after the moves are applied
  708. X * is stored in the mybd field.  These two boards, along with the
  709. X * current board in the board field, make up the three boards that can
  710. X * be displayed with the "Board" command in the user menus in process.c
  711. X *---------------------------------------------------------------------------
  712. X */
  713. X
  714. Xopmove(g)
  715. Xstruct game *g;
  716. X{
  717. Xint i, n, r;
  718. Xstatic char buf[] = "Opponent move dated DDD MMM NN HH:MM:SS YYYY";
  719. Xstatic int boardnums[3] = {BD_AFTOP, BD_BEFOP, BD_CUR};
  720. X
  721. Xcopyboard(g->board,g->opbd);    /* save board before opponent moved */
  722. Xg->curbd = boardnums[*rc.initboard - 'a'];    /* display initial board */
  723. Xclearmvs(g->opmvs);        /* clear old moves */
  724. Xg->opmvs[0] = P.mvs[0];    /* extract opponent's moves */
  725. Xg->opmvs[1] = P.mvs[1];
  726. Xif (g->opmvs[0].roll == g->opmvs[1].roll) {
  727. X    g->opmvs[2] = P.mvs[2];    /* he got doubles */
  728. X    g->opmvs[3] = P.mvs[3];    /* extract 2 more moves */
  729. X    n = 4;
  730. X    }
  731. Xelse
  732. X    n = 2;
  733. Xfor (i = 0; i < 4; i++)
  734. X    g->blot[i] = 0;        /* clear all blot locations */
  735. Xfor (i = 0; i < n; i++) {
  736. X    if ((r=apply(g,0,i,0,NULL)) < 0) {
  737. X        copyboard(g->opbd,g->board);    /* restore board */
  738. X        fprintf(stderr,"ERROR: Opponent move rejected, id=%s\n",
  739. X            P.gameid);
  740. X        fprintf(stderr,"       %s\n",rejmsg[-r]);
  741. X        return;
  742. X        }
  743. X    else        /* if opponent hit our blot */
  744. X        g->blot[i] = r;        /* save location */
  745. X    }
  746. Xcopyboard(g->board,g->mybd);        /* save board after op move */
  747. Xif (g->board[OFFPT(g->opdir)].qty == 15) {    /* opponent won */
  748. X    g->state = ST_GAMEOVER;
  749. X    g->term = T_ILOSE;
  750. X    }
  751. Xelse
  752. X    g->state = ST_MYTURN;        /* opponent has moved, it's our turn */
  753. Xclearmvs(g->mvs);        /* erase our previous move */
  754. Xif (P.timestamp > 0) {        /* if we got a timestamp */
  755. X    strncpy(&buf[20],ctime(&P.timestamp),24);    /* gen message */
  756. X    g->dispmsg = save(buf);        /* save copy in game */
  757. X    }
  758. X}
  759. X
  760. X
  761. X/*---------------------------------------------------------------------------
  762. X *    opofr -- opponent offered to double
  763. X *
  764. X * This function is called when we receive an OFRDBL packet, indicating
  765. X * the opponent wishes to double.  The game moves to state MYACCEPT,
  766. X * where the user will decide to accept or decline the double.
  767. X *---------------------------------------------------------------------------
  768. X */
  769. X
  770. Xopofr(g)
  771. Xstruct game *g;
  772. X{
  773. X
  774. Xg->state = ST_MYACCEPT;            /* send us to an accept screen */
  775. Xg->flags &= ~F_IDOUBLED;        /* I didn't double last */
  776. Xcopyboard(g->board,g->opbd);
  777. Xcopyboard(g->board,g->mybd);
  778. Xg->curbd = BD_CUR;        /* display current board */
  779. Xclearmvs(g->opmvs);        /* clear old moves */
  780. Xclearmvs(g->mvs);        /* erase our previous move */
  781. X}
  782. X
  783. X
  784. X/*---------------------------------------------------------------------------
  785. X *    opconc -- opponent conceded
  786. X *
  787. X * This function is called when we receive a CONCEDE packet, indicating
  788. X * the opponent has given up.
  789. X *---------------------------------------------------------------------------
  790. X */
  791. X
  792. Xopconc(g)
  793. Xstruct game *g;
  794. X{
  795. Xg->state = ST_GAMEOVER;            /* this game is toast */
  796. Xg->term = T_OPCONCEDE;            /* wimp */
  797. X}
  798. X
  799. X
  800. X/*---------------------------------------------------------------------------
  801. X *    opacpt -- opponent accepted double
  802. X *
  803. X * This function is called when we receive an ACPTDBL packet, indicating
  804. X * the opponent has accepted our double.  The IDOUBLED flag is set to
  805. X * prevent us from doubling again until the opponent doubles.  Since it
  806. X * is now our turn, we go ahead and roll the dice and proceed to the
  807. X * MYMOVE state.
  808. X *---------------------------------------------------------------------------
  809. X */
  810. X
  811. Xopacpt(g)
  812. Xstruct game *g;
  813. X{
  814. X
  815. Xg->gameval *= 2;        /* double game value */
  816. Xg->flags |= F_IDOUBLED;        /* I doubled last */
  817. Xg->state = ST_MYMOVE;        /* It is my move */
  818. Xcopyboard(g->board,g->opbd);
  819. Xcopyboard(g->board,g->mybd);
  820. Xg->curbd = BD_CUR;        /* display current board */
  821. Xclearmvs(g->opmvs);        /* clear old moves */
  822. Xclearmvs(g->mvs);        /* erase our previous move */
  823. Xrolldice(g);            /* go ahead and roll, I can't double */
  824. Xg->dispmsg = save("Opponent has accepted your double.");/* notify user */
  825. X}
  826. X
  827. X
  828. X/*---------------------------------------------------------------------------
  829. X *    opdec -- opponent declined double
  830. X *
  831. X * This function is called when a DECDBL packet is received.  This
  832. X * indicates that the opponent has declined our double, and the game is over.
  833. X *---------------------------------------------------------------------------
  834. X */
  835. X
  836. Xopdec(g)
  837. Xstruct game *g;
  838. X{
  839. X
  840. Xg->state = ST_GAMEOVER;
  841. Xg->term = T_OPDECLINE;
  842. Xcopyboard(g->board,g->opbd);
  843. Xcopyboard(g->board,g->mybd);
  844. Xg->curbd = BD_CUR;        /* display current board */
  845. Xclearmvs(g->opmvs);        /* clear old moves */
  846. Xclearmvs(g->mvs);        /* erase our previous move */
  847. X}
  848. X
  849. X
  850. X/*---------------------------------------------------------------------------
  851. X *    smerr -- an illegal operation was received for this state
  852. X *
  853. X * This function is called when a packet is received that is invalid
  854. X * for the game state.  An example of this would be receiving an
  855. X * ACPTDBL packet when we did not send an OFRDBL.
  856. X *---------------------------------------------------------------------------
  857. X */
  858. X
  859. Xsmerr(g)
  860. Xstruct game *g;
  861. X{
  862. X
  863. Xfprintf(stderr,"ERROR: invalid operation (%d) for state (%d), id=%s\n",
  864. X    P.opcode, g->state, g->gameid);
  865. X}
  866. END_OF_FILE
  867. if test 11577 -ne `wc -c <'rcvop.c'`; then
  868.     echo shar: \"'rcvop.c'\" unpacked with wrong size!
  869. fi
  870. chmod +x 'rcvop.c'
  871. # end of 'rcvop.c'
  872. fi
  873. if test -f 'save.c' -a "${1}" != "-c" ; then 
  874.   echo shar: Will not clobber existing file \"'save.c'\"
  875. else
  876. echo shar: Extracting \"'save.c'\" \(14745 characters\)
  877. sed "s/^X//" >'save.c' <<'END_OF_FILE'
  878. X/*    save.c        8/4/91
  879. X *
  880. X * Copyright 1991  Perry R. Ross
  881. X *
  882. X * Permission to use, copy, modify, and distribute this software and its
  883. X * documentation without fee is hereby granted, subject to the restrictions
  884. X * detailed in the README file, which is included here by reference.
  885. X * Any other use requires written permission from the author.  This software
  886. X * is distributed "as is" without any warranty, including any implied
  887. X * warranties of merchantability or fitness for a particular purpose.
  888. X * The author shall not be liable for any damages resulting from the
  889. X * use of this software.  By using this software, the user agrees
  890. X * to these terms.
  891. X */
  892. X
  893. X#include "ldb.h"
  894. X
  895. X/*===========================================================================
  896. X * This file contains miscellaneous functions that save and load things.
  897. X *===========================================================================
  898. X */
  899. X
  900. X/*---------------------------------------------------------------------------
  901. X *    save -- make a copy of a string
  902. X *
  903. X * This function makes a copy of a string in malloc memory, and returns
  904. X * a pointer to the copy.
  905. X *---------------------------------------------------------------------------
  906. X */
  907. X
  908. Xchar *save(s)
  909. Xchar *s;
  910. X{
  911. Xchar *n;
  912. X
  913. Xif ( (n = calloc(strlen(s)+1,1)) == NULL) {
  914. X    FeFinishSession();
  915. X    TFinishSession();    /* close down transport */
  916. X    fprintf(stderr,"Out of memory!\n");
  917. X    exit(1);
  918. X    }
  919. Xstrcpy(n,s);
  920. Xreturn(n);
  921. X}
  922. X
  923. X
  924. X/*---------------------------------------------------------------------------
  925. X *    readldbrc -- read in the .ldbrc file
  926. X *
  927. X * This function reads the .ldbrc file, which contains the setup info
  928. X * for this user.  If the HOME environment variable is set, we chdir to it.
  929. X * If the LDBRC environment variable is set, it is used as the file to
  930. X * read instead of .ldbrc.
  931. X *---------------------------------------------------------------------------
  932. X */
  933. X
  934. Xreadldbrc()
  935. X{
  936. XFILE *fp;
  937. Xchar *s, *n, *getenv();
  938. Xchar buf[80];
  939. X
  940. Xif ( (s = getenv("HOME")) != NULL)    /* if we have a home */
  941. X    chdir(s);            /* go there */
  942. Xif ( (s = getenv("LDBRC")) == NULL)    /* LDBRC not set */
  943. X    s = ".ldbrc";            /* use default file name */
  944. Xif ( (fp = fopen(s,"r")) == NULL) {
  945. X    printf("'%s' does not exist.  Do you want to create it?",s);
  946. X    if ( (gets(buf) == NULL) || ( (*buf != 'y') && (*buf != 'Y') ) ) {
  947. X        printf("ldb aborted.\n");
  948. X        exit(1);
  949. X        }
  950. X    if ( (fp = fopen(s,"w")) == NULL) {
  951. X        printf("Sorry, could not create %s.\n",s);
  952. X        exit(1);
  953. X        }
  954. X    printf("Please enter your personal name: ");
  955. X    if (gets(buf) == NULL) {
  956. X        fclose(fp);
  957. X        unlink(s);
  958. X        printf("ldb aborted.\n");
  959. X        exit(1);
  960. X        }
  961. X    fprintf(fp,"myname=%s\n",buf);
  962. X    printf("Please enter your e-mail address: ");
  963. X    if (gets(buf) == NULL) {
  964. X        fclose(fp);
  965. X        unlink(s);
  966. X        printf("ldb aborted.\n");
  967. X        exit(1);
  968. X        }
  969. X    fprintf(fp,"myaddr=%s\n",buf);
  970. X    fprintf(fp,"gamefile=.ldbdata\n");
  971. X    fprintf(fp,"backupfile=.oldldbdata\n");
  972. X    fprintf(fp,"mailfile=ldb.rcv\n");
  973. X    fprintf(fp,"sendcmd=mail -s '$s' $a < $f\n");
  974. X    fprintf(fp,"tempfile=ldb.tmp\n");
  975. X    fprintf(fp,"colors=rw\n");
  976. X    fprintf(fp,"direction=up\n");
  977. X    fprintf(fp,"initialboard=current\n");
  978. X    fprintf(fp,"autoroll=yes\n");
  979. X    fprintf(fp,"automove=no\n");
  980. X    fprintf(fp,"autodouble=0\n");
  981. X    fprintf(fp,"supercmd=sh\n");
  982. X    fprintf(fp,"superkey=0\n");
  983. X    fprintf(fp,"checkpoint=yes\n");
  984. X    fclose(fp);
  985. X    printf("\nYour %s file was created.  You may want to read the\n",s);
  986. X    printf("manual for additional options available in this file.\n\n");
  987. X    if ( (fp = fopen(s,"r")) == NULL) {
  988. X        printf("I can't re-open your %s file!\n",s);
  989. X        exit(1);
  990. X        }
  991. X    }
  992. Xrc.myname = NULL;        /* these fields are required */
  993. Xrc.myaddr = NULL;
  994. Xrc.gfile = ".ldbdata";    /* default game storage file */
  995. Xrc.gbackup = ".oldldbdata";    /* game backup file */
  996. Xrc.mfile = "ldb.rcv";    /* default file for received mail */
  997. Xrc.sendcmd = "mail -s '$s' $a < $f";    /* default mail command */
  998. Xrc.tempfile = "ldb.tmp";        /* default temp file */
  999. Xrc.defclrs = "rw";            /* "default" default colors */
  1000. Xrc.defdir = "u";            /* and direction */
  1001. Xrc.initboard = "c";            /* show current board by default */
  1002. Xrc.autoroll = "y";            /* enable autoroll by default */
  1003. Xrc.automove = "n";        /* disabled by default (it's really annoying */
  1004. Xrc.autodouble = 0;        /* disable autodouble by default */
  1005. Xrc.supercmd = "sh";        /* command to run when we have company */
  1006. Xrc.superkey = 0;        /* key to activate supercmd (dflt=disabled) */
  1007. Xrc.chkpt = "y";            /* checkpoint is enabled by default */
  1008. Xif ( (n = nvscan(fp,nv_rcfile,&rc,opcodes)) != NULL) {
  1009. X    fprintf(stderr,"Invalid line in .ldbrc: %s\n",n);
  1010. X    exit(1);
  1011. X    }
  1012. Xfclose(fp);
  1013. Xif (rc.myname == NULL) {
  1014. X    fprintf(stderr,"ERROR: missing 'myname' line in %s\n",s);
  1015. X    exit(1);
  1016. X    }
  1017. Xif (rc.myaddr == NULL) {
  1018. X    fprintf(stderr,"ERROR: missing 'myaddr' line in %s\n",s);
  1019. X    exit(1);
  1020. X    }
  1021. Xif ( (strlen(rc.defclrs) != 2) ||
  1022. X     (strchr("rwb",rc.defclrs[0]) == NULL) ||
  1023. X     (strchr("rwb",rc.defclrs[1]) == NULL) ||
  1024. X     (rc.defclrs[0] == rc.defclrs[1]) ) {
  1025. X    fprintf(stderr,"ERROR: invalid color string in %s: %s",s,rc.defclrs);
  1026. X    exit(1);
  1027. X    }
  1028. Xif (strchr("ud",*rc.defdir) == NULL) {
  1029. X    fprintf(stderr,"ERROR: direction must be 'up' or 'down' in %s\n",s);
  1030. X    exit(1);
  1031. X    }
  1032. Xif (strchr("bac",*rc.initboard) == NULL) {
  1033. X    fprintf(stderr,
  1034. X     "ERROR: initialboard must be 'before', 'after', or 'current' in %s\n"
  1035. X     ,s);
  1036. X    exit(1);
  1037. X    }
  1038. Xif (strchr("yn",*rc.autoroll) == NULL) {
  1039. X    fprintf(stderr,"ERROR: autoroll must be 'yes' or 'no' in %s\n",s);
  1040. X    exit(1);
  1041. X    }
  1042. Xif (strchr("yn",*rc.automove) == NULL) {
  1043. X    fprintf(stderr,"ERROR: automove must be 'yes' or 'no' in %s\n",s);
  1044. X    exit(1);
  1045. X    }
  1046. Xif (strchr("yn",*rc.chkpt) == NULL) {
  1047. X    fprintf(stderr,"ERROR: checkpoint must be 'yes' or 'no' in %s\n",s);
  1048. X    exit(1);
  1049. X    }
  1050. X}
  1051. X
  1052. X
  1053. X/*---------------------------------------------------------------------------
  1054. X *    readgames -- read in games in progress
  1055. X *
  1056. X * This function reads the games file specified in .ldbrc and loads
  1057. X * the games into the games list (ghead/gtail).
  1058. X *---------------------------------------------------------------------------
  1059. X */
  1060. X
  1061. Xreadgames()
  1062. X{
  1063. XFILE *fp;
  1064. Xchar *s;
  1065. Xstruct game *g;
  1066. X
  1067. Xif ( (fp = fopen(rc.gfile,"r")) == NULL)
  1068. X    return;                /* no games */
  1069. Xwhile (! feof(fp)) {
  1070. X    g = addgame();        /* insert a new game */
  1071. X    g->opaddr = NULL;
  1072. X    g->opname = NULL;
  1073. X    g->mycmt = NULL;
  1074. X    g->mycmt2 = NULL;
  1075. X    g->opcmt = NULL;
  1076. X    g->opcmt2 = NULL;
  1077. X    g->dispmsg = NULL;
  1078. X    g->hiused = 0;
  1079. X    g->maxused = 0;
  1080. X    clearmvs(g->opmvs);
  1081. X    clearmvs(g->mvs);
  1082. X    if ( (s = nvscan(fp,nv_gfile,g,opcodes)) != NULL) {    /* read game */
  1083. X        FeFinishSession();    /* close down front-end */
  1084. X        TFinishSession();    /* close down transport */
  1085. X        fprintf(stderr,"ERROR: invalid line in %s: %s\n", rc.gfile, s);
  1086. X        exit(1);
  1087. X        }
  1088. X    if (g->gameid == NULL)        /* empty game (prob. EOF) */
  1089. X        deletegame(g);
  1090. X    }
  1091. Xfclose(fp);
  1092. X}
  1093. X
  1094. X
  1095. X/*----------------------------------------------------------------------
  1096. X *    writegames -- save the game list to a file
  1097. X *
  1098. X * This function writes each game in the game list to the specified
  1099. X * file.  Games with F_DELETE set in their flags are skipped, and
  1100. X * are thus effectively deleted.  If the bkup arg is not NULL and
  1101. X * is not an empty string, the old file is renamed to the string
  1102. X * specified in bkup before the new file is created.
  1103. X *----------------------------------------------------------------------
  1104. X */
  1105. X
  1106. Xwritegames(file,bkup)
  1107. Xchar *file, *bkup;
  1108. X{
  1109. XFILE *fp;
  1110. Xstruct game *g;
  1111. X
  1112. Xif ( (bkup != NULL) && (*bkup != '\0') )
  1113. X    rename(file,bkup);    /* save old game file */
  1114. Xelse
  1115. X    unlink(file);        /* prevent multiple versions on VMS */
  1116. Xif ( (fp = fopen(file,"w")) == NULL) {
  1117. X    fprintf(stderr,"ERROR: can't save games in %s\n",file);
  1118. X    return;            /* should do something to save games... */
  1119. X    }
  1120. Xfor (g = ghead; g != NULL; g = g->next) {
  1121. X    if (g->flags & F_DELETE)
  1122. X        continue;            /* game is over, delete it */
  1123. X    nvwrite(fp,nv_gfile,g,opcodes);    /* write the struct */
  1124. X    }
  1125. Xfclose(fp);
  1126. X}
  1127. X
  1128. X
  1129. X
  1130. X/*----------------------------------------------------------------------
  1131. X *    boardstr -- generate an ascii representation of a board
  1132. X *
  1133. X * This function produces a visible representation of a board.  Each point
  1134. X * on the board takes two characters; the quantity is offset by 65,
  1135. X * putting it in the range [A-P], and the color is unchanged since it
  1136. X * is already a printable character.  This results in a string of
  1137. X * 28 character pairs, one for each point on the board.  These are
  1138. X * in the order:
  1139. X *    0:    BAR point for upbound player
  1140. X *    1-24:    board points
  1141. X *    25:    BAR point for downbound player
  1142. X *    26:    OFF point for upbound player
  1143. X *    27:    OFF point for downbound player
  1144. X *----------------------------------------------------------------------
  1145. X */
  1146. X
  1147. Xchar *boardstr(b)
  1148. Xboard b;
  1149. X{
  1150. Xstatic char buf[BOARDSIZE*2+1];
  1151. Xchar *s, c;
  1152. Xint i;
  1153. X
  1154. Xs = buf;
  1155. Xfor (i = 0; i < BOARDSIZE; i++) {
  1156. X    *s++ = b[i].qty + 'A';        /* offset qty into u.c. letters */
  1157. X    if ( ((c = b[i].color) < 'a') || (c > 'z') )
  1158. X        c = 'x';        /* use printing chars */
  1159. X    *s++ = c;
  1160. X    }
  1161. X*s = '\0';
  1162. Xreturn(buf);
  1163. X}
  1164. X
  1165. X
  1166. X
  1167. X/*----------------------------------------------------------------------
  1168. X *    nvscan -- read name/value pairs from a file
  1169. X *
  1170. X * This function provides a generalized method for reading name/value
  1171. X * pairs.  The names and value types are specified in an array of
  1172. X * struct namevalue's, along with an offset which is used to store
  1173. X * the value.  The offset is added to a base pointer, passed as the
  1174. X * "st" argument, to form a pointer, which is then converted to the
  1175. X * type indicated by the "type" field of the namevalue table using
  1176. X * the "nvtypes" union, and used to store the value.  The legal
  1177. X * value types are defined in ldb.h as T_*.  Name/value pairs are
  1178. X * expected to be in the form "name=value\n", with no spaces before
  1179. X * or after name, and with any spaces after the = or before the
  1180. X * newline being part of the value string.  Comments are not allowed in
  1181. X * the input file, although the caller can read any lines that precede
  1182. X * the name/value list before calling nvscan.
  1183. X *----------------------------------------------------------------------
  1184. X */
  1185. X
  1186. Xchar *nvscan(fp,t,st,strings)
  1187. XFILE *fp;        /* file to scan */
  1188. Xstruct namevalue *t;    /* table of name/value pairs */
  1189. Xchar *st;        /* really a pointer to a structure */
  1190. Xchar *strings[];    /* string table for FT_STRLKUP */
  1191. X{
  1192. Xstatic char buf[128];
  1193. Xchar *s, **p;
  1194. Xint i, j;
  1195. Xunion nvtypes u;
  1196. Xlong atol();
  1197. X
  1198. Xwhile (fgets(buf,sizeof(buf),fp) != NULL) {
  1199. X    buf[strlen(buf)-1] = '\0';        /* clobber the newline */
  1200. X    if ( (s = strchr(buf,'=')) == NULL)
  1201. X        return(buf);            /* bad line, return it */
  1202. X    *s++ = '\0';
  1203. X    for (i = 0; t[i].name != NULL; i++)
  1204. X        if (strcmp(t[i].name,buf) == 0)
  1205. X            break;
  1206. X    if (t[i].name == NULL)        /* got a name we don't recognize */
  1207. X        continue;        /* ignore it */
  1208. X    u.nvchar = st + t[i].offset;    /* put pointer into union */
  1209. X    switch (t[i].type) {
  1210. X    case FT_CHAR:            /* just store a single char */
  1211. X        *u.nvchar = atoi(s);    /* chars stored as ints in the file */
  1212. X        break;
  1213. X    case FT_INT:            /* store an int */
  1214. X        *u.nvint = atoi(s);
  1215. X        break;
  1216. X    case FT_STRING:            /* store a copy of a string */
  1217. X        *u.nvstring = save(s);
  1218. X        break;
  1219. X    case FT_MOVE:            /* store a struct mv */
  1220. X        str2mv(s,u.nvmove);
  1221. X        break;
  1222. X    case FT_BOARD:            /* store an entire board */
  1223. X        for (j = 0; j < BOARDSIZE; j++) {
  1224. X            u.nvboard[j].qty = *s++ - 'A';
  1225. X            u.nvboard[j].color = *s++;
  1226. X            }
  1227. X        break;
  1228. X    case FT_STRLKUP:        /* look up string & store index */
  1229. X        if (strings == NULL) {        /* choke... */
  1230. X            FeFinishSession();    /* close down front-end */
  1231. X            TFinishSession();    /* close down transport */
  1232. X            fprintf(stderr,"ERROR: NULL string table in nvscan\n");
  1233. X            exit(1);
  1234. X            }
  1235. X        for (j = 0, p = strings; *p; j++, p++)
  1236. X            if (strcmp(s,*p) == 0)
  1237. X                break;
  1238. X        if (*p == NULL) {
  1239. X            FeFinishSession();    /* close down front-end */
  1240. X            TFinishSession();    /* close down transport */
  1241. X            fprintf(stderr,"ERROR: unknown opcode: %s\n",s);
  1242. X            exit(1);    /* shouldn't do this... */
  1243. X            }
  1244. X        *u.nvint = j;    /* store integer opcode */
  1245. X        break;
  1246. X    case FT_TIME:            /* read in a timestamp */
  1247. X        *u.nvtime = atol(s);
  1248. X        break;
  1249. X    case FT_END:            /* we hit the end marker */
  1250. X        return(NULL);        /* return success */
  1251. X    default:            /* we have a bad nv table */
  1252. X        *--s = '=';        /* restore the buffer */
  1253. X        return(buf);        /* return bad line */
  1254. X        }
  1255. X    }
  1256. Xreturn(NULL);
  1257. X}
  1258. X
  1259. X
  1260. X
  1261. X/*----------------------------------------------------------------------
  1262. X *    nvwrite -- write name/value pairs into a file
  1263. X *
  1264. X * This function writes name/value pairs to a file in the same format
  1265. X * used by nvscan.  Nvwrite is merely the inverse of nvscan, taking values
  1266. X * out of the structure in the same manner nvscan used to store them
  1267. X * there, and generating "name=value" lines.  One line is generated for
  1268. X * each element in the namevalue table, except that elements of type
  1269. X * FT_STRING whose value is NULL are skipped, as are elements of a
  1270. X * move array whose "roll" field is <= 0.
  1271. X *----------------------------------------------------------------------
  1272. X */
  1273. X
  1274. Xnvwrite(fp,t,st,strings)
  1275. XFILE *fp;            /* file to write to */
  1276. Xstruct namevalue *t;        /* table of name/value pairs */
  1277. Xchar *st;            /* really a pointer to a structure */
  1278. Xchar *strings[];        /* table of strings for FT_STRLKUP */
  1279. X{
  1280. Xstruct namevalue *n;
  1281. Xstatic char buf[128];
  1282. Xint nstr = -1;
  1283. Xunion nvtypes u;
  1284. Xlong time();
  1285. X
  1286. Xfor (n = t; n->name != NULL; n++) {
  1287. X    u.nvchar = st + n->offset;
  1288. X    switch (n->type) {
  1289. X    case FT_CHAR:            /* just store a single char */
  1290. X        fprintf(fp,"%s=%d\n",n->name,*u.nvchar);
  1291. X        break;
  1292. X    case FT_INT:            /* store an int */
  1293. X        fprintf(fp,"%s=%d\n",n->name,*u.nvint);
  1294. X        break;
  1295. X    case FT_STRING:            /* store a copy of a string */
  1296. X        if (*u.nvstring != NULL)    /* skip NULL strings */
  1297. X            fprintf(fp,"%s=%s\n",n->name,*u.nvstring);
  1298. X        break;
  1299. X    case FT_MOVE:            /* store a struct mv */
  1300. X        if (u.nvmove->roll > 0) {
  1301. X            mv2str(u.nvmove,buf);
  1302. X            fprintf(fp,"%s=%s\n",n->name,buf);
  1303. X            }
  1304. X        break;
  1305. X    case FT_BOARD:            /* store an entire board */
  1306. X        fprintf(fp,"%s=%s\n",n->name,boardstr(u.nvboard));
  1307. X        break;
  1308. X    case FT_STRLKUP:        /* look up string & store index */
  1309. X        if (nstr < 0) {
  1310. X            if (strings == NULL) {
  1311. X                FeFinishSession();/* close down front-end */
  1312. X                TFinishSession();/* close down transport */
  1313. X                fprintf(stderr,
  1314. X                   "ERROR: NULL string table in nvwrite.\n");
  1315. X                exit(1);
  1316. X                }
  1317. X            for (nstr = 0; strings[nstr]; nstr++); /* # strings */
  1318. X            }
  1319. X        if ( (*u.nvint < 0) || (*u.nvint >= nstr) ) {
  1320. X            FeFinishSession();    /* close down front-end */
  1321. X            TFinishSession();    /* close down transport */
  1322. X            fprintf(stderr,"ERROR: invalid index: %s=%d\n",
  1323. X                n->name,*u.nvint);
  1324. X            exit(1);    /* shouldn't do this... */
  1325. X            }
  1326. X        fprintf(fp,"%s=%s\n",n->name,strings[*u.nvint]);
  1327. X        break;
  1328. X    case FT_TIME:            /* generate a timestamp */
  1329. X        fprintf(fp,"%s=%lu\n",n->name,time(0));
  1330. X        break;
  1331. X    case FT_END:            /* we hit the end marker */
  1332. X        fprintf(fp,"%s=end\n",n->name);
  1333. X        break;
  1334. X    default:            /* we have a bad nv table */
  1335. X        FeFinishSession();    /* close down front-end */
  1336. X        TFinishSession();    /* close down transport */
  1337. X        fprintf(stderr,"ERROR: bad namevalue type: %s (%d)\n",
  1338. X            n->name,n->type);
  1339. X        exit(1);        /* should have saved games? */
  1340. X        }
  1341. X    }
  1342. X}
  1343. END_OF_FILE
  1344. if test 14745 -ne `wc -c <'save.c'`; then
  1345.     echo shar: \"'save.c'\" unpacked with wrong size!
  1346. fi
  1347. chmod +x 'save.c'
  1348. # end of 'save.c'
  1349. fi
  1350. echo shar: End of archive 3 \(of 5\).
  1351. cp /dev/null ark3isdone
  1352. MISSING=""
  1353. for I in 1 2 3 4 5 ; do
  1354.     if test ! -f ark${I}isdone ; then
  1355.     MISSING="${MISSING} ${I}"
  1356.     fi
  1357. done
  1358. if test "${MISSING}" = "" ; then
  1359.     echo You have unpacked all 5 archives.
  1360.     rm -f ark[1-9]isdone
  1361. else
  1362.     echo You still need to unpack the following archives:
  1363.     echo "        " ${MISSING}
  1364. fi
  1365. ##  End of shell archive.
  1366. exit 0
  1367.  
  1368.  
  1369. exit 0 # Just in case...
  1370.