/* Build the list of games in the open file f. * Returns 0 for success or error number. */ int GameListBuild (FILE *f) { ChessMove cm, lastStart; int gameNumber; ListGame *currentListGame = NULL; int error, scratch=100, plyNr=0, fromX, fromY, toX, toY; int offset; char lastComment[MSG_SIZ], buf[MSG_SIZ]; TimeMark t, t2; GetTimeMark(&t); GameListFree(&gameList); yynewfile(f); gameNumber = 0; movePtr = 0; lastStart = (ChessMove) 0; yyskipmoves = FALSE; do { yyboardindex = scratch; offset = yyoffset(); quickFlag = plyNr + 1; cm = (ChessMove) Myylex(); switch (cm) { case GNUChessGame: if ((error = GameListNewGame(¤tListGame))) { rewind(f); yyskipmoves = FALSE; return(error); } currentListGame->number = ++gameNumber; currentListGame->offset = offset; if(1) { CopyBoard(boards[scratch], initialPosition); plyNr = 0; currentListGame->moves = PackGame(boards[scratch]); } if (currentListGame->gameInfo.event != NULL) { free(currentListGame->gameInfo.event); } currentListGame->gameInfo.event = StrSave(yy_text); lastStart = cm; break; case XBoardGame: lastStart = cm; break; case MoveNumberOne: switch (lastStart) { case GNUChessGame: break; /* ignore */ case PGNTag: lastStart = cm; break; /* Already started */ case (ChessMove) 0: case MoveNumberOne: case XBoardGame: if ((error = GameListNewGame(¤tListGame))) { rewind(f); yyskipmoves = FALSE; return(error); } currentListGame->number = ++gameNumber; currentListGame->offset = offset; if(1) { CopyBoard(boards[scratch], initialPosition); plyNr = 0; currentListGame->moves = PackGame(boards[scratch]); } lastStart = cm; break; default: break; /* impossible */ } break; case PGNTag: lastStart = cm; if ((error = GameListNewGame(¤tListGame))) { rewind(f); yyskipmoves = FALSE; return(error); } currentListGame->number = ++gameNumber; currentListGame->offset = offset; ParsePGNTag(yy_text, ¤tListGame->gameInfo); do { yyboardindex = 1; offset = yyoffset(); cm = (ChessMove) Myylex(); if (cm == PGNTag) { ParsePGNTag(yy_text, ¤tListGame->gameInfo); } } while (cm == PGNTag || cm == Comment); if(1) { int btm=0; if(currentListGame->gameInfo.fen) ParseFEN(boards[scratch], &btm, currentListGame->gameInfo.fen, FALSE); else CopyBoard(boards[scratch], initialPosition); plyNr = (btm != 0); currentListGame->moves = PackGame(boards[scratch]); } if(cm != NormalMove) break; case IllegalMove: if(appData.testLegality) break; case NormalMove: /* Allow the first game to start with an unnumbered move */ yyskipmoves = FALSE; if (lastStart == (ChessMove) 0) { if ((error = GameListNewGame(¤tListGame))) { rewind(f); yyskipmoves = FALSE; return(error); } currentListGame->number = ++gameNumber; currentListGame->offset = offset; if(1) { CopyBoard(boards[scratch], initialPosition); plyNr = 0; currentListGame->moves = PackGame(boards[scratch]); } lastStart = MoveNumberOne; } case WhiteCapturesEnPassant: case BlackCapturesEnPassant: case WhitePromotion: case BlackPromotion: case WhiteNonPromotion: case BlackNonPromotion: case WhiteKingSideCastle: case WhiteQueenSideCastle: case BlackKingSideCastle: case BlackQueenSideCastle: case WhiteKingSideCastleWild: case WhiteQueenSideCastleWild: case BlackKingSideCastleWild: case BlackQueenSideCastleWild: case WhiteHSideCastleFR: case WhiteASideCastleFR: case BlackHSideCastleFR: case BlackASideCastleFR: fromX = currentMoveString[0] - AAA; fromY = currentMoveString[1] - ONE; toX = currentMoveString[2] - AAA; toY = currentMoveString[3] - ONE; plyNr++; ApplyMove(fromX, fromY, toX, toY, currentMoveString[4], boards[scratch]); if(currentListGame && currentListGame->moves) PackMove(fromX, fromY, toX, toY, boards[scratch][toY][toX]); break; case WhiteWins: // [HGM] rescom: save last comment as result details case BlackWins: case GameIsDrawn: case GameUnfinished: if(!currentListGame) break; if(currentListGame->gameInfo.result == GameUnfinished) currentListGame->gameInfo.result = cm; // correct result tag with actual result if (currentListGame->gameInfo.resultDetails != NULL) { free(currentListGame->gameInfo.resultDetails); } if(yy_text[0] == '{') { char *p; safeStrCpy(lastComment, yy_text+1, sizeof(lastComment)/sizeof(lastComment[0])); if((p = strchr(lastComment, '}'))) *p = 0; currentListGame->gameInfo.resultDetails = StrSave(lastComment); } break; default: break; } if(gameNumber % 1000 == 0) { snprintf(buf, MSG_SIZ, _("Reading game file (%d)"), gameNumber); DisplayTitle(buf); DoEvents(); } } while (cm != (ChessMove) 0); if(currentListGame) { if(!currentListGame->moves) DisplayError("Game cache overflowed\nPosition-searching might not work properly", 0); if (appData.debugMode) { for (currentListGame = (ListGame *) gameList.head; currentListGame->node.succ; currentListGame = (ListGame *) currentListGame->node.succ) { fprintf(debugFP, "Parsed game number %d, offset %ld:\n", currentListGame->number, currentListGame->offset); PrintPGNTags(debugFP, ¤tListGame->gameInfo); } } } if(appData.debugMode) { GetTimeMark(&t2);printf("GameListBuild %ld msec\n", SubtractTimeMarks(&t2,&t)); } quickFlag = 0; PackGame(boards[scratch]); // for appending end-of-game marker. DisplayTitle("WinBoard"); rewind(f); yyskipmoves = FALSE; return 0; }
/** * Loops until we have received buflen characters * return -1 on failure */ static int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen, unsigned char unfinished) { double start, global_start; int got = 0; int canceled = 0; if (buf == NULL || buflen < 1 || IS_TDSDEAD(tds)) return 0; global_start = start = GetTimeMark(); while (buflen > 0) { int len; double now; if (IS_TDSDEAD(tds)) return -1; if ((len = tds_select(tds, TDSSELREAD, tds->query_timeout, global_start)) > 0) { len = READSOCKET(tds->s, buf + got, buflen); if (len < 0 && TDSSOCK_WOULDBLOCK(sock_errno)) continue; /* detect connection close */ if (len <= 0) { tds_close_socket(tds); if (len == 0) { tds_client_msg(tds->tds_ctx, tds, 20011, 6, 0, 0, "EOF in the socket."); } else { tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); } return -1; } } else if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; tds_close_socket(tds); tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout > 0 && now - start >= tds->query_timeout) { int timeout_action = TDS_INT_CONTINUE; if (canceled) return got ? got : -1; if (tds->query_timeout_func && tds->query_timeout) timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start)); switch (timeout_action) { case TDS_INT_EXIT: exit(EXIT_FAILURE); break; case TDS_INT_CANCEL: tds_send_cancel(tds); canceled = 1; /* fall through to wait while cancelling happens */ case TDS_INT_CONTINUE: start = now; default: break; } } } buflen -= len; got += len; if (unfinished && got) return got; } return got; }
static int tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, int len, unsigned char last) { double start, now; const unsigned char *p = buffer; int rc; assert(tds && buffer); if (TDS_IS_SOCKET_INVALID(tds->s)) return -1; while (p - buffer < len) { start = GetTimeMark(); now = start; if ((rc = tds_select(tds, TDSSELWRITE, tds->query_timeout, start)) > 0) { int err; size_t remaining = len - (p - buffer); #ifdef USE_MSGMORE ssize_t nput = send(tds->s, p, remaining, last ? MSG_NOSIGNAL : MSG_NOSIGNAL|MSG_MORE); /* In case the kernel does not support MSG_MORE, try again without it */ if (nput < 0 && errno == EINVAL && !last) nput = send(tds->s, p, remaining, MSG_NOSIGNAL); #elif defined(__APPLE__) && defined(SO_NOSIGPIPE) ssize_t nput = send(tds->s, p, remaining, 0); #else ssize_t nput = WRITESOCKET(tds->s, p, remaining); #endif if (nput > 0) { p += nput; continue; } err = sock_errno; if (0 == nput || TDSSOCK_WOULDBLOCK(err) || err == TDSSOCK_EINTR) continue; assert(nput < 0); tdsdump_log(TDS_DBG_NETWORK, "send(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20017, "Write to SQL Server failed"); tds_close_socket(tds); return -1; } else if (rc < 0) { int err = sock_errno; if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */ continue; tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n", err, strerror(err)); tds_report_error(tds->tds_ctx, tds, err, 20005, "select/send finished with error"); tds_close_socket(tds); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout && (now - start) >= tds->query_timeout) { tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout"); tds_close_socket(tds); return -1; } tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n"); switch (rc = tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0, "Writing to SQL server exceeded timeout")) { case TDS_INT_CONTINUE: continue; case TDS_INT_TIMEOUT: /* * "Cancel the operation ... but leave the dbproc in working condition." * We must try to send the cancel packet, else we have to abandon the dbproc. * If it can't be done, a harder error e.g. ECONNRESET will bubble up. */ tds_send_cancel(tds); continue; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } assert(0); /* not reached */ } assert(0); /* not reached */ } #ifdef USE_CORK /* force packet flush */ if (last) { int opt; opt = 0; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); opt = 1; setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt)); } #endif return len; }
/** * Select on a socket until it's available or the timeout expires. * Meanwhile, call the interrupt function. * \return >0 ready descriptors * 0 timeout * <0 error (cf. errno). Caller should close socket and return failure. * This function does not call tdserror or close the socket because it can't know the context in which it's being called. */ static int tds_select(TDSSOCKET * tds, unsigned tds_sel, int timeout_seconds, double global_start) { int rc, seconds; unsigned int poll_seconds; static const char method[] = "poll(2)"; double now, start = global_start; assert(tds != NULL); assert(timeout_seconds >= 0); /* * The select loop. * If an interrupt handler is installed, we iterate once per second, * else we try once, timing out after timeout_seconds (0 == never). * If select(2) is interrupted by a signal (e.g. press ^C in sqsh), we timeout. * (The application can retry if desired by installing a signal handler.) * * We do not measure current time against end time, to avoid being tricked by ntpd(8) or similar. * Instead, we just count down. * * We exit on the first of these events: * 1. a descriptor is ready. (return to caller) * 2. select(2) returns an important error. (return to caller) * A timeout of zero says "wait forever". We do that by passing a NULL timeval pointer to select(2). */ poll_seconds = timeout_seconds; if (tds->query_timeout > 0) { if (tds->query_timeout_func && tds_sel == TDSSELREAD) { poll_seconds = 1; } else { poll_seconds = timeout_seconds = tds->query_timeout; } } for (seconds = timeout_seconds; timeout_seconds == 0 || seconds > 0; seconds -= poll_seconds) { struct pollfd fd; int timeout = poll_seconds ? poll_seconds * 1000 : -1; fd.fd = tds->s; fd.events = tds_sel; fd.revents = 0; rc = poll(&fd, 1, timeout); if (rc > 0 ) { return rc; } if (rc < 0) { switch (sock_errno) { case TDSSOCK_EINTR: case EAGAIN: case TDSSOCK_EINPROGRESS: break; /* let interrupt handler be called */ default: /* documented: EFAULT, EBADF, EINVAL */ tdsdump_log(TDS_DBG_ERROR, "error: %s returned %d, \"%s\"\n", method, sock_errno, strerror(sock_errno)); return rc; } } assert(rc == 0 || (rc < 0 && sock_errno == TDSSOCK_EINTR)); if (tds->query_timeout > 0) { int timeout_action = TDS_INT_CONTINUE; now = GetTimeMark(); /* * "If hndlintr() returns INT_CANCEL, DB-Library sends an attention token [TDS_BUFSTAT_ATTN] * to the server. This causes the server to discontinue command processing. * The server may send additional results that have already been computed. * When control returns to the mainline code, the mainline code should do * one of the following: * - Flush the results using dbcancel * - Process the results normally" */ if (tds->query_timeout_func && tds_sel == TDSSELREAD && now - start >= tds->query_timeout) { timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start)); #if 0 tdsdump_log(TDS_DBG_ERROR, "tds_ctx->query_timeout_func returned %d\n", timeout_action); #endif start = now; } switch (timeout_action) { case TDS_INT_CONTINUE: /* keep waiting */ continue; case TDS_INT_CANCEL: /* abort the current command batch */ /* FIXME tell tds_goodread() not to call tdserror() */ return 0; default: tdsdump_log(TDS_DBG_NETWORK, "tds_select: invalid interupt handler return code: %d\n", timeout_action); return -1; } } /* * We can reach here if no interrupt handler was installed and we either timed out or got EINTR. * We cannot be polling, so we are about to drop out of the loop. */ assert(poll_seconds == timeout_seconds); } return 0; }