Mon, 03 Oct 2022 12:56:28 +0200
rewrite Makefile to work with different getopt implementations
Makefile | file | annotate | diff | comparison | revisions | |
test/bigtest.c | file | annotate | diff | comparison | revisions | |
test/bigtestfile.c | 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/emptyfile.c | file | annotate | diff | comparison | revisions | |
test/golden-master/empty.html | file | annotate | diff | comparison | revisions | |
test/golden-master/emptyfile.html | file | annotate | diff | comparison | revisions | |
test/javatest.java | file | annotate | diff | comparison | revisions | |
test/javatestfile.java | file | annotate | diff | comparison | revisions | |
test/plain.csp | file | annotate | diff | comparison | revisions | |
test/plain.txt | file | annotate | diff | comparison | revisions |
1.1 --- a/Makefile Mon Oct 03 12:27:10 2022 +0200 1.2 +++ b/Makefile Mon Oct 03 12:56:28 2022 +0200 1.3 @@ -47,23 +47,37 @@ 1.4 1.5 build: 1.6 $(MKDIR) $@ 1.7 - 1.8 -test: all 1.9 - ./build/$(BIN) test/ctestfile.c -o build/ctest.html \ 1.10 - -H test/header.html -F test/footer.html 1.11 - ./build/$(BIN) -j test/javatestfile.java -o build/javatest.html \ 1.12 - -H test/jheader.html -F test/footer.html 1.13 - ./build/$(BIN) test/bigtestfile.c -o build/bigtest.html \ 1.14 - -H test/header.html -F test/footer.html 1.15 - ./build/$(BIN) -p test/plain.csp -o build/plain.html \ 1.16 - -H test/header.html -F test/footer.html 1.17 - ./build/$(BIN) -p test/emptyfile.c -o build/emptyfile.html \ 1.18 - -H test/header.html -F test/footer.html 1.19 - diff build/ctest.html test/golden-master/ctest.html && \ 1.20 - diff build/javatest.html test/golden-master/javatest.html && \ 1.21 - diff build/bigtest.html test/golden-master/bigtest.html && \ 1.22 - diff build/plain.html test/golden-master/plain.html && \ 1.23 - diff build/emptyfile.html test/golden-master/emptyfile.html 1.24 + 1.25 +test-c: all 1.26 + for f in ctest bigtest empty ; do \ 1.27 + echo "test/$$f.c" ; \ 1.28 + ./build/$(BIN) -o "build/$$f.html" \ 1.29 + -H test/header.html \ 1.30 + -F test/footer.html \ 1.31 + "test/$$f.c" && \ 1.32 + diff "build/$$f.html" "test/golden-master/$$f.html" ; \ 1.33 + done 1.34 + 1.35 +test-java: all 1.36 + for f in javatest ; do \ 1.37 + ./build/$(BIN) -j -o "build/$$f.html" \ 1.38 + -H test/jheader.html \ 1.39 + -F test/footer.html \ 1.40 + "test/$$f.java" && \ 1.41 + diff "build/$$f.html" "test/golden-master/$$f.html" ; \ 1.42 + done 1.43 + 1.44 +test-plain: all 1.45 + for f in plain ; do \ 1.46 + ./build/$(BIN) -p -o "build/$$f.html" \ 1.47 + -H test/header.html \ 1.48 + -F test/footer.html \ 1.49 + "test/$$f.txt" && \ 1.50 + diff "build/$$f.html" "test/golden-master/$$f.html" ; \ 1.51 + done 1.52 + 1.53 +test: test-c test-java test-plain 1.54 + @echo "Tests successful." 1.55 1.56 clean: 1.57 $(RM) $(RMFLAGS) build
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/bigtest.c Mon Oct 03 12:56:28 2022 +0200 2.3 @@ -0,0 +1,808 @@ 2.4 +/* 2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2.6 + * 2.7 + * Copyright 2014 Mike Becker. All rights reserved. 2.8 + * 2.9 + * Redistribution and use in source and binary forms, with or without 2.10 + * modification, are permitted provided that the following conditions are met: 2.11 + * 2.12 + * 1. Redistributions of source code must retain the above copyright 2.13 + * notice, this list of conditions and the following disclaimer. 2.14 + * 2.15 + * 2. Redistributions in binary form must reproduce the above copyright 2.16 + * notice, this list of conditions and the following disclaimer in the 2.17 + * documentation and/or other materials provided with the distribution. 2.18 + * 2.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 2.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2.29 + * POSSIBILITY OF SUCH DAMAGE. 2.30 + * 2.31 + */ 2.32 + 2.33 +#include "rules.h" 2.34 +#include "chess.h" 2.35 +#include <string.h> 2.36 +#include <stdlib.h> 2.37 +#include <sys/time.h> 2.38 + 2.39 +static GameState gamestate_copy_sim(GameState *gamestate) { 2.40 + GameState simulation = *gamestate; 2.41 + if (simulation.lastmove) { 2.42 + MoveList *lastmovecopy = malloc(sizeof(MoveList)); 2.43 + *lastmovecopy = *(simulation.lastmove); 2.44 + simulation.movelist = simulation.lastmove = lastmovecopy; 2.45 + } 2.46 + 2.47 + return simulation; 2.48 +} 2.49 + 2.50 +void gamestate_init(GameState *gamestate) { 2.51 + memset(gamestate, 0, sizeof(GameState)); 2.52 + 2.53 + Board initboard = { 2.54 + {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 2.55 + {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 2.56 + {0, 0, 0, 0, 0, 0, 0, 0}, 2.57 + {0, 0, 0, 0, 0, 0, 0, 0}, 2.58 + {0, 0, 0, 0, 0, 0, 0, 0}, 2.59 + {0, 0, 0, 0, 0, 0, 0, 0}, 2.60 + {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 2.61 + {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 2.62 + }; 2.63 + memcpy(gamestate->board, initboard, sizeof(Board)); 2.64 +} 2.65 + 2.66 +void gamestate_cleanup(GameState *gamestate) { 2.67 + MoveList *elem; 2.68 + elem = gamestate->movelist; 2.69 + while (elem) { 2.70 + MoveList *cur = elem; 2.71 + elem = elem->next; 2.72 + free(cur); 2.73 + }; 2.74 +} 2.75 + 2.76 +/* MUST be called IMMEDIATLY after applying a move to work correctly */ 2.77 +static void format_move(GameState *gamestate, Move *move) { 2.78 + char *string = move->string; 2.79 + 2.80 + /* at least 8 characters should be available, wipe them out */ 2.81 + memset(string, 0, 8); 2.82 + 2.83 + /* special formats for castling */ 2.84 + if ((move->piece&PIECE_MASK) == KING && 2.85 + abs(move->tofile-move->fromfile) == 2) { 2.86 + if (move->tofile==fileidx('c')) { 2.87 + memcpy(string, "O-O-O", 5); 2.88 + } else { 2.89 + memcpy(string, "O-O", 3); 2.90 + } 2.91 + } 2.92 + 2.93 + /* start by notating the piece character */ 2.94 + string[0] = getpiecechr(move->piece); 2.95 + int idx = string[0] ? 1 : 0; 2.96 + 2.97 + /* find out how many source information we do need */ 2.98 + uint8_t piece = move->piece & PIECE_MASK; 2.99 + if (piece == PAWN) { 2.100 + if (move->capture) { 2.101 + string[idx++] = filechr(move->fromfile); 2.102 + } 2.103 + } else if (piece != KING) { 2.104 + Move threats[16]; 2.105 + uint8_t threatcount; 2.106 + get_real_threats(gamestate, move->torow, move->tofile, 2.107 + move->piece&COLOR_MASK, threats, &threatcount); 2.108 + if (threatcount > 1) { 2.109 + int ambrows = 0, ambfiles = 0; 2.110 + for (uint8_t i = 0 ; i < threatcount ; i++) { 2.111 + if (threats[i].fromrow == move->fromrow) { 2.112 + ambrows++; 2.113 + } 2.114 + if (threats[i].fromfile == move->fromfile) { 2.115 + ambfiles++; 2.116 + } 2.117 + } 2.118 + /* ambiguous row, name file */ 2.119 + if (ambrows > 1) { 2.120 + string[idx++] = filechr(move->fromfile); 2.121 + } 2.122 + /* ambiguous file, name row */ 2.123 + if (ambfiles > 1) { 2.124 + string[idx++] = filechr(move->fromrow); 2.125 + } 2.126 + } 2.127 + } 2.128 + 2.129 + /* capturing? */ 2.130 + if (move->capture) { 2.131 + string[idx++] = 'x'; 2.132 + } 2.133 + 2.134 + /* destination */ 2.135 + string[idx++] = filechr(move->tofile); 2.136 + string[idx++] = rowchr(move->torow); 2.137 + 2.138 + /* promotion? */ 2.139 + if (move->promotion) { 2.140 + string[idx++] = '='; 2.141 + string[idx++] = getpiecechr(move->promotion); 2.142 + } 2.143 + 2.144 + /* check? */ 2.145 + if (move->check) { 2.146 + /* works only, if this function is called when applying the move */ 2.147 + string[idx++] = gamestate->checkmate?'#':'+'; 2.148 + } 2.149 +} 2.150 + 2.151 +static void addmove(GameState* gamestate, Move *move) { 2.152 + MoveList *elem = malloc(sizeof(MoveList)); 2.153 + elem->next = NULL; 2.154 + elem->move = *move; 2.155 + 2.156 + struct timeval curtimestamp; 2.157 + gettimeofday(&curtimestamp, NULL); 2.158 + elem->move.timestamp.tv_sec = curtimestamp.tv_sec; 2.159 + elem->move.timestamp.tv_usec = curtimestamp.tv_usec; 2.160 + 2.161 + if (gamestate->lastmove) { 2.162 + struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); 2.163 + uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; 2.164 + suseconds_t micros; 2.165 + if (curtimestamp.tv_usec < lasttstamp->tv_usec) { 2.166 + micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); 2.167 + sec--; 2.168 + } else { 2.169 + micros = curtimestamp.tv_usec - lasttstamp->tv_usec; 2.170 + } 2.171 + 2.172 + elem->move.movetime.tv_sec = sec; 2.173 + elem->move.movetime.tv_usec = micros; 2.174 + 2.175 + gamestate->lastmove->next = elem; 2.176 + gamestate->lastmove = elem; 2.177 + } else { 2.178 + elem->move.movetime.tv_usec = 0; 2.179 + elem->move.movetime.tv_sec = 0; 2.180 + gamestate->movelist = gamestate->lastmove = elem; 2.181 + } 2.182 +} 2.183 + 2.184 +char getpiecechr(uint8_t piece) { 2.185 + switch (piece & PIECE_MASK) { 2.186 + case ROOK: return 'R'; 2.187 + case KNIGHT: return 'N'; 2.188 + case BISHOP: return 'B'; 2.189 + case QUEEN: return 'Q'; 2.190 + case KING: return 'K'; 2.191 + default: return '\0'; 2.192 + } 2.193 +} 2.194 + 2.195 +uint8_t getpiece(char c) { 2.196 + switch (c) { 2.197 + case 'R': return ROOK; 2.198 + case 'N': return KNIGHT; 2.199 + case 'B': return BISHOP; 2.200 + case 'Q': return QUEEN; 2.201 + case 'K': return KING; 2.202 + default: return 0; 2.203 + } 2.204 +} 2.205 + 2.206 +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 2.207 + uint8_t piece = move->piece & PIECE_MASK; 2.208 + uint8_t color = move->piece & COLOR_MASK; 2.209 + 2.210 + /* en passant capture */ 2.211 + if (move->capture && piece == PAWN && 2.212 + mdst(gamestate->board, move) == 0) { 2.213 + gamestate->board[move->fromrow][move->tofile] = 0; 2.214 + } 2.215 + 2.216 + /* remove old en passant threats */ 2.217 + for (uint8_t file = 0 ; file < 8 ; file++) { 2.218 + gamestate->board[3][file] &= ~ENPASSANT_THREAT; 2.219 + gamestate->board[4][file] &= ~ENPASSANT_THREAT; 2.220 + } 2.221 + 2.222 + /* add new en passant threat */ 2.223 + if (piece == PAWN && ( 2.224 + (move->fromrow == 1 && move->torow == 3) || 2.225 + (move->fromrow == 6 && move->torow == 4))) { 2.226 + move->piece |= ENPASSANT_THREAT; 2.227 + } 2.228 + 2.229 + /* move (and maybe capture or promote) */ 2.230 + msrc(gamestate->board, move) = 0; 2.231 + if (move->promotion) { 2.232 + mdst(gamestate->board, move) = move->promotion; 2.233 + } else { 2.234 + mdst(gamestate->board, move) = move->piece; 2.235 + } 2.236 + 2.237 + /* castling */ 2.238 + if (piece == KING && move->fromfile == fileidx('e')) { 2.239 + 2.240 + if (move->tofile == fileidx('g')) { 2.241 + gamestate->board[move->torow][fileidx('h')] = 0; 2.242 + gamestate->board[move->torow][fileidx('f')] = color|ROOK; 2.243 + } else if (move->tofile == fileidx('c')) { 2.244 + gamestate->board[move->torow][fileidx('a')] = 0; 2.245 + gamestate->board[move->torow][fileidx('d')] = color|ROOK; 2.246 + } 2.247 + } 2.248 + 2.249 + if (!simulate) { 2.250 + if (!move->string[0]) { 2.251 + format_move(gamestate, move); 2.252 + } 2.253 + } 2.254 + /* add move, even in simulation (checkmate test needs it) */ 2.255 + addmove(gamestate, move); 2.256 +} 2.257 + 2.258 +void apply_move(GameState *gamestate, Move *move) { 2.259 + apply_move_impl(gamestate, move, 0); 2.260 +} 2.261 + 2.262 +static int validate_move_rules(GameState *gamestate, Move *move) { 2.263 + /* validate indices (don't trust opponent) */ 2.264 + if (!chkidx(move)) { 2.265 + return INVALID_POSITION; 2.266 + } 2.267 + 2.268 + /* must move */ 2.269 + if (move->fromfile == move->tofile && move->fromrow == move->torow) { 2.270 + return INVALID_MOVE_SYNTAX; 2.271 + } 2.272 + 2.273 + /* does piece exist */ 2.274 + if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 2.275 + != (move->piece&(PIECE_MASK|COLOR_MASK))) { 2.276 + return INVALID_POSITION; 2.277 + } 2.278 + 2.279 + /* can't capture own pieces */ 2.280 + if ((mdst(gamestate->board, move) & COLOR_MASK) 2.281 + == (move->piece & COLOR_MASK)) { 2.282 + return RULES_VIOLATED; 2.283 + } 2.284 + 2.285 + /* must capture, if and only if destination is occupied */ 2.286 + if ((mdst(gamestate->board, move) == 0 && move->capture) || 2.287 + (mdst(gamestate->board, move) != 0 && !move->capture)) { 2.288 + return INVALID_MOVE_SYNTAX; 2.289 + } 2.290 + 2.291 + /* validate individual rules */ 2.292 + _Bool chkrules; 2.293 + switch (move->piece & PIECE_MASK) { 2.294 + case PAWN: 2.295 + chkrules = pawn_chkrules(gamestate, move) && 2.296 + !pawn_isblocked(gamestate, move); 2.297 + break; 2.298 + case ROOK: 2.299 + chkrules = rook_chkrules(move) && 2.300 + !rook_isblocked(gamestate, move); 2.301 + break; 2.302 + case KNIGHT: 2.303 + chkrules = knight_chkrules(move); /* knight is never blocked */ 2.304 + break; 2.305 + case BISHOP: 2.306 + chkrules = bishop_chkrules(move) && 2.307 + !bishop_isblocked(gamestate, move); 2.308 + break; 2.309 + case QUEEN: 2.310 + chkrules = queen_chkrules(move) && 2.311 + !queen_isblocked(gamestate, move); 2.312 + break; 2.313 + case KING: 2.314 + chkrules = king_chkrules(gamestate, move) && 2.315 + !king_isblocked(gamestate, move); 2.316 + break; 2.317 + default: 2.318 + return INVALID_MOVE_SYNTAX; 2.319 + } 2.320 + 2.321 + return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; 2.322 +} 2.323 + 2.324 +int validate_move(GameState *gamestate, Move *move) { 2.325 + 2.326 + int result = validate_move_rules(gamestate, move); 2.327 + 2.328 + /* cancel processing to save resources */ 2.329 + if (result != VALID_MOVE_SEMANTICS) { 2.330 + return result; 2.331 + } 2.332 + 2.333 + /* find kings for check validation */ 2.334 + uint8_t piececolor = (move->piece & COLOR_MASK); 2.335 + 2.336 + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 2.337 + for (uint8_t row = 0 ; row < 8 ; row++) { 2.338 + for (uint8_t file = 0 ; file < 8 ; file++) { 2.339 + if (gamestate->board[row][file] == 2.340 + (piececolor == WHITE?WKING:BKING)) { 2.341 + mykingfile = file; 2.342 + mykingrow = row; 2.343 + } else if (gamestate->board[row][file] == 2.344 + (piececolor == WHITE?BKING:WKING)) { 2.345 + opkingfile = file; 2.346 + opkingrow = row; 2.347 + } 2.348 + } 2.349 + } 2.350 + 2.351 + /* simulate move for check validation */ 2.352 + GameState simulation = gamestate_copy_sim(gamestate); 2.353 + Move simmove = *move; 2.354 + apply_move_impl(&simulation, &simmove, 1); 2.355 + 2.356 + /* don't move into or stay in check position */ 2.357 + if (is_covered(&simulation, mykingrow, mykingfile, 2.358 + opponent_color(piececolor))) { 2.359 + 2.360 + gamestate_cleanup(&simulation); 2.361 + if ((move->piece & PIECE_MASK) == KING) { 2.362 + return KING_MOVES_INTO_CHECK; 2.363 + } else { 2.364 + /* last move is always not null in this case */ 2.365 + return gamestate->lastmove->move.check ? 2.366 + KING_IN_CHECK : PIECE_PINNED; 2.367 + } 2.368 + } 2.369 + 2.370 + /* correct check and checkmate flags (move is still valid) */ 2.371 + Move threats[16]; 2.372 + uint8_t threatcount; 2.373 + move->check = get_threats(&simulation, opkingrow, opkingfile, 2.374 + piececolor, threats, &threatcount); 2.375 + 2.376 + if (move->check) { 2.377 + /* determine possible escape fields */ 2.378 + _Bool canescape = 0; 2.379 + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 2.380 + for (int df = -1 ; df <= 1 && !canescape ; df++) { 2.381 + if (!(dr == 0 && df == 0) && 2.382 + isidx(opkingrow + dr) && isidx(opkingfile + df)) { 2.383 + 2.384 + /* escape field neither blocked nor covered */ 2.385 + if ((simulation.board[opkingrow + dr][opkingfile + df] 2.386 + & COLOR_MASK) != opponent_color(piececolor)) { 2.387 + canescape |= !is_covered(&simulation, 2.388 + opkingrow + dr, opkingfile + df, piececolor); 2.389 + } 2.390 + } 2.391 + } 2.392 + } 2.393 + /* can't escape, can he capture? */ 2.394 + if (!canescape && threatcount == 1) { 2.395 + canescape = is_attacked(&simulation, threats[0].fromrow, 2.396 + threats[0].fromfile, opponent_color(piececolor)); 2.397 + } 2.398 + 2.399 + /* can't capture, can he block? */ 2.400 + if (!canescape && threatcount == 1) { 2.401 + Move *threat = &(threats[0]); 2.402 + uint8_t threatpiece = threat->piece & PIECE_MASK; 2.403 + 2.404 + /* knight, pawns and the king cannot be blocked */ 2.405 + if (threatpiece == BISHOP || threatpiece == ROOK 2.406 + || threatpiece == QUEEN) { 2.407 + if (threat->fromrow == threat->torow) { 2.408 + /* rook aspect (on row) */ 2.409 + int d = threat->tofile > threat->fromfile ? 1 : -1; 2.410 + uint8_t file = threat->fromfile; 2.411 + while (!canescape && file != threat->tofile - d) { 2.412 + file += d; 2.413 + canescape |= is_protected(&simulation, 2.414 + threat->torow, file, opponent_color(piececolor)); 2.415 + } 2.416 + } else if (threat->fromfile == threat->tofile) { 2.417 + /* rook aspect (on file) */ 2.418 + int d = threat->torow > threat->fromrow ? 1 : -1; 2.419 + uint8_t row = threat->fromrow; 2.420 + while (!canescape && row != threat->torow - d) { 2.421 + row += d; 2.422 + canescape |= is_protected(&simulation, 2.423 + row, threat->tofile, opponent_color(piececolor)); 2.424 + } 2.425 + } else { 2.426 + /* bishop aspect */ 2.427 + int dr = threat->torow > threat->fromrow ? 1 : -1; 2.428 + int df = threat->tofile > threat->fromfile ? 1 : -1; 2.429 + 2.430 + uint8_t row = threat->fromrow; 2.431 + uint8_t file = threat->fromfile; 2.432 + while (!canescape && file != threat->tofile - df 2.433 + && row != threat->torow - dr) { 2.434 + row += dr; 2.435 + file += df; 2.436 + canescape |= is_protected(&simulation, row, file, 2.437 + opponent_color(piececolor)); 2.438 + } 2.439 + } 2.440 + } 2.441 + } 2.442 + 2.443 + if (!canescape) { 2.444 + gamestate->checkmate = 1; 2.445 + } 2.446 + } 2.447 + 2.448 + gamestate_cleanup(&simulation); 2.449 + 2.450 + return VALID_MOVE_SEMANTICS; 2.451 +} 2.452 + 2.453 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 2.454 + uint8_t color, Move *threats, uint8_t *threatcount) { 2.455 + Move candidates[32]; 2.456 + int candidatecount = 0; 2.457 + for (uint8_t r = 0 ; r < 8 ; r++) { 2.458 + for (uint8_t f = 0 ; f < 8 ; f++) { 2.459 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 2.460 + // non-capturing move 2.461 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 2.462 + candidates[candidatecount].piece = gamestate->board[r][f]; 2.463 + candidates[candidatecount].fromrow = r; 2.464 + candidates[candidatecount].fromfile = f; 2.465 + candidates[candidatecount].torow = row; 2.466 + candidates[candidatecount].tofile = file; 2.467 + candidatecount++; 2.468 + 2.469 + // capturing move 2.470 + memcpy(&(candidates[candidatecount]), 2.471 + &(candidates[candidatecount-1]), sizeof(Move)); 2.472 + candidates[candidatecount].capture = 1; 2.473 + candidatecount++; 2.474 + } 2.475 + } 2.476 + } 2.477 + 2.478 + if (threatcount) { 2.479 + *threatcount = 0; 2.480 + } 2.481 + 2.482 + 2.483 + _Bool result = 0; 2.484 + 2.485 + for (int i = 0 ; i < candidatecount ; i++) { 2.486 + if (validate_move_rules(gamestate, &(candidates[i])) 2.487 + == VALID_MOVE_SEMANTICS) { 2.488 + result = 1; 2.489 + if (threats && threatcount) { 2.490 + threats[(*threatcount)++] = candidates[i]; 2.491 + } 2.492 + } 2.493 + } 2.494 + 2.495 + return result; 2.496 +} 2.497 + 2.498 +_Bool is_pinned(GameState *gamestate, Move *move) { 2.499 + uint8_t color = move->piece & COLOR_MASK; 2.500 + 2.501 + uint8_t kingfile = 0, kingrow = 0; 2.502 + for (uint8_t row = 0 ; row < 8 ; row++) { 2.503 + for (uint8_t file = 0 ; file < 8 ; file++) { 2.504 + if (gamestate->board[row][file] == (color|KING)) { 2.505 + kingfile = file; 2.506 + kingrow = row; 2.507 + } 2.508 + } 2.509 + } 2.510 + 2.511 + GameState simulation = gamestate_copy_sim(gamestate); 2.512 + Move simmove = *move; 2.513 + apply_move(&simulation, &simmove); 2.514 + _Bool covered = is_covered(&simulation, 2.515 + kingrow, kingfile, opponent_color(color)); 2.516 + gamestate_cleanup(&simulation); 2.517 + 2.518 + return covered; 2.519 +} 2.520 + 2.521 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 2.522 + uint8_t color, Move *threats, uint8_t *threatcount) { 2.523 + 2.524 + if (threatcount) { 2.525 + *threatcount = 0; 2.526 + } 2.527 + 2.528 + Move candidates[16]; 2.529 + uint8_t candidatecount; 2.530 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 2.531 + 2.532 + _Bool result = 0; 2.533 + uint8_t kingfile = 0, kingrow = 0; 2.534 + for (uint8_t row = 0 ; row < 8 ; row++) { 2.535 + for (uint8_t file = 0 ; file < 8 ; file++) { 2.536 + if (gamestate->board[row][file] == (color|KING)) { 2.537 + kingfile = file; 2.538 + kingrow = row; 2.539 + } 2.540 + } 2.541 + } 2.542 + 2.543 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 2.544 + GameState simulation = gamestate_copy_sim(gamestate); 2.545 + Move simmove = candidates[i]; 2.546 + apply_move(&simulation, &simmove); 2.547 + if (!is_covered(&simulation, kingrow, kingfile, 2.548 + opponent_color(color))) { 2.549 + result = 1; 2.550 + if (threats && threatcount) { 2.551 + threats[(*threatcount)++] = candidates[i]; 2.552 + } 2.553 + } 2.554 + } 2.555 + 2.556 + return result; 2.557 + } else { 2.558 + return 0; 2.559 + } 2.560 +} 2.561 + 2.562 +static int getlocation(GameState *gamestate, Move *move) { 2.563 + 2.564 + uint8_t color = move->piece & COLOR_MASK; 2.565 + _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 2.566 + 2.567 + Move threats[16], *threat = NULL; 2.568 + uint8_t threatcount; 2.569 + 2.570 + if (get_threats(gamestate, move->torow, move->tofile, color, 2.571 + threats, &threatcount)) { 2.572 + 2.573 + int reason = INVALID_POSITION; 2.574 + 2.575 + // find threats for the specified position 2.576 + for (uint8_t i = 0 ; i < threatcount ; i++) { 2.577 + if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 2.578 + == move->piece && 2.579 + (move->fromrow == POS_UNSPECIFIED || 2.580 + move->fromrow == threats[i].fromrow) && 2.581 + (move->fromfile == POS_UNSPECIFIED || 2.582 + move->fromfile == threats[i].fromfile)) { 2.583 + 2.584 + if (threat) { 2.585 + return AMBIGUOUS_MOVE; 2.586 + } else { 2.587 + // found threat is no real threat 2.588 + if (is_pinned(gamestate, &(threats[i]))) { 2.589 + reason = incheck?KING_IN_CHECK:PIECE_PINNED; 2.590 + } else { 2.591 + threat = &(threats[i]); 2.592 + } 2.593 + } 2.594 + } 2.595 + } 2.596 + 2.597 + // can't threaten specified position 2.598 + if (!threat) { 2.599 + return reason; 2.600 + } 2.601 + 2.602 + memcpy(move, threat, sizeof(Move)); 2.603 + return VALID_MOVE_SYNTAX; 2.604 + } else { 2.605 + return INVALID_POSITION; 2.606 + } 2.607 +} 2.608 + 2.609 +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { 2.610 + memset(move, 0, sizeof(Move)); 2.611 + move->fromfile = POS_UNSPECIFIED; 2.612 + move->fromrow = POS_UNSPECIFIED; 2.613 + 2.614 + size_t len = strlen(mstr); 2.615 + if (len < 1 || len > 6) { 2.616 + return INVALID_MOVE_SYNTAX; 2.617 + } 2.618 + 2.619 + /* evaluate check/checkmate flags */ 2.620 + if (mstr[len-1] == '+') { 2.621 + len--; mstr[len] = '\0'; 2.622 + move->check = 1; 2.623 + } else if (mstr[len-1] == '#') { 2.624 + len--; mstr[len] = '\0'; 2.625 + /* ignore - validation should set game state */ 2.626 + } 2.627 + 2.628 + /* evaluate promotion */ 2.629 + if (len > 3 && mstr[len-2] == '=') { 2.630 + move->promotion = getpiece(mstr[len-1]); 2.631 + if (!move->promotion) { 2.632 + return INVALID_MOVE_SYNTAX; 2.633 + } else { 2.634 + move->promotion |= color; 2.635 + len -= 2; 2.636 + mstr[len] = 0; 2.637 + } 2.638 + } 2.639 + 2.640 + if (len == 2) { 2.641 + /* pawn move (e.g. "e4") */ 2.642 + move->piece = PAWN; 2.643 + move->tofile = fileidx(mstr[0]); 2.644 + move->torow = rowidx(mstr[1]); 2.645 + } else if (len == 3) { 2.646 + if (strcmp(mstr, "O-O") == 0) { 2.647 + /* king side castling */ 2.648 + move->piece = KING; 2.649 + move->fromfile = fileidx('e'); 2.650 + move->tofile = fileidx('g'); 2.651 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 2.652 + } else { 2.653 + /* move (e.g. "Nf3") */ 2.654 + move->piece = getpiece(mstr[0]); 2.655 + move->tofile = fileidx(mstr[1]); 2.656 + move->torow = rowidx(mstr[2]); 2.657 + } 2.658 + } else if (len == 4) { 2.659 + move->piece = getpiece(mstr[0]); 2.660 + if (!move->piece) { 2.661 + move->piece = PAWN; 2.662 + move->fromfile = fileidx(mstr[0]); 2.663 + } 2.664 + if (mstr[1] == 'x') { 2.665 + /* capture (e.g. "Nxf3", "dxe5") */ 2.666 + move->capture = 1; 2.667 + } else { 2.668 + /* move (e.g. "Ndf3", "N2c3", "e2e4") */ 2.669 + if (isfile(mstr[1])) { 2.670 + move->fromfile = fileidx(mstr[1]); 2.671 + if (move->piece == PAWN) { 2.672 + move->piece = 0; 2.673 + } 2.674 + } else { 2.675 + move->fromrow = rowidx(mstr[1]); 2.676 + } 2.677 + } 2.678 + move->tofile = fileidx(mstr[2]); 2.679 + move->torow = rowidx(mstr[3]); 2.680 + } else if (len == 5) { 2.681 + if (strcmp(mstr, "O-O-O") == 0) { 2.682 + /* queen side castling "O-O-O" */ 2.683 + move->piece = KING; 2.684 + move->fromfile = fileidx('e'); 2.685 + move->tofile = fileidx('c'); 2.686 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 2.687 + } else { 2.688 + move->piece = getpiece(mstr[0]); 2.689 + if (mstr[2] == 'x') { 2.690 + move->capture = 1; 2.691 + if (move->piece) { 2.692 + /* capture (e.g. "Ndxf3") */ 2.693 + move->fromfile = fileidx(mstr[1]); 2.694 + } else { 2.695 + /* long notation capture (e.g. "e5xf6") */ 2.696 + move->piece = PAWN; 2.697 + move->fromfile = fileidx(mstr[0]); 2.698 + move->fromrow = rowidx(mstr[1]); 2.699 + } 2.700 + } else { 2.701 + /* long notation move (e.g. "Nc5a4") */ 2.702 + move->fromfile = fileidx(mstr[1]); 2.703 + move->fromrow = rowidx(mstr[2]); 2.704 + } 2.705 + move->tofile = fileidx(mstr[3]); 2.706 + move->torow = rowidx(mstr[4]); 2.707 + } 2.708 + } else if (len == 6) { 2.709 + /* long notation capture (e.g. "Nc5xf3") */ 2.710 + if (mstr[3] == 'x') { 2.711 + move->capture = 1; 2.712 + move->piece = getpiece(mstr[0]); 2.713 + move->fromfile = fileidx(mstr[1]); 2.714 + move->fromrow = rowidx(mstr[2]); 2.715 + move->tofile = fileidx(mstr[4]); 2.716 + move->torow = rowidx(mstr[5]); 2.717 + } 2.718 + } 2.719 + 2.720 + 2.721 + if (move->piece) { 2.722 + if (move->piece == PAWN 2.723 + && move->torow == (color==WHITE?7:0) 2.724 + && !move->promotion) { 2.725 + return NEED_PROMOTION; 2.726 + } 2.727 + 2.728 + move->piece |= color; 2.729 + if (move->fromfile == POS_UNSPECIFIED 2.730 + || move->fromrow == POS_UNSPECIFIED) { 2.731 + return getlocation(gamestate, move); 2.732 + } else { 2.733 + return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; 2.734 + } 2.735 + } else { 2.736 + return INVALID_MOVE_SYNTAX; 2.737 + } 2.738 +} 2.739 + 2.740 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 2.741 + uint8_t color) { 2.742 + 2.743 + Move threats[16]; 2.744 + uint8_t threatcount; 2.745 + if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 2.746 + for (int i = 0 ; i < threatcount ; i++) { 2.747 + if (threats[i].piece != (color|KING)) { 2.748 + return 1; 2.749 + } 2.750 + } 2.751 + return 0; 2.752 + } else { 2.753 + return 0; 2.754 + } 2.755 +} 2.756 + 2.757 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, 2.758 + uint8_t color) { 2.759 + if (!gameinfo->timecontrol) { 2.760 + return 0; 2.761 + } 2.762 + 2.763 + if (gamestate->movelist) { 2.764 + uint16_t time = gameinfo->time; 2.765 + suseconds_t micros = 0; 2.766 + 2.767 + MoveList *movelist = color == WHITE ? 2.768 + gamestate->movelist : gamestate->movelist->next; 2.769 + 2.770 + while (movelist) { 2.771 + time += gameinfo->addtime; 2.772 + 2.773 + struct movetimeval *movetime = &(movelist->move.movetime); 2.774 + if (movetime->tv_sec >= time) { 2.775 + return 0; 2.776 + } 2.777 + 2.778 + time -= movetime->tv_sec; 2.779 + micros += movetime->tv_usec; 2.780 + 2.781 + movelist = movelist->next ? movelist->next->next : NULL; 2.782 + } 2.783 + 2.784 + time_t sec; 2.785 + movelist = gamestate->lastmove; 2.786 + if ((movelist->move.piece & COLOR_MASK) != color) { 2.787 + struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); 2.788 + struct timeval currenttstamp; 2.789 + gettimeofday(¤ttstamp, NULL); 2.790 + micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; 2.791 + sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; 2.792 + if (sec >= time) { 2.793 + return 0; 2.794 + } 2.795 + 2.796 + time -= sec; 2.797 + } 2.798 + 2.799 + sec = micros / 1e6L; 2.800 + 2.801 + if (sec >= time) { 2.802 + return 0; 2.803 + } 2.804 + 2.805 + time -= sec; 2.806 + 2.807 + return time; 2.808 + } else { 2.809 + return gameinfo->time; 2.810 + } 2.811 +}
3.1 --- a/test/bigtestfile.c Mon Oct 03 12:27:10 2022 +0200 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,808 +0,0 @@ 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 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/ctest.c Mon Oct 03 12:56:28 2022 +0200 4.3 @@ -0,0 +1,391 @@ 4.4 +/* 4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 4.6 + * 4.7 + * Copyright 2015 Olaf Wintermann. 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 +#include <time.h> 4.33 +#include <stdio.h> 4.34 +#include <stdlib.h> 4.35 +#include <string.h> 4.36 +#include <ucx/string.h> 4.37 +#include <ucx/buffer.h> 4.38 +#include <ucx/utils.h> 4.39 +#include <libxml/tree.h> 4.40 +#include <curl/curl.h> 4.41 + 4.42 +#include <openssl/sha.h> 4.43 +#include <openssl/hmac.h> 4.44 +#include <openssl/evp.h> 4.45 +#include <openssl/bio.h> 4.46 +#include <openssl/buffer.h> 4.47 +#include <openssl/rand.h> 4.48 + 4.49 +#include "utils.h" 4.50 +#include "crypto.h" 4.51 +#include "webdav.h" 4.52 + 4.53 +#define MACRO1337 1337L 4.54 + 4.55 +/* -------------------- This is a testing file. -------------------------- */ 4.56 +/* 4.57 +time_t util_parse_creationdate(char *str) { 4.58 + // example: 2012-11-29T21:35:35Z 4.59 + if(!str) { 4.60 + return 0; 4.61 + } 4.62 + // TODO 4.63 + return 0; 4.64 +} 4.65 +*/ 4.66 +time_t util_parse_lastmodified(char *str) { 4.67 + // example: Thu, 29 Nov 2012 21:35:35 GMT 4.68 + if(!str) { 4.69 + return 0; 4.70 + } else { 4.71 + return curl_getdate(str, NULL); 4.72 + } 4.73 +} 4.74 + 4.75 +int util_getboolean(char *v) { 4.76 + if(v[0] == 'T' || v[0] == 't') { 4.77 + return 1; 4.78 + } 4.79 + return 0; 4.80 +} 4.81 + 4.82 +int util_strtoint(char *str, int64_t *value) { 4.83 + char *end; 4.84 + int64_t val = strtoll(str, &end, 0); 4.85 + if(strlen(end) == 0) { 4.86 + *value = val; 4.87 + return 1; 4.88 + } else { 4.89 + return 0; 4.90 + } 4.91 +} 4.92 + 4.93 +char* util_url_path(char *url) { 4.94 + char *path = NULL; 4.95 + size_t len = strlen(url); 4.96 + int slashcount = 0; 4.97 + int slmax; 4.98 + if(len > 7 && !strncasecmp(url, "http://", 7)) { 4.99 + slmax = 3; 4.100 + } else if(len > 8 && !strncasecmp(url, "https://", 8)) { 4.101 + slmax = 3; 4.102 + } else { 4.103 + slmax = 1; 4.104 + } 4.105 + char c; 4.106 + for(int i=0;i<len;i++) { 4.107 + c = url[i]; 4.108 + if(c == '/') { 4.109 + slashcount++; 4.110 + if(slashcount == slmax) { 4.111 + path = url + i; 4.112 + break; 4.113 + } 4.114 + } 4.115 + } 4.116 + return path; 4.117 +} 4.118 + 4.119 +char* util_url_decode(DavSession *sn, char *url) { 4.120 + char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); 4.121 + char *ret = strdup(unesc); 4.122 + curl_free(unesc); 4.123 + return ret; 4.124 +} 4.125 + 4.126 +char* util_resource_name(char *url) { 4.127 + int si = 0; 4.128 + int osi = 0; 4.129 + int i = 0; 4.130 + int p = 0; 4.131 + char c; 4.132 + while((c = url[i]) != 0) { 4.133 + if(c == '/') { 4.134 + osi = si; 4.135 + si = i; 4.136 + p = 1; 4.137 + } 4.138 + i++; 4.139 + } 4.140 + 4.141 + char *name = url + si + p; 4.142 + if(name[0] == 0) { 4.143 + name = url + osi + p; 4.144 + if(name[0] == 0) { 4.145 + return url; 4.146 + } 4.147 + } 4.148 + 4.149 + return name; 4.150 +} 4.151 + 4.152 +int util_mkdir(char *path, mode_t mode) { 4.153 +#ifdef _WIN32 4.154 + return mkdir(path); 4.155 +#else 4.156 + return mkdir(path, mode); 4.157 +#endif 4.158 +} 4.159 + 4.160 +char* util_concat_path(char *url_base, char *p) { 4.161 + sstr_t base = sstr(url_base); 4.162 + sstr_t path; 4.163 + if(p) { 4.164 + path = sstr(p); 4.165 + } else { 4.166 + path = sstrn("", 0); 4.167 + } 4.168 + 4.169 + int add_separator = 0; 4.170 + if(base.ptr[base.length-1] == '/') { 4.171 + if(path.ptr[0] == '/') { 4.172 + base.length--; 4.173 + } 4.174 + } else { 4.175 + if(path.length == 0 || path.ptr[0] != '/') { 4.176 + add_separator = 1; 4.177 + } 4.178 + } 4.179 + 4.180 + sstr_t url; 4.181 + if(add_separator) { 4.182 + url = sstrcat(3, base, sstr("/"), path); 4.183 + } else { 4.184 + url = sstrcat(2, base, path); 4.185 + } 4.186 + 4.187 + return url.ptr; 4.188 +} 4.189 + 4.190 +void util_set_url(DavSession *sn, char *href) { 4.191 + sstr_t base = sstr(sn->base_url); 4.192 + sstr_t href_str = sstr(href); 4.193 + 4.194 + char *base_path = util_url_path(sn->base_url); 4.195 + base.length -= strlen(base_path); 4.196 + 4.197 + sstr_t url = sstrcat(2, base, href_str); 4.198 + 4.199 + curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr); 4.200 + free(url.ptr); 4.201 +} 4.202 + 4.203 +char* util_path_to_url(DavSession *sn, char *path) { 4.204 + char *space = malloc(256); 4.205 + UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND); 4.206 + 4.207 + // add base url 4.208 + ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); 4.209 + // remove trailing slash 4.210 + ucx_buffer_seek(url, -1, SEEK_CUR); 4.211 + 4.212 + sstr_t p = sstr(path); 4.213 + ssize_t ntk = 0; 4.214 + sstr_t *tks = sstrsplit(p, S("/"), &ntk); 4.215 + 4.216 + for(int i=0;i<ntk;i++) { 4.217 + sstr_t node = tks[i]; 4.218 + if(node.length > 0) { 4.219 + char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 4.220 + ucx_buffer_putc(url, '/'); 4.221 + ucx_buffer_write(esc, 1, strlen(esc), url); 4.222 + curl_free(esc); 4.223 + } 4.224 + free(node.ptr); 4.225 + } 4.226 + free(tks); 4.227 + if(path[p.length-1] == '/') { 4.228 + ucx_buffer_putc(url, '/'); 4.229 + } 4.230 + ucx_buffer_putc(url, 0); 4.231 + 4.232 + space = url->space; 4.233 + ucx_buffer_free(url); 4.234 + 4.235 + return space; 4.236 +} 4.237 + 4.238 +char* util_parent_path(char *path) { 4.239 + char *name = util_resource_name(path); 4.240 + size_t namelen = strlen(name); 4.241 + size_t pathlen = strlen(path); 4.242 + size_t parentlen = pathlen - namelen; 4.243 + char *parent = malloc(parentlen + 1); 4.244 + memcpy(parent, path, parentlen); 4.245 + parent[parentlen] = '\0'; 4.246 + return parent; 4.247 +} 4.248 + 4.249 + 4.250 +char* util_xml_get_text(xmlNode *elm) { 4.251 + xmlNode *node = elm->children; 4.252 + while(node) { 4.253 + if(node->type == XML_TEXT_NODE) { 4.254 + return (char*)node->content; 4.255 + } 4.256 + node = node->next; 4.257 + } 4.258 + return NULL; 4.259 +} 4.260 + 4.261 + 4.262 +char* util_base64decode(char *in) { 4.263 + int len = 0; 4.264 + return util_base64decode_len(in, &len); 4.265 +} 4.266 + 4.267 +char* util_base64decode_len(char* in, int *outlen) { 4.268 + size_t len = strlen(in); 4.269 + char *out = calloc(1, len); 4.270 + 4.271 + BIO* b = BIO_new_mem_buf(in, len); 4.272 + BIO *d = BIO_new(BIO_f_base64()); 4.273 + BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL); 4.274 + b = BIO_push(d, b); 4.275 + 4.276 + *outlen = BIO_read(b, out, len); 4.277 + BIO_free_all(b); 4.278 + 4.279 + return out; 4.280 +} 4.281 + 4.282 +char* util_base64encode(char *in, size_t len) { 4.283 + BIO *b; 4.284 + BIO *e; 4.285 + BUF_MEM *mem; 4.286 + 4.287 + e = BIO_new(BIO_f_base64()); 4.288 + b = BIO_new(BIO_s_mem()); 4.289 + 4.290 + e = BIO_push(e, b); 4.291 + BIO_write(e, in, len); 4.292 + BIO_flush(e); 4.293 + 4.294 + BIO_get_mem_ptr(e, &mem); 4.295 + char *out = malloc(mem->length); 4.296 + memcpy(out, mem->data, mem->length -1); 4.297 + out[mem->length - 1] = '\0'; 4.298 + 4.299 + BIO_free_all(e); 4.300 + 4.301 + return out; 4.302 +} 4.303 + 4.304 +char* util_encrypt_str(DavSession *sn, char *str, char *key) { 4.305 + DavKey *k = dav_context_get_key(sn->context, key); 4.306 + if(!k) { 4.307 + // TODO: session error 4.308 + return NULL; 4.309 + } 4.310 + 4.311 + char *enc_str = aes_encrypt(str, k); 4.312 + char *ret_str = dav_session_strdup(sn, enc_str); 4.313 + free(enc_str); 4.314 + return ret_str; 4.315 +} 4.316 + 4.317 +/* commented out for testing reasons */ 4.318 +/* 4.319 +char* util_decrypt_str(DavSession *sn, char *str, char *key) { 4.320 + DavKey *k = dav_context_get_key(sn->context, key); 4.321 + if(!k) { 4.322 + // TODO: session error 4.323 + return NULL; 4.324 + } 4.325 + 4.326 + char *dec_str = aes_decrypt(str, k); 4.327 + char *ret_str = dav_session_strdup(sn, dec_str); 4.328 + free(dec_str); 4.329 + return ret_str; 4.330 +} 4.331 +*/ 4.332 +char* util_random_str() { 4.333 + unsigned char *str = malloc(25); 4.334 + str[24] = '\0'; 4.335 + 4.336 + sstr_t t = S( 4.337 + "01234567890" 4.338 + "abcdefghijklmnopqrstuvwxyz" 4.339 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 4.340 + const unsigned char *table = (const unsigned char*)t.ptr; 4.341 + 4.342 + RAND_pseudo_bytes(str, 24); 4.343 + for(int i=0;i<24;i++) { 4.344 + int c = str[i] % t.length; 4.345 + str[i] = table[c]; 4.346 + } 4.347 + 4.348 + return (char*)str; 4.349 +} 4.350 + 4.351 +/* 4.352 + * gets a substring from 0 to the appearance of the token 4.353 + * tokens are separated by space 4.354 + * sets sub to the substring and returns the remaining string 4.355 + */ 4.356 +sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 4.357 + int i; 4.358 + int token_start = -1; 4.359 + int token_end = -1; 4.360 + for(i=0;i<=str.length;i++) { 4.361 + int c; 4.362 + if(i == str.length) { 4.363 + c = ' '; 4.364 + } else { 4.365 + c = str.ptr[i]; 4.366 + } 4.367 + if(c < 33) { 4.368 + if(token_start != -1) { 4.369 + token_end = i; 4.370 + size_t len = token_end - token_start; 4.371 + sstr_t tk = sstrsubsl(str, token_start, len); 4.372 + //printf("token: {%.*s}\n", token.length, token.ptr); 4.373 + if(!sstrcmp(tk, token)) { 4.374 + *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 4.375 + break; 4.376 + } 4.377 + token_start = -1; 4.378 + token_end = -1; 4.379 + } 4.380 + } else { 4.381 + if(token_start == -1) { 4.382 + token_start = i; 4.383 + } 4.384 + } 4.385 + } 4.386 + 4.387 + if(i < str.length) { 4.388 + return sstrtrim(sstrsubs(str, i)); 4.389 + } else { 4.390 + str.ptr = NULL; 4.391 + str.length = 0; 4.392 + return str; 4.393 + } 4.394 +}
5.1 --- a/test/ctestfile.c Mon Oct 03 12:27:10 2022 +0200 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,391 +0,0 @@ 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, UCX_BUFFER_AUTOEXTEND); 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 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/golden-master/empty.html Mon Oct 03 12:56:28 2022 +0200 6.3 @@ -0,0 +1,53 @@ 6.4 +<!DOCTYPE html> 6.5 +<html> 6.6 + <head> 6.7 + <title>c2html</title> 6.8 + <style type="text/css"> 6.9 + a.c2html-lineno { 6.10 + /* as long as user-select isn't widely spread, we throw the bomb */ 6.11 + -webkit-user-select: none; 6.12 + -moz-user-select: none; 6.13 + -ms-user-select: none; 6.14 + user-select: none; 6.15 + display: inline-block; 6.16 + font-style: italic; 6.17 + text-decoration: none; 6.18 + color: grey; 6.19 + } 6.20 + span.c2html-keyword { 6.21 + color: blue; 6.22 + } 6.23 + span.c2html-macroconst { 6.24 + color: cornflowerblue; 6.25 + } 6.26 + span.c2html-type { 6.27 + color: cornflowerblue; 6.28 + } 6.29 + span.c2html-directive { 6.30 + color: green; 6.31 + } 6.32 + span.c2html-string { 6.33 + color: darkorange; 6.34 + } 6.35 + span.c2html-comment { 6.36 + color: grey; 6.37 + } 6.38 + span.c2html-stdinclude { 6.39 + color: darkorange; 6.40 + } 6.41 + span.c2html-userinclude { 6.42 + color: darkorange; 6.43 + } 6.44 + a.c2html-userinclude { 6.45 + color: darkorange; 6.46 + text-decoration: underline; 6.47 + } 6.48 + </style> 6.49 + </head> 6.50 + <body> 6.51 + 6.52 +<pre> 6.53 +<a class="c2html-lineno" name="l1" href="#l1">1 </a></pre> 6.54 + </body> 6.55 +</html> 6.56 +
7.1 --- a/test/golden-master/emptyfile.html Mon Oct 03 12:27:10 2022 +0200 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,54 +0,0 @@ 7.4 -<!DOCTYPE html> 7.5 -<html> 7.6 - <head> 7.7 - <title>c2html</title> 7.8 - <style type="text/css"> 7.9 - a.c2html-lineno { 7.10 - /* as long as user-select isn't widely spread, we throw the bomb */ 7.11 - -webkit-user-select: none; 7.12 - -moz-user-select: none; 7.13 - -ms-user-select: none; 7.14 - user-select: none; 7.15 - display: inline-block; 7.16 - font-style: italic; 7.17 - text-decoration: none; 7.18 - color: grey; 7.19 - } 7.20 - span.c2html-keyword { 7.21 - color: blue; 7.22 - } 7.23 - span.c2html-macroconst { 7.24 - color: cornflowerblue; 7.25 - } 7.26 - span.c2html-type { 7.27 - color: cornflowerblue; 7.28 - } 7.29 - span.c2html-directive { 7.30 - color: green; 7.31 - } 7.32 - span.c2html-string { 7.33 - color: darkorange; 7.34 - } 7.35 - span.c2html-comment { 7.36 - color: grey; 7.37 - } 7.38 - span.c2html-stdinclude { 7.39 - color: darkorange; 7.40 - } 7.41 - span.c2html-userinclude { 7.42 - color: darkorange; 7.43 - } 7.44 - a.c2html-userinclude { 7.45 - color: darkorange; 7.46 - text-decoration: underline; 7.47 - } 7.48 - </style> 7.49 - </head> 7.50 - <body> 7.51 - 7.52 -<pre> 7.53 -<a class="c2html-lineno" name="l1" href="#l1">1 </a> 7.54 -</pre> 7.55 - </body> 7.56 -</html> 7.57 -
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/test/javatest.java Mon Oct 03 12:56:28 2022 +0200 8.3 @@ -0,0 +1,176 @@ 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/javatestfile.java Mon Oct 03 12:27:10 2022 +0200 9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 9.3 @@ -1,176 +0,0 @@ 9.4 -/* 9.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 9.6 - * 9.7 - * Copyright 2014 Mike Becker. All rights reserved. 9.8 - * 9.9 - * Redistribution and use in source and binary forms, with or without 9.10 - * modification, are permitted provided that the following conditions are met: 9.11 - * 9.12 - * 1. Redistributions of source code must retain the above copyright 9.13 - * notice, this list of conditions and the following disclaimer. 9.14 - * 9.15 - * 2. Redistributions in binary form must reproduce the above copyright 9.16 - * notice, this list of conditions and the following disclaimer in the 9.17 - * documentation and/or other materials provided with the distribution. 9.18 - * 9.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 9.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 9.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 9.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 9.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 9.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 9.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 9.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 9.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 9.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 9.29 - * POSSIBILITY OF SUCH DAMAGE. 9.30 - * 9.31 - */ 9.32 - 9.33 -package de.uapcore.sigred.doc.base; 9.34 - 9.35 -import de.uapcore.sigred.doc.Resources; 9.36 -import de.uapcore.sigrapi.impl.Digraph; 9.37 -import de.uapcore.sigrapi.impl.Graph; 9.38 -import de.uapcore.sigrapi.IGraph; 9.39 -import java.io.IOException; 9.40 -import java.io.InputStream; 9.41 -import java.io.OutputStream; 9.42 -import java.util.concurrent.atomic.AtomicBoolean; 9.43 -import java.util.concurrent.atomic.AtomicReference; 9.44 -import org.apache.xerces.impl.Constants; 9.45 -import org.dom4j.Document; 9.46 -import org.dom4j.DocumentException; 9.47 -import org.dom4j.DocumentHelper; 9.48 -import org.dom4j.Element; 9.49 -import org.dom4j.Namespace; 9.50 -import org.dom4j.QName; 9.51 -import org.dom4j.io.OutputFormat; 9.52 -import org.dom4j.io.SAXReader; 9.53 -import org.dom4j.io.XMLWriter; 9.54 -import org.xml.sax.ErrorHandler; 9.55 -import org.xml.sax.SAXException; 9.56 -import org.xml.sax.SAXParseException; 9.57 - 9.58 -public abstract class AbstractGraphDocument<T extends IGraph> 9.59 - extends FileBackedDocument { 9.60 - 9.61 - protected static final Namespace NAMESPACE = Namespace.get("sigred", 9.62 - "http://develop.uap-core.de/sigred/"); 9.63 - 9.64 - private static final 9.65 - QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE); 9.66 - private static final 9.67 - QName TAG_GRAPH = QName.get("graph", NAMESPACE); 9.68 - private static final 9.69 - QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE); 9.70 - private static final 9.71 - QName TAG_METADATA = QName.get("metadata", NAMESPACE); 9.72 - 9.73 - protected final T graph; 9.74 - 9.75 - private final GraphDocumentMetadata metadata; 9.76 - 9.77 - public AbstractGraphDocument(Class<T> graphType) { 9.78 - T g; 9.79 - try { 9.80 - g = graphType.newInstance(); 9.81 - } catch (ReflectiveOperationException e) { 9.82 - assert false; 9.83 - g = null; // for the compiler 9.84 - } 9.85 - graph = g; 9.86 - metadata = new GraphDocumentMetadata(); 9.87 - } 9.88 - 9.89 - public T getGraph() { 9.90 - return graph; 9.91 - } 9.92 - 9.93 - public GraphDocumentMetadata getMetadata() { 9.94 - return metadata; 9.95 - } 9.96 - 9.97 - protected abstract void writeGraph(Element rootNode) throws IOException; 9.98 - protected abstract void readGraph(Element rootNode) throws IOException; 9.99 - 9.100 - @Override 9.101 - public void writeTo(OutputStream out) throws IOException { 9.102 - Document doc = DocumentHelper.createDocument(); 9.103 - 9.104 - Element rootNode = doc.addElement(TAG_GRAPHDOC); 9.105 - 9.106 - Element metadataNode = rootNode.addElement(TAG_METADATA); 9.107 - 9.108 - metadata.write(metadataNode); 9.109 - 9.110 - if (graph instanceof Graph) { 9.111 - writeGraph(rootNode.addElement(TAG_GRAPH)); 9.112 - } else if (graph instanceof Digraph) { 9.113 - writeGraph(rootNode.addElement(TAG_DIGRAPH)); 9.114 - } else { 9.115 - throw new IOException("unsupported graph type"); 9.116 - } 9.117 - 9.118 - XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); 9.119 - writer.write(doc); 9.120 - writer.flush(); 9.121 - } 9.122 - 9.123 - @Override 9.124 - public void readFrom(InputStream in) throws IOException { 9.125 - try { 9.126 - SAXReader reader = new SAXReader(true); 9.127 - reader.setStripWhitespaceText(true); 9.128 - 9.129 - reader.setFeature(Constants.XERCES_FEATURE_PREFIX+ 9.130 - Constants.SCHEMA_VALIDATION_FEATURE, true); 9.131 - reader.setProperty(Constants.XERCES_PROPERTY_PREFIX + 9.132 - Constants.SCHEMA_LOCATION, String.format("%s %s", 9.133 - NAMESPACE.getURI(), Resources.class.getResource( 9.134 - "graph-document.xsd").toExternalForm())); 9.135 - 9.136 - final AtomicBoolean passed = new AtomicBoolean(true); 9.137 - final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>(); 9.138 - // TODO: we should do more detailed error handling here 9.139 - reader.setErrorHandler(new ErrorHandler() { 9.140 - @Override 9.141 - public void warning(SAXParseException exception) throws SAXException { 9.142 - } 9.143 - 9.144 - @Override 9.145 - public void error(SAXParseException exception) throws SAXException { 9.146 - xmlerror.set(exception); 9.147 - passed.set(false); 9.148 - } 9.149 - 9.150 - @Override 9.151 - public void fatalError(SAXParseException exception) throws SAXException { 9.152 - xmlerror.set(exception); 9.153 - passed.set(false); 9.154 - } 9.155 - 9.156 - }); 9.157 - Document doc = reader.read(in); 9.158 - if (!passed.get()) { 9.159 - // TODO: provide details (maybe via separate error object?) 9.160 - throw xmlerror.get(); 9.161 - } 9.162 - 9.163 - doc.normalize(); 9.164 - 9.165 - Element root = doc.getRootElement(); 9.166 - metadata.read(root.element(TAG_METADATA)); 9.167 - 9.168 - if (graph instanceof Graph) { 9.169 - readGraph(root.element(TAG_GRAPH)); 9.170 - } else if (graph instanceof Digraph) { 9.171 - readGraph(root.element(TAG_DIGRAPH)); 9.172 - } else { 9.173 - throw new IOException("unsupported graph type"); 9.174 - } 9.175 - } catch (DocumentException | SAXException ex) { 9.176 - throw new IOException(ex); 9.177 - } 9.178 - } 9.179 -}
10.1 --- a/test/plain.csp Mon Oct 03 12:27:10 2022 +0200 10.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 10.3 @@ -1,6 +0,0 @@ 10.4 -</body> 10.5 -</html> 10.6 -<!c 10.7 -pblock_free(q); 10.8 -!> 10.9 -
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/test/plain.txt Mon Oct 03 12:56:28 2022 +0200 11.3 @@ -0,0 +1,6 @@ 11.4 +</body> 11.5 +</html> 11.6 +<!c 11.7 +pblock_free(q); 11.8 +!> 11.9 +