|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T c
Length: 24364 (0x5f2c) Types: TextFile Names: »cmd.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Cchess/cmd.c«
/* CCHESS USER INTERFACE version 1.00 * * (C) Copyright - Jan D. Wolter - Apr 1986 * * Mostly routines to talk to the user. */ #include "cchess.h" #ifndef NOTERMCAP extern char *DL; #endif NOTERMCAP int mredraw; /* flag used for redrawing while reading in move */ /* DOMOVE() * * This is the move mode driver. Show board and command menu. Accept * move, forfiet, draw, and playback commands. This is the real center of * the program. * * If <drawp> is RE_DRAW, then a draw has been proposed, and the options are * to accept or reject the draw. If <drawp> is RE_CANCEL, then a cancelation * has been proposed, and the same commands are available. */ domove(drawp) int drawp; { int fr,fc,tr,tc; int row,col; char new_move[15]; char ch; schar took,p; boolean thru,himcheck,checked,firsttime=TRUE; int i; new_move[C_CMD] = 'M'; new_move[C_MAT] = ' '; myillmoves = 0; scrolled = TRUE; mredraw = 0; must_take = Musttake && canmove(b,mycolor,TRUE); /* Loop until he is satisfied with a command */ for (;;) { #ifndef NOTERMCAP /* make sure there is room for an error message */ if (moveok && errcode != E_OK && errcode != E_SY) { if (!DL && cy == LINES) wputchar('\n'); } else errcode = E_OK; #endif NOTERMCAP /* draw board - "scrolled" basically just tells us to do so */ if (scrolled) { disp(b,kibitz?WHITE:mycolor); printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid); printcom(); wputchar('\n'); if (cy >= LINES) msgarea = LINES - 1; else msgarea = cy; } /* Prompt for a command */ if (!moveok) { /* Not allowed to move - Playback or nothing */ if (!kibitz) wprint("It's not your turn to move.\n"); wprint("Do you want a playback? "); ch = xenter("YNVOL\014"); if (ch == 'Y') { ch = 'P'; wprint("Yes\n"); } if (ch == 'N') { ch = 'Z'; wprint("No\n"); } } else if (drawp == RE_NONE) { /* Normal case - Get a move command */ if (errcode != E_OK) { prterror(); wputchar('\n'); } errcode = E_OK; if (firsttime) { if (movecnt < 2) wprint("Help, "); else if (tamemoves >= MAXTAME) wprint("Under the 50-move rule, you may unilaterally declare a draw.\n"); firsttime = FALSE; } wprint("Move, Playback, Forfeit or Draw? "); if (mredraw > 0) ch = 'M'; else ch = xenter("MPFDECQVH?OL\014"); } else { /* A draw or cancellation has been proposed */ if (drawp == RE_DRAW) wprint("A draw"); else wprint("A cancellation"); wprint(" has been proposed.\nAccept or Reject? "); ch = xenter("PARVEQOL\014"); } switch (ch) { case 'H': case '?': wprint("Help\n"); initmore(); mprint("Move - Enter a chess move\n"); mprint("Playback - View a replay of previous moves\n"); mprint("Forfeit - Admit that the other player won\n"); mprint("Draw - Propose that the game be a draw\n"); mprint("Last - Display date of last move made\n"); mprint("Options - Display the current game options\n"); mprint("^L - Redraw the screen\n"); mprint("Exit - Exit the program without moving\n"); mprint("Cancel - Propose that the game be abandoned\n"); break; case 'L': wprintf("Last move made on %s\n", cday(lastday)); break; case 'O': wprint("Options\n"); initmore(); printopts(mprintf); break; case 'M': wprint("Move\n"); ++consmade; took = taken; checked = incheck; /* Get his move */ mget(mycolor,new_move+C_MOV,&fr,&fc,&tr,&tc); if (mredraw > 0) { scrolled = TRUE; --consmade; break; } /* If it's an illegal move, give it up */ if (!(thru = (errcode==E_MC && consmade < consmoves && Thrucheck))) { if (errcode == E_OK) wputchar('\n'); else { consmade--; break; } } /* Test if other player is in check */ if (Kingpiece(-mycolor) != SQ) { find(bc,Kingpiece(-mycolor),&row,&col); himcheck = check(bc,mycolor,row,col); } else himcheck = FALSE; /* Has enemy been stripped of all forces? */ if (Stripresult != RE_NONE && taken != SQ && !forces(-mycolor,bc)) { stripped = TRUE; wprint("Capture of all enemy pieces "); switch (Stripresult) { case RE_LOSE: wprintf("is a win for you.\n"); break; case RE_WIN: wprintf("is a win for the other player.\n"); break; case RE_DRAW: wprintf("forces a draw.\n"); break; } } else if (consmade < consmoves) { /* May not apply check until last move */ if (himcheck) { errcode = E_MM; consmade--; break; } repeated = inmate = FALSE; incheck = thru; } else { /* Test if other player is in mate */ incheck = himcheck; inmate = !canmove(bc,-mycolor,FALSE); /* How often has this position has occured? */ if (inmate || !istame || tamemoves >= MAXTAME-1) repeated = FALSE; else repeated = isrepeat(b,bc); } dispnew: /* Display the new position */ #ifndef NOTERMCAP if (!scrolled && issmart && (!Kriegspiel || !inmate)) doupdate(b,bc,new_move,TRUE); else { #endif NOTERMCAP if (inmate) hidden = FALSE; disp(bc, kibitz ? WHITE : mycolor); printmov(new_move,myillmoves,hisid); printcom(); wputchar('\n'); #ifndef NOTERMCAP } #endif NOTERMCAP /* Warn him about effects of stalemate */ if (inmate && !incheck) { wprint("Stalemate "); switch(Staleresult) { case RE_WIN: wprint("is a win for the other player.\n"); break; case RE_LOSE: wprint("is a win for you.\n"); break; case RE_DRAW: wprint("forces a draw.\n"); break; } } /* Let him declare a draw under fifty move rule */ if (repeated) { wprint("This position has occurred twice before.\n"); wprint("Would you like to declare a draw?"); repeated = (enteryn()); } /* Give him a chance to change his mind */ if (!Kriegspiel) { if (Help_me && (consmade == consmoves) && (p = compkib(bc,mycolor)) != SQ) wprintf("Your %s may be at risk.\n", piecename[p]); wprint("Are you sure? "); if ((ch = enter("YN\014")) == '\014') { /* Redraw the screen */ scrolled = TRUE; goto dispnew; } if (ch == 'N') { /* Don't make this move after all */ wprint("o\n"); taken = took; incheck = checked; consmade--; inmate = FALSE; stripped = FALSE; errcode = E_OK; #ifndef NOTERMCAP if (issmart) doupdate(bc,b,mbuffer,FALSE); else #endif NOTERMCAP scrolled = TRUE; break; } else wprint("es\n"); } savemove((consmoves == consmade || stripped)?'M':'m', new_move+C_MOV); /* Finish off ended games */ if (inmate || repeated || stripped) { copy(bc,b); /* So playbacks will work right */ if (incheck && inmate) askplayback(RE_WIN,EV_CHECKMATE); else if (inmate) askplayback(flip(Staleresult),EV_STALEMATE); else if (stripped) askplayback(flip(Stripresult),EV_FORCES); else askplayback(RE_DRAW,EV_REPEATED); asktrans(); return; } if (consmoves == consmade) return; else { /* Multi-move game: ask him for another */ errcode = E_OK; copy(bc,b); must_take = Musttake && canmove(b,mycolor,TRUE); break; } case 'F': wprint("Forfeit\nDo you want to forfeit? "); if (!enteryn()) break; if (Kriegspiel) { hidden = FALSE; disp(bc, kibitz ? WHITE : mycolor); printmov(new_move,myillmoves,hisid); printcom(); wputchar('\n'); } savemove('F',"forf "); askplayback(RE_LOSE,EV_FORFIET); asktrans(); return; case 'D': /* Propose Draw */ wprintf("Draw\nDo you want to %s a draw? ", (tamemoves >= MAXTAME)?"declare":"propose"); if (!enteryn()) break; savemove('D',"draw?"); /* Unilateral if more than fifty moves have been made */ if (tamemoves >= MAXTAME) { askplayback(RE_DRAW,EV_FIFTYMOVES); asktrans(); } return; case 'C': /* Propose Cancelation */ wprint("Cancel\n"); wprint("Do you want to propose that this game be discarded? "); if (!enteryn()) break; savemove('K',"canc?"); return; case 'A': /* Accept Draw or Cancellation */ wprint("Accept\n"); savemove('Y',"yes "); askplayback(drawp,EV_AGREEMENT); asktrans(); return; case 'R': /* Reject Draw or Cancellation */ wprint("Reject\n"); savemove('N',"no "); return; case 'Q': /* Quit */ wprint("Quit\n"); case 'E': /* Exit */ if (ch == 'E') wprint("Exit\n"); if (!interupt) { wprint("Sorry, not until you've moved..."); break; } wprint("Do you really want to "); if (eachgame) wprint("skip to the next game? "); else wprint("exit the program? "); if (!enteryn()) break; case 'Z': /* Exit without asking for confirmation */ return; case 'P': /* Playback previous positions */ if (moveok) wprint("Playback\n"); if (playcnt == 0) { wprint("No moves to Playback.\n"); break; } wprintf("How many moves (1-%d) do you want replayed? ", playcnt+(consmade > 0)); if ((i=readint(playcnt + (consmade > 0))) > 0) { if (!issmart) wputchar('\n'); playback(playcnt - i + (consmade > 0)); } wputchar('\n'); /* If move arrives during replay...experiment */ if (!moveok && !kibitz && mycolor == toplay) { moveok = TRUE; fseek(wfp,0L,2); } break; case '\014': /* Control-L redraws the screen */ wprint("Redraw\n"); scrolled = TRUE; break; case 'V': /* Version */ versprint(); break; } } } /* DOMATE() * * End a game. There has been a checkmate, or the game has been drawn. * * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what * became of the current player. * * <why> is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED, * or EV_AGREEMENT depending on why the game ended. */ domate(what,why) char what,why; { disp(b,kibitz?WHITE:mycolor); printmov(mbuffer,illmoves,(mycolor == toplay) ? hisid : myid); printcom(); wputchar('\n'); askplayback(what,why); if (moveok) { /* MUST do the endgame *before* the transcript, */ /* curious though it seems. */ endgame(what); asktrans(); } } /* ASKPLAYBACK() * * Ask the player for a playback, and keep asking him until he doesn't want one. * Print the message before asking each time. It displays both sides, even if * the game is kriegspiel. It is only meant to be called after a game is over. * * <what> is RE_WIN, RE_LOSE, RE_DRAW, or RE_CANCEL depending on what * became of the current player. * * <why> is EV_CHECKMATE, EV_STALEMATE, EV_FORCES, EV_FIFTYMOVES, EV_REPEATED, * or EV_AGREEMENT depending on why the game ended. */ askplayback(what,why) char what,why; { static char tbuf[70]; boolean flag; int i; if (hidden) scrolled = TRUE; hidden = FALSE; /* No reason to hide moves anymore */ switch (what) { case RE_WIN: if (solo && !kibitz) strcpy(tbuf,"Congratulations!\nYou have won"); else sprintf(tbuf,"%s has won", (mycolor == toplay) ? myid : hisid); break; case RE_LOSE: if (solo && !kibitz) strcpy(tbuf,"You have lost"); else sprintf(tbuf,"%s has won", (mycolor != toplay) ? myid : hisid); break; case RE_DRAW: strcpy(tbuf,"The game is drawn"); break; case RE_CANCEL: strcpy(tbuf,"The game is canceled"); break; } switch (why) { case EV_CHECKMATE: strcat(tbuf," by a checkmate."); break; case EV_STALEMATE: strcat(tbuf," by a stalemate."); break; case EV_FORCES: strcat(tbuf," by lose of forces."); break; case EV_FIFTYMOVES: strcat(tbuf," under the fifty move rule."); break; case EV_REPEATED: strcat(tbuf," because of repeated positions."); break; case EV_AGREEMENT: strcat(tbuf," by mutual agreement."); break; case EV_FORFIET: strcat(tbuf," by a forfieting."); break; } do { wprint(tbuf); if (playcnt == 0 && consmade == 0) { wputchar('\n'); return; } wprint("\nDo you want a playback? "); if (flag = enteryn()) { wprintf("How many moves (1-%d) do you want replayed? ", playcnt+(consmade > 0)); if ((i=readint(playcnt + (consmade > 0))) > 0) { if (!issmart) wputchar('\n'); playback(playcnt - i + (consmade > 0)); wputchar('\n'); } } } while (flag); } #ifndef NOTERMCAP /* DOUPDATE() * *Update() plus update the labels on the display. * */ doupdate(bo,bn,move,mine) schar bo[R_SIZE][C_SIZE], bn[R_SIZE][C_SIZE]; char *move; boolean mine; { short ox=cx, oy=cy; update(bo,bn); printmov(move,mine?myillmoves:illmoves,mine?myid:hisid); cursor(ox,oy); } #endif NOTERMCAP /* MGET() * * Read a possibly illegal move from the player. Set the error code if he * Gave an illegal response. Note that E_SY can only result if he hits * return prematurely. It is not really an error; it is used as a command * cancellation. */ mget(co,buffer,fr,fc,tr,tc) char *buffer; int *fr,*fc,*tr,*tc; /* from (row, column) to (row, column) */ int co; { char ch; if (consmoves > 1) wprintf("Enter your move (%d of %d): ",consmade,consmoves); else wprint("Enter your move: "); if (mread(buffer)) if (mconv(buffer,fr,fc,tr,tc) && legal(co,*fr,*fc,*tr,*tc)) { if (promote) { /* Patch up pawn promotion */ if (Promotion == PR_MINISTER) /* Only Ministers can be promoted to */ bc[*tr][*tc] = promote = WM*co; else if (Promotion == PR_NONE) /* Can't Promote */ bc[*tr][*tc] = promote = WP*co; else { /* Ask the player what to promote to */ for(;;) { wputchar('\n'); wprint("Promote pawn to: "); if ((ch = xenter("QRBNK\014"))=='\014') { errcode = E_OK; mredraw = 5; return; } promote = PIECES-(index(pc,ch)-pc); wprint(piecename[promote]); if (promote != Kingtype(co)) break; wprint("\nCan't promote to mateable piece"); } bc[*tr][*tc] = promote *= co; } } errcode = E_OK; return; } else /* Illegal move */ { /* Unless he should have know better, record it */ if (Kriegspiel && ( (errcode == E_CP) || (errcode == E_CT) || (errcode == E_PI) || (errcode == E_QB) || (errcode == E_BB) || (errcode == E_BR) || (errcode == E_MC) || (errcode == E_MM) ) ) { interupt = FALSE; myillmoves++; fprintf(wfp,"%5ld:I %5.5s\n",day(),buffer); } wputchar('\n'); } else if (mredraw == 0) errcode = E_SY; return; } /* PERROR() * * Print error messages. Current error is given by the global errcode. */ /* Three common messages */ static char kierrmsg[] = "Illegal Move."; static char kcerrmsg[] = "Can't Castle."; static char scerrmsg[] = "Castling has not been invented."; prterror() { /* If no error, do nothing */ if (errcode == E_OK) return; wprint("Error: "); switch (errcode) { case E_MT: /* move from empty square */ wprint("No friendly piece in starting square."); break; case E_AF: /* attack on friendly piece */ wprint("Can't capture your own pieces."); break; case E_EQ: /* source and destination square are equal */ wprint("Null move."); break; case E_SY: /* Move syntax error */ wprint("Command syntax unrecognizable."); break; case E_CR: wprint("Must capture if possible."); break; case E_NC: wprint("Piece may not capture."); break; case E_CP: if (Oldcastle) wprint(scerrmsg); else if (hidden) wprint(kcerrmsg); else wprint("Not in position to castle."); break; case E_CM: if (Oldcastle) wprint(scerrmsg); else wprint("Can't castle after moving king or rook."); break; case E_CT: if (Oldcastle) wprint(scerrmsg); else if (hidden) wprint(kcerrmsg); else wprint("Can't castle through or out of check."); break; case E_MC: /* Move into Check */ if (hidden) wprint(kierrmsg); else wprint("Move leaves you in check."); break; case E_MM: /* No Check on Multi-Moves */ if (hidden) wprint(kierrmsg); else wprint("Can't apply check until last move."); break; case E_CC: /* Ta'biyat violation */ wprint("Can't cross center on first turn."); break; case E_IL: /* Illegal move */ wprint(kierrmsg); break; case E_KI: /* Illegal move of King or Jester */ case E_JI: wprintf("%s moves only to adjacent squares.", piecename[errcode & 033]); break; case E_QI: /* Illegal move of Queen */ wprintf("%s moves only along rows, columns and diagonals.", piecename[WQ]); break; case E_BI: /* Illegal move of Bishop */ wprintf("%s moves only along diagonals.",piecename[WB]); break; case E_NI: /* Illegal move of Knight */ wprintf("%s moves two squares straight and one to the side.", piecename[WN]); break; case E_RI: /* Illegal move of Rook */ wprintf("%s moves only along rows or columns.",piecename[WR]); break; case E_PI: /* Illegal move of Pawn */ if (hidden) wprint(kierrmsg); else wprintf("%s moves forward or captures diagonally.", piecename[WP]); break; case E_MI: /* Illegal move of Minister */ wprintf("%s moves only to diagonally adjacent squares.", piecename[WM]); break; case E_EI: /* Illegal move of Elephant */ wprintf("%s moves two squares diagonally.",piecename[WE]); break; case E_DI: /* Illegal move of Duke */ wprintf("%s moves only to orthogonally adjacent squares.", piecename[WD]); break; case E_HI: /* Illegal move of Maharajah */ wprintf("%s moves like knight or like queen.",piecename[WH]); break; case E_KB: /* Blocked move of Kamikazi King */ if (hidden) wprint(kierrmsg); else wprintf("%s can't capture in this version of chess.", piecename[WK]); break; case E_QB: /* Blocked move of Queen */ case E_BB: /* Blocked move of Bishop */ case E_BR: /* Blocked move of Rook */ case E_BH: /* Blocked move of Maharajah */ if (hidden) wprint(kierrmsg); else wprintf("%s can't jump over other pieces.", piecename[errcode & 033]); break; default: wprintf("Weird Error %d.",errcode); } } /* MREAD() * * Read a move from the terminal, return FALSE if player chickens out * or asks for a redraw. In the latter case, mredraw is set. Supports * backspaces. Sorry Edsger. */ char nums[] = "87654321\b\n\014"; char lets[] = "LKJIHGFEDCBA\b\n\014"; mread(buffer) REGISTER char *buffer; { register int m; short ox=cx; if (mredraw > 0) { m = mredraw; mredraw = 0; if (m == 1) goto n0; wputchar(buffer[0]); if (m == 2) goto n1; wputchar(buffer[1]); wputchar('-'); if (m == 3) goto n2; wputchar(buffer[3]); if (m == 4) goto n3; wputchar(buffer[4]); return(TRUE); } n0: buffer[0] = enter(&lets[C_SIZE-1-Cols]); switch (buffer[0]) { case '\b': bell(); wputchar(' '); goto n0; case '\014': mredraw = 1; case '\n': return (FALSE); } n1: buffer[1] = enter(&nums[R_SIZE-1-Rows]); switch (buffer[1]) { case '\b': if (erases) { wputchar(' '); backspace(); } else indent(ox-1); goto n0; case '\014': mredraw = 2; case '\n': return (FALSE); } buffer[2] = '-'; wputchar('-'); n2: buffer[3] = enter(&lets[C_SIZE-1-Cols]); switch (buffer[3]) { case '\b': if (erases) { backspace(); wprint(blanks(2)); backspace(); backspace(); } else { indent(ox-1); wputchar(buffer[0]); } goto n1; case '\014': mredraw = 3; case '\n': return (FALSE); } n3: buffer[4] = enter(&nums[R_SIZE-1-Rows]); switch (buffer[4]) { case '\b': if (erases) { wputchar(' '); backspace(); } else { indent(ox-1); wputchar(buffer[0]); wputchar(buffer[1]); wputchar(buffer[2]); } goto n2; case '\014': mredraw = 4; case '\n': return (FALSE); } return(TRUE); } /* PREAD() * * Read a position from the terminal, return FALSE if player chickens out. * Supports backspaces. Sorry Edsger. */ pread(buffer) char *buffer; { short ox=cx; n0: buffer[0] = enter(&lets[C_SIZE-1-Cols]); if (buffer[0] == '\b') { bell(); wputchar(' '); goto n0; } if (buffer[0] == '\n') return (FALSE); buffer[1] = enter(&nums[R_SIZE-1-Rows]); if (buffer[1] == '\n') return (FALSE); if (buffer[1] == '\b') { if (erases) { wputchar(' '); backspace(); } else indent(ox-1); goto n0; } return(TRUE); } /* PLAYBACK() * * Display a playback of the game. */ playback(n) REGISTER int n; { int fr,fc,tr,tc; register int cnt,subcnt=0; int ill = 0; boolean wasmoveok; char probuf[15]; char c; boolean prompt = FALSE; boolean didmove = FALSE; if (n < 0) { wputchar('\n'); return; } rewind(rfp); wasmoveok = moveok; moveok = FALSE; /* Read in the initial placement */ do { if (fgetl(mbuffer,MB_LEN,rfp)==NULL) { printf("Panic: No accept line\n"); done(1); } if (mbuffer[C_CMD] == 'C') copy(ib,bc); else if (mbuffer[C_CMD] == 'P') place(bc,mbuffer[C_MOV],mbuffer+C_MOV+2); } while (mbuffer[C_CMD] != 'A'); cnt = 0; toplay = WHITE; for (;;) { /* Prompt the user */ if (prompt) { cprint("*Hit return for next board:"); c=xenter("\nQ\014"); if (!issmart) wputchar('\n'); if (c != '\n') if (c == 'Q') { /* No more updates or prompts */ n = playcnt + (consmade > 0); prompt = FALSE; } else { /* Redraw the screen */ disp(b,kibitz?WHITE:mycolor); wprint("\n\n"); continue; } } /* Display the position */ if (cnt >= n || (cnt == playcnt && consmade == subcnt)) { #ifndef NOTERMCAP if (cleardown && !scrolled) { update(b,bc); if (didmove) printmov(mbuffer,ill, toplay == mycolor ? hisid : myid); clbot(); } else { #endif NOTERMCAP disp(bc,kibitz?WHITE:mycolor); if (didmove) printmov(mbuffer,ill, toplay == mycolor ? hisid : myid); #ifndef NOTERMCAP } #endif NOTERMCAP prompt = TRUE; /* Prompt after a redraw */ copy(bc,b); if (cnt != 0) printcom(); else wputchar('\n'); } ill = 0; /* Read until we find a move line */ for( ; ; ) { if (fgetl(mbuffer,MB_LEN,rfp)==NULL) { moveok = wasmoveok; return; } if (mbuffer[C_CMD] == 'I') ill++; else if (mbuffer[C_CMD] != 'R') break; } didmove = TRUE; if (mbuffer[C_CMD] == 'm' || mbuffer[C_CMD] == 'M') { /* Checked or Mated? */ incheck = (mbuffer[C_MAT] == 'C' || mbuffer[C_MAT] == 'M'); inmate = (mbuffer[C_MAT] == 'M' || mbuffer[C_MAT] == 'S'); /* Play it on the board */ if (mconv(mbuffer+C_MOV,&fr,&fc,&tr,&tc)) { makemove(bc,toplay,fr,fc,tr,tc,FALSE); if (promote) { fgetl(probuf,14,rfp); bc[tr][tc] = promote = toplay*(PIECES-(index(pc,probuf[C_MOV])-pc)); } } else { wprint("Format error: Bad move syntax.\n"); done(1); } } if (mbuffer[C_CMD] != 'm') { cnt++; subcnt = 0; toplay = -toplay; } else subcnt++; } } /* ASKTRANS() * * Give him a transcript, if he wants one. */ asktrans() { wprint("Do you want a transcript of the game? "); if (enteryn()) trans(hidden); } /* TRANS() * * Ask what sort of transcript he wants. In visual mode. * If <masked> is true, don't print the other players moves. */ trans(masked) { FILE *tfp; char tfile[80]; boolean comments; clr(); wprintf("Printing transcript for game between %s and %s\n", myid,hisid); /* Revert to self so output files can be opened */ /* Note: Can't open game files any more after this */ setuid(getuid()); setgid(getgid()); /* Get output device */ for (;;) { wprint("\nDo you want it in a file? "); if (!enteryn()) { tfp = stdout; break; } else { wprint("Enter the filename: "); if (!readstr(tfile,0,79,TRUE)) continue; if ((tfp = fopen(tfile,"w")) == NULL) wprintf("Cannot open %s\n",tfile); else break; } } wprint("Do you want comments included? "); comments = enteryn(); /* In the future, ask about format */ cctrans(tfp,comments,masked); }