Mon, 24 Apr 2023 21:01:41 +0200
merge upstream changes
.hgignore | file | annotate | diff | comparison | revisions | |
Makefile | file | annotate | diff | comparison | revisions | |
src/c2html.c | file | annotate | diff | comparison | revisions | |
src/frontend.c | file | annotate | diff | comparison | revisions | |
src/highlighter.c | file | annotate | diff | comparison | revisions | |
src/highlighter.h | file | annotate | diff | comparison | revisions | |
test/ctest.c | file | annotate | diff | comparison | revisions | |
test/ctestfile.c | file | annotate | diff | comparison | revisions | |
test/empty.c | file | annotate | diff | comparison | revisions | |
test/golden-master/bigtest.html | file | annotate | diff | comparison | revisions | |
test/golden-master/ctest.html | file | annotate | diff | comparison | revisions | |
test/golden-master/empty.html | file | annotate | diff | comparison | revisions | |
test/golden-master/javatest.html | file | annotate | diff | comparison | revisions | |
test/golden-master/plain.html | file | annotate | diff | comparison | revisions |
1.1 --- a/.hgignore Mon Apr 24 20:54:38 2023 +0200 1.2 +++ b/.hgignore Mon Apr 24 21:01:41 2023 +0200 1.3 @@ -5,4 +5,5 @@ 1.4 \.conflict\~$ 1.5 ^build/.*$ 1.6 ^nbproject/.*$ 1.7 -^.idea/.*$ 1.8 +.idea/ 1.9 +
2.1 --- a/Makefile Mon Apr 24 20:54:38 2023 +0200 2.2 +++ b/Makefile Mon Apr 24 21:01:41 2023 +0200 2.3 @@ -49,18 +49,19 @@ 2.4 $(MKDIR) $@ 2.5 2.6 test: all 2.7 - ./build/$(BIN) test/ctestfile.c -o build/ctest.html \ 2.8 + ./build/$(BIN) test/ctest.c -o build/ctest.html \ 2.9 -H test/header.html -F test/footer.html 2.10 - ./build/$(BIN) -j test/javatestfile.java -o build/javatest.html \ 2.11 + ./build/$(BIN) -j test/javatest.java -o build/javatest.html \ 2.12 -H test/jheader.html -F test/footer.html 2.13 - ./build/$(BIN) test/bigtestfile.c -o build/bigtest.html \ 2.14 + ./build/$(BIN) test/bigtest.c -o build/bigtest.html \ 2.15 -H test/header.html -F test/footer.html 2.16 - ./build/$(BIN) -p test/plain.csp -o build/plain.html \ 2.17 + ./build/$(BIN) -p test/plain.txt -o build/plain.html \ 2.18 -H test/header.html -F test/footer.html 2.19 diff build/ctest.html test/gs/ctest.html && \ 2.20 diff build/javatest.html test/gs/javatest.html && \ 2.21 diff build/bigtest.html test/gs/bigtest.html && \ 2.22 diff build/plain.html test/gs/plain.html 2.23 + @echo "Tests successful." 2.24 2.25 clean: 2.26 $(RM) $(RMFLAGS) build
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/bigtest.c Mon Apr 24 21:01:41 2023 +0200 3.3 @@ -0,0 +1,808 @@ 3.4 +/* 3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3.6 + * 3.7 + * Copyright 2014 Mike Becker. All rights reserved. 3.8 + * 3.9 + * Redistribution and use in source and binary forms, with or without 3.10 + * modification, are permitted provided that the following conditions are met: 3.11 + * 3.12 + * 1. Redistributions of source code must retain the above copyright 3.13 + * notice, this list of conditions and the following disclaimer. 3.14 + * 3.15 + * 2. Redistributions in binary form must reproduce the above copyright 3.16 + * notice, this list of conditions and the following disclaimer in the 3.17 + * documentation and/or other materials provided with the distribution. 3.18 + * 3.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 3.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 3.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3.29 + * POSSIBILITY OF SUCH DAMAGE. 3.30 + * 3.31 + */ 3.32 + 3.33 +#include "rules.h" 3.34 +#include "chess.h" 3.35 +#include <string.h> 3.36 +#include <stdlib.h> 3.37 +#include <sys/time.h> 3.38 + 3.39 +static GameState gamestate_copy_sim(GameState *gamestate) { 3.40 + GameState simulation = *gamestate; 3.41 + if (simulation.lastmove) { 3.42 + MoveList *lastmovecopy = malloc(sizeof(MoveList)); 3.43 + *lastmovecopy = *(simulation.lastmove); 3.44 + simulation.movelist = simulation.lastmove = lastmovecopy; 3.45 + } 3.46 + 3.47 + return simulation; 3.48 +} 3.49 + 3.50 +void gamestate_init(GameState *gamestate) { 3.51 + memset(gamestate, 0, sizeof(GameState)); 3.52 + 3.53 + Board initboard = { 3.54 + {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 3.55 + {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 3.56 + {0, 0, 0, 0, 0, 0, 0, 0}, 3.57 + {0, 0, 0, 0, 0, 0, 0, 0}, 3.58 + {0, 0, 0, 0, 0, 0, 0, 0}, 3.59 + {0, 0, 0, 0, 0, 0, 0, 0}, 3.60 + {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 3.61 + {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 3.62 + }; 3.63 + memcpy(gamestate->board, initboard, sizeof(Board)); 3.64 +} 3.65 + 3.66 +void gamestate_cleanup(GameState *gamestate) { 3.67 + MoveList *elem; 3.68 + elem = gamestate->movelist; 3.69 + while (elem) { 3.70 + MoveList *cur = elem; 3.71 + elem = elem->next; 3.72 + free(cur); 3.73 + }; 3.74 +} 3.75 + 3.76 +/* MUST be called IMMEDIATLY after applying a move to work correctly */ 3.77 +static void format_move(GameState *gamestate, Move *move) { 3.78 + char *string = move->string; 3.79 + 3.80 + /* at least 8 characters should be available, wipe them out */ 3.81 + memset(string, 0, 8); 3.82 + 3.83 + /* special formats for castling */ 3.84 + if ((move->piece&PIECE_MASK) == KING && 3.85 + abs(move->tofile-move->fromfile) == 2) { 3.86 + if (move->tofile==fileidx('c')) { 3.87 + memcpy(string, "O-O-O", 5); 3.88 + } else { 3.89 + memcpy(string, "O-O", 3); 3.90 + } 3.91 + } 3.92 + 3.93 + /* start by notating the piece character */ 3.94 + string[0] = getpiecechr(move->piece); 3.95 + int idx = string[0] ? 1 : 0; 3.96 + 3.97 + /* find out how many source information we do need */ 3.98 + uint8_t piece = move->piece & PIECE_MASK; 3.99 + if (piece == PAWN) { 3.100 + if (move->capture) { 3.101 + string[idx++] = filechr(move->fromfile); 3.102 + } 3.103 + } else if (piece != KING) { 3.104 + Move threats[16]; 3.105 + uint8_t threatcount; 3.106 + get_real_threats(gamestate, move->torow, move->tofile, 3.107 + move->piece&COLOR_MASK, threats, &threatcount); 3.108 + if (threatcount > 1) { 3.109 + int ambrows = 0, ambfiles = 0; 3.110 + for (uint8_t i = 0 ; i < threatcount ; i++) { 3.111 + if (threats[i].fromrow == move->fromrow) { 3.112 + ambrows++; 3.113 + } 3.114 + if (threats[i].fromfile == move->fromfile) { 3.115 + ambfiles++; 3.116 + } 3.117 + } 3.118 + /* ambiguous row, name file */ 3.119 + if (ambrows > 1) { 3.120 + string[idx++] = filechr(move->fromfile); 3.121 + } 3.122 + /* ambiguous file, name row */ 3.123 + if (ambfiles > 1) { 3.124 + string[idx++] = filechr(move->fromrow); 3.125 + } 3.126 + } 3.127 + } 3.128 + 3.129 + /* capturing? */ 3.130 + if (move->capture) { 3.131 + string[idx++] = 'x'; 3.132 + } 3.133 + 3.134 + /* destination */ 3.135 + string[idx++] = filechr(move->tofile); 3.136 + string[idx++] = rowchr(move->torow); 3.137 + 3.138 + /* promotion? */ 3.139 + if (move->promotion) { 3.140 + string[idx++] = '='; 3.141 + string[idx++] = getpiecechr(move->promotion); 3.142 + } 3.143 + 3.144 + /* check? */ 3.145 + if (move->check) { 3.146 + /* works only, if this function is called when applying the move */ 3.147 + string[idx++] = gamestate->checkmate?'#':'+'; 3.148 + } 3.149 +} 3.150 + 3.151 +static void addmove(GameState* gamestate, Move *move) { 3.152 + MoveList *elem = malloc(sizeof(MoveList)); 3.153 + elem->next = NULL; 3.154 + elem->move = *move; 3.155 + 3.156 + struct timeval curtimestamp; 3.157 + gettimeofday(&curtimestamp, NULL); 3.158 + elem->move.timestamp.tv_sec = curtimestamp.tv_sec; 3.159 + elem->move.timestamp.tv_usec = curtimestamp.tv_usec; 3.160 + 3.161 + if (gamestate->lastmove) { 3.162 + struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); 3.163 + uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; 3.164 + suseconds_t micros; 3.165 + if (curtimestamp.tv_usec < lasttstamp->tv_usec) { 3.166 + micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); 3.167 + sec--; 3.168 + } else { 3.169 + micros = curtimestamp.tv_usec - lasttstamp->tv_usec; 3.170 + } 3.171 + 3.172 + elem->move.movetime.tv_sec = sec; 3.173 + elem->move.movetime.tv_usec = micros; 3.174 + 3.175 + gamestate->lastmove->next = elem; 3.176 + gamestate->lastmove = elem; 3.177 + } else { 3.178 + elem->move.movetime.tv_usec = 0; 3.179 + elem->move.movetime.tv_sec = 0; 3.180 + gamestate->movelist = gamestate->lastmove = elem; 3.181 + } 3.182 +} 3.183 + 3.184 +char getpiecechr(uint8_t piece) { 3.185 + switch (piece & PIECE_MASK) { 3.186 + case ROOK: return 'R'; 3.187 + case KNIGHT: return 'N'; 3.188 + case BISHOP: return 'B'; 3.189 + case QUEEN: return 'Q'; 3.190 + case KING: return 'K'; 3.191 + default: return '\0'; 3.192 + } 3.193 +} 3.194 + 3.195 +uint8_t getpiece(char c) { 3.196 + switch (c) { 3.197 + case 'R': return ROOK; 3.198 + case 'N': return KNIGHT; 3.199 + case 'B': return BISHOP; 3.200 + case 'Q': return QUEEN; 3.201 + case 'K': return KING; 3.202 + default: return 0; 3.203 + } 3.204 +} 3.205 + 3.206 +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 3.207 + uint8_t piece = move->piece & PIECE_MASK; 3.208 + uint8_t color = move->piece & COLOR_MASK; 3.209 + 3.210 + /* en passant capture */ 3.211 + if (move->capture && piece == PAWN && 3.212 + mdst(gamestate->board, move) == 0) { 3.213 + gamestate->board[move->fromrow][move->tofile] = 0; 3.214 + } 3.215 + 3.216 + /* remove old en passant threats */ 3.217 + for (uint8_t file = 0 ; file < 8 ; file++) { 3.218 + gamestate->board[3][file] &= ~ENPASSANT_THREAT; 3.219 + gamestate->board[4][file] &= ~ENPASSANT_THREAT; 3.220 + } 3.221 + 3.222 + /* add new en passant threat */ 3.223 + if (piece == PAWN && ( 3.224 + (move->fromrow == 1 && move->torow == 3) || 3.225 + (move->fromrow == 6 && move->torow == 4))) { 3.226 + move->piece |= ENPASSANT_THREAT; 3.227 + } 3.228 + 3.229 + /* move (and maybe capture or promote) */ 3.230 + msrc(gamestate->board, move) = 0; 3.231 + if (move->promotion) { 3.232 + mdst(gamestate->board, move) = move->promotion; 3.233 + } else { 3.234 + mdst(gamestate->board, move) = move->piece; 3.235 + } 3.236 + 3.237 + /* castling */ 3.238 + if (piece == KING && move->fromfile == fileidx('e')) { 3.239 + 3.240 + if (move->tofile == fileidx('g')) { 3.241 + gamestate->board[move->torow][fileidx('h')] = 0; 3.242 + gamestate->board[move->torow][fileidx('f')] = color|ROOK; 3.243 + } else if (move->tofile == fileidx('c')) { 3.244 + gamestate->board[move->torow][fileidx('a')] = 0; 3.245 + gamestate->board[move->torow][fileidx('d')] = color|ROOK; 3.246 + } 3.247 + } 3.248 + 3.249 + if (!simulate) { 3.250 + if (!move->string[0]) { 3.251 + format_move(gamestate, move); 3.252 + } 3.253 + } 3.254 + /* add move, even in simulation (checkmate test needs it) */ 3.255 + addmove(gamestate, move); 3.256 +} 3.257 + 3.258 +void apply_move(GameState *gamestate, Move *move) { 3.259 + apply_move_impl(gamestate, move, 0); 3.260 +} 3.261 + 3.262 +static int validate_move_rules(GameState *gamestate, Move *move) { 3.263 + /* validate indices (don't trust opponent) */ 3.264 + if (!chkidx(move)) { 3.265 + return INVALID_POSITION; 3.266 + } 3.267 + 3.268 + /* must move */ 3.269 + if (move->fromfile == move->tofile && move->fromrow == move->torow) { 3.270 + return INVALID_MOVE_SYNTAX; 3.271 + } 3.272 + 3.273 + /* does piece exist */ 3.274 + if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 3.275 + != (move->piece&(PIECE_MASK|COLOR_MASK))) { 3.276 + return INVALID_POSITION; 3.277 + } 3.278 + 3.279 + /* can't capture own pieces */ 3.280 + if ((mdst(gamestate->board, move) & COLOR_MASK) 3.281 + == (move->piece & COLOR_MASK)) { 3.282 + return RULES_VIOLATED; 3.283 + } 3.284 + 3.285 + /* must capture, if and only if destination is occupied */ 3.286 + if ((mdst(gamestate->board, move) == 0 && move->capture) || 3.287 + (mdst(gamestate->board, move) != 0 && !move->capture)) { 3.288 + return INVALID_MOVE_SYNTAX; 3.289 + } 3.290 + 3.291 + /* validate individual rules */ 3.292 + _Bool chkrules; 3.293 + switch (move->piece & PIECE_MASK) { 3.294 + case PAWN: 3.295 + chkrules = pawn_chkrules(gamestate, move) && 3.296 + !pawn_isblocked(gamestate, move); 3.297 + break; 3.298 + case ROOK: 3.299 + chkrules = rook_chkrules(move) && 3.300 + !rook_isblocked(gamestate, move); 3.301 + break; 3.302 + case KNIGHT: 3.303 + chkrules = knight_chkrules(move); /* knight is never blocked */ 3.304 + break; 3.305 + case BISHOP: 3.306 + chkrules = bishop_chkrules(move) && 3.307 + !bishop_isblocked(gamestate, move); 3.308 + break; 3.309 + case QUEEN: 3.310 + chkrules = queen_chkrules(move) && 3.311 + !queen_isblocked(gamestate, move); 3.312 + break; 3.313 + case KING: 3.314 + chkrules = king_chkrules(gamestate, move) && 3.315 + !king_isblocked(gamestate, move); 3.316 + break; 3.317 + default: 3.318 + return INVALID_MOVE_SYNTAX; 3.319 + } 3.320 + 3.321 + return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; 3.322 +} 3.323 + 3.324 +int validate_move(GameState *gamestate, Move *move) { 3.325 + 3.326 + int result = validate_move_rules(gamestate, move); 3.327 + 3.328 + /* cancel processing to save resources */ 3.329 + if (result != VALID_MOVE_SEMANTICS) { 3.330 + return result; 3.331 + } 3.332 + 3.333 + /* find kings for check validation */ 3.334 + uint8_t piececolor = (move->piece & COLOR_MASK); 3.335 + 3.336 + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 3.337 + for (uint8_t row = 0 ; row < 8 ; row++) { 3.338 + for (uint8_t file = 0 ; file < 8 ; file++) { 3.339 + if (gamestate->board[row][file] == 3.340 + (piececolor == WHITE?WKING:BKING)) { 3.341 + mykingfile = file; 3.342 + mykingrow = row; 3.343 + } else if (gamestate->board[row][file] == 3.344 + (piececolor == WHITE?BKING:WKING)) { 3.345 + opkingfile = file; 3.346 + opkingrow = row; 3.347 + } 3.348 + } 3.349 + } 3.350 + 3.351 + /* simulate move for check validation */ 3.352 + GameState simulation = gamestate_copy_sim(gamestate); 3.353 + Move simmove = *move; 3.354 + apply_move_impl(&simulation, &simmove, 1); 3.355 + 3.356 + /* don't move into or stay in check position */ 3.357 + if (is_covered(&simulation, mykingrow, mykingfile, 3.358 + opponent_color(piececolor))) { 3.359 + 3.360 + gamestate_cleanup(&simulation); 3.361 + if ((move->piece & PIECE_MASK) == KING) { 3.362 + return KING_MOVES_INTO_CHECK; 3.363 + } else { 3.364 + /* last move is always not null in this case */ 3.365 + return gamestate->lastmove->move.check ? 3.366 + KING_IN_CHECK : PIECE_PINNED; 3.367 + } 3.368 + } 3.369 + 3.370 + /* correct check and checkmate flags (move is still valid) */ 3.371 + Move threats[16]; 3.372 + uint8_t threatcount; 3.373 + move->check = get_threats(&simulation, opkingrow, opkingfile, 3.374 + piececolor, threats, &threatcount); 3.375 + 3.376 + if (move->check) { 3.377 + /* determine possible escape fields */ 3.378 + _Bool canescape = 0; 3.379 + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 3.380 + for (int df = -1 ; df <= 1 && !canescape ; df++) { 3.381 + if (!(dr == 0 && df == 0) && 3.382 + isidx(opkingrow + dr) && isidx(opkingfile + df)) { 3.383 + 3.384 + /* escape field neither blocked nor covered */ 3.385 + if ((simulation.board[opkingrow + dr][opkingfile + df] 3.386 + & COLOR_MASK) != opponent_color(piececolor)) { 3.387 + canescape |= !is_covered(&simulation, 3.388 + opkingrow + dr, opkingfile + df, piececolor); 3.389 + } 3.390 + } 3.391 + } 3.392 + } 3.393 + /* can't escape, can he capture? */ 3.394 + if (!canescape && threatcount == 1) { 3.395 + canescape = is_attacked(&simulation, threats[0].fromrow, 3.396 + threats[0].fromfile, opponent_color(piececolor)); 3.397 + } 3.398 + 3.399 + /* can't capture, can he block? */ 3.400 + if (!canescape && threatcount == 1) { 3.401 + Move *threat = &(threats[0]); 3.402 + uint8_t threatpiece = threat->piece & PIECE_MASK; 3.403 + 3.404 + /* knight, pawns and the king cannot be blocked */ 3.405 + if (threatpiece == BISHOP || threatpiece == ROOK 3.406 + || threatpiece == QUEEN) { 3.407 + if (threat->fromrow == threat->torow) { 3.408 + /* rook aspect (on row) */ 3.409 + int d = threat->tofile > threat->fromfile ? 1 : -1; 3.410 + uint8_t file = threat->fromfile; 3.411 + while (!canescape && file != threat->tofile - d) { 3.412 + file += d; 3.413 + canescape |= is_protected(&simulation, 3.414 + threat->torow, file, opponent_color(piececolor)); 3.415 + } 3.416 + } else if (threat->fromfile == threat->tofile) { 3.417 + /* rook aspect (on file) */ 3.418 + int d = threat->torow > threat->fromrow ? 1 : -1; 3.419 + uint8_t row = threat->fromrow; 3.420 + while (!canescape && row != threat->torow - d) { 3.421 + row += d; 3.422 + canescape |= is_protected(&simulation, 3.423 + row, threat->tofile, opponent_color(piececolor)); 3.424 + } 3.425 + } else { 3.426 + /* bishop aspect */ 3.427 + int dr = threat->torow > threat->fromrow ? 1 : -1; 3.428 + int df = threat->tofile > threat->fromfile ? 1 : -1; 3.429 + 3.430 + uint8_t row = threat->fromrow; 3.431 + uint8_t file = threat->fromfile; 3.432 + while (!canescape && file != threat->tofile - df 3.433 + && row != threat->torow - dr) { 3.434 + row += dr; 3.435 + file += df; 3.436 + canescape |= is_protected(&simulation, row, file, 3.437 + opponent_color(piececolor)); 3.438 + } 3.439 + } 3.440 + } 3.441 + } 3.442 + 3.443 + if (!canescape) { 3.444 + gamestate->checkmate = 1; 3.445 + } 3.446 + } 3.447 + 3.448 + gamestate_cleanup(&simulation); 3.449 + 3.450 + return VALID_MOVE_SEMANTICS; 3.451 +} 3.452 + 3.453 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 3.454 + uint8_t color, Move *threats, uint8_t *threatcount) { 3.455 + Move candidates[32]; 3.456 + int candidatecount = 0; 3.457 + for (uint8_t r = 0 ; r < 8 ; r++) { 3.458 + for (uint8_t f = 0 ; f < 8 ; f++) { 3.459 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 3.460 + // non-capturing move 3.461 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 3.462 + candidates[candidatecount].piece = gamestate->board[r][f]; 3.463 + candidates[candidatecount].fromrow = r; 3.464 + candidates[candidatecount].fromfile = f; 3.465 + candidates[candidatecount].torow = row; 3.466 + candidates[candidatecount].tofile = file; 3.467 + candidatecount++; 3.468 + 3.469 + // capturing move 3.470 + memcpy(&(candidates[candidatecount]), 3.471 + &(candidates[candidatecount-1]), sizeof(Move)); 3.472 + candidates[candidatecount].capture = 1; 3.473 + candidatecount++; 3.474 + } 3.475 + } 3.476 + } 3.477 + 3.478 + if (threatcount) { 3.479 + *threatcount = 0; 3.480 + } 3.481 + 3.482 + 3.483 + _Bool result = 0; 3.484 + 3.485 + for (int i = 0 ; i < candidatecount ; i++) { 3.486 + if (validate_move_rules(gamestate, &(candidates[i])) 3.487 + == VALID_MOVE_SEMANTICS) { 3.488 + result = 1; 3.489 + if (threats && threatcount) { 3.490 + threats[(*threatcount)++] = candidates[i]; 3.491 + } 3.492 + } 3.493 + } 3.494 + 3.495 + return result; 3.496 +} 3.497 + 3.498 +_Bool is_pinned(GameState *gamestate, Move *move) { 3.499 + uint8_t color = move->piece & COLOR_MASK; 3.500 + 3.501 + uint8_t kingfile = 0, kingrow = 0; 3.502 + for (uint8_t row = 0 ; row < 8 ; row++) { 3.503 + for (uint8_t file = 0 ; file < 8 ; file++) { 3.504 + if (gamestate->board[row][file] == (color|KING)) { 3.505 + kingfile = file; 3.506 + kingrow = row; 3.507 + } 3.508 + } 3.509 + } 3.510 + 3.511 + GameState simulation = gamestate_copy_sim(gamestate); 3.512 + Move simmove = *move; 3.513 + apply_move(&simulation, &simmove); 3.514 + _Bool covered = is_covered(&simulation, 3.515 + kingrow, kingfile, opponent_color(color)); 3.516 + gamestate_cleanup(&simulation); 3.517 + 3.518 + return covered; 3.519 +} 3.520 + 3.521 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 3.522 + uint8_t color, Move *threats, uint8_t *threatcount) { 3.523 + 3.524 + if (threatcount) { 3.525 + *threatcount = 0; 3.526 + } 3.527 + 3.528 + Move candidates[16]; 3.529 + uint8_t candidatecount; 3.530 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 3.531 + 3.532 + _Bool result = 0; 3.533 + uint8_t kingfile = 0, kingrow = 0; 3.534 + for (uint8_t row = 0 ; row < 8 ; row++) { 3.535 + for (uint8_t file = 0 ; file < 8 ; file++) { 3.536 + if (gamestate->board[row][file] == (color|KING)) { 3.537 + kingfile = file; 3.538 + kingrow = row; 3.539 + } 3.540 + } 3.541 + } 3.542 + 3.543 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 3.544 + GameState simulation = gamestate_copy_sim(gamestate); 3.545 + Move simmove = candidates[i]; 3.546 + apply_move(&simulation, &simmove); 3.547 + if (!is_covered(&simulation, kingrow, kingfile, 3.548 + opponent_color(color))) { 3.549 + result = 1; 3.550 + if (threats && threatcount) { 3.551 + threats[(*threatcount)++] = candidates[i]; 3.552 + } 3.553 + } 3.554 + } 3.555 + 3.556 + return result; 3.557 + } else { 3.558 + return 0; 3.559 + } 3.560 +} 3.561 + 3.562 +static int getlocation(GameState *gamestate, Move *move) { 3.563 + 3.564 + uint8_t color = move->piece & COLOR_MASK; 3.565 + _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 3.566 + 3.567 + Move threats[16], *threat = NULL; 3.568 + uint8_t threatcount; 3.569 + 3.570 + if (get_threats(gamestate, move->torow, move->tofile, color, 3.571 + threats, &threatcount)) { 3.572 + 3.573 + int reason = INVALID_POSITION; 3.574 + 3.575 + // find threats for the specified position 3.576 + for (uint8_t i = 0 ; i < threatcount ; i++) { 3.577 + if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 3.578 + == move->piece && 3.579 + (move->fromrow == POS_UNSPECIFIED || 3.580 + move->fromrow == threats[i].fromrow) && 3.581 + (move->fromfile == POS_UNSPECIFIED || 3.582 + move->fromfile == threats[i].fromfile)) { 3.583 + 3.584 + if (threat) { 3.585 + return AMBIGUOUS_MOVE; 3.586 + } else { 3.587 + // found threat is no real threat 3.588 + if (is_pinned(gamestate, &(threats[i]))) { 3.589 + reason = incheck?KING_IN_CHECK:PIECE_PINNED; 3.590 + } else { 3.591 + threat = &(threats[i]); 3.592 + } 3.593 + } 3.594 + } 3.595 + } 3.596 + 3.597 + // can't threaten specified position 3.598 + if (!threat) { 3.599 + return reason; 3.600 + } 3.601 + 3.602 + memcpy(move, threat, sizeof(Move)); 3.603 + return VALID_MOVE_SYNTAX; 3.604 + } else { 3.605 + return INVALID_POSITION; 3.606 + } 3.607 +} 3.608 + 3.609 +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { 3.610 + memset(move, 0, sizeof(Move)); 3.611 + move->fromfile = POS_UNSPECIFIED; 3.612 + move->fromrow = POS_UNSPECIFIED; 3.613 + 3.614 + size_t len = strlen(mstr); 3.615 + if (len < 1 || len > 6) { 3.616 + return INVALID_MOVE_SYNTAX; 3.617 + } 3.618 + 3.619 + /* evaluate check/checkmate flags */ 3.620 + if (mstr[len-1] == '+') { 3.621 + len--; mstr[len] = '\0'; 3.622 + move->check = 1; 3.623 + } else if (mstr[len-1] == '#') { 3.624 + len--; mstr[len] = '\0'; 3.625 + /* ignore - validation should set game state */ 3.626 + } 3.627 + 3.628 + /* evaluate promotion */ 3.629 + if (len > 3 && mstr[len-2] == '=') { 3.630 + move->promotion = getpiece(mstr[len-1]); 3.631 + if (!move->promotion) { 3.632 + return INVALID_MOVE_SYNTAX; 3.633 + } else { 3.634 + move->promotion |= color; 3.635 + len -= 2; 3.636 + mstr[len] = 0; 3.637 + } 3.638 + } 3.639 + 3.640 + if (len == 2) { 3.641 + /* pawn move (e.g. "e4") */ 3.642 + move->piece = PAWN; 3.643 + move->tofile = fileidx(mstr[0]); 3.644 + move->torow = rowidx(mstr[1]); 3.645 + } else if (len == 3) { 3.646 + if (strcmp(mstr, "O-O") == 0) { 3.647 + /* king side castling */ 3.648 + move->piece = KING; 3.649 + move->fromfile = fileidx('e'); 3.650 + move->tofile = fileidx('g'); 3.651 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 3.652 + } else { 3.653 + /* move (e.g. "Nf3") */ 3.654 + move->piece = getpiece(mstr[0]); 3.655 + move->tofile = fileidx(mstr[1]); 3.656 + move->torow = rowidx(mstr[2]); 3.657 + } 3.658 + } else if (len == 4) { 3.659 + move->piece = getpiece(mstr[0]); 3.660 + if (!move->piece) { 3.661 + move->piece = PAWN; 3.662 + move->fromfile = fileidx(mstr[0]); 3.663 + } 3.664 + if (mstr[1] == 'x') { 3.665 + /* capture (e.g. "Nxf3", "dxe5") */ 3.666 + move->capture = 1; 3.667 + } else { 3.668 + /* move (e.g. "Ndf3", "N2c3", "e2e4") */ 3.669 + if (isfile(mstr[1])) { 3.670 + move->fromfile = fileidx(mstr[1]); 3.671 + if (move->piece == PAWN) { 3.672 + move->piece = 0; 3.673 + } 3.674 + } else { 3.675 + move->fromrow = rowidx(mstr[1]); 3.676 + } 3.677 + } 3.678 + move->tofile = fileidx(mstr[2]); 3.679 + move->torow = rowidx(mstr[3]); 3.680 + } else if (len == 5) { 3.681 + if (strcmp(mstr, "O-O-O") == 0) { 3.682 + /* queen side castling "O-O-O" */ 3.683 + move->piece = KING; 3.684 + move->fromfile = fileidx('e'); 3.685 + move->tofile = fileidx('c'); 3.686 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 3.687 + } else { 3.688 + move->piece = getpiece(mstr[0]); 3.689 + if (mstr[2] == 'x') { 3.690 + move->capture = 1; 3.691 + if (move->piece) { 3.692 + /* capture (e.g. "Ndxf3") */ 3.693 + move->fromfile = fileidx(mstr[1]); 3.694 + } else { 3.695 + /* long notation capture (e.g. "e5xf6") */ 3.696 + move->piece = PAWN; 3.697 + move->fromfile = fileidx(mstr[0]); 3.698 + move->fromrow = rowidx(mstr[1]); 3.699 + } 3.700 + } else { 3.701 + /* long notation move (e.g. "Nc5a4") */ 3.702 + move->fromfile = fileidx(mstr[1]); 3.703 + move->fromrow = rowidx(mstr[2]); 3.704 + } 3.705 + move->tofile = fileidx(mstr[3]); 3.706 + move->torow = rowidx(mstr[4]); 3.707 + } 3.708 + } else if (len == 6) { 3.709 + /* long notation capture (e.g. "Nc5xf3") */ 3.710 + if (mstr[3] == 'x') { 3.711 + move->capture = 1; 3.712 + move->piece = getpiece(mstr[0]); 3.713 + move->fromfile = fileidx(mstr[1]); 3.714 + move->fromrow = rowidx(mstr[2]); 3.715 + move->tofile = fileidx(mstr[4]); 3.716 + move->torow = rowidx(mstr[5]); 3.717 + } 3.718 + } 3.719 + 3.720 + 3.721 + if (move->piece) { 3.722 + if (move->piece == PAWN 3.723 + && move->torow == (color==WHITE?7:0) 3.724 + && !move->promotion) { 3.725 + return NEED_PROMOTION; 3.726 + } 3.727 + 3.728 + move->piece |= color; 3.729 + if (move->fromfile == POS_UNSPECIFIED 3.730 + || move->fromrow == POS_UNSPECIFIED) { 3.731 + return getlocation(gamestate, move); 3.732 + } else { 3.733 + return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; 3.734 + } 3.735 + } else { 3.736 + return INVALID_MOVE_SYNTAX; 3.737 + } 3.738 +} 3.739 + 3.740 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 3.741 + uint8_t color) { 3.742 + 3.743 + Move threats[16]; 3.744 + uint8_t threatcount; 3.745 + if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 3.746 + for (int i = 0 ; i < threatcount ; i++) { 3.747 + if (threats[i].piece != (color|KING)) { 3.748 + return 1; 3.749 + } 3.750 + } 3.751 + return 0; 3.752 + } else { 3.753 + return 0; 3.754 + } 3.755 +} 3.756 + 3.757 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, 3.758 + uint8_t color) { 3.759 + if (!gameinfo->timecontrol) { 3.760 + return 0; 3.761 + } 3.762 + 3.763 + if (gamestate->movelist) { 3.764 + uint16_t time = gameinfo->time; 3.765 + suseconds_t micros = 0; 3.766 + 3.767 + MoveList *movelist = color == WHITE ? 3.768 + gamestate->movelist : gamestate->movelist->next; 3.769 + 3.770 + while (movelist) { 3.771 + time += gameinfo->addtime; 3.772 + 3.773 + struct movetimeval *movetime = &(movelist->move.movetime); 3.774 + if (movetime->tv_sec >= time) { 3.775 + return 0; 3.776 + } 3.777 + 3.778 + time -= movetime->tv_sec; 3.779 + micros += movetime->tv_usec; 3.780 + 3.781 + movelist = movelist->next ? movelist->next->next : NULL; 3.782 + } 3.783 + 3.784 + time_t sec; 3.785 + movelist = gamestate->lastmove; 3.786 + if ((movelist->move.piece & COLOR_MASK) != color) { 3.787 + struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); 3.788 + struct timeval currenttstamp; 3.789 + gettimeofday(¤ttstamp, NULL); 3.790 + micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; 3.791 + sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; 3.792 + if (sec >= time) { 3.793 + return 0; 3.794 + } 3.795 + 3.796 + time -= sec; 3.797 + } 3.798 + 3.799 + sec = micros / 1e6L; 3.800 + 3.801 + if (sec >= time) { 3.802 + return 0; 3.803 + } 3.804 + 3.805 + time -= sec; 3.806 + 3.807 + return time; 3.808 + } else { 3.809 + return gameinfo->time; 3.810 + } 3.811 +}
4.1 --- a/test/bigtestfile.c Mon Apr 24 20:54:38 2023 +0200 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,808 +0,0 @@ 4.4 -/* 4.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 4.6 - * 4.7 - * Copyright 2014 Mike Becker. All rights reserved. 4.8 - * 4.9 - * Redistribution and use in source and binary forms, with or without 4.10 - * modification, are permitted provided that the following conditions are met: 4.11 - * 4.12 - * 1. Redistributions of source code must retain the above copyright 4.13 - * notice, this list of conditions and the following disclaimer. 4.14 - * 4.15 - * 2. Redistributions in binary form must reproduce the above copyright 4.16 - * notice, this list of conditions and the following disclaimer in the 4.17 - * documentation and/or other materials provided with the distribution. 4.18 - * 4.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 4.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 4.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 4.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 4.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 4.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 4.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 4.29 - * POSSIBILITY OF SUCH DAMAGE. 4.30 - * 4.31 - */ 4.32 - 4.33 -#include "rules.h" 4.34 -#include "chess.h" 4.35 -#include <string.h> 4.36 -#include <stdlib.h> 4.37 -#include <sys/time.h> 4.38 - 4.39 -static GameState gamestate_copy_sim(GameState *gamestate) { 4.40 - GameState simulation = *gamestate; 4.41 - if (simulation.lastmove) { 4.42 - MoveList *lastmovecopy = malloc(sizeof(MoveList)); 4.43 - *lastmovecopy = *(simulation.lastmove); 4.44 - simulation.movelist = simulation.lastmove = lastmovecopy; 4.45 - } 4.46 - 4.47 - return simulation; 4.48 -} 4.49 - 4.50 -void gamestate_init(GameState *gamestate) { 4.51 - memset(gamestate, 0, sizeof(GameState)); 4.52 - 4.53 - Board initboard = { 4.54 - {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 4.55 - {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 4.56 - {0, 0, 0, 0, 0, 0, 0, 0}, 4.57 - {0, 0, 0, 0, 0, 0, 0, 0}, 4.58 - {0, 0, 0, 0, 0, 0, 0, 0}, 4.59 - {0, 0, 0, 0, 0, 0, 0, 0}, 4.60 - {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 4.61 - {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 4.62 - }; 4.63 - memcpy(gamestate->board, initboard, sizeof(Board)); 4.64 -} 4.65 - 4.66 -void gamestate_cleanup(GameState *gamestate) { 4.67 - MoveList *elem; 4.68 - elem = gamestate->movelist; 4.69 - while (elem) { 4.70 - MoveList *cur = elem; 4.71 - elem = elem->next; 4.72 - free(cur); 4.73 - }; 4.74 -} 4.75 - 4.76 -/* MUST be called IMMEDIATLY after applying a move to work correctly */ 4.77 -static void format_move(GameState *gamestate, Move *move) { 4.78 - char *string = move->string; 4.79 - 4.80 - /* at least 8 characters should be available, wipe them out */ 4.81 - memset(string, 0, 8); 4.82 - 4.83 - /* special formats for castling */ 4.84 - if ((move->piece&PIECE_MASK) == KING && 4.85 - abs(move->tofile-move->fromfile) == 2) { 4.86 - if (move->tofile==fileidx('c')) { 4.87 - memcpy(string, "O-O-O", 5); 4.88 - } else { 4.89 - memcpy(string, "O-O", 3); 4.90 - } 4.91 - } 4.92 - 4.93 - /* start by notating the piece character */ 4.94 - string[0] = getpiecechr(move->piece); 4.95 - int idx = string[0] ? 1 : 0; 4.96 - 4.97 - /* find out how many source information we do need */ 4.98 - uint8_t piece = move->piece & PIECE_MASK; 4.99 - if (piece == PAWN) { 4.100 - if (move->capture) { 4.101 - string[idx++] = filechr(move->fromfile); 4.102 - } 4.103 - } else if (piece != KING) { 4.104 - Move threats[16]; 4.105 - uint8_t threatcount; 4.106 - get_real_threats(gamestate, move->torow, move->tofile, 4.107 - move->piece&COLOR_MASK, threats, &threatcount); 4.108 - if (threatcount > 1) { 4.109 - int ambrows = 0, ambfiles = 0; 4.110 - for (uint8_t i = 0 ; i < threatcount ; i++) { 4.111 - if (threats[i].fromrow == move->fromrow) { 4.112 - ambrows++; 4.113 - } 4.114 - if (threats[i].fromfile == move->fromfile) { 4.115 - ambfiles++; 4.116 - } 4.117 - } 4.118 - /* ambiguous row, name file */ 4.119 - if (ambrows > 1) { 4.120 - string[idx++] = filechr(move->fromfile); 4.121 - } 4.122 - /* ambiguous file, name row */ 4.123 - if (ambfiles > 1) { 4.124 - string[idx++] = filechr(move->fromrow); 4.125 - } 4.126 - } 4.127 - } 4.128 - 4.129 - /* capturing? */ 4.130 - if (move->capture) { 4.131 - string[idx++] = 'x'; 4.132 - } 4.133 - 4.134 - /* destination */ 4.135 - string[idx++] = filechr(move->tofile); 4.136 - string[idx++] = rowchr(move->torow); 4.137 - 4.138 - /* promotion? */ 4.139 - if (move->promotion) { 4.140 - string[idx++] = '='; 4.141 - string[idx++] = getpiecechr(move->promotion); 4.142 - } 4.143 - 4.144 - /* check? */ 4.145 - if (move->check) { 4.146 - /* works only, if this function is called when applying the move */ 4.147 - string[idx++] = gamestate->checkmate?'#':'+'; 4.148 - } 4.149 -} 4.150 - 4.151 -static void addmove(GameState* gamestate, Move *move) { 4.152 - MoveList *elem = malloc(sizeof(MoveList)); 4.153 - elem->next = NULL; 4.154 - elem->move = *move; 4.155 - 4.156 - struct timeval curtimestamp; 4.157 - gettimeofday(&curtimestamp, NULL); 4.158 - elem->move.timestamp.tv_sec = curtimestamp.tv_sec; 4.159 - elem->move.timestamp.tv_usec = curtimestamp.tv_usec; 4.160 - 4.161 - if (gamestate->lastmove) { 4.162 - struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); 4.163 - uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; 4.164 - suseconds_t micros; 4.165 - if (curtimestamp.tv_usec < lasttstamp->tv_usec) { 4.166 - micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); 4.167 - sec--; 4.168 - } else { 4.169 - micros = curtimestamp.tv_usec - lasttstamp->tv_usec; 4.170 - } 4.171 - 4.172 - elem->move.movetime.tv_sec = sec; 4.173 - elem->move.movetime.tv_usec = micros; 4.174 - 4.175 - gamestate->lastmove->next = elem; 4.176 - gamestate->lastmove = elem; 4.177 - } else { 4.178 - elem->move.movetime.tv_usec = 0; 4.179 - elem->move.movetime.tv_sec = 0; 4.180 - gamestate->movelist = gamestate->lastmove = elem; 4.181 - } 4.182 -} 4.183 - 4.184 -char getpiecechr(uint8_t piece) { 4.185 - switch (piece & PIECE_MASK) { 4.186 - case ROOK: return 'R'; 4.187 - case KNIGHT: return 'N'; 4.188 - case BISHOP: return 'B'; 4.189 - case QUEEN: return 'Q'; 4.190 - case KING: return 'K'; 4.191 - default: return '\0'; 4.192 - } 4.193 -} 4.194 - 4.195 -uint8_t getpiece(char c) { 4.196 - switch (c) { 4.197 - case 'R': return ROOK; 4.198 - case 'N': return KNIGHT; 4.199 - case 'B': return BISHOP; 4.200 - case 'Q': return QUEEN; 4.201 - case 'K': return KING; 4.202 - default: return 0; 4.203 - } 4.204 -} 4.205 - 4.206 -static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 4.207 - uint8_t piece = move->piece & PIECE_MASK; 4.208 - uint8_t color = move->piece & COLOR_MASK; 4.209 - 4.210 - /* en passant capture */ 4.211 - if (move->capture && piece == PAWN && 4.212 - mdst(gamestate->board, move) == 0) { 4.213 - gamestate->board[move->fromrow][move->tofile] = 0; 4.214 - } 4.215 - 4.216 - /* remove old en passant threats */ 4.217 - for (uint8_t file = 0 ; file < 8 ; file++) { 4.218 - gamestate->board[3][file] &= ~ENPASSANT_THREAT; 4.219 - gamestate->board[4][file] &= ~ENPASSANT_THREAT; 4.220 - } 4.221 - 4.222 - /* add new en passant threat */ 4.223 - if (piece == PAWN && ( 4.224 - (move->fromrow == 1 && move->torow == 3) || 4.225 - (move->fromrow == 6 && move->torow == 4))) { 4.226 - move->piece |= ENPASSANT_THREAT; 4.227 - } 4.228 - 4.229 - /* move (and maybe capture or promote) */ 4.230 - msrc(gamestate->board, move) = 0; 4.231 - if (move->promotion) { 4.232 - mdst(gamestate->board, move) = move->promotion; 4.233 - } else { 4.234 - mdst(gamestate->board, move) = move->piece; 4.235 - } 4.236 - 4.237 - /* castling */ 4.238 - if (piece == KING && move->fromfile == fileidx('e')) { 4.239 - 4.240 - if (move->tofile == fileidx('g')) { 4.241 - gamestate->board[move->torow][fileidx('h')] = 0; 4.242 - gamestate->board[move->torow][fileidx('f')] = color|ROOK; 4.243 - } else if (move->tofile == fileidx('c')) { 4.244 - gamestate->board[move->torow][fileidx('a')] = 0; 4.245 - gamestate->board[move->torow][fileidx('d')] = color|ROOK; 4.246 - } 4.247 - } 4.248 - 4.249 - if (!simulate) { 4.250 - if (!move->string[0]) { 4.251 - format_move(gamestate, move); 4.252 - } 4.253 - } 4.254 - /* add move, even in simulation (checkmate test needs it) */ 4.255 - addmove(gamestate, move); 4.256 -} 4.257 - 4.258 -void apply_move(GameState *gamestate, Move *move) { 4.259 - apply_move_impl(gamestate, move, 0); 4.260 -} 4.261 - 4.262 -static int validate_move_rules(GameState *gamestate, Move *move) { 4.263 - /* validate indices (don't trust opponent) */ 4.264 - if (!chkidx(move)) { 4.265 - return INVALID_POSITION; 4.266 - } 4.267 - 4.268 - /* must move */ 4.269 - if (move->fromfile == move->tofile && move->fromrow == move->torow) { 4.270 - return INVALID_MOVE_SYNTAX; 4.271 - } 4.272 - 4.273 - /* does piece exist */ 4.274 - if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 4.275 - != (move->piece&(PIECE_MASK|COLOR_MASK))) { 4.276 - return INVALID_POSITION; 4.277 - } 4.278 - 4.279 - /* can't capture own pieces */ 4.280 - if ((mdst(gamestate->board, move) & COLOR_MASK) 4.281 - == (move->piece & COLOR_MASK)) { 4.282 - return RULES_VIOLATED; 4.283 - } 4.284 - 4.285 - /* must capture, if and only if destination is occupied */ 4.286 - if ((mdst(gamestate->board, move) == 0 && move->capture) || 4.287 - (mdst(gamestate->board, move) != 0 && !move->capture)) { 4.288 - return INVALID_MOVE_SYNTAX; 4.289 - } 4.290 - 4.291 - /* validate individual rules */ 4.292 - _Bool chkrules; 4.293 - switch (move->piece & PIECE_MASK) { 4.294 - case PAWN: 4.295 - chkrules = pawn_chkrules(gamestate, move) && 4.296 - !pawn_isblocked(gamestate, move); 4.297 - break; 4.298 - case ROOK: 4.299 - chkrules = rook_chkrules(move) && 4.300 - !rook_isblocked(gamestate, move); 4.301 - break; 4.302 - case KNIGHT: 4.303 - chkrules = knight_chkrules(move); /* knight is never blocked */ 4.304 - break; 4.305 - case BISHOP: 4.306 - chkrules = bishop_chkrules(move) && 4.307 - !bishop_isblocked(gamestate, move); 4.308 - break; 4.309 - case QUEEN: 4.310 - chkrules = queen_chkrules(move) && 4.311 - !queen_isblocked(gamestate, move); 4.312 - break; 4.313 - case KING: 4.314 - chkrules = king_chkrules(gamestate, move) && 4.315 - !king_isblocked(gamestate, move); 4.316 - break; 4.317 - default: 4.318 - return INVALID_MOVE_SYNTAX; 4.319 - } 4.320 - 4.321 - return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; 4.322 -} 4.323 - 4.324 -int validate_move(GameState *gamestate, Move *move) { 4.325 - 4.326 - int result = validate_move_rules(gamestate, move); 4.327 - 4.328 - /* cancel processing to save resources */ 4.329 - if (result != VALID_MOVE_SEMANTICS) { 4.330 - return result; 4.331 - } 4.332 - 4.333 - /* find kings for check validation */ 4.334 - uint8_t piececolor = (move->piece & COLOR_MASK); 4.335 - 4.336 - uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 4.337 - for (uint8_t row = 0 ; row < 8 ; row++) { 4.338 - for (uint8_t file = 0 ; file < 8 ; file++) { 4.339 - if (gamestate->board[row][file] == 4.340 - (piececolor == WHITE?WKING:BKING)) { 4.341 - mykingfile = file; 4.342 - mykingrow = row; 4.343 - } else if (gamestate->board[row][file] == 4.344 - (piececolor == WHITE?BKING:WKING)) { 4.345 - opkingfile = file; 4.346 - opkingrow = row; 4.347 - } 4.348 - } 4.349 - } 4.350 - 4.351 - /* simulate move for check validation */ 4.352 - GameState simulation = gamestate_copy_sim(gamestate); 4.353 - Move simmove = *move; 4.354 - apply_move_impl(&simulation, &simmove, 1); 4.355 - 4.356 - /* don't move into or stay in check position */ 4.357 - if (is_covered(&simulation, mykingrow, mykingfile, 4.358 - opponent_color(piececolor))) { 4.359 - 4.360 - gamestate_cleanup(&simulation); 4.361 - if ((move->piece & PIECE_MASK) == KING) { 4.362 - return KING_MOVES_INTO_CHECK; 4.363 - } else { 4.364 - /* last move is always not null in this case */ 4.365 - return gamestate->lastmove->move.check ? 4.366 - KING_IN_CHECK : PIECE_PINNED; 4.367 - } 4.368 - } 4.369 - 4.370 - /* correct check and checkmate flags (move is still valid) */ 4.371 - Move threats[16]; 4.372 - uint8_t threatcount; 4.373 - move->check = get_threats(&simulation, opkingrow, opkingfile, 4.374 - piececolor, threats, &threatcount); 4.375 - 4.376 - if (move->check) { 4.377 - /* determine possible escape fields */ 4.378 - _Bool canescape = 0; 4.379 - for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 4.380 - for (int df = -1 ; df <= 1 && !canescape ; df++) { 4.381 - if (!(dr == 0 && df == 0) && 4.382 - isidx(opkingrow + dr) && isidx(opkingfile + df)) { 4.383 - 4.384 - /* escape field neither blocked nor covered */ 4.385 - if ((simulation.board[opkingrow + dr][opkingfile + df] 4.386 - & COLOR_MASK) != opponent_color(piececolor)) { 4.387 - canescape |= !is_covered(&simulation, 4.388 - opkingrow + dr, opkingfile + df, piececolor); 4.389 - } 4.390 - } 4.391 - } 4.392 - } 4.393 - /* can't escape, can he capture? */ 4.394 - if (!canescape && threatcount == 1) { 4.395 - canescape = is_attacked(&simulation, threats[0].fromrow, 4.396 - threats[0].fromfile, opponent_color(piececolor)); 4.397 - } 4.398 - 4.399 - /* can't capture, can he block? */ 4.400 - if (!canescape && threatcount == 1) { 4.401 - Move *threat = &(threats[0]); 4.402 - uint8_t threatpiece = threat->piece & PIECE_MASK; 4.403 - 4.404 - /* knight, pawns and the king cannot be blocked */ 4.405 - if (threatpiece == BISHOP || threatpiece == ROOK 4.406 - || threatpiece == QUEEN) { 4.407 - if (threat->fromrow == threat->torow) { 4.408 - /* rook aspect (on row) */ 4.409 - int d = threat->tofile > threat->fromfile ? 1 : -1; 4.410 - uint8_t file = threat->fromfile; 4.411 - while (!canescape && file != threat->tofile - d) { 4.412 - file += d; 4.413 - canescape |= is_protected(&simulation, 4.414 - threat->torow, file, opponent_color(piececolor)); 4.415 - } 4.416 - } else if (threat->fromfile == threat->tofile) { 4.417 - /* rook aspect (on file) */ 4.418 - int d = threat->torow > threat->fromrow ? 1 : -1; 4.419 - uint8_t row = threat->fromrow; 4.420 - while (!canescape && row != threat->torow - d) { 4.421 - row += d; 4.422 - canescape |= is_protected(&simulation, 4.423 - row, threat->tofile, opponent_color(piececolor)); 4.424 - } 4.425 - } else { 4.426 - /* bishop aspect */ 4.427 - int dr = threat->torow > threat->fromrow ? 1 : -1; 4.428 - int df = threat->tofile > threat->fromfile ? 1 : -1; 4.429 - 4.430 - uint8_t row = threat->fromrow; 4.431 - uint8_t file = threat->fromfile; 4.432 - while (!canescape && file != threat->tofile - df 4.433 - && row != threat->torow - dr) { 4.434 - row += dr; 4.435 - file += df; 4.436 - canescape |= is_protected(&simulation, row, file, 4.437 - opponent_color(piececolor)); 4.438 - } 4.439 - } 4.440 - } 4.441 - } 4.442 - 4.443 - if (!canescape) { 4.444 - gamestate->checkmate = 1; 4.445 - } 4.446 - } 4.447 - 4.448 - gamestate_cleanup(&simulation); 4.449 - 4.450 - return VALID_MOVE_SEMANTICS; 4.451 -} 4.452 - 4.453 -_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 4.454 - uint8_t color, Move *threats, uint8_t *threatcount) { 4.455 - Move candidates[32]; 4.456 - int candidatecount = 0; 4.457 - for (uint8_t r = 0 ; r < 8 ; r++) { 4.458 - for (uint8_t f = 0 ; f < 8 ; f++) { 4.459 - if ((gamestate->board[r][f] & COLOR_MASK) == color) { 4.460 - // non-capturing move 4.461 - memset(&(candidates[candidatecount]), 0, sizeof(Move)); 4.462 - candidates[candidatecount].piece = gamestate->board[r][f]; 4.463 - candidates[candidatecount].fromrow = r; 4.464 - candidates[candidatecount].fromfile = f; 4.465 - candidates[candidatecount].torow = row; 4.466 - candidates[candidatecount].tofile = file; 4.467 - candidatecount++; 4.468 - 4.469 - // capturing move 4.470 - memcpy(&(candidates[candidatecount]), 4.471 - &(candidates[candidatecount-1]), sizeof(Move)); 4.472 - candidates[candidatecount].capture = 1; 4.473 - candidatecount++; 4.474 - } 4.475 - } 4.476 - } 4.477 - 4.478 - if (threatcount) { 4.479 - *threatcount = 0; 4.480 - } 4.481 - 4.482 - 4.483 - _Bool result = 0; 4.484 - 4.485 - for (int i = 0 ; i < candidatecount ; i++) { 4.486 - if (validate_move_rules(gamestate, &(candidates[i])) 4.487 - == VALID_MOVE_SEMANTICS) { 4.488 - result = 1; 4.489 - if (threats && threatcount) { 4.490 - threats[(*threatcount)++] = candidates[i]; 4.491 - } 4.492 - } 4.493 - } 4.494 - 4.495 - return result; 4.496 -} 4.497 - 4.498 -_Bool is_pinned(GameState *gamestate, Move *move) { 4.499 - uint8_t color = move->piece & COLOR_MASK; 4.500 - 4.501 - uint8_t kingfile = 0, kingrow = 0; 4.502 - for (uint8_t row = 0 ; row < 8 ; row++) { 4.503 - for (uint8_t file = 0 ; file < 8 ; file++) { 4.504 - if (gamestate->board[row][file] == (color|KING)) { 4.505 - kingfile = file; 4.506 - kingrow = row; 4.507 - } 4.508 - } 4.509 - } 4.510 - 4.511 - GameState simulation = gamestate_copy_sim(gamestate); 4.512 - Move simmove = *move; 4.513 - apply_move(&simulation, &simmove); 4.514 - _Bool covered = is_covered(&simulation, 4.515 - kingrow, kingfile, opponent_color(color)); 4.516 - gamestate_cleanup(&simulation); 4.517 - 4.518 - return covered; 4.519 -} 4.520 - 4.521 -_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 4.522 - uint8_t color, Move *threats, uint8_t *threatcount) { 4.523 - 4.524 - if (threatcount) { 4.525 - *threatcount = 0; 4.526 - } 4.527 - 4.528 - Move candidates[16]; 4.529 - uint8_t candidatecount; 4.530 - if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 4.531 - 4.532 - _Bool result = 0; 4.533 - uint8_t kingfile = 0, kingrow = 0; 4.534 - for (uint8_t row = 0 ; row < 8 ; row++) { 4.535 - for (uint8_t file = 0 ; file < 8 ; file++) { 4.536 - if (gamestate->board[row][file] == (color|KING)) { 4.537 - kingfile = file; 4.538 - kingrow = row; 4.539 - } 4.540 - } 4.541 - } 4.542 - 4.543 - for (uint8_t i = 0 ; i < candidatecount ; i++) { 4.544 - GameState simulation = gamestate_copy_sim(gamestate); 4.545 - Move simmove = candidates[i]; 4.546 - apply_move(&simulation, &simmove); 4.547 - if (!is_covered(&simulation, kingrow, kingfile, 4.548 - opponent_color(color))) { 4.549 - result = 1; 4.550 - if (threats && threatcount) { 4.551 - threats[(*threatcount)++] = candidates[i]; 4.552 - } 4.553 - } 4.554 - } 4.555 - 4.556 - return result; 4.557 - } else { 4.558 - return 0; 4.559 - } 4.560 -} 4.561 - 4.562 -static int getlocation(GameState *gamestate, Move *move) { 4.563 - 4.564 - uint8_t color = move->piece & COLOR_MASK; 4.565 - _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 4.566 - 4.567 - Move threats[16], *threat = NULL; 4.568 - uint8_t threatcount; 4.569 - 4.570 - if (get_threats(gamestate, move->torow, move->tofile, color, 4.571 - threats, &threatcount)) { 4.572 - 4.573 - int reason = INVALID_POSITION; 4.574 - 4.575 - // find threats for the specified position 4.576 - for (uint8_t i = 0 ; i < threatcount ; i++) { 4.577 - if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 4.578 - == move->piece && 4.579 - (move->fromrow == POS_UNSPECIFIED || 4.580 - move->fromrow == threats[i].fromrow) && 4.581 - (move->fromfile == POS_UNSPECIFIED || 4.582 - move->fromfile == threats[i].fromfile)) { 4.583 - 4.584 - if (threat) { 4.585 - return AMBIGUOUS_MOVE; 4.586 - } else { 4.587 - // found threat is no real threat 4.588 - if (is_pinned(gamestate, &(threats[i]))) { 4.589 - reason = incheck?KING_IN_CHECK:PIECE_PINNED; 4.590 - } else { 4.591 - threat = &(threats[i]); 4.592 - } 4.593 - } 4.594 - } 4.595 - } 4.596 - 4.597 - // can't threaten specified position 4.598 - if (!threat) { 4.599 - return reason; 4.600 - } 4.601 - 4.602 - memcpy(move, threat, sizeof(Move)); 4.603 - return VALID_MOVE_SYNTAX; 4.604 - } else { 4.605 - return INVALID_POSITION; 4.606 - } 4.607 -} 4.608 - 4.609 -int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { 4.610 - memset(move, 0, sizeof(Move)); 4.611 - move->fromfile = POS_UNSPECIFIED; 4.612 - move->fromrow = POS_UNSPECIFIED; 4.613 - 4.614 - size_t len = strlen(mstr); 4.615 - if (len < 1 || len > 6) { 4.616 - return INVALID_MOVE_SYNTAX; 4.617 - } 4.618 - 4.619 - /* evaluate check/checkmate flags */ 4.620 - if (mstr[len-1] == '+') { 4.621 - len--; mstr[len] = '\0'; 4.622 - move->check = 1; 4.623 - } else if (mstr[len-1] == '#') { 4.624 - len--; mstr[len] = '\0'; 4.625 - /* ignore - validation should set game state */ 4.626 - } 4.627 - 4.628 - /* evaluate promotion */ 4.629 - if (len > 3 && mstr[len-2] == '=') { 4.630 - move->promotion = getpiece(mstr[len-1]); 4.631 - if (!move->promotion) { 4.632 - return INVALID_MOVE_SYNTAX; 4.633 - } else { 4.634 - move->promotion |= color; 4.635 - len -= 2; 4.636 - mstr[len] = 0; 4.637 - } 4.638 - } 4.639 - 4.640 - if (len == 2) { 4.641 - /* pawn move (e.g. "e4") */ 4.642 - move->piece = PAWN; 4.643 - move->tofile = fileidx(mstr[0]); 4.644 - move->torow = rowidx(mstr[1]); 4.645 - } else if (len == 3) { 4.646 - if (strcmp(mstr, "O-O") == 0) { 4.647 - /* king side castling */ 4.648 - move->piece = KING; 4.649 - move->fromfile = fileidx('e'); 4.650 - move->tofile = fileidx('g'); 4.651 - move->fromrow = move->torow = color == WHITE ? 0 : 7; 4.652 - } else { 4.653 - /* move (e.g. "Nf3") */ 4.654 - move->piece = getpiece(mstr[0]); 4.655 - move->tofile = fileidx(mstr[1]); 4.656 - move->torow = rowidx(mstr[2]); 4.657 - } 4.658 - } else if (len == 4) { 4.659 - move->piece = getpiece(mstr[0]); 4.660 - if (!move->piece) { 4.661 - move->piece = PAWN; 4.662 - move->fromfile = fileidx(mstr[0]); 4.663 - } 4.664 - if (mstr[1] == 'x') { 4.665 - /* capture (e.g. "Nxf3", "dxe5") */ 4.666 - move->capture = 1; 4.667 - } else { 4.668 - /* move (e.g. "Ndf3", "N2c3", "e2e4") */ 4.669 - if (isfile(mstr[1])) { 4.670 - move->fromfile = fileidx(mstr[1]); 4.671 - if (move->piece == PAWN) { 4.672 - move->piece = 0; 4.673 - } 4.674 - } else { 4.675 - move->fromrow = rowidx(mstr[1]); 4.676 - } 4.677 - } 4.678 - move->tofile = fileidx(mstr[2]); 4.679 - move->torow = rowidx(mstr[3]); 4.680 - } else if (len == 5) { 4.681 - if (strcmp(mstr, "O-O-O") == 0) { 4.682 - /* queen side castling "O-O-O" */ 4.683 - move->piece = KING; 4.684 - move->fromfile = fileidx('e'); 4.685 - move->tofile = fileidx('c'); 4.686 - move->fromrow = move->torow = color == WHITE ? 0 : 7; 4.687 - } else { 4.688 - move->piece = getpiece(mstr[0]); 4.689 - if (mstr[2] == 'x') { 4.690 - move->capture = 1; 4.691 - if (move->piece) { 4.692 - /* capture (e.g. "Ndxf3") */ 4.693 - move->fromfile = fileidx(mstr[1]); 4.694 - } else { 4.695 - /* long notation capture (e.g. "e5xf6") */ 4.696 - move->piece = PAWN; 4.697 - move->fromfile = fileidx(mstr[0]); 4.698 - move->fromrow = rowidx(mstr[1]); 4.699 - } 4.700 - } else { 4.701 - /* long notation move (e.g. "Nc5a4") */ 4.702 - move->fromfile = fileidx(mstr[1]); 4.703 - move->fromrow = rowidx(mstr[2]); 4.704 - } 4.705 - move->tofile = fileidx(mstr[3]); 4.706 - move->torow = rowidx(mstr[4]); 4.707 - } 4.708 - } else if (len == 6) { 4.709 - /* long notation capture (e.g. "Nc5xf3") */ 4.710 - if (mstr[3] == 'x') { 4.711 - move->capture = 1; 4.712 - move->piece = getpiece(mstr[0]); 4.713 - move->fromfile = fileidx(mstr[1]); 4.714 - move->fromrow = rowidx(mstr[2]); 4.715 - move->tofile = fileidx(mstr[4]); 4.716 - move->torow = rowidx(mstr[5]); 4.717 - } 4.718 - } 4.719 - 4.720 - 4.721 - if (move->piece) { 4.722 - if (move->piece == PAWN 4.723 - && move->torow == (color==WHITE?7:0) 4.724 - && !move->promotion) { 4.725 - return NEED_PROMOTION; 4.726 - } 4.727 - 4.728 - move->piece |= color; 4.729 - if (move->fromfile == POS_UNSPECIFIED 4.730 - || move->fromrow == POS_UNSPECIFIED) { 4.731 - return getlocation(gamestate, move); 4.732 - } else { 4.733 - return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; 4.734 - } 4.735 - } else { 4.736 - return INVALID_MOVE_SYNTAX; 4.737 - } 4.738 -} 4.739 - 4.740 -_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 4.741 - uint8_t color) { 4.742 - 4.743 - Move threats[16]; 4.744 - uint8_t threatcount; 4.745 - if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 4.746 - for (int i = 0 ; i < threatcount ; i++) { 4.747 - if (threats[i].piece != (color|KING)) { 4.748 - return 1; 4.749 - } 4.750 - } 4.751 - return 0; 4.752 - } else { 4.753 - return 0; 4.754 - } 4.755 -} 4.756 - 4.757 -uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, 4.758 - uint8_t color) { 4.759 - if (!gameinfo->timecontrol) { 4.760 - return 0; 4.761 - } 4.762 - 4.763 - if (gamestate->movelist) { 4.764 - uint16_t time = gameinfo->time; 4.765 - suseconds_t micros = 0; 4.766 - 4.767 - MoveList *movelist = color == WHITE ? 4.768 - gamestate->movelist : gamestate->movelist->next; 4.769 - 4.770 - while (movelist) { 4.771 - time += gameinfo->addtime; 4.772 - 4.773 - struct movetimeval *movetime = &(movelist->move.movetime); 4.774 - if (movetime->tv_sec >= time) { 4.775 - return 0; 4.776 - } 4.777 - 4.778 - time -= movetime->tv_sec; 4.779 - micros += movetime->tv_usec; 4.780 - 4.781 - movelist = movelist->next ? movelist->next->next : NULL; 4.782 - } 4.783 - 4.784 - time_t sec; 4.785 - movelist = gamestate->lastmove; 4.786 - if ((movelist->move.piece & COLOR_MASK) != color) { 4.787 - struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); 4.788 - struct timeval currenttstamp; 4.789 - gettimeofday(¤ttstamp, NULL); 4.790 - micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; 4.791 - sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; 4.792 - if (sec >= time) { 4.793 - return 0; 4.794 - } 4.795 - 4.796 - time -= sec; 4.797 - } 4.798 - 4.799 - sec = micros / 1e6L; 4.800 - 4.801 - if (sec >= time) { 4.802 - return 0; 4.803 - } 4.804 - 4.805 - time -= sec; 4.806 - 4.807 - return time; 4.808 - } else { 4.809 - return gameinfo->time; 4.810 - } 4.811 -}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/ctest.c Mon Apr 24 21:01:41 2023 +0200 5.3 @@ -0,0 +1,391 @@ 5.4 +/* 5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 5.6 + * 5.7 + * Copyright 2015 Olaf Wintermann. All rights reserved. 5.8 + * 5.9 + * Redistribution and use in source and binary forms, with or without 5.10 + * modification, are permitted provided that the following conditions are met: 5.11 + * 5.12 + * 1. Redistributions of source code must retain the above copyright 5.13 + * notice, this list of conditions and the following disclaimer. 5.14 + * 5.15 + * 2. Redistributions in binary form must reproduce the above copyright 5.16 + * notice, this list of conditions and the following disclaimer in the 5.17 + * documentation and/or other materials provided with the distribution. 5.18 + * 5.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 5.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 5.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 5.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 5.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 5.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 5.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 5.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 5.29 + * POSSIBILITY OF SUCH DAMAGE. 5.30 + */ 5.31 + 5.32 +#include <time.h> 5.33 +#include <stdio.h> 5.34 +#include <stdlib.h> 5.35 +#include <string.h> 5.36 +#include <ucx/string.h> 5.37 +#include <ucx/buffer.h> 5.38 +#include <ucx/utils.h> 5.39 +#include <libxml/tree.h> 5.40 +#include <curl/curl.h> 5.41 + 5.42 +#include <openssl/sha.h> 5.43 +#include <openssl/hmac.h> 5.44 +#include <openssl/evp.h> 5.45 +#include <openssl/bio.h> 5.46 +#include <openssl/buffer.h> 5.47 +#include <openssl/rand.h> 5.48 + 5.49 +#include "utils.h" 5.50 +#include "crypto.h" 5.51 +#include "webdav.h" 5.52 + 5.53 +#define MACRO1337 1337L 5.54 + 5.55 +/* -------------------- This is a testing file. -------------------------- */ 5.56 +/* 5.57 +time_t util_parse_creationdate(char *str) { 5.58 + // example: 2012-11-29T21:35:35Z 5.59 + if(!str) { 5.60 + return 0; 5.61 + } 5.62 + // TODO 5.63 + return 0; 5.64 +} 5.65 +*/ 5.66 +time_t util_parse_lastmodified(char *str) { 5.67 + // example: Thu, 29 Nov 2012 21:35:35 GMT 5.68 + if(!str) { 5.69 + return 0; 5.70 + } else { 5.71 + return curl_getdate(str, NULL); 5.72 + } 5.73 +} 5.74 + 5.75 +int util_getboolean(char *v) { 5.76 + if(v[0] == 'T' || v[0] == 't') { 5.77 + return 1; 5.78 + } 5.79 + return 0; 5.80 +} 5.81 + 5.82 +int util_strtoint(char *str, int64_t *value) { 5.83 + char *end; 5.84 + int64_t val = strtoll(str, &end, 0); 5.85 + if(strlen(end) == 0) { 5.86 + *value = val; 5.87 + return 1; 5.88 + } else { 5.89 + return 0; 5.90 + } 5.91 +} 5.92 + 5.93 +char* util_url_path(char *url) { 5.94 + char *path = NULL; 5.95 + size_t len = strlen(url); 5.96 + int slashcount = 0; 5.97 + int slmax; 5.98 + if(len > 7 && !strncasecmp(url, "http://", 7)) { 5.99 + slmax = 3; 5.100 + } else if(len > 8 && !strncasecmp(url, "https://", 8)) { 5.101 + slmax = 3; 5.102 + } else { 5.103 + slmax = 1; 5.104 + } 5.105 + char c; 5.106 + for(int i=0;i<len;i++) { 5.107 + c = url[i]; 5.108 + if(c == '/') { 5.109 + slashcount++; 5.110 + if(slashcount == slmax) { 5.111 + path = url + i; 5.112 + break; 5.113 + } 5.114 + } 5.115 + } 5.116 + return path; 5.117 +} 5.118 + 5.119 +char* util_url_decode(DavSession *sn, char *url) { 5.120 + char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); 5.121 + char *ret = strdup(unesc); 5.122 + curl_free(unesc); 5.123 + return ret; 5.124 +} 5.125 + 5.126 +char* util_resource_name(char *url) { 5.127 + int si = 0; 5.128 + int osi = 0; 5.129 + int i = 0; 5.130 + int p = 0; 5.131 + char c; 5.132 + while((c = url[i]) != 0) { 5.133 + if(c == '/') { 5.134 + osi = si; 5.135 + si = i; 5.136 + p = 1; 5.137 + } 5.138 + i++; 5.139 + } 5.140 + 5.141 + char *name = url + si + p; 5.142 + if(name[0] == 0) { 5.143 + name = url + osi + p; 5.144 + if(name[0] == 0) { 5.145 + return url; 5.146 + } 5.147 + } 5.148 + 5.149 + return name; 5.150 +} 5.151 + 5.152 +int util_mkdir(char *path, mode_t mode) { 5.153 +#ifdef _WIN32 5.154 + return mkdir(path); 5.155 +#else 5.156 + return mkdir(path, mode); 5.157 +#endif 5.158 +} 5.159 + 5.160 +char* util_concat_path(char *url_base, char *p) { 5.161 + sstr_t base = sstr(url_base); 5.162 + sstr_t path; 5.163 + if(p) { 5.164 + path = sstr(p); 5.165 + } else { 5.166 + path = sstrn("", 0); 5.167 + } 5.168 + 5.169 + int add_separator = 0; 5.170 + if(base.ptr[base.length-1] == '/') { 5.171 + if(path.ptr[0] == '/') { 5.172 + base.length--; 5.173 + } 5.174 + } else { 5.175 + if(path.length == 0 || path.ptr[0] != '/') { 5.176 + add_separator = 1; 5.177 + } 5.178 + } 5.179 + 5.180 + sstr_t url; 5.181 + if(add_separator) { 5.182 + url = sstrcat(3, base, sstr("/"), path); 5.183 + } else { 5.184 + url = sstrcat(2, base, path); 5.185 + } 5.186 + 5.187 + return url.ptr; 5.188 +} 5.189 + 5.190 +void util_set_url(DavSession *sn, char *href) { 5.191 + sstr_t base = sstr(sn->base_url); 5.192 + sstr_t href_str = sstr(href); 5.193 + 5.194 + char *base_path = util_url_path(sn->base_url); 5.195 + base.length -= strlen(base_path); 5.196 + 5.197 + sstr_t url = sstrcat(2, base, href_str); 5.198 + 5.199 + curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr); 5.200 + free(url.ptr); 5.201 +} 5.202 + 5.203 +char* util_path_to_url(DavSession *sn, char *path) { 5.204 + char *space = malloc(256); 5.205 + UcxBuffer *url = ucx_buffer_new(space, 256, CX_BUFFER_AUTO_EXTEND); 5.206 + 5.207 + // add base url 5.208 + ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); 5.209 + // remove trailing slash 5.210 + ucx_buffer_seek(url, -1, SEEK_CUR); 5.211 + 5.212 + sstr_t p = sstr(path); 5.213 + ssize_t ntk = 0; 5.214 + sstr_t *tks = sstrsplit(p, S("/"), &ntk); 5.215 + 5.216 + for(int i=0;i<ntk;i++) { 5.217 + sstr_t node = tks[i]; 5.218 + if(node.length > 0) { 5.219 + char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 5.220 + ucx_buffer_putc(url, '/'); 5.221 + ucx_buffer_write(esc, 1, strlen(esc), url); 5.222 + curl_free(esc); 5.223 + } 5.224 + free(node.ptr); 5.225 + } 5.226 + free(tks); 5.227 + if(path[p.length-1] == '/') { 5.228 + ucx_buffer_putc(url, '/'); 5.229 + } 5.230 + ucx_buffer_putc(url, 0); 5.231 + 5.232 + space = url->space; 5.233 + ucx_buffer_free(url); 5.234 + 5.235 + return space; 5.236 +} 5.237 + 5.238 +char* util_parent_path(char *path) { 5.239 + char *name = util_resource_name(path); 5.240 + size_t namelen = strlen(name); 5.241 + size_t pathlen = strlen(path); 5.242 + size_t parentlen = pathlen - namelen; 5.243 + char *parent = malloc(parentlen + 1); 5.244 + memcpy(parent, path, parentlen); 5.245 + parent[parentlen] = '\0'; 5.246 + return parent; 5.247 +} 5.248 + 5.249 + 5.250 +char* util_xml_get_text(xmlNode *elm) { 5.251 + xmlNode *node = elm->children; 5.252 + while(node) { 5.253 + if(node->type == XML_TEXT_NODE) { 5.254 + return (char*)node->content; 5.255 + } 5.256 + node = node->next; 5.257 + } 5.258 + return NULL; 5.259 +} 5.260 + 5.261 + 5.262 +char* util_base64decode(char *in) { 5.263 + int len = 0; 5.264 + return util_base64decode_len(in, &len); 5.265 +} 5.266 + 5.267 +char* util_base64decode_len(char* in, int *outlen) { 5.268 + size_t len = strlen(in); 5.269 + char *out = calloc(1, len); 5.270 + 5.271 + BIO* b = BIO_new_mem_buf(in, len); 5.272 + BIO *d = BIO_new(BIO_f_base64()); 5.273 + BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL); 5.274 + b = BIO_push(d, b); 5.275 + 5.276 + *outlen = BIO_read(b, out, len); 5.277 + BIO_free_all(b); 5.278 + 5.279 + return out; 5.280 +} 5.281 + 5.282 +char* util_base64encode(char *in, size_t len) { 5.283 + BIO *b; 5.284 + BIO *e; 5.285 + BUF_MEM *mem; 5.286 + 5.287 + e = BIO_new(BIO_f_base64()); 5.288 + b = BIO_new(BIO_s_mem()); 5.289 + 5.290 + e = BIO_push(e, b); 5.291 + BIO_write(e, in, len); 5.292 + BIO_flush(e); 5.293 + 5.294 + BIO_get_mem_ptr(e, &mem); 5.295 + char *out = malloc(mem->length); 5.296 + memcpy(out, mem->data, mem->length -1); 5.297 + out[mem->length - 1] = '\0'; 5.298 + 5.299 + BIO_free_all(e); 5.300 + 5.301 + return out; 5.302 +} 5.303 + 5.304 +char* util_encrypt_str(DavSession *sn, char *str, char *key) { 5.305 + DavKey *k = dav_context_get_key(sn->context, key); 5.306 + if(!k) { 5.307 + // TODO: session error 5.308 + return NULL; 5.309 + } 5.310 + 5.311 + char *enc_str = aes_encrypt(str, k); 5.312 + char *ret_str = dav_session_strdup(sn, enc_str); 5.313 + free(enc_str); 5.314 + return ret_str; 5.315 +} 5.316 + 5.317 +/* commented out for testing reasons */ 5.318 +/* 5.319 +char* util_decrypt_str(DavSession *sn, char *str, char *key) { 5.320 + DavKey *k = dav_context_get_key(sn->context, key); 5.321 + if(!k) { 5.322 + // TODO: session error 5.323 + return NULL; 5.324 + } 5.325 + 5.326 + char *dec_str = aes_decrypt(str, k); 5.327 + char *ret_str = dav_session_strdup(sn, dec_str); 5.328 + free(dec_str); 5.329 + return ret_str; 5.330 +} 5.331 +*/ 5.332 +char* util_random_str() { 5.333 + unsigned char *str = malloc(25); 5.334 + str[24] = '\0'; 5.335 + 5.336 + sstr_t t = S( 5.337 + "01234567890" 5.338 + "abcdefghijklmnopqrstuvwxyz" 5.339 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 5.340 + const unsigned char *table = (const unsigned char*)t.ptr; 5.341 + 5.342 + RAND_pseudo_bytes(str, 24); 5.343 + for(int i=0;i<24;i++) { 5.344 + int c = str[i] % t.length; 5.345 + str[i] = table[c]; 5.346 + } 5.347 + 5.348 + return (char*)str; 5.349 +} 5.350 + 5.351 +/* 5.352 + * gets a substring from 0 to the appearance of the token 5.353 + * tokens are separated by space 5.354 + * sets sub to the substring and returns the remaining string 5.355 + */ 5.356 +sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 5.357 + int i; 5.358 + int token_start = -1; 5.359 + int token_end = -1; 5.360 + for(i=0;i<=str.length;i++) { 5.361 + int c; 5.362 + if(i == str.length) { 5.363 + c = ' '; 5.364 + } else { 5.365 + c = str.ptr[i]; 5.366 + } 5.367 + if(c < 33) { 5.368 + if(token_start != -1) { 5.369 + token_end = i; 5.370 + size_t len = token_end - token_start; 5.371 + sstr_t tk = sstrsubsl(str, token_start, len); 5.372 + //printf("token: {%.*s}\n", token.length, token.ptr); 5.373 + if(!sstrcmp(tk, token)) { 5.374 + *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 5.375 + break; 5.376 + } 5.377 + token_start = -1; 5.378 + token_end = -1; 5.379 + } 5.380 + } else { 5.381 + if(token_start == -1) { 5.382 + token_start = i; 5.383 + } 5.384 + } 5.385 + } 5.386 + 5.387 + if(i < str.length) { 5.388 + return sstrtrim(sstrsubs(str, i)); 5.389 + } else { 5.390 + str.ptr = NULL; 5.391 + str.length = 0; 5.392 + return str; 5.393 + } 5.394 +}
6.1 --- a/test/ctestfile.c Mon Apr 24 20:54:38 2023 +0200 6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 6.3 @@ -1,391 +0,0 @@ 6.4 -/* 6.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 6.6 - * 6.7 - * Copyright 2015 Olaf Wintermann. All rights reserved. 6.8 - * 6.9 - * Redistribution and use in source and binary forms, with or without 6.10 - * modification, are permitted provided that the following conditions are met: 6.11 - * 6.12 - * 1. Redistributions of source code must retain the above copyright 6.13 - * notice, this list of conditions and the following disclaimer. 6.14 - * 6.15 - * 2. Redistributions in binary form must reproduce the above copyright 6.16 - * notice, this list of conditions and the following disclaimer in the 6.17 - * documentation and/or other materials provided with the distribution. 6.18 - * 6.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 6.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 6.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 6.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 6.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 6.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 6.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 6.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 6.29 - * POSSIBILITY OF SUCH DAMAGE. 6.30 - */ 6.31 - 6.32 -#include <time.h> 6.33 -#include <stdio.h> 6.34 -#include <stdlib.h> 6.35 -#include <string.h> 6.36 -#include <ucx/string.h> 6.37 -#include <ucx/buffer.h> 6.38 -#include <ucx/utils.h> 6.39 -#include <libxml/tree.h> 6.40 -#include <curl/curl.h> 6.41 - 6.42 -#include <openssl/sha.h> 6.43 -#include <openssl/hmac.h> 6.44 -#include <openssl/evp.h> 6.45 -#include <openssl/bio.h> 6.46 -#include <openssl/buffer.h> 6.47 -#include <openssl/rand.h> 6.48 - 6.49 -#include "utils.h" 6.50 -#include "crypto.h" 6.51 -#include "webdav.h" 6.52 - 6.53 -#define MACRO1337 1337L 6.54 - 6.55 -/* -------------------- This is a testing file. -------------------------- */ 6.56 -/* 6.57 -time_t util_parse_creationdate(char *str) { 6.58 - // example: 2012-11-29T21:35:35Z 6.59 - if(!str) { 6.60 - return 0; 6.61 - } 6.62 - // TODO 6.63 - return 0; 6.64 -} 6.65 -*/ 6.66 -time_t util_parse_lastmodified(char *str) { 6.67 - // example: Thu, 29 Nov 2012 21:35:35 GMT 6.68 - if(!str) { 6.69 - return 0; 6.70 - } else { 6.71 - return curl_getdate(str, NULL); 6.72 - } 6.73 -} 6.74 - 6.75 -int util_getboolean(char *v) { 6.76 - if(v[0] == 'T' || v[0] == 't') { 6.77 - return 1; 6.78 - } 6.79 - return 0; 6.80 -} 6.81 - 6.82 -int util_strtoint(char *str, int64_t *value) { 6.83 - char *end; 6.84 - int64_t val = strtoll(str, &end, 0); 6.85 - if(strlen(end) == 0) { 6.86 - *value = val; 6.87 - return 1; 6.88 - } else { 6.89 - return 0; 6.90 - } 6.91 -} 6.92 - 6.93 -char* util_url_path(char *url) { 6.94 - char *path = NULL; 6.95 - size_t len = strlen(url); 6.96 - int slashcount = 0; 6.97 - int slmax; 6.98 - if(len > 7 && !strncasecmp(url, "http://", 7)) { 6.99 - slmax = 3; 6.100 - } else if(len > 8 && !strncasecmp(url, "https://", 8)) { 6.101 - slmax = 3; 6.102 - } else { 6.103 - slmax = 1; 6.104 - } 6.105 - char c; 6.106 - for(int i=0;i<len;i++) { 6.107 - c = url[i]; 6.108 - if(c == '/') { 6.109 - slashcount++; 6.110 - if(slashcount == slmax) { 6.111 - path = url + i; 6.112 - break; 6.113 - } 6.114 - } 6.115 - } 6.116 - return path; 6.117 -} 6.118 - 6.119 -char* util_url_decode(DavSession *sn, char *url) { 6.120 - char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); 6.121 - char *ret = strdup(unesc); 6.122 - curl_free(unesc); 6.123 - return ret; 6.124 -} 6.125 - 6.126 -char* util_resource_name(char *url) { 6.127 - int si = 0; 6.128 - int osi = 0; 6.129 - int i = 0; 6.130 - int p = 0; 6.131 - char c; 6.132 - while((c = url[i]) != 0) { 6.133 - if(c == '/') { 6.134 - osi = si; 6.135 - si = i; 6.136 - p = 1; 6.137 - } 6.138 - i++; 6.139 - } 6.140 - 6.141 - char *name = url + si + p; 6.142 - if(name[0] == 0) { 6.143 - name = url + osi + p; 6.144 - if(name[0] == 0) { 6.145 - return url; 6.146 - } 6.147 - } 6.148 - 6.149 - return name; 6.150 -} 6.151 - 6.152 -int util_mkdir(char *path, mode_t mode) { 6.153 -#ifdef _WIN32 6.154 - return mkdir(path); 6.155 -#else 6.156 - return mkdir(path, mode); 6.157 -#endif 6.158 -} 6.159 - 6.160 -char* util_concat_path(char *url_base, char *p) { 6.161 - sstr_t base = sstr(url_base); 6.162 - sstr_t path; 6.163 - if(p) { 6.164 - path = sstr(p); 6.165 - } else { 6.166 - path = sstrn("", 0); 6.167 - } 6.168 - 6.169 - int add_separator = 0; 6.170 - if(base.ptr[base.length-1] == '/') { 6.171 - if(path.ptr[0] == '/') { 6.172 - base.length--; 6.173 - } 6.174 - } else { 6.175 - if(path.length == 0 || path.ptr[0] != '/') { 6.176 - add_separator = 1; 6.177 - } 6.178 - } 6.179 - 6.180 - sstr_t url; 6.181 - if(add_separator) { 6.182 - url = sstrcat(3, base, sstr("/"), path); 6.183 - } else { 6.184 - url = sstrcat(2, base, path); 6.185 - } 6.186 - 6.187 - return url.ptr; 6.188 -} 6.189 - 6.190 -void util_set_url(DavSession *sn, char *href) { 6.191 - sstr_t base = sstr(sn->base_url); 6.192 - sstr_t href_str = sstr(href); 6.193 - 6.194 - char *base_path = util_url_path(sn->base_url); 6.195 - base.length -= strlen(base_path); 6.196 - 6.197 - sstr_t url = sstrcat(2, base, href_str); 6.198 - 6.199 - curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr); 6.200 - free(url.ptr); 6.201 -} 6.202 - 6.203 -char* util_path_to_url(DavSession *sn, char *path) { 6.204 - char *space = malloc(256); 6.205 - UcxBuffer *url = ucx_buffer_new(space, 256, CX_BUFFER_AUTO_EXTEND); 6.206 - 6.207 - // add base url 6.208 - ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); 6.209 - // remove trailing slash 6.210 - ucx_buffer_seek(url, -1, SEEK_CUR); 6.211 - 6.212 - sstr_t p = sstr(path); 6.213 - ssize_t ntk = 0; 6.214 - sstr_t *tks = sstrsplit(p, S("/"), &ntk); 6.215 - 6.216 - for(int i=0;i<ntk;i++) { 6.217 - sstr_t node = tks[i]; 6.218 - if(node.length > 0) { 6.219 - char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 6.220 - ucx_buffer_putc(url, '/'); 6.221 - ucx_buffer_write(esc, 1, strlen(esc), url); 6.222 - curl_free(esc); 6.223 - } 6.224 - free(node.ptr); 6.225 - } 6.226 - free(tks); 6.227 - if(path[p.length-1] == '/') { 6.228 - ucx_buffer_putc(url, '/'); 6.229 - } 6.230 - ucx_buffer_putc(url, 0); 6.231 - 6.232 - space = url->space; 6.233 - ucx_buffer_free(url); 6.234 - 6.235 - return space; 6.236 -} 6.237 - 6.238 -char* util_parent_path(char *path) { 6.239 - char *name = util_resource_name(path); 6.240 - size_t namelen = strlen(name); 6.241 - size_t pathlen = strlen(path); 6.242 - size_t parentlen = pathlen - namelen; 6.243 - char *parent = malloc(parentlen + 1); 6.244 - memcpy(parent, path, parentlen); 6.245 - parent[parentlen] = '\0'; 6.246 - return parent; 6.247 -} 6.248 - 6.249 - 6.250 -char* util_xml_get_text(xmlNode *elm) { 6.251 - xmlNode *node = elm->children; 6.252 - while(node) { 6.253 - if(node->type == XML_TEXT_NODE) { 6.254 - return (char*)node->content; 6.255 - } 6.256 - node = node->next; 6.257 - } 6.258 - return NULL; 6.259 -} 6.260 - 6.261 - 6.262 -char* util_base64decode(char *in) { 6.263 - int len = 0; 6.264 - return util_base64decode_len(in, &len); 6.265 -} 6.266 - 6.267 -char* util_base64decode_len(char* in, int *outlen) { 6.268 - size_t len = strlen(in); 6.269 - char *out = calloc(1, len); 6.270 - 6.271 - BIO* b = BIO_new_mem_buf(in, len); 6.272 - BIO *d = BIO_new(BIO_f_base64()); 6.273 - BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL); 6.274 - b = BIO_push(d, b); 6.275 - 6.276 - *outlen = BIO_read(b, out, len); 6.277 - BIO_free_all(b); 6.278 - 6.279 - return out; 6.280 -} 6.281 - 6.282 -char* util_base64encode(char *in, size_t len) { 6.283 - BIO *b; 6.284 - BIO *e; 6.285 - BUF_MEM *mem; 6.286 - 6.287 - e = BIO_new(BIO_f_base64()); 6.288 - b = BIO_new(BIO_s_mem()); 6.289 - 6.290 - e = BIO_push(e, b); 6.291 - BIO_write(e, in, len); 6.292 - BIO_flush(e); 6.293 - 6.294 - BIO_get_mem_ptr(e, &mem); 6.295 - char *out = malloc(mem->length); 6.296 - memcpy(out, mem->data, mem->length -1); 6.297 - out[mem->length - 1] = '\0'; 6.298 - 6.299 - BIO_free_all(e); 6.300 - 6.301 - return out; 6.302 -} 6.303 - 6.304 -char* util_encrypt_str(DavSession *sn, char *str, char *key) { 6.305 - DavKey *k = dav_context_get_key(sn->context, key); 6.306 - if(!k) { 6.307 - // TODO: session error 6.308 - return NULL; 6.309 - } 6.310 - 6.311 - char *enc_str = aes_encrypt(str, k); 6.312 - char *ret_str = dav_session_strdup(sn, enc_str); 6.313 - free(enc_str); 6.314 - return ret_str; 6.315 -} 6.316 - 6.317 -/* commented out for testing reasons */ 6.318 -/* 6.319 -char* util_decrypt_str(DavSession *sn, char *str, char *key) { 6.320 - DavKey *k = dav_context_get_key(sn->context, key); 6.321 - if(!k) { 6.322 - // TODO: session error 6.323 - return NULL; 6.324 - } 6.325 - 6.326 - char *dec_str = aes_decrypt(str, k); 6.327 - char *ret_str = dav_session_strdup(sn, dec_str); 6.328 - free(dec_str); 6.329 - return ret_str; 6.330 -} 6.331 -*/ 6.332 -char* util_random_str() { 6.333 - unsigned char *str = malloc(25); 6.334 - str[24] = '\0'; 6.335 - 6.336 - sstr_t t = S( 6.337 - "01234567890" 6.338 - "abcdefghijklmnopqrstuvwxyz" 6.339 - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 6.340 - const unsigned char *table = (const unsigned char*)t.ptr; 6.341 - 6.342 - RAND_pseudo_bytes(str, 24); 6.343 - for(int i=0;i<24;i++) { 6.344 - int c = str[i] % t.length; 6.345 - str[i] = table[c]; 6.346 - } 6.347 - 6.348 - return (char*)str; 6.349 -} 6.350 - 6.351 -/* 6.352 - * gets a substring from 0 to the appearance of the token 6.353 - * tokens are separated by space 6.354 - * sets sub to the substring and returns the remaining string 6.355 - */ 6.356 -sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 6.357 - int i; 6.358 - int token_start = -1; 6.359 - int token_end = -1; 6.360 - for(i=0;i<=str.length;i++) { 6.361 - int c; 6.362 - if(i == str.length) { 6.363 - c = ' '; 6.364 - } else { 6.365 - c = str.ptr[i]; 6.366 - } 6.367 - if(c < 33) { 6.368 - if(token_start != -1) { 6.369 - token_end = i; 6.370 - size_t len = token_end - token_start; 6.371 - sstr_t tk = sstrsubsl(str, token_start, len); 6.372 - //printf("token: {%.*s}\n", token.length, token.ptr); 6.373 - if(!sstrcmp(tk, token)) { 6.374 - *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 6.375 - break; 6.376 - } 6.377 - token_start = -1; 6.378 - token_end = -1; 6.379 - } 6.380 - } else { 6.381 - if(token_start == -1) { 6.382 - token_start = i; 6.383 - } 6.384 - } 6.385 - } 6.386 - 6.387 - if(i < str.length) { 6.388 - return sstrtrim(sstrsubs(str, i)); 6.389 - } else { 6.390 - str.ptr = NULL; 6.391 - str.length = 0; 6.392 - return str; 6.393 - } 6.394 -}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/javatest.java Mon Apr 24 21:01:41 2023 +0200 7.3 @@ -0,0 +1,176 @@ 7.4 +/* 7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 7.6 + * 7.7 + * Copyright 2014 Mike Becker. All rights reserved. 7.8 + * 7.9 + * Redistribution and use in source and binary forms, with or without 7.10 + * modification, are permitted provided that the following conditions are met: 7.11 + * 7.12 + * 1. Redistributions of source code must retain the above copyright 7.13 + * notice, this list of conditions and the following disclaimer. 7.14 + * 7.15 + * 2. Redistributions in binary form must reproduce the above copyright 7.16 + * notice, this list of conditions and the following disclaimer in the 7.17 + * documentation and/or other materials provided with the distribution. 7.18 + * 7.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 7.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 7.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 7.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 7.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 7.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 7.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 7.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 7.29 + * POSSIBILITY OF SUCH DAMAGE. 7.30 + * 7.31 + */ 7.32 + 7.33 +package de.uapcore.sigred.doc.base; 7.34 + 7.35 +import de.uapcore.sigred.doc.Resources; 7.36 +import de.uapcore.sigrapi.impl.Digraph; 7.37 +import de.uapcore.sigrapi.impl.Graph; 7.38 +import de.uapcore.sigrapi.IGraph; 7.39 +import java.io.IOException; 7.40 +import java.io.InputStream; 7.41 +import java.io.OutputStream; 7.42 +import java.util.concurrent.atomic.AtomicBoolean; 7.43 +import java.util.concurrent.atomic.AtomicReference; 7.44 +import org.apache.xerces.impl.Constants; 7.45 +import org.dom4j.Document; 7.46 +import org.dom4j.DocumentException; 7.47 +import org.dom4j.DocumentHelper; 7.48 +import org.dom4j.Element; 7.49 +import org.dom4j.Namespace; 7.50 +import org.dom4j.QName; 7.51 +import org.dom4j.io.OutputFormat; 7.52 +import org.dom4j.io.SAXReader; 7.53 +import org.dom4j.io.XMLWriter; 7.54 +import org.xml.sax.ErrorHandler; 7.55 +import org.xml.sax.SAXException; 7.56 +import org.xml.sax.SAXParseException; 7.57 + 7.58 +public abstract class AbstractGraphDocument<T extends IGraph> 7.59 + extends FileBackedDocument { 7.60 + 7.61 + protected static final Namespace NAMESPACE = Namespace.get("sigred", 7.62 + "http://develop.uap-core.de/sigred/"); 7.63 + 7.64 + private static final 7.65 + QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE); 7.66 + private static final 7.67 + QName TAG_GRAPH = QName.get("graph", NAMESPACE); 7.68 + private static final 7.69 + QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE); 7.70 + private static final 7.71 + QName TAG_METADATA = QName.get("metadata", NAMESPACE); 7.72 + 7.73 + protected final T graph; 7.74 + 7.75 + private final GraphDocumentMetadata metadata; 7.76 + 7.77 + public AbstractGraphDocument(Class<T> graphType) { 7.78 + T g; 7.79 + try { 7.80 + g = graphType.newInstance(); 7.81 + } catch (ReflectiveOperationException e) { 7.82 + assert false; 7.83 + g = null; // for the compiler 7.84 + } 7.85 + graph = g; 7.86 + metadata = new GraphDocumentMetadata(); 7.87 + } 7.88 + 7.89 + public T getGraph() { 7.90 + return graph; 7.91 + } 7.92 + 7.93 + public GraphDocumentMetadata getMetadata() { 7.94 + return metadata; 7.95 + } 7.96 + 7.97 + protected abstract void writeGraph(Element rootNode) throws IOException; 7.98 + protected abstract void readGraph(Element rootNode) throws IOException; 7.99 + 7.100 + @Override 7.101 + public void writeTo(OutputStream out) throws IOException { 7.102 + Document doc = DocumentHelper.createDocument(); 7.103 + 7.104 + Element rootNode = doc.addElement(TAG_GRAPHDOC); 7.105 + 7.106 + Element metadataNode = rootNode.addElement(TAG_METADATA); 7.107 + 7.108 + metadata.write(metadataNode); 7.109 + 7.110 + if (graph instanceof Graph) { 7.111 + writeGraph(rootNode.addElement(TAG_GRAPH)); 7.112 + } else if (graph instanceof Digraph) { 7.113 + writeGraph(rootNode.addElement(TAG_DIGRAPH)); 7.114 + } else { 7.115 + throw new IOException("unsupported graph type"); 7.116 + } 7.117 + 7.118 + XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); 7.119 + writer.write(doc); 7.120 + writer.flush(); 7.121 + } 7.122 + 7.123 + @Override 7.124 + public void readFrom(InputStream in) throws IOException { 7.125 + try { 7.126 + SAXReader reader = new SAXReader(true); 7.127 + reader.setStripWhitespaceText(true); 7.128 + 7.129 + reader.setFeature(Constants.XERCES_FEATURE_PREFIX+ 7.130 + Constants.SCHEMA_VALIDATION_FEATURE, true); 7.131 + reader.setProperty(Constants.XERCES_PROPERTY_PREFIX + 7.132 + Constants.SCHEMA_LOCATION, String.format("%s %s", 7.133 + NAMESPACE.getURI(), Resources.class.getResource( 7.134 + "graph-document.xsd").toExternalForm())); 7.135 + 7.136 + final AtomicBoolean passed = new AtomicBoolean(true); 7.137 + final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>(); 7.138 + // TODO: we should do more detailed error handling here 7.139 + reader.setErrorHandler(new ErrorHandler() { 7.140 + @Override 7.141 + public void warning(SAXParseException exception) throws SAXException { 7.142 + } 7.143 + 7.144 + @Override 7.145 + public void error(SAXParseException exception) throws SAXException { 7.146 + xmlerror.set(exception); 7.147 + passed.set(false); 7.148 + } 7.149 + 7.150 + @Override 7.151 + public void fatalError(SAXParseException exception) throws SAXException { 7.152 + xmlerror.set(exception); 7.153 + passed.set(false); 7.154 + } 7.155 + 7.156 + }); 7.157 + Document doc = reader.read(in); 7.158 + if (!passed.get()) { 7.159 + // TODO: provide details (maybe via separate error object?) 7.160 + throw xmlerror.get(); 7.161 + } 7.162 + 7.163 + doc.normalize(); 7.164 + 7.165 + Element root = doc.getRootElement(); 7.166 + metadata.read(root.element(TAG_METADATA)); 7.167 + 7.168 + if (graph instanceof Graph) { 7.169 + readGraph(root.element(TAG_GRAPH)); 7.170 + } else if (graph instanceof Digraph) { 7.171 + readGraph(root.element(TAG_DIGRAPH)); 7.172 + } else { 7.173 + throw new IOException("unsupported graph type"); 7.174 + } 7.175 + } catch (DocumentException | SAXException ex) { 7.176 + throw new IOException(ex); 7.177 + } 7.178 + } 7.179 +}
8.1 --- a/test/javatestfile.java Mon Apr 24 20:54:38 2023 +0200 8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 8.3 @@ -1,176 +0,0 @@ 8.4 -/* 8.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 8.6 - * 8.7 - * Copyright 2014 Mike Becker. All rights reserved. 8.8 - * 8.9 - * Redistribution and use in source and binary forms, with or without 8.10 - * modification, are permitted provided that the following conditions are met: 8.11 - * 8.12 - * 1. Redistributions of source code must retain the above copyright 8.13 - * notice, this list of conditions and the following disclaimer. 8.14 - * 8.15 - * 2. Redistributions in binary form must reproduce the above copyright 8.16 - * notice, this list of conditions and the following disclaimer in the 8.17 - * documentation and/or other materials provided with the distribution. 8.18 - * 8.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 8.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 8.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 8.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 8.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 8.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 8.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 8.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 8.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 8.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 8.29 - * POSSIBILITY OF SUCH DAMAGE. 8.30 - * 8.31 - */ 8.32 - 8.33 -package de.uapcore.sigred.doc.base; 8.34 - 8.35 -import de.uapcore.sigred.doc.Resources; 8.36 -import de.uapcore.sigrapi.impl.Digraph; 8.37 -import de.uapcore.sigrapi.impl.Graph; 8.38 -import de.uapcore.sigrapi.IGraph; 8.39 -import java.io.IOException; 8.40 -import java.io.InputStream; 8.41 -import java.io.OutputStream; 8.42 -import java.util.concurrent.atomic.AtomicBoolean; 8.43 -import java.util.concurrent.atomic.AtomicReference; 8.44 -import org.apache.xerces.impl.Constants; 8.45 -import org.dom4j.Document; 8.46 -import org.dom4j.DocumentException; 8.47 -import org.dom4j.DocumentHelper; 8.48 -import org.dom4j.Element; 8.49 -import org.dom4j.Namespace; 8.50 -import org.dom4j.QName; 8.51 -import org.dom4j.io.OutputFormat; 8.52 -import org.dom4j.io.SAXReader; 8.53 -import org.dom4j.io.XMLWriter; 8.54 -import org.xml.sax.ErrorHandler; 8.55 -import org.xml.sax.SAXException; 8.56 -import org.xml.sax.SAXParseException; 8.57 - 8.58 -public abstract class AbstractGraphDocument<T extends IGraph> 8.59 - extends FileBackedDocument { 8.60 - 8.61 - protected static final Namespace NAMESPACE = Namespace.get("sigred", 8.62 - "http://develop.uap-core.de/sigred/"); 8.63 - 8.64 - private static final 8.65 - QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE); 8.66 - private static final 8.67 - QName TAG_GRAPH = QName.get("graph", NAMESPACE); 8.68 - private static final 8.69 - QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE); 8.70 - private static final 8.71 - QName TAG_METADATA = QName.get("metadata", NAMESPACE); 8.72 - 8.73 - protected final T graph; 8.74 - 8.75 - private final GraphDocumentMetadata metadata; 8.76 - 8.77 - public AbstractGraphDocument(Class<T> graphType) { 8.78 - T g; 8.79 - try { 8.80 - g = graphType.newInstance(); 8.81 - } catch (ReflectiveOperationException e) { 8.82 - assert false; 8.83 - g = null; // for the compiler 8.84 - } 8.85 - graph = g; 8.86 - metadata = new GraphDocumentMetadata(); 8.87 - } 8.88 - 8.89 - public T getGraph() { 8.90 - return graph; 8.91 - } 8.92 - 8.93 - public GraphDocumentMetadata getMetadata() { 8.94 - return metadata; 8.95 - } 8.96 - 8.97 - protected abstract void writeGraph(Element rootNode) throws IOException; 8.98 - protected abstract void readGraph(Element rootNode) throws IOException; 8.99 - 8.100 - @Override 8.101 - public void writeTo(OutputStream out) throws IOException { 8.102 - Document doc = DocumentHelper.createDocument(); 8.103 - 8.104 - Element rootNode = doc.addElement(TAG_GRAPHDOC); 8.105 - 8.106 - Element metadataNode = rootNode.addElement(TAG_METADATA); 8.107 - 8.108 - metadata.write(metadataNode); 8.109 - 8.110 - if (graph instanceof Graph) { 8.111 - writeGraph(rootNode.addElement(TAG_GRAPH)); 8.112 - } else if (graph instanceof Digraph) { 8.113 - writeGraph(rootNode.addElement(TAG_DIGRAPH)); 8.114 - } else { 8.115 - throw new IOException("unsupported graph type"); 8.116 - } 8.117 - 8.118 - XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); 8.119 - writer.write(doc); 8.120 - writer.flush(); 8.121 - } 8.122 - 8.123 - @Override 8.124 - public void readFrom(InputStream in) throws IOException { 8.125 - try { 8.126 - SAXReader reader = new SAXReader(true); 8.127 - reader.setStripWhitespaceText(true); 8.128 - 8.129 - reader.setFeature(Constants.XERCES_FEATURE_PREFIX+ 8.130 - Constants.SCHEMA_VALIDATION_FEATURE, true); 8.131 - reader.setProperty(Constants.XERCES_PROPERTY_PREFIX + 8.132 - Constants.SCHEMA_LOCATION, String.format("%s %s", 8.133 - NAMESPACE.getURI(), Resources.class.getResource( 8.134 - "graph-document.xsd").toExternalForm())); 8.135 - 8.136 - final AtomicBoolean passed = new AtomicBoolean(true); 8.137 - final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>(); 8.138 - // TODO: we should do more detailed error handling here 8.139 - reader.setErrorHandler(new ErrorHandler() { 8.140 - @Override 8.141 - public void warning(SAXParseException exception) throws SAXException { 8.142 - } 8.143 - 8.144 - @Override 8.145 - public void error(SAXParseException exception) throws SAXException { 8.146 - xmlerror.set(exception); 8.147 - passed.set(false); 8.148 - } 8.149 - 8.150 - @Override 8.151 - public void fatalError(SAXParseException exception) throws SAXException { 8.152 - xmlerror.set(exception); 8.153 - passed.set(false); 8.154 - } 8.155 - 8.156 - }); 8.157 - Document doc = reader.read(in); 8.158 - if (!passed.get()) { 8.159 - // TODO: provide details (maybe via separate error object?) 8.160 - throw xmlerror.get(); 8.161 - } 8.162 - 8.163 - doc.normalize(); 8.164 - 8.165 - Element root = doc.getRootElement(); 8.166 - metadata.read(root.element(TAG_METADATA)); 8.167 - 8.168 - if (graph instanceof Graph) { 8.169 - readGraph(root.element(TAG_GRAPH)); 8.170 - } else if (graph instanceof Digraph) { 8.171 - readGraph(root.element(TAG_DIGRAPH)); 8.172 - } else { 8.173 - throw new IOException("unsupported graph type"); 8.174 - } 8.175 - } catch (DocumentException | SAXException ex) { 8.176 - throw new IOException(ex); 8.177 - } 8.178 - } 8.179 -}
9.1 --- a/test/plain.csp Mon Apr 24 20:54:38 2023 +0200 9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 9.3 @@ -1,6 +0,0 @@ 9.4 -</body> 9.5 -</html> 9.6 -<!c 9.7 -pblock_free(q); 9.8 -!> 9.9 -
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/test/plain.txt Mon Apr 24 21:01:41 2023 +0200 10.3 @@ -0,0 +1,6 @@ 10.4 +</body> 10.5 +</html> 10.6 +<!c 10.7 +pblock_free(q); 10.8 +!> 10.9 +