more and better test cases + fixed memory leak introduced by changeset e43dee5892f4

Tue, 21 Apr 2015 09:47:52 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 21 Apr 2015 09:47:52 +0200
changeset 25
f82aa7afe872
parent 24
e43dee5892f4
child 26
05c3c6842aef

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(&currenttstamp, 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 +}

mercurial