Tue, 21 Apr 2015 09:47:52 +0200
more and better test cases + fixed memory leak introduced by changeset e43dee5892f4
Makefile | file | annotate | diff | comparison | revisions | |
src/c2html.c | file | annotate | diff | comparison | revisions | |
test/Game.java | file | annotate | diff | comparison | revisions | |
test/bigtestfile.c | file | annotate | diff | comparison | revisions | |
test/ctestfile.c | file | annotate | diff | comparison | revisions | |
test/javatestfile.java | file | annotate | diff | comparison | revisions |
1.1 --- a/Makefile Sun Apr 19 10:48:00 2015 +0200 1.2 +++ b/Makefile Tue Apr 21 09:47:52 2015 +0200 1.3 @@ -1,7 +1,7 @@ 1.4 # 1.5 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 # 1.7 -# Copyright 2014 Mike Becker. All rights reserved. 1.8 +# Copyright 2015 Mike Becker. All rights reserved. 1.9 # 1.10 # Redistribution and use in source and binary forms, with or without 1.11 # modification, are permitted provided that the following conditions are met: 1.12 @@ -37,12 +37,12 @@ 1.13 $(MKDIR) build 1.14 1.15 test: compile 1.16 - ./build/$(BIN) $(ARGS) src/c2html.c -o build/code.html \ 1.17 + ./build/$(BIN) $(ARGS) test/ctestfile.c -o build/ctest.html \ 1.18 + -H test/header.html -F test/footer.html 1.19 + ./build/$(BIN) $(ARGS) -j test/javatestfile.java -o build/javatest.html \ 1.20 + -H test/header.html -F test/footer.html 1.21 + ./build/$(BIN) $(ARGS) test/bigtestfile.c -o build/bigtest.html \ 1.22 -H test/header.html -F test/footer.html 1.23 1.24 -test-java: compile 1.25 - ./build/$(BIN) $(ARGS) -j test/Game.java -o build/code.html \ 1.26 - -H test/header.html -F test/footer.html 1.27 - 1.28 clean: 1.29 $(RM) -f -R build
2.1 --- a/src/c2html.c Sun Apr 19 10:48:00 2015 +0200 2.2 +++ b/src/c2html.c Tue Apr 21 09:47:52 2015 +0200 2.3 @@ -44,7 +44,8 @@ 2.4 l[width] = 0; 2.5 if (inputfile->count >= inputfile->capacity) { 2.6 inputfile->capacity <<= 1; 2.7 - inputfile->lines = realloc(inputfile->lines, inputfile->capacity); 2.8 + inputfile->lines = realloc(inputfile->lines, 2.9 + sizeof(char*)*inputfile->capacity); 2.10 } 2.11 inputfile->lines[inputfile->count] = l; 2.12 inputfile->maxlinewidth = 2.13 @@ -286,6 +287,7 @@ 2.14 (fmt_write_func)fwrite, 2.15 fout, 2.16 settings.showlinenumbers); 2.17 + freeinputfilebuffer(inputfile); 2.18 } else { 2.19 perror("Error opening input file"); 2.20 retcode = -1;
3.1 --- a/test/Game.java Sun Apr 19 10:48:00 2015 +0200 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,94 +0,0 @@ 3.4 -package de.uapcore.threelittlestars; 3.5 - 3.6 -import java.awt.BorderLayout; 3.7 -import java.io.IOException; 3.8 - 3.9 -import javax.swing.JFrame; 3.10 -import javax.swing.JOptionPane; 3.11 - 3.12 -import de.uapcore.threelittlestars.entities.Player; 3.13 -import de.uapcore.threelittlestars.entities.Player.PlayerCharacter; 3.14 -import de.uapcore.threelittlestars.managers.AssetManager; 3.15 -import de.uapcore.threelittlestars.managers.InputManager; 3.16 -import de.uapcore.threelittlestars.managers.WorldManager; 3.17 -import de.uapcore.threelittlestars.renderers.GameRenderer; 3.18 - 3.19 - 3.20 -public class Game implements Runnable { 3.21 - 3.22 - public static final int TICK_RATE = 32; 3.23 - 3.24 - private InputManager im; 3.25 - private AssetManager am; 3.26 - private WorldManager wm; 3.27 - 3.28 - private MainFrame frame; 3.29 - private MainPanel canvas; 3.30 - 3.31 - public Game(MainFrame frame, PlayerCharacter playerCharacter) { 3.32 - 3.33 - this.frame = frame; 3.34 - canvas = new MainPanel(); 3.35 - 3.36 - am = new AssetManager(canvas); 3.37 - wm = new WorldManager(am); 3.38 - im = new InputManager(); 3.39 - 3.40 - canvas.setRenderer(new GameRenderer(wm)); 3.41 - canvas.addKeyListener(im); 3.42 - 3.43 - frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 3.44 - frame.add(canvas, BorderLayout.CENTER); 3.45 - frame.pack(); 3.46 - canvas.requestFocusInWindow(); 3.47 - 3.48 - try { 3.49 - startGame(playerCharacter); 3.50 - } catch (IOException e) { 3.51 - JOptionPane.showMessageDialog(frame, "Die Weltdaten sind beschÃĪdigt.", 3.52 - frame.getTitle(), JOptionPane.ERROR_MESSAGE); 3.53 - System.exit(1); 3.54 - } 3.55 - } 3.56 - 3.57 - public void startGame(PlayerCharacter character) throws IOException { 3.58 - Player.setCharacter(character); 3.59 - 3.60 - // TODO: asset / savegame loading 3.61 - wm.loadAsset("testworld"); // just testing here 3.62 - 3.63 - new Thread(this).start(); 3.64 - } 3.65 - 3.66 - @Override 3.67 - public void run() { 3.68 - long lastTick = System.currentTimeMillis(); 3.69 - do { 3.70 - long currentTick = System.currentTimeMillis(); 3.71 - if (currentTick - lastTick >= TICK_RATE) { 3.72 - lastTick += TICK_RATE; 3.73 - 3.74 - // Deliver buffered input events 3.75 - im.deliverEvents(wm); 3.76 - 3.77 - // Call updates 3.78 - wm.update(); 3.79 - 3.80 - // Catch other key events 3.81 - if (im.isEscapePressed()) { 3.82 - frame.setVisible(false); 3.83 - } 3.84 - 3.85 - 3.86 - // Repaint canvas 3.87 - canvas.repaint(); 3.88 - } 3.89 - Thread.yield(); 3.90 - } while (frame.isVisible()); 3.91 - 3.92 - // Cleanup stuff 3.93 - frame.dispose(); 3.94 - System.exit(0); 3.95 - } 3.96 - 3.97 -}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/bigtestfile.c Tue Apr 21 09:47:52 2015 +0200 4.3 @@ -0,0 +1,808 @@ 4.4 +/* 4.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 4.6 + * 4.7 + * Copyright 2014 Mike Becker. All rights reserved. 4.8 + * 4.9 + * Redistribution and use in source and binary forms, with or without 4.10 + * modification, are permitted provided that the following conditions are met: 4.11 + * 4.12 + * 1. Redistributions of source code must retain the above copyright 4.13 + * notice, this list of conditions and the following disclaimer. 4.14 + * 4.15 + * 2. Redistributions in binary form must reproduce the above copyright 4.16 + * notice, this list of conditions and the following disclaimer in the 4.17 + * documentation and/or other materials provided with the distribution. 4.18 + * 4.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 4.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 4.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 4.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 4.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 4.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 4.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 4.29 + * POSSIBILITY OF SUCH DAMAGE. 4.30 + * 4.31 + */ 4.32 + 4.33 +#include "rules.h" 4.34 +#include "chess.h" 4.35 +#include <string.h> 4.36 +#include <stdlib.h> 4.37 +#include <sys/time.h> 4.38 + 4.39 +static GameState gamestate_copy_sim(GameState *gamestate) { 4.40 + GameState simulation = *gamestate; 4.41 + if (simulation.lastmove) { 4.42 + MoveList *lastmovecopy = malloc(sizeof(MoveList)); 4.43 + *lastmovecopy = *(simulation.lastmove); 4.44 + simulation.movelist = simulation.lastmove = lastmovecopy; 4.45 + } 4.46 + 4.47 + return simulation; 4.48 +} 4.49 + 4.50 +void gamestate_init(GameState *gamestate) { 4.51 + memset(gamestate, 0, sizeof(GameState)); 4.52 + 4.53 + Board initboard = { 4.54 + {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 4.55 + {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 4.56 + {0, 0, 0, 0, 0, 0, 0, 0}, 4.57 + {0, 0, 0, 0, 0, 0, 0, 0}, 4.58 + {0, 0, 0, 0, 0, 0, 0, 0}, 4.59 + {0, 0, 0, 0, 0, 0, 0, 0}, 4.60 + {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, 4.61 + {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} 4.62 + }; 4.63 + memcpy(gamestate->board, initboard, sizeof(Board)); 4.64 +} 4.65 + 4.66 +void gamestate_cleanup(GameState *gamestate) { 4.67 + MoveList *elem; 4.68 + elem = gamestate->movelist; 4.69 + while (elem) { 4.70 + MoveList *cur = elem; 4.71 + elem = elem->next; 4.72 + free(cur); 4.73 + }; 4.74 +} 4.75 + 4.76 +/* MUST be called IMMEDIATLY after applying a move to work correctly */ 4.77 +static void format_move(GameState *gamestate, Move *move) { 4.78 + char *string = move->string; 4.79 + 4.80 + /* at least 8 characters should be available, wipe them out */ 4.81 + memset(string, 0, 8); 4.82 + 4.83 + /* special formats for castling */ 4.84 + if ((move->piece&PIECE_MASK) == KING && 4.85 + abs(move->tofile-move->fromfile) == 2) { 4.86 + if (move->tofile==fileidx('c')) { 4.87 + memcpy(string, "O-O-O", 5); 4.88 + } else { 4.89 + memcpy(string, "O-O", 3); 4.90 + } 4.91 + } 4.92 + 4.93 + /* start by notating the piece character */ 4.94 + string[0] = getpiecechr(move->piece); 4.95 + int idx = string[0] ? 1 : 0; 4.96 + 4.97 + /* find out how many source information we do need */ 4.98 + uint8_t piece = move->piece & PIECE_MASK; 4.99 + if (piece == PAWN) { 4.100 + if (move->capture) { 4.101 + string[idx++] = filechr(move->fromfile); 4.102 + } 4.103 + } else if (piece != KING) { 4.104 + Move threats[16]; 4.105 + uint8_t threatcount; 4.106 + get_real_threats(gamestate, move->torow, move->tofile, 4.107 + move->piece&COLOR_MASK, threats, &threatcount); 4.108 + if (threatcount > 1) { 4.109 + int ambrows = 0, ambfiles = 0; 4.110 + for (uint8_t i = 0 ; i < threatcount ; i++) { 4.111 + if (threats[i].fromrow == move->fromrow) { 4.112 + ambrows++; 4.113 + } 4.114 + if (threats[i].fromfile == move->fromfile) { 4.115 + ambfiles++; 4.116 + } 4.117 + } 4.118 + /* ambiguous row, name file */ 4.119 + if (ambrows > 1) { 4.120 + string[idx++] = filechr(move->fromfile); 4.121 + } 4.122 + /* ambiguous file, name row */ 4.123 + if (ambfiles > 1) { 4.124 + string[idx++] = filechr(move->fromrow); 4.125 + } 4.126 + } 4.127 + } 4.128 + 4.129 + /* capturing? */ 4.130 + if (move->capture) { 4.131 + string[idx++] = 'x'; 4.132 + } 4.133 + 4.134 + /* destination */ 4.135 + string[idx++] = filechr(move->tofile); 4.136 + string[idx++] = rowchr(move->torow); 4.137 + 4.138 + /* promotion? */ 4.139 + if (move->promotion) { 4.140 + string[idx++] = '='; 4.141 + string[idx++] = getpiecechr(move->promotion); 4.142 + } 4.143 + 4.144 + /* check? */ 4.145 + if (move->check) { 4.146 + /* works only, if this function is called when applying the move */ 4.147 + string[idx++] = gamestate->checkmate?'#':'+'; 4.148 + } 4.149 +} 4.150 + 4.151 +static void addmove(GameState* gamestate, Move *move) { 4.152 + MoveList *elem = malloc(sizeof(MoveList)); 4.153 + elem->next = NULL; 4.154 + elem->move = *move; 4.155 + 4.156 + struct timeval curtimestamp; 4.157 + gettimeofday(&curtimestamp, NULL); 4.158 + elem->move.timestamp.tv_sec = curtimestamp.tv_sec; 4.159 + elem->move.timestamp.tv_usec = curtimestamp.tv_usec; 4.160 + 4.161 + if (gamestate->lastmove) { 4.162 + struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); 4.163 + uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; 4.164 + suseconds_t micros; 4.165 + if (curtimestamp.tv_usec < lasttstamp->tv_usec) { 4.166 + micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); 4.167 + sec--; 4.168 + } else { 4.169 + micros = curtimestamp.tv_usec - lasttstamp->tv_usec; 4.170 + } 4.171 + 4.172 + elem->move.movetime.tv_sec = sec; 4.173 + elem->move.movetime.tv_usec = micros; 4.174 + 4.175 + gamestate->lastmove->next = elem; 4.176 + gamestate->lastmove = elem; 4.177 + } else { 4.178 + elem->move.movetime.tv_usec = 0; 4.179 + elem->move.movetime.tv_sec = 0; 4.180 + gamestate->movelist = gamestate->lastmove = elem; 4.181 + } 4.182 +} 4.183 + 4.184 +char getpiecechr(uint8_t piece) { 4.185 + switch (piece & PIECE_MASK) { 4.186 + case ROOK: return 'R'; 4.187 + case KNIGHT: return 'N'; 4.188 + case BISHOP: return 'B'; 4.189 + case QUEEN: return 'Q'; 4.190 + case KING: return 'K'; 4.191 + default: return '\0'; 4.192 + } 4.193 +} 4.194 + 4.195 +uint8_t getpiece(char c) { 4.196 + switch (c) { 4.197 + case 'R': return ROOK; 4.198 + case 'N': return KNIGHT; 4.199 + case 'B': return BISHOP; 4.200 + case 'Q': return QUEEN; 4.201 + case 'K': return KING; 4.202 + default: return 0; 4.203 + } 4.204 +} 4.205 + 4.206 +static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { 4.207 + uint8_t piece = move->piece & PIECE_MASK; 4.208 + uint8_t color = move->piece & COLOR_MASK; 4.209 + 4.210 + /* en passant capture */ 4.211 + if (move->capture && piece == PAWN && 4.212 + mdst(gamestate->board, move) == 0) { 4.213 + gamestate->board[move->fromrow][move->tofile] = 0; 4.214 + } 4.215 + 4.216 + /* remove old en passant threats */ 4.217 + for (uint8_t file = 0 ; file < 8 ; file++) { 4.218 + gamestate->board[3][file] &= ~ENPASSANT_THREAT; 4.219 + gamestate->board[4][file] &= ~ENPASSANT_THREAT; 4.220 + } 4.221 + 4.222 + /* add new en passant threat */ 4.223 + if (piece == PAWN && ( 4.224 + (move->fromrow == 1 && move->torow == 3) || 4.225 + (move->fromrow == 6 && move->torow == 4))) { 4.226 + move->piece |= ENPASSANT_THREAT; 4.227 + } 4.228 + 4.229 + /* move (and maybe capture or promote) */ 4.230 + msrc(gamestate->board, move) = 0; 4.231 + if (move->promotion) { 4.232 + mdst(gamestate->board, move) = move->promotion; 4.233 + } else { 4.234 + mdst(gamestate->board, move) = move->piece; 4.235 + } 4.236 + 4.237 + /* castling */ 4.238 + if (piece == KING && move->fromfile == fileidx('e')) { 4.239 + 4.240 + if (move->tofile == fileidx('g')) { 4.241 + gamestate->board[move->torow][fileidx('h')] = 0; 4.242 + gamestate->board[move->torow][fileidx('f')] = color|ROOK; 4.243 + } else if (move->tofile == fileidx('c')) { 4.244 + gamestate->board[move->torow][fileidx('a')] = 0; 4.245 + gamestate->board[move->torow][fileidx('d')] = color|ROOK; 4.246 + } 4.247 + } 4.248 + 4.249 + if (!simulate) { 4.250 + if (!move->string[0]) { 4.251 + format_move(gamestate, move); 4.252 + } 4.253 + } 4.254 + /* add move, even in simulation (checkmate test needs it) */ 4.255 + addmove(gamestate, move); 4.256 +} 4.257 + 4.258 +void apply_move(GameState *gamestate, Move *move) { 4.259 + apply_move_impl(gamestate, move, 0); 4.260 +} 4.261 + 4.262 +static int validate_move_rules(GameState *gamestate, Move *move) { 4.263 + /* validate indices (don't trust opponent) */ 4.264 + if (!chkidx(move)) { 4.265 + return INVALID_POSITION; 4.266 + } 4.267 + 4.268 + /* must move */ 4.269 + if (move->fromfile == move->tofile && move->fromrow == move->torow) { 4.270 + return INVALID_MOVE_SYNTAX; 4.271 + } 4.272 + 4.273 + /* does piece exist */ 4.274 + if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 4.275 + != (move->piece&(PIECE_MASK|COLOR_MASK))) { 4.276 + return INVALID_POSITION; 4.277 + } 4.278 + 4.279 + /* can't capture own pieces */ 4.280 + if ((mdst(gamestate->board, move) & COLOR_MASK) 4.281 + == (move->piece & COLOR_MASK)) { 4.282 + return RULES_VIOLATED; 4.283 + } 4.284 + 4.285 + /* must capture, if and only if destination is occupied */ 4.286 + if ((mdst(gamestate->board, move) == 0 && move->capture) || 4.287 + (mdst(gamestate->board, move) != 0 && !move->capture)) { 4.288 + return INVALID_MOVE_SYNTAX; 4.289 + } 4.290 + 4.291 + /* validate individual rules */ 4.292 + _Bool chkrules; 4.293 + switch (move->piece & PIECE_MASK) { 4.294 + case PAWN: 4.295 + chkrules = pawn_chkrules(gamestate, move) && 4.296 + !pawn_isblocked(gamestate, move); 4.297 + break; 4.298 + case ROOK: 4.299 + chkrules = rook_chkrules(move) && 4.300 + !rook_isblocked(gamestate, move); 4.301 + break; 4.302 + case KNIGHT: 4.303 + chkrules = knight_chkrules(move); /* knight is never blocked */ 4.304 + break; 4.305 + case BISHOP: 4.306 + chkrules = bishop_chkrules(move) && 4.307 + !bishop_isblocked(gamestate, move); 4.308 + break; 4.309 + case QUEEN: 4.310 + chkrules = queen_chkrules(move) && 4.311 + !queen_isblocked(gamestate, move); 4.312 + break; 4.313 + case KING: 4.314 + chkrules = king_chkrules(gamestate, move) && 4.315 + !king_isblocked(gamestate, move); 4.316 + break; 4.317 + default: 4.318 + return INVALID_MOVE_SYNTAX; 4.319 + } 4.320 + 4.321 + return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; 4.322 +} 4.323 + 4.324 +int validate_move(GameState *gamestate, Move *move) { 4.325 + 4.326 + int result = validate_move_rules(gamestate, move); 4.327 + 4.328 + /* cancel processing to save resources */ 4.329 + if (result != VALID_MOVE_SEMANTICS) { 4.330 + return result; 4.331 + } 4.332 + 4.333 + /* find kings for check validation */ 4.334 + uint8_t piececolor = (move->piece & COLOR_MASK); 4.335 + 4.336 + uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; 4.337 + for (uint8_t row = 0 ; row < 8 ; row++) { 4.338 + for (uint8_t file = 0 ; file < 8 ; file++) { 4.339 + if (gamestate->board[row][file] == 4.340 + (piececolor == WHITE?WKING:BKING)) { 4.341 + mykingfile = file; 4.342 + mykingrow = row; 4.343 + } else if (gamestate->board[row][file] == 4.344 + (piececolor == WHITE?BKING:WKING)) { 4.345 + opkingfile = file; 4.346 + opkingrow = row; 4.347 + } 4.348 + } 4.349 + } 4.350 + 4.351 + /* simulate move for check validation */ 4.352 + GameState simulation = gamestate_copy_sim(gamestate); 4.353 + Move simmove = *move; 4.354 + apply_move_impl(&simulation, &simmove, 1); 4.355 + 4.356 + /* don't move into or stay in check position */ 4.357 + if (is_covered(&simulation, mykingrow, mykingfile, 4.358 + opponent_color(piececolor))) { 4.359 + 4.360 + gamestate_cleanup(&simulation); 4.361 + if ((move->piece & PIECE_MASK) == KING) { 4.362 + return KING_MOVES_INTO_CHECK; 4.363 + } else { 4.364 + /* last move is always not null in this case */ 4.365 + return gamestate->lastmove->move.check ? 4.366 + KING_IN_CHECK : PIECE_PINNED; 4.367 + } 4.368 + } 4.369 + 4.370 + /* correct check and checkmate flags (move is still valid) */ 4.371 + Move threats[16]; 4.372 + uint8_t threatcount; 4.373 + move->check = get_threats(&simulation, opkingrow, opkingfile, 4.374 + piececolor, threats, &threatcount); 4.375 + 4.376 + if (move->check) { 4.377 + /* determine possible escape fields */ 4.378 + _Bool canescape = 0; 4.379 + for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { 4.380 + for (int df = -1 ; df <= 1 && !canescape ; df++) { 4.381 + if (!(dr == 0 && df == 0) && 4.382 + isidx(opkingrow + dr) && isidx(opkingfile + df)) { 4.383 + 4.384 + /* escape field neither blocked nor covered */ 4.385 + if ((simulation.board[opkingrow + dr][opkingfile + df] 4.386 + & COLOR_MASK) != opponent_color(piececolor)) { 4.387 + canescape |= !is_covered(&simulation, 4.388 + opkingrow + dr, opkingfile + df, piececolor); 4.389 + } 4.390 + } 4.391 + } 4.392 + } 4.393 + /* can't escape, can he capture? */ 4.394 + if (!canescape && threatcount == 1) { 4.395 + canescape = is_attacked(&simulation, threats[0].fromrow, 4.396 + threats[0].fromfile, opponent_color(piececolor)); 4.397 + } 4.398 + 4.399 + /* can't capture, can he block? */ 4.400 + if (!canescape && threatcount == 1) { 4.401 + Move *threat = &(threats[0]); 4.402 + uint8_t threatpiece = threat->piece & PIECE_MASK; 4.403 + 4.404 + /* knight, pawns and the king cannot be blocked */ 4.405 + if (threatpiece == BISHOP || threatpiece == ROOK 4.406 + || threatpiece == QUEEN) { 4.407 + if (threat->fromrow == threat->torow) { 4.408 + /* rook aspect (on row) */ 4.409 + int d = threat->tofile > threat->fromfile ? 1 : -1; 4.410 + uint8_t file = threat->fromfile; 4.411 + while (!canescape && file != threat->tofile - d) { 4.412 + file += d; 4.413 + canescape |= is_protected(&simulation, 4.414 + threat->torow, file, opponent_color(piececolor)); 4.415 + } 4.416 + } else if (threat->fromfile == threat->tofile) { 4.417 + /* rook aspect (on file) */ 4.418 + int d = threat->torow > threat->fromrow ? 1 : -1; 4.419 + uint8_t row = threat->fromrow; 4.420 + while (!canescape && row != threat->torow - d) { 4.421 + row += d; 4.422 + canescape |= is_protected(&simulation, 4.423 + row, threat->tofile, opponent_color(piececolor)); 4.424 + } 4.425 + } else { 4.426 + /* bishop aspect */ 4.427 + int dr = threat->torow > threat->fromrow ? 1 : -1; 4.428 + int df = threat->tofile > threat->fromfile ? 1 : -1; 4.429 + 4.430 + uint8_t row = threat->fromrow; 4.431 + uint8_t file = threat->fromfile; 4.432 + while (!canescape && file != threat->tofile - df 4.433 + && row != threat->torow - dr) { 4.434 + row += dr; 4.435 + file += df; 4.436 + canescape |= is_protected(&simulation, row, file, 4.437 + opponent_color(piececolor)); 4.438 + } 4.439 + } 4.440 + } 4.441 + } 4.442 + 4.443 + if (!canescape) { 4.444 + gamestate->checkmate = 1; 4.445 + } 4.446 + } 4.447 + 4.448 + gamestate_cleanup(&simulation); 4.449 + 4.450 + return VALID_MOVE_SEMANTICS; 4.451 +} 4.452 + 4.453 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 4.454 + uint8_t color, Move *threats, uint8_t *threatcount) { 4.455 + Move candidates[32]; 4.456 + int candidatecount = 0; 4.457 + for (uint8_t r = 0 ; r < 8 ; r++) { 4.458 + for (uint8_t f = 0 ; f < 8 ; f++) { 4.459 + if ((gamestate->board[r][f] & COLOR_MASK) == color) { 4.460 + // non-capturing move 4.461 + memset(&(candidates[candidatecount]), 0, sizeof(Move)); 4.462 + candidates[candidatecount].piece = gamestate->board[r][f]; 4.463 + candidates[candidatecount].fromrow = r; 4.464 + candidates[candidatecount].fromfile = f; 4.465 + candidates[candidatecount].torow = row; 4.466 + candidates[candidatecount].tofile = file; 4.467 + candidatecount++; 4.468 + 4.469 + // capturing move 4.470 + memcpy(&(candidates[candidatecount]), 4.471 + &(candidates[candidatecount-1]), sizeof(Move)); 4.472 + candidates[candidatecount].capture = 1; 4.473 + candidatecount++; 4.474 + } 4.475 + } 4.476 + } 4.477 + 4.478 + if (threatcount) { 4.479 + *threatcount = 0; 4.480 + } 4.481 + 4.482 + 4.483 + _Bool result = 0; 4.484 + 4.485 + for (int i = 0 ; i < candidatecount ; i++) { 4.486 + if (validate_move_rules(gamestate, &(candidates[i])) 4.487 + == VALID_MOVE_SEMANTICS) { 4.488 + result = 1; 4.489 + if (threats && threatcount) { 4.490 + threats[(*threatcount)++] = candidates[i]; 4.491 + } 4.492 + } 4.493 + } 4.494 + 4.495 + return result; 4.496 +} 4.497 + 4.498 +_Bool is_pinned(GameState *gamestate, Move *move) { 4.499 + uint8_t color = move->piece & COLOR_MASK; 4.500 + 4.501 + uint8_t kingfile = 0, kingrow = 0; 4.502 + for (uint8_t row = 0 ; row < 8 ; row++) { 4.503 + for (uint8_t file = 0 ; file < 8 ; file++) { 4.504 + if (gamestate->board[row][file] == (color|KING)) { 4.505 + kingfile = file; 4.506 + kingrow = row; 4.507 + } 4.508 + } 4.509 + } 4.510 + 4.511 + GameState simulation = gamestate_copy_sim(gamestate); 4.512 + Move simmove = *move; 4.513 + apply_move(&simulation, &simmove); 4.514 + _Bool covered = is_covered(&simulation, 4.515 + kingrow, kingfile, opponent_color(color)); 4.516 + gamestate_cleanup(&simulation); 4.517 + 4.518 + return covered; 4.519 +} 4.520 + 4.521 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 4.522 + uint8_t color, Move *threats, uint8_t *threatcount) { 4.523 + 4.524 + if (threatcount) { 4.525 + *threatcount = 0; 4.526 + } 4.527 + 4.528 + Move candidates[16]; 4.529 + uint8_t candidatecount; 4.530 + if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { 4.531 + 4.532 + _Bool result = 0; 4.533 + uint8_t kingfile = 0, kingrow = 0; 4.534 + for (uint8_t row = 0 ; row < 8 ; row++) { 4.535 + for (uint8_t file = 0 ; file < 8 ; file++) { 4.536 + if (gamestate->board[row][file] == (color|KING)) { 4.537 + kingfile = file; 4.538 + kingrow = row; 4.539 + } 4.540 + } 4.541 + } 4.542 + 4.543 + for (uint8_t i = 0 ; i < candidatecount ; i++) { 4.544 + GameState simulation = gamestate_copy_sim(gamestate); 4.545 + Move simmove = candidates[i]; 4.546 + apply_move(&simulation, &simmove); 4.547 + if (!is_covered(&simulation, kingrow, kingfile, 4.548 + opponent_color(color))) { 4.549 + result = 1; 4.550 + if (threats && threatcount) { 4.551 + threats[(*threatcount)++] = candidates[i]; 4.552 + } 4.553 + } 4.554 + } 4.555 + 4.556 + return result; 4.557 + } else { 4.558 + return 0; 4.559 + } 4.560 +} 4.561 + 4.562 +static int getlocation(GameState *gamestate, Move *move) { 4.563 + 4.564 + uint8_t color = move->piece & COLOR_MASK; 4.565 + _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 4.566 + 4.567 + Move threats[16], *threat = NULL; 4.568 + uint8_t threatcount; 4.569 + 4.570 + if (get_threats(gamestate, move->torow, move->tofile, color, 4.571 + threats, &threatcount)) { 4.572 + 4.573 + int reason = INVALID_POSITION; 4.574 + 4.575 + // find threats for the specified position 4.576 + for (uint8_t i = 0 ; i < threatcount ; i++) { 4.577 + if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) 4.578 + == move->piece && 4.579 + (move->fromrow == POS_UNSPECIFIED || 4.580 + move->fromrow == threats[i].fromrow) && 4.581 + (move->fromfile == POS_UNSPECIFIED || 4.582 + move->fromfile == threats[i].fromfile)) { 4.583 + 4.584 + if (threat) { 4.585 + return AMBIGUOUS_MOVE; 4.586 + } else { 4.587 + // found threat is no real threat 4.588 + if (is_pinned(gamestate, &(threats[i]))) { 4.589 + reason = incheck?KING_IN_CHECK:PIECE_PINNED; 4.590 + } else { 4.591 + threat = &(threats[i]); 4.592 + } 4.593 + } 4.594 + } 4.595 + } 4.596 + 4.597 + // can't threaten specified position 4.598 + if (!threat) { 4.599 + return reason; 4.600 + } 4.601 + 4.602 + memcpy(move, threat, sizeof(Move)); 4.603 + return VALID_MOVE_SYNTAX; 4.604 + } else { 4.605 + return INVALID_POSITION; 4.606 + } 4.607 +} 4.608 + 4.609 +int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { 4.610 + memset(move, 0, sizeof(Move)); 4.611 + move->fromfile = POS_UNSPECIFIED; 4.612 + move->fromrow = POS_UNSPECIFIED; 4.613 + 4.614 + size_t len = strlen(mstr); 4.615 + if (len < 1 || len > 6) { 4.616 + return INVALID_MOVE_SYNTAX; 4.617 + } 4.618 + 4.619 + /* evaluate check/checkmate flags */ 4.620 + if (mstr[len-1] == '+') { 4.621 + len--; mstr[len] = '\0'; 4.622 + move->check = 1; 4.623 + } else if (mstr[len-1] == '#') { 4.624 + len--; mstr[len] = '\0'; 4.625 + /* ignore - validation should set game state */ 4.626 + } 4.627 + 4.628 + /* evaluate promotion */ 4.629 + if (len > 3 && mstr[len-2] == '=') { 4.630 + move->promotion = getpiece(mstr[len-1]); 4.631 + if (!move->promotion) { 4.632 + return INVALID_MOVE_SYNTAX; 4.633 + } else { 4.634 + move->promotion |= color; 4.635 + len -= 2; 4.636 + mstr[len] = 0; 4.637 + } 4.638 + } 4.639 + 4.640 + if (len == 2) { 4.641 + /* pawn move (e.g. "e4") */ 4.642 + move->piece = PAWN; 4.643 + move->tofile = fileidx(mstr[0]); 4.644 + move->torow = rowidx(mstr[1]); 4.645 + } else if (len == 3) { 4.646 + if (strcmp(mstr, "O-O") == 0) { 4.647 + /* king side castling */ 4.648 + move->piece = KING; 4.649 + move->fromfile = fileidx('e'); 4.650 + move->tofile = fileidx('g'); 4.651 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 4.652 + } else { 4.653 + /* move (e.g. "Nf3") */ 4.654 + move->piece = getpiece(mstr[0]); 4.655 + move->tofile = fileidx(mstr[1]); 4.656 + move->torow = rowidx(mstr[2]); 4.657 + } 4.658 + } else if (len == 4) { 4.659 + move->piece = getpiece(mstr[0]); 4.660 + if (!move->piece) { 4.661 + move->piece = PAWN; 4.662 + move->fromfile = fileidx(mstr[0]); 4.663 + } 4.664 + if (mstr[1] == 'x') { 4.665 + /* capture (e.g. "Nxf3", "dxe5") */ 4.666 + move->capture = 1; 4.667 + } else { 4.668 + /* move (e.g. "Ndf3", "N2c3", "e2e4") */ 4.669 + if (isfile(mstr[1])) { 4.670 + move->fromfile = fileidx(mstr[1]); 4.671 + if (move->piece == PAWN) { 4.672 + move->piece = 0; 4.673 + } 4.674 + } else { 4.675 + move->fromrow = rowidx(mstr[1]); 4.676 + } 4.677 + } 4.678 + move->tofile = fileidx(mstr[2]); 4.679 + move->torow = rowidx(mstr[3]); 4.680 + } else if (len == 5) { 4.681 + if (strcmp(mstr, "O-O-O") == 0) { 4.682 + /* queen side castling "O-O-O" */ 4.683 + move->piece = KING; 4.684 + move->fromfile = fileidx('e'); 4.685 + move->tofile = fileidx('c'); 4.686 + move->fromrow = move->torow = color == WHITE ? 0 : 7; 4.687 + } else { 4.688 + move->piece = getpiece(mstr[0]); 4.689 + if (mstr[2] == 'x') { 4.690 + move->capture = 1; 4.691 + if (move->piece) { 4.692 + /* capture (e.g. "Ndxf3") */ 4.693 + move->fromfile = fileidx(mstr[1]); 4.694 + } else { 4.695 + /* long notation capture (e.g. "e5xf6") */ 4.696 + move->piece = PAWN; 4.697 + move->fromfile = fileidx(mstr[0]); 4.698 + move->fromrow = rowidx(mstr[1]); 4.699 + } 4.700 + } else { 4.701 + /* long notation move (e.g. "Nc5a4") */ 4.702 + move->fromfile = fileidx(mstr[1]); 4.703 + move->fromrow = rowidx(mstr[2]); 4.704 + } 4.705 + move->tofile = fileidx(mstr[3]); 4.706 + move->torow = rowidx(mstr[4]); 4.707 + } 4.708 + } else if (len == 6) { 4.709 + /* long notation capture (e.g. "Nc5xf3") */ 4.710 + if (mstr[3] == 'x') { 4.711 + move->capture = 1; 4.712 + move->piece = getpiece(mstr[0]); 4.713 + move->fromfile = fileidx(mstr[1]); 4.714 + move->fromrow = rowidx(mstr[2]); 4.715 + move->tofile = fileidx(mstr[4]); 4.716 + move->torow = rowidx(mstr[5]); 4.717 + } 4.718 + } 4.719 + 4.720 + 4.721 + if (move->piece) { 4.722 + if (move->piece == PAWN 4.723 + && move->torow == (color==WHITE?7:0) 4.724 + && !move->promotion) { 4.725 + return NEED_PROMOTION; 4.726 + } 4.727 + 4.728 + move->piece |= color; 4.729 + if (move->fromfile == POS_UNSPECIFIED 4.730 + || move->fromrow == POS_UNSPECIFIED) { 4.731 + return getlocation(gamestate, move); 4.732 + } else { 4.733 + return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; 4.734 + } 4.735 + } else { 4.736 + return INVALID_MOVE_SYNTAX; 4.737 + } 4.738 +} 4.739 + 4.740 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, 4.741 + uint8_t color) { 4.742 + 4.743 + Move threats[16]; 4.744 + uint8_t threatcount; 4.745 + if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { 4.746 + for (int i = 0 ; i < threatcount ; i++) { 4.747 + if (threats[i].piece != (color|KING)) { 4.748 + return 1; 4.749 + } 4.750 + } 4.751 + return 0; 4.752 + } else { 4.753 + return 0; 4.754 + } 4.755 +} 4.756 + 4.757 +uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, 4.758 + uint8_t color) { 4.759 + if (!gameinfo->timecontrol) { 4.760 + return 0; 4.761 + } 4.762 + 4.763 + if (gamestate->movelist) { 4.764 + uint16_t time = gameinfo->time; 4.765 + suseconds_t micros = 0; 4.766 + 4.767 + MoveList *movelist = color == WHITE ? 4.768 + gamestate->movelist : gamestate->movelist->next; 4.769 + 4.770 + while (movelist) { 4.771 + time += gameinfo->addtime; 4.772 + 4.773 + struct movetimeval *movetime = &(movelist->move.movetime); 4.774 + if (movetime->tv_sec >= time) { 4.775 + return 0; 4.776 + } 4.777 + 4.778 + time -= movetime->tv_sec; 4.779 + micros += movetime->tv_usec; 4.780 + 4.781 + movelist = movelist->next ? movelist->next->next : NULL; 4.782 + } 4.783 + 4.784 + time_t sec; 4.785 + movelist = gamestate->lastmove; 4.786 + if ((movelist->move.piece & COLOR_MASK) != color) { 4.787 + struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); 4.788 + struct timeval currenttstamp; 4.789 + gettimeofday(¤ttstamp, NULL); 4.790 + micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; 4.791 + sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; 4.792 + if (sec >= time) { 4.793 + return 0; 4.794 + } 4.795 + 4.796 + time -= sec; 4.797 + } 4.798 + 4.799 + sec = micros / 1e6L; 4.800 + 4.801 + if (sec >= time) { 4.802 + return 0; 4.803 + } 4.804 + 4.805 + time -= sec; 4.806 + 4.807 + return time; 4.808 + } else { 4.809 + return gameinfo->time; 4.810 + } 4.811 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/ctestfile.c Tue Apr 21 09:47:52 2015 +0200 5.3 @@ -0,0 +1,386 @@ 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 + 5.54 +time_t util_parse_creationdate(char *str) { 5.55 + // example: 2012-11-29T21:35:35Z 5.56 + if(!str) { 5.57 + return 0; 5.58 + } 5.59 + // TODO 5.60 + return 0; 5.61 +} 5.62 + 5.63 +time_t util_parse_lastmodified(char *str) { 5.64 + // example: Thu, 29 Nov 2012 21:35:35 GMT 5.65 + if(!str) { 5.66 + return 0; 5.67 + } else { 5.68 + return curl_getdate(str, NULL); 5.69 + } 5.70 +} 5.71 + 5.72 +int util_getboolean(char *v) { 5.73 + if(v[0] == 'T' || v[0] == 't') { 5.74 + return 1; 5.75 + } 5.76 + return 0; 5.77 +} 5.78 + 5.79 +int util_strtoint(char *str, int64_t *value) { 5.80 + char *end; 5.81 + int64_t val = strtoll(str, &end, 0); 5.82 + if(strlen(end) == 0) { 5.83 + *value = val; 5.84 + return 1; 5.85 + } else { 5.86 + return 0; 5.87 + } 5.88 +} 5.89 + 5.90 +char* util_url_path(char *url) { 5.91 + char *path = NULL; 5.92 + size_t len = strlen(url); 5.93 + int slashcount = 0; 5.94 + int slmax; 5.95 + if(len > 7 && !strncasecmp(url, "http://", 7)) { 5.96 + slmax = 3; 5.97 + } else if(len > 8 && !strncasecmp(url, "https://", 8)) { 5.98 + slmax = 3; 5.99 + } else { 5.100 + slmax = 1; 5.101 + } 5.102 + char c; 5.103 + for(int i=0;i<len;i++) { 5.104 + c = url[i]; 5.105 + if(c == '/') { 5.106 + slashcount++; 5.107 + if(slashcount == slmax) { 5.108 + path = url + i; 5.109 + break; 5.110 + } 5.111 + } 5.112 + } 5.113 + return path; 5.114 +} 5.115 + 5.116 +char* util_url_decode(DavSession *sn, char *url) { 5.117 + char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); 5.118 + char *ret = strdup(unesc); 5.119 + curl_free(unesc); 5.120 + return ret; 5.121 +} 5.122 + 5.123 +char* util_resource_name(char *url) { 5.124 + int si = 0; 5.125 + int osi = 0; 5.126 + int i = 0; 5.127 + int p = 0; 5.128 + char c; 5.129 + while((c = url[i]) != 0) { 5.130 + if(c == '/') { 5.131 + osi = si; 5.132 + si = i; 5.133 + p = 1; 5.134 + } 5.135 + i++; 5.136 + } 5.137 + 5.138 + char *name = url + si + p; 5.139 + if(name[0] == 0) { 5.140 + name = url + osi + p; 5.141 + if(name[0] == 0) { 5.142 + return url; 5.143 + } 5.144 + } 5.145 + 5.146 + return name; 5.147 +} 5.148 + 5.149 +int util_mkdir(char *path, mode_t mode) { 5.150 +#ifdef _WIN32 5.151 + return mkdir(path); 5.152 +#else 5.153 + return mkdir(path, mode); 5.154 +#endif 5.155 +} 5.156 + 5.157 +char* util_concat_path(char *url_base, char *p) { 5.158 + sstr_t base = sstr(url_base); 5.159 + sstr_t path; 5.160 + if(p) { 5.161 + path = sstr(p); 5.162 + } else { 5.163 + path = sstrn("", 0); 5.164 + } 5.165 + 5.166 + int add_separator = 0; 5.167 + if(base.ptr[base.length-1] == '/') { 5.168 + if(path.ptr[0] == '/') { 5.169 + base.length--; 5.170 + } 5.171 + } else { 5.172 + if(path.length == 0 || path.ptr[0] != '/') { 5.173 + add_separator = 1; 5.174 + } 5.175 + } 5.176 + 5.177 + sstr_t url; 5.178 + if(add_separator) { 5.179 + url = sstrcat(3, base, sstr("/"), path); 5.180 + } else { 5.181 + url = sstrcat(2, base, path); 5.182 + } 5.183 + 5.184 + return url.ptr; 5.185 +} 5.186 + 5.187 +void util_set_url(DavSession *sn, char *href) { 5.188 + sstr_t base = sstr(sn->base_url); 5.189 + sstr_t href_str = sstr(href); 5.190 + 5.191 + char *base_path = util_url_path(sn->base_url); 5.192 + base.length -= strlen(base_path); 5.193 + 5.194 + sstr_t url = sstrcat(2, base, href_str); 5.195 + 5.196 + curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr); 5.197 + free(url.ptr); 5.198 +} 5.199 + 5.200 +char* util_path_to_url(DavSession *sn, char *path) { 5.201 + char *space = malloc(256); 5.202 + UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND); 5.203 + 5.204 + // add base url 5.205 + ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); 5.206 + // remove trailing slash 5.207 + ucx_buffer_seek(url, -1, SEEK_CUR); 5.208 + 5.209 + sstr_t p = sstr(path); 5.210 + ssize_t ntk = 0; 5.211 + sstr_t *tks = sstrsplit(p, S("/"), &ntk); 5.212 + 5.213 + for(int i=0;i<ntk;i++) { 5.214 + sstr_t node = tks[i]; 5.215 + if(node.length > 0) { 5.216 + char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 5.217 + ucx_buffer_putc(url, '/'); 5.218 + ucx_buffer_write(esc, 1, strlen(esc), url); 5.219 + curl_free(esc); 5.220 + } 5.221 + free(node.ptr); 5.222 + } 5.223 + free(tks); 5.224 + if(path[p.length-1] == '/') { 5.225 + ucx_buffer_putc(url, '/'); 5.226 + } 5.227 + ucx_buffer_putc(url, 0); 5.228 + 5.229 + space = url->space; 5.230 + ucx_buffer_free(url); 5.231 + 5.232 + return space; 5.233 +} 5.234 + 5.235 +char* util_parent_path(char *path) { 5.236 + char *name = util_resource_name(path); 5.237 + size_t namelen = strlen(name); 5.238 + size_t pathlen = strlen(path); 5.239 + size_t parentlen = pathlen - namelen; 5.240 + char *parent = malloc(parentlen + 1); 5.241 + memcpy(parent, path, parentlen); 5.242 + parent[parentlen] = '\0'; 5.243 + return parent; 5.244 +} 5.245 + 5.246 + 5.247 +char* util_xml_get_text(xmlNode *elm) { 5.248 + xmlNode *node = elm->children; 5.249 + while(node) { 5.250 + if(node->type == XML_TEXT_NODE) { 5.251 + return (char*)node->content; 5.252 + } 5.253 + node = node->next; 5.254 + } 5.255 + return NULL; 5.256 +} 5.257 + 5.258 + 5.259 +char* util_base64decode(char *in) { 5.260 + int len = 0; 5.261 + return util_base64decode_len(in, &len); 5.262 +} 5.263 + 5.264 +char* util_base64decode_len(char* in, int *outlen) { 5.265 + size_t len = strlen(in); 5.266 + char *out = calloc(1, len); 5.267 + 5.268 + BIO* b = BIO_new_mem_buf(in, len); 5.269 + BIO *d = BIO_new(BIO_f_base64()); 5.270 + BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL); 5.271 + b = BIO_push(d, b); 5.272 + 5.273 + *outlen = BIO_read(b, out, len); 5.274 + BIO_free_all(b); 5.275 + 5.276 + return out; 5.277 +} 5.278 + 5.279 +char* util_base64encode(char *in, size_t len) { 5.280 + BIO *b; 5.281 + BIO *e; 5.282 + BUF_MEM *mem; 5.283 + 5.284 + e = BIO_new(BIO_f_base64()); 5.285 + b = BIO_new(BIO_s_mem()); 5.286 + 5.287 + e = BIO_push(e, b); 5.288 + BIO_write(e, in, len); 5.289 + BIO_flush(e); 5.290 + 5.291 + BIO_get_mem_ptr(e, &mem); 5.292 + char *out = malloc(mem->length); 5.293 + memcpy(out, mem->data, mem->length -1); 5.294 + out[mem->length - 1] = '\0'; 5.295 + 5.296 + BIO_free_all(e); 5.297 + 5.298 + return out; 5.299 +} 5.300 + 5.301 +char* util_encrypt_str(DavSession *sn, char *str, char *key) { 5.302 + DavKey *k = dav_context_get_key(sn->context, key); 5.303 + if(!k) { 5.304 + // TODO: session error 5.305 + return NULL; 5.306 + } 5.307 + 5.308 + char *enc_str = aes_encrypt(str, k); 5.309 + char *ret_str = dav_session_strdup(sn, enc_str); 5.310 + free(enc_str); 5.311 + return ret_str; 5.312 +} 5.313 + 5.314 +char* util_decrypt_str(DavSession *sn, char *str, char *key) { 5.315 + DavKey *k = dav_context_get_key(sn->context, key); 5.316 + if(!k) { 5.317 + // TODO: session error 5.318 + return NULL; 5.319 + } 5.320 + 5.321 + char *dec_str = aes_decrypt(str, k); 5.322 + char *ret_str = dav_session_strdup(sn, dec_str); 5.323 + free(dec_str); 5.324 + return ret_str; 5.325 +} 5.326 + 5.327 +char* util_random_str() { 5.328 + unsigned char *str = malloc(25); 5.329 + str[24] = '\0'; 5.330 + 5.331 + sstr_t t = S( 5.332 + "01234567890" 5.333 + "abcdefghijklmnopqrstuvwxyz" 5.334 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 5.335 + const unsigned char *table = (const unsigned char*)t.ptr; 5.336 + 5.337 + RAND_pseudo_bytes(str, 24); 5.338 + for(int i=0;i<24;i++) { 5.339 + int c = str[i] % t.length; 5.340 + str[i] = table[c]; 5.341 + } 5.342 + 5.343 + return (char*)str; 5.344 +} 5.345 + 5.346 +/* 5.347 + * gets a substring from 0 to the appearance of the token 5.348 + * tokens are separated by space 5.349 + * sets sub to the substring and returns the remaining string 5.350 + */ 5.351 +sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 5.352 + int i; 5.353 + int token_start = -1; 5.354 + int token_end = -1; 5.355 + for(i=0;i<=str.length;i++) { 5.356 + int c; 5.357 + if(i == str.length) { 5.358 + c = ' '; 5.359 + } else { 5.360 + c = str.ptr[i]; 5.361 + } 5.362 + if(c < 33) { 5.363 + if(token_start != -1) { 5.364 + token_end = i; 5.365 + size_t len = token_end - token_start; 5.366 + sstr_t tk = sstrsubsl(str, token_start, len); 5.367 + //printf("token: {%.*s}\n", token.length, token.ptr); 5.368 + if(!sstrcmp(tk, token)) { 5.369 + *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 5.370 + break; 5.371 + } 5.372 + token_start = -1; 5.373 + token_end = -1; 5.374 + } 5.375 + } else { 5.376 + if(token_start == -1) { 5.377 + token_start = i; 5.378 + } 5.379 + } 5.380 + } 5.381 + 5.382 + if(i < str.length) { 5.383 + return sstrtrim(sstrsubs(str, i)); 5.384 + } else { 5.385 + str.ptr = NULL; 5.386 + str.length = 0; 5.387 + return str; 5.388 + } 5.389 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/test/javatestfile.java Tue Apr 21 09:47:52 2015 +0200 6.3 @@ -0,0 +1,176 @@ 6.4 +/* 6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 6.6 + * 6.7 + * Copyright 2014 Mike Becker. All rights reserved. 6.8 + * 6.9 + * Redistribution and use in source and binary forms, with or without 6.10 + * modification, are permitted provided that the following conditions are met: 6.11 + * 6.12 + * 1. Redistributions of source code must retain the above copyright 6.13 + * notice, this list of conditions and the following disclaimer. 6.14 + * 6.15 + * 2. Redistributions in binary form must reproduce the above copyright 6.16 + * notice, this list of conditions and the following disclaimer in the 6.17 + * documentation and/or other materials provided with the distribution. 6.18 + * 6.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 6.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 6.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 6.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 6.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 6.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 6.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 6.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 6.29 + * POSSIBILITY OF SUCH DAMAGE. 6.30 + * 6.31 + */ 6.32 + 6.33 +package de.uapcore.sigred.doc.base; 6.34 + 6.35 +import de.uapcore.sigred.doc.Resources; 6.36 +import de.uapcore.sigrapi.impl.Digraph; 6.37 +import de.uapcore.sigrapi.impl.Graph; 6.38 +import de.uapcore.sigrapi.IGraph; 6.39 +import java.io.IOException; 6.40 +import java.io.InputStream; 6.41 +import java.io.OutputStream; 6.42 +import java.util.concurrent.atomic.AtomicBoolean; 6.43 +import java.util.concurrent.atomic.AtomicReference; 6.44 +import org.apache.xerces.impl.Constants; 6.45 +import org.dom4j.Document; 6.46 +import org.dom4j.DocumentException; 6.47 +import org.dom4j.DocumentHelper; 6.48 +import org.dom4j.Element; 6.49 +import org.dom4j.Namespace; 6.50 +import org.dom4j.QName; 6.51 +import org.dom4j.io.OutputFormat; 6.52 +import org.dom4j.io.SAXReader; 6.53 +import org.dom4j.io.XMLWriter; 6.54 +import org.xml.sax.ErrorHandler; 6.55 +import org.xml.sax.SAXException; 6.56 +import org.xml.sax.SAXParseException; 6.57 + 6.58 +public abstract class AbstractGraphDocument<T extends IGraph> 6.59 + extends FileBackedDocument { 6.60 + 6.61 + protected static final Namespace NAMESPACE = Namespace.get("sigred", 6.62 + "http://develop.uap-core.de/sigred/"); 6.63 + 6.64 + private static final 6.65 + QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE); 6.66 + private static final 6.67 + QName TAG_GRAPH = QName.get("graph", NAMESPACE); 6.68 + private static final 6.69 + QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE); 6.70 + private static final 6.71 + QName TAG_METADATA = QName.get("metadata", NAMESPACE); 6.72 + 6.73 + protected final T graph; 6.74 + 6.75 + private final GraphDocumentMetadata metadata; 6.76 + 6.77 + public AbstractGraphDocument(Class<T> graphType) { 6.78 + T g; 6.79 + try { 6.80 + g = graphType.newInstance(); 6.81 + } catch (ReflectiveOperationException e) { 6.82 + assert false; 6.83 + g = null; // for the compiler 6.84 + } 6.85 + graph = g; 6.86 + metadata = new GraphDocumentMetadata(); 6.87 + } 6.88 + 6.89 + public T getGraph() { 6.90 + return graph; 6.91 + } 6.92 + 6.93 + public GraphDocumentMetadata getMetadata() { 6.94 + return metadata; 6.95 + } 6.96 + 6.97 + protected abstract void writeGraph(Element rootNode) throws IOException; 6.98 + protected abstract void readGraph(Element rootNode) throws IOException; 6.99 + 6.100 + @Override 6.101 + public void writeTo(OutputStream out) throws IOException { 6.102 + Document doc = DocumentHelper.createDocument(); 6.103 + 6.104 + Element rootNode = doc.addElement(TAG_GRAPHDOC); 6.105 + 6.106 + Element metadataNode = rootNode.addElement(TAG_METADATA); 6.107 + 6.108 + metadata.write(metadataNode); 6.109 + 6.110 + if (graph instanceof Graph) { 6.111 + writeGraph(rootNode.addElement(TAG_GRAPH)); 6.112 + } else if (graph instanceof Digraph) { 6.113 + writeGraph(rootNode.addElement(TAG_DIGRAPH)); 6.114 + } else { 6.115 + throw new IOException("unsupported graph type"); 6.116 + } 6.117 + 6.118 + XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint()); 6.119 + writer.write(doc); 6.120 + writer.flush(); 6.121 + } 6.122 + 6.123 + @Override 6.124 + public void readFrom(InputStream in) throws IOException { 6.125 + try { 6.126 + SAXReader reader = new SAXReader(true); 6.127 + reader.setStripWhitespaceText(true); 6.128 + 6.129 + reader.setFeature(Constants.XERCES_FEATURE_PREFIX+ 6.130 + Constants.SCHEMA_VALIDATION_FEATURE, true); 6.131 + reader.setProperty(Constants.XERCES_PROPERTY_PREFIX + 6.132 + Constants.SCHEMA_LOCATION, String.format("%s %s", 6.133 + NAMESPACE.getURI(), Resources.class.getResource( 6.134 + "graph-document.xsd").toExternalForm())); 6.135 + 6.136 + final AtomicBoolean passed = new AtomicBoolean(true); 6.137 + final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>(); 6.138 + // TODO: we should do more detailed error handling here 6.139 + reader.setErrorHandler(new ErrorHandler() { 6.140 + @Override 6.141 + public void warning(SAXParseException exception) throws SAXException { 6.142 + } 6.143 + 6.144 + @Override 6.145 + public void error(SAXParseException exception) throws SAXException { 6.146 + xmlerror.set(exception); 6.147 + passed.set(false); 6.148 + } 6.149 + 6.150 + @Override 6.151 + public void fatalError(SAXParseException exception) throws SAXException { 6.152 + xmlerror.set(exception); 6.153 + passed.set(false); 6.154 + } 6.155 + 6.156 + }); 6.157 + Document doc = reader.read(in); 6.158 + if (!passed.get()) { 6.159 + // TODO: provide details (maybe via separate error object?) 6.160 + throw xmlerror.get(); 6.161 + } 6.162 + 6.163 + doc.normalize(); 6.164 + 6.165 + Element root = doc.getRootElement(); 6.166 + metadata.read(root.element(TAG_METADATA)); 6.167 + 6.168 + if (graph instanceof Graph) { 6.169 + readGraph(root.element(TAG_GRAPH)); 6.170 + } else if (graph instanceof Digraph) { 6.171 + readGraph(root.element(TAG_DIGRAPH)); 6.172 + } else { 6.173 + throw new IOException("unsupported graph type"); 6.174 + } 6.175 + } catch (DocumentException | SAXException ex) { 6.176 + throw new IOException(ex); 6.177 + } 6.178 + } 6.179 +}