# HG changeset patch # User Mike Becker # Date 1664792093 -7200 # Node ID 12d8f0f6ef062aaf86e1dc9190c42687bea68226 # Parent 3fff4c364ffcd18c1397c34efb4408b56769f962 update tests diff -r 3fff4c364ffc -r 12d8f0f6ef06 .hgignore --- a/.hgignore Mon Nov 13 14:17:46 2017 +0100 +++ b/.hgignore Mon Oct 03 12:14:53 2022 +0200 @@ -5,3 +5,5 @@ \.conflict\~$ ^build/.*$ ^nbproject/.*$ +.idea/ + diff -r 3fff4c364ffc -r 12d8f0f6ef06 Makefile --- a/Makefile Mon Nov 13 14:17:46 2017 +0100 +++ b/Makefile Mon Oct 03 12:14:53 2022 +0200 @@ -57,12 +57,10 @@ -H test/header.html -F test/footer.html ./build/$(BIN) -p test/plain.csp -o build/plain.html \ -H test/header.html -F test/footer.html - -test-v2-regression: test - diff build/ctest.html test/v2-regression/ctest.html && \ - diff build/javatest.html test/v2-regression/javatest.html && \ - diff build/bigtest.html test/v2-regression/bigtest.html && \ - diff build/plain.html test/v2-regression/plain.html + diff build/ctest.html test/golden-master/ctest.html && \ + diff build/javatest.html test/golden-master/javatest.html && \ + diff build/bigtest.html test/golden-master/bigtest.html && \ + diff build/plain.html test/golden-master/plain.html clean: $(RM) $(RMFLAGS) build diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/golden-master/bigtest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/golden-master/bigtest.html Mon Oct 03 12:14:53 2022 +0200 @@ -0,0 +1,861 @@ + + + + c2html + + + + +
+  1 /*
+  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+  3  *
+  4  * Copyright 2014 Mike Becker. All rights reserved.
+  5  *
+  6  * Redistribution and use in source and binary forms, with or without
+  7  * modification, are permitted provided that the following conditions are met:
+  8  *
+  9  *   1. Redistributions of source code must retain the above copyright
+ 10  *      notice, this list of conditions and the following disclaimer.
+ 11  *
+ 12  *   2. Redistributions in binary form must reproduce the above copyright
+ 13  *      notice, this list of conditions and the following disclaimer in the
+ 14  *      documentation and/or other materials provided with the distribution.
+ 15  *
+ 16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ 17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ 18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ 19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ 20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ 21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ 22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ 23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ 24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ 25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ 26  * POSSIBILITY OF SUCH DAMAGE.
+ 27  *
+ 28  */
+ 29 
+ 30 #include "rules.h"
+ 31 #include "chess.h"
+ 32 #include <string.h>
+ 33 #include <stdlib.h>
+ 34 #include <sys/time.h>
+ 35 
+ 36 static GameState gamestate_copy_sim(GameState *gamestate) {
+ 37     GameState simulation = *gamestate;
+ 38     if (simulation.lastmove) {
+ 39         MoveList *lastmovecopy = malloc(sizeof(MoveList));
+ 40         *lastmovecopy = *(simulation.lastmove);
+ 41         simulation.movelist = simulation.lastmove = lastmovecopy;
+ 42     }
+ 43 
+ 44     return simulation;
+ 45 }
+ 46 
+ 47 void gamestate_init(GameState *gamestate) {
+ 48     memset(gamestate, 0, sizeof(GameState));
+ 49     
+ 50     Board initboard = {
+ 51         {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
+ 52         {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
+ 53         {0,     0,       0,       0,      0,     0,       0,       0},
+ 54         {0,     0,       0,       0,      0,     0,       0,       0},
+ 55         {0,     0,       0,       0,      0,     0,       0,       0},
+ 56         {0,     0,       0,       0,      0,     0,       0,       0},
+ 57         {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
+ 58         {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
+ 59     };
+ 60     memcpy(gamestate->board, initboard, sizeof(Board));
+ 61 }
+ 62 
+ 63 void gamestate_cleanup(GameState *gamestate) {
+ 64     MoveList *elem;
+ 65     elem = gamestate->movelist;
+ 66     while (elem) {
+ 67         MoveList *cur = elem;
+ 68         elem = elem->next;
+ 69         free(cur);
+ 70     };
+ 71 }
+ 72 
+ 73 /* MUST be called IMMEDIATLY after applying a move to work correctly */
+ 74 static void format_move(GameState *gamestate, Move *move) {
+ 75     char *string = move->string;
+ 76     
+ 77     /* at least 8 characters should be available, wipe them out */
+ 78     memset(string, 0, 8);
+ 79     
+ 80     /* special formats for castling */
+ 81     if ((move->piece&PIECE_MASK) == KING &&
+ 82             abs(move->tofile-move->fromfile) == 2) {
+ 83         if (move->tofile==fileidx('c')) {
+ 84             memcpy(string, "O-O-O", 5);
+ 85         } else {
+ 86             memcpy(string, "O-O", 3);
+ 87         }
+ 88     }
+ 89 
+ 90     /* start by notating the piece character */
+ 91     string[0] = getpiecechr(move->piece);
+ 92     int idx = string[0] ? 1 : 0;
+ 93     
+ 94     /* find out how many source information we do need */
+ 95     uint8_t piece = move->piece & PIECE_MASK;
+ 96     if (piece == PAWN) {
+ 97         if (move->capture) {
+ 98             string[idx++] = filechr(move->fromfile);
+ 99         }
+100     } else if (piece != KING) {
+101         Move threats[16];
+102         uint8_t threatcount;
+103         get_real_threats(gamestate, move->torow, move->tofile,
+104             move->piece&COLOR_MASK, threats, &threatcount);
+105         if (threatcount > 1) {
+106             int ambrows = 0, ambfiles = 0;
+107             for (uint8_t i = 0 ; i < threatcount ; i++) {
+108                 if (threats[i].fromrow == move->fromrow) {
+109                     ambrows++;
+110                 }
+111                 if (threats[i].fromfile == move->fromfile) {
+112                     ambfiles++;
+113                 }
+114             }
+115             /* ambiguous row, name file */
+116             if (ambrows > 1) {
+117                 string[idx++] = filechr(move->fromfile);
+118             }
+119             /* ambiguous file, name row */
+120             if (ambfiles > 1) {
+121                 string[idx++] = filechr(move->fromrow);
+122             }
+123         }
+124     }
+125     
+126     /* capturing? */
+127     if (move->capture) {
+128         string[idx++] = 'x';
+129     }
+130     
+131     /* destination */
+132     string[idx++] = filechr(move->tofile);
+133     string[idx++] = rowchr(move->torow);
+134     
+135     /* promotion? */
+136     if (move->promotion) {
+137         string[idx++] = '=';
+138         string[idx++] = getpiecechr(move->promotion);
+139     }
+140     
+141     /* check? */
+142     if (move->check) {
+143         /* works only, if this function is called when applying the move */
+144         string[idx++] = gamestate->checkmate?'#':'+';
+145     }
+146 }
+147 
+148 static void addmove(GameState* gamestate, Move *move) {
+149     MoveList *elem = malloc(sizeof(MoveList));
+150     elem->next = NULL;
+151     elem->move = *move;
+152     
+153     struct timeval curtimestamp;
+154     gettimeofday(&curtimestamp, NULL);
+155     elem->move.timestamp.tv_sec = curtimestamp.tv_sec;
+156     elem->move.timestamp.tv_usec = curtimestamp.tv_usec;
+157     
+158     if (gamestate->lastmove) {
+159         struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp);
+160         uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec;
+161         suseconds_t micros;
+162         if (curtimestamp.tv_usec < lasttstamp->tv_usec) {
+163             micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec);
+164             sec--;
+165         } else {
+166             micros = curtimestamp.tv_usec - lasttstamp->tv_usec;
+167         }
+168         
+169         elem->move.movetime.tv_sec = sec;
+170         elem->move.movetime.tv_usec = micros;
+171         
+172         gamestate->lastmove->next = elem;
+173         gamestate->lastmove = elem;
+174     } else {
+175         elem->move.movetime.tv_usec = 0;
+176         elem->move.movetime.tv_sec = 0;
+177         gamestate->movelist = gamestate->lastmove = elem;
+178     }
+179 }
+180 
+181 char getpiecechr(uint8_t piece) {
+182     switch (piece & PIECE_MASK) {
+183     case ROOK: return 'R';
+184     case KNIGHT: return 'N';
+185     case BISHOP: return 'B';
+186     case QUEEN: return 'Q';
+187     case KING: return 'K';
+188     default: return '\0';
+189     }
+190 }
+191 
+192 uint8_t getpiece(char c) {
+193     switch (c) {
+194         case 'R': return ROOK;
+195         case 'N': return KNIGHT;
+196         case 'B': return BISHOP;
+197         case 'Q': return QUEEN;
+198         case 'K': return KING;
+199         default: return 0;
+200     }
+201 }
+202 
+203 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
+204     uint8_t piece = move->piece & PIECE_MASK;
+205     uint8_t color = move->piece & COLOR_MASK;
+206     
+207     /* en passant capture */
+208     if (move->capture && piece == PAWN &&
+209         mdst(gamestate->board, move) == 0) {
+210         gamestate->board[move->fromrow][move->tofile] = 0;
+211     }
+212     
+213     /* remove old en passant threats */
+214     for (uint8_t file = 0 ; file < 8 ; file++) {
+215         gamestate->board[3][file] &= ~ENPASSANT_THREAT;
+216         gamestate->board[4][file] &= ~ENPASSANT_THREAT;
+217     }
+218     
+219     /* add new en passant threat */
+220     if (piece == PAWN && (
+221         (move->fromrow == 1 && move->torow == 3) ||
+222         (move->fromrow == 6 && move->torow == 4))) {
+223         move->piece |= ENPASSANT_THREAT;
+224     }
+225     
+226     /* move (and maybe capture or promote) */
+227     msrc(gamestate->board, move) = 0;
+228     if (move->promotion) {
+229         mdst(gamestate->board, move) = move->promotion;
+230     } else {
+231         mdst(gamestate->board, move) = move->piece;
+232     }
+233     
+234     /* castling */
+235     if (piece == KING && move->fromfile == fileidx('e')) {
+236         
+237         if (move->tofile == fileidx('g')) {
+238             gamestate->board[move->torow][fileidx('h')] = 0;
+239             gamestate->board[move->torow][fileidx('f')] = color|ROOK;
+240         } else if (move->tofile == fileidx('c')) {
+241             gamestate->board[move->torow][fileidx('a')] = 0;
+242             gamestate->board[move->torow][fileidx('d')] = color|ROOK;
+243         }
+244     }
+245 
+246     if (!simulate) {
+247         if (!move->string[0]) {
+248             format_move(gamestate, move);
+249         }
+250     }
+251     /* add move, even in simulation (checkmate test needs it) */
+252     addmove(gamestate, move);
+253 }
+254 
+255 void apply_move(GameState *gamestate, Move *move) {
+256     apply_move_impl(gamestate, move, 0);
+257 }
+258 
+259 static int validate_move_rules(GameState *gamestate, Move *move) {
+260     /* validate indices (don't trust opponent) */
+261     if (!chkidx(move)) {
+262         return INVALID_POSITION;
+263     }
+264     
+265     /* must move */
+266     if (move->fromfile == move->tofile && move->fromrow == move->torow) {
+267         return INVALID_MOVE_SYNTAX;
+268     }
+269     
+270     /* does piece exist */
+271     if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
+272            != (move->piece&(PIECE_MASK|COLOR_MASK))) {
+273         return INVALID_POSITION;
+274     }
+275     
+276     /* can't capture own pieces */
+277     if ((mdst(gamestate->board, move) & COLOR_MASK)
+278             == (move->piece & COLOR_MASK)) {
+279         return RULES_VIOLATED;
+280     }
+281     
+282     /* must capture, if and only if destination is occupied */
+283     if ((mdst(gamestate->board, move) == 0 && move->capture) ||
+284             (mdst(gamestate->board, move) != 0 && !move->capture)) {
+285         return INVALID_MOVE_SYNTAX;
+286     }
+287     
+288     /* validate individual rules */
+289     _Bool chkrules;
+290     switch (move->piece & PIECE_MASK) {
+291     case PAWN:
+292         chkrules = pawn_chkrules(gamestate, move) &&
+293             !pawn_isblocked(gamestate, move);
+294         break;
+295     case ROOK:
+296         chkrules = rook_chkrules(move) &&
+297             !rook_isblocked(gamestate, move);
+298         break;
+299     case KNIGHT:
+300         chkrules = knight_chkrules(move); /* knight is never blocked */
+301         break;
+302     case BISHOP:
+303         chkrules = bishop_chkrules(move) &&
+304             !bishop_isblocked(gamestate, move);
+305         break;
+306     case QUEEN:
+307         chkrules = queen_chkrules(move) &&
+308             !queen_isblocked(gamestate, move);
+309         break;
+310     case KING:
+311         chkrules = king_chkrules(gamestate, move) &&
+312             !king_isblocked(gamestate, move);
+313         break;
+314     default:
+315         return INVALID_MOVE_SYNTAX;
+316     }
+317     
+318     return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
+319 }
+320 
+321 int validate_move(GameState *gamestate, Move *move) {
+322     
+323     int result = validate_move_rules(gamestate, move);
+324     
+325     /* cancel processing to save resources */
+326     if (result != VALID_MOVE_SEMANTICS) {
+327         return result;
+328     }
+329     
+330     /* find kings for check validation */
+331     uint8_t piececolor = (move->piece & COLOR_MASK);
+332     
+333     uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
+334     for (uint8_t row = 0 ; row < 8 ; row++) {
+335         for (uint8_t file = 0 ; file < 8 ; file++) {
+336             if (gamestate->board[row][file] ==
+337                     (piececolor == WHITE?WKING:BKING)) {
+338                 mykingfile = file;
+339                 mykingrow = row;
+340             } else if (gamestate->board[row][file] ==
+341                     (piececolor == WHITE?BKING:WKING)) {
+342                 opkingfile = file;
+343                 opkingrow = row;
+344             }
+345         }
+346     }
+347     
+348     /* simulate move for check validation */
+349     GameState simulation = gamestate_copy_sim(gamestate);
+350     Move simmove = *move;
+351     apply_move_impl(&simulation, &simmove, 1);
+352     
+353     /* don't move into or stay in check position */
+354     if (is_covered(&simulation, mykingrow, mykingfile,
+355         opponent_color(piececolor))) {
+356         
+357         gamestate_cleanup(&simulation);
+358         if ((move->piece & PIECE_MASK) == KING) {
+359             return KING_MOVES_INTO_CHECK;
+360         } else {
+361             /* last move is always not null in this case */
+362             return gamestate->lastmove->move.check ?
+363                 KING_IN_CHECK : PIECE_PINNED;
+364         }
+365     }
+366     
+367     /* correct check and checkmate flags (move is still valid) */
+368     Move threats[16];
+369     uint8_t threatcount;
+370     move->check = get_threats(&simulation, opkingrow, opkingfile,
+371         piececolor, threats, &threatcount);
+372     
+373     if (move->check) {
+374         /* determine possible escape fields */
+375         _Bool canescape = 0;
+376         for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
+377             for (int df = -1 ; df <= 1 && !canescape ; df++) {
+378                 if (!(dr == 0 && df == 0)  &&
+379                         isidx(opkingrow + dr) && isidx(opkingfile + df)) {
+380                     
+381                     /* escape field neither blocked nor covered */
+382                     if ((simulation.board[opkingrow + dr][opkingfile + df]
+383                             & COLOR_MASK) != opponent_color(piececolor)) {
+384                         canescape |= !is_covered(&simulation,
+385                             opkingrow + dr, opkingfile + df, piececolor);
+386                     }
+387                 }
+388             }
+389         }
+390         /* can't escape, can he capture? */
+391         if (!canescape && threatcount == 1) {
+392             canescape = is_attacked(&simulation, threats[0].fromrow,
+393                 threats[0].fromfile, opponent_color(piececolor));
+394         }
+395         
+396         /* can't capture, can he block? */
+397         if (!canescape && threatcount == 1) {
+398             Move *threat = &(threats[0]);
+399             uint8_t threatpiece = threat->piece & PIECE_MASK;
+400             
+401             /* knight, pawns and the king cannot be blocked */
+402             if (threatpiece == BISHOP || threatpiece == ROOK
+403                 || threatpiece == QUEEN) {
+404                 if (threat->fromrow == threat->torow) {
+405                     /* rook aspect (on row) */
+406                     int d = threat->tofile > threat->fromfile ? 1 : -1;
+407                     uint8_t file = threat->fromfile;
+408                     while (!canescape && file != threat->tofile - d) {
+409                         file += d;
+410                         canescape |= is_protected(&simulation,
+411                             threat->torow, file, opponent_color(piececolor));
+412                     }
+413                 } else if (threat->fromfile == threat->tofile) {
+414                     /* rook aspect (on file) */
+415                     int d = threat->torow > threat->fromrow ? 1 : -1;
+416                     uint8_t row = threat->fromrow;
+417                     while (!canescape && row != threat->torow - d) {
+418                         row += d;
+419                         canescape |= is_protected(&simulation,
+420                             row, threat->tofile, opponent_color(piececolor));
+421                     }
+422                 } else {
+423                     /* bishop aspect */
+424                     int dr = threat->torow > threat->fromrow ? 1 : -1;
+425                     int df = threat->tofile > threat->fromfile ? 1 : -1;
+426 
+427                     uint8_t row = threat->fromrow;
+428                     uint8_t file = threat->fromfile;
+429                     while (!canescape && file != threat->tofile - df
+430                         && row != threat->torow - dr) {
+431                         row += dr;
+432                         file += df;
+433                         canescape |= is_protected(&simulation, row, file,
+434                             opponent_color(piececolor));
+435                     }
+436                 }
+437             }
+438         }
+439             
+440         if (!canescape) {
+441             gamestate->checkmate = 1;
+442         }
+443     }
+444     
+445     gamestate_cleanup(&simulation);
+446     
+447     return VALID_MOVE_SEMANTICS;
+448 }
+449 
+450 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
+451         uint8_t color, Move *threats, uint8_t *threatcount) {
+452     Move candidates[32];
+453     int candidatecount = 0;
+454     for (uint8_t r = 0 ; r < 8 ; r++) {
+455         for (uint8_t f = 0 ; f < 8 ; f++) {
+456             if ((gamestate->board[r][f] & COLOR_MASK) == color) {
+457                 // non-capturing move
+458                 memset(&(candidates[candidatecount]), 0, sizeof(Move));
+459                 candidates[candidatecount].piece = gamestate->board[r][f];
+460                 candidates[candidatecount].fromrow = r;
+461                 candidates[candidatecount].fromfile = f;
+462                 candidates[candidatecount].torow = row;
+463                 candidates[candidatecount].tofile = file;
+464                 candidatecount++;
+465 
+466                 // capturing move
+467                 memcpy(&(candidates[candidatecount]),
+468                     &(candidates[candidatecount-1]), sizeof(Move));
+469                 candidates[candidatecount].capture = 1;
+470                 candidatecount++;
+471             }
+472         }
+473     }
+474 
+475     if (threatcount) {
+476         *threatcount = 0;
+477     }
+478     
+479     
+480     _Bool result = 0;
+481     
+482     for (int i = 0 ; i < candidatecount ; i++) {
+483         if (validate_move_rules(gamestate, &(candidates[i]))
+484                 == VALID_MOVE_SEMANTICS) {
+485             result = 1;
+486             if (threats && threatcount) {
+487                 threats[(*threatcount)++] = candidates[i];
+488             }
+489         }
+490     }
+491     
+492     return result;
+493 }
+494 
+495 _Bool is_pinned(GameState *gamestate, Move *move) {
+496     uint8_t color = move->piece & COLOR_MASK;
+497 
+498     uint8_t kingfile = 0, kingrow = 0;
+499     for (uint8_t row = 0 ; row < 8 ; row++) {
+500         for (uint8_t file = 0 ; file < 8 ; file++) {
+501             if (gamestate->board[row][file] == (color|KING)) {
+502                 kingfile = file;
+503                 kingrow = row;
+504             }
+505         }
+506     }
+507 
+508     GameState simulation = gamestate_copy_sim(gamestate);
+509     Move simmove = *move;
+510     apply_move(&simulation, &simmove);
+511     _Bool covered = is_covered(&simulation,
+512         kingrow, kingfile, opponent_color(color));
+513     gamestate_cleanup(&simulation);
+514     
+515     return covered;
+516 }
+517 
+518 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
+519         uint8_t color, Move *threats, uint8_t *threatcount) {
+520     
+521     if (threatcount) {
+522         *threatcount = 0;
+523     }
+524 
+525     Move candidates[16];
+526     uint8_t candidatecount;
+527     if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
+528         
+529         _Bool result = 0;
+530         uint8_t kingfile = 0, kingrow = 0;
+531         for (uint8_t row = 0 ; row < 8 ; row++) {
+532             for (uint8_t file = 0 ; file < 8 ; file++) {
+533                 if (gamestate->board[row][file] == (color|KING)) {
+534                     kingfile = file;
+535                     kingrow = row;
+536                 }
+537             }
+538         }
+539 
+540         for (uint8_t i = 0 ; i < candidatecount ; i++) {
+541             GameState simulation = gamestate_copy_sim(gamestate);
+542             Move simmove = candidates[i];
+543             apply_move(&simulation, &simmove);
+544             if (!is_covered(&simulation, kingrow, kingfile,
+545                     opponent_color(color))) {
+546                 result = 1;
+547                 if (threats && threatcount) {
+548                     threats[(*threatcount)++] = candidates[i];
+549                 }
+550             }
+551         }
+552         
+553         return result;
+554     } else {
+555         return 0;
+556     }
+557 }
+558 
+559 static int getlocation(GameState *gamestate, Move *move) {   
+560 
+561     uint8_t color = move->piece & COLOR_MASK;
+562     _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
+563     
+564     Move threats[16], *threat = NULL;
+565     uint8_t threatcount;
+566     
+567     if (get_threats(gamestate, move->torow, move->tofile, color,
+568             threats, &threatcount)) {
+569         
+570         int reason = INVALID_POSITION;
+571         
+572         // find threats for the specified position
+573         for (uint8_t i = 0 ; i < threatcount ; i++) {
+574             if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
+575                     == move->piece &&
+576                     (move->fromrow == POS_UNSPECIFIED ||
+577                     move->fromrow == threats[i].fromrow) &&
+578                     (move->fromfile == POS_UNSPECIFIED ||
+579                     move->fromfile == threats[i].fromfile)) {
+580 
+581                 if (threat) {
+582                     return AMBIGUOUS_MOVE;
+583                 } else {
+584                     // found threat is no real threat
+585                     if (is_pinned(gamestate, &(threats[i]))) {
+586                         reason = incheck?KING_IN_CHECK:PIECE_PINNED;
+587                     } else {
+588                         threat = &(threats[i]);
+589                     }
+590                 }
+591             }
+592         }
+593         
+594         // can't threaten specified position
+595         if (!threat) {
+596             return reason;
+597         }
+598 
+599         memcpy(move, threat, sizeof(Move));
+600         return VALID_MOVE_SYNTAX;
+601     } else {
+602         return INVALID_POSITION;
+603     }
+604 }
+605 
+606 int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) {
+607     memset(move, 0, sizeof(Move));
+608     move->fromfile = POS_UNSPECIFIED;
+609     move->fromrow = POS_UNSPECIFIED;
+610 
+611     size_t len = strlen(mstr);
+612     if (len < 1 || len > 6) {
+613         return INVALID_MOVE_SYNTAX;
+614     }
+615     
+616     /* evaluate check/checkmate flags */
+617     if (mstr[len-1] == '+') {
+618         len--; mstr[len] = '\0';
+619         move->check = 1;
+620     } else if (mstr[len-1] == '#') {
+621         len--; mstr[len] = '\0';
+622         /* ignore - validation should set game state */
+623     }
+624     
+625     /* evaluate promotion */
+626     if (len > 3 && mstr[len-2] == '=') {
+627         move->promotion = getpiece(mstr[len-1]);
+628         if (!move->promotion) {
+629             return INVALID_MOVE_SYNTAX;
+630         } else {
+631             move->promotion |= color;
+632             len -= 2;
+633             mstr[len] = 0;
+634         }
+635     }
+636     
+637     if (len == 2) {
+638         /* pawn move (e.g. "e4") */
+639         move->piece = PAWN;
+640         move->tofile = fileidx(mstr[0]);
+641         move->torow = rowidx(mstr[1]);
+642     } else if (len == 3) {
+643         if (strcmp(mstr, "O-O") == 0) {
+644             /* king side castling */
+645             move->piece = KING;
+646             move->fromfile = fileidx('e');
+647             move->tofile = fileidx('g');
+648             move->fromrow = move->torow = color == WHITE ? 0 : 7;
+649         } else {
+650             /* move (e.g. "Nf3") */
+651             move->piece = getpiece(mstr[0]);
+652             move->tofile = fileidx(mstr[1]);
+653             move->torow = rowidx(mstr[2]);
+654         }
+655     } else if (len == 4) {
+656         move->piece = getpiece(mstr[0]);
+657         if (!move->piece) {
+658             move->piece = PAWN;
+659             move->fromfile = fileidx(mstr[0]);
+660         }
+661         if (mstr[1] == 'x') {
+662             /* capture (e.g. "Nxf3", "dxe5") */
+663             move->capture = 1;
+664         } else {
+665             /* move (e.g. "Ndf3", "N2c3", "e2e4") */
+666             if (isfile(mstr[1])) {
+667                 move->fromfile = fileidx(mstr[1]);
+668                 if (move->piece == PAWN) {
+669                     move->piece = 0;
+670                 }
+671             } else {
+672                 move->fromrow = rowidx(mstr[1]);
+673             }
+674         }
+675         move->tofile = fileidx(mstr[2]);
+676         move->torow = rowidx(mstr[3]);
+677     } else if (len == 5) {
+678         if (strcmp(mstr, "O-O-O") == 0) {
+679             /* queen side castling "O-O-O" */
+680             move->piece = KING;
+681             move->fromfile = fileidx('e');
+682             move->tofile = fileidx('c');
+683             move->fromrow = move->torow = color == WHITE ? 0 : 7;
+684         } else {
+685             move->piece = getpiece(mstr[0]);
+686             if (mstr[2] == 'x') {
+687                 move->capture = 1;
+688                 if (move->piece) {
+689                     /* capture (e.g. "Ndxf3") */
+690                     move->fromfile = fileidx(mstr[1]);
+691                 } else {
+692                     /* long notation capture (e.g. "e5xf6") */
+693                     move->piece = PAWN;
+694                     move->fromfile = fileidx(mstr[0]);
+695                     move->fromrow = rowidx(mstr[1]);
+696                 }
+697             } else {
+698                 /* long notation move (e.g. "Nc5a4") */
+699                 move->fromfile = fileidx(mstr[1]);
+700                 move->fromrow = rowidx(mstr[2]);
+701             }
+702             move->tofile = fileidx(mstr[3]);
+703             move->torow = rowidx(mstr[4]);
+704         }
+705     } else if (len == 6) {
+706         /* long notation capture (e.g. "Nc5xf3") */
+707         if (mstr[3] == 'x') {
+708             move->capture = 1;
+709             move->piece = getpiece(mstr[0]);
+710             move->fromfile = fileidx(mstr[1]);
+711             move->fromrow = rowidx(mstr[2]);
+712             move->tofile = fileidx(mstr[4]);
+713             move->torow = rowidx(mstr[5]);
+714         }
+715     }
+716 
+717     
+718     if (move->piece) {
+719         if (move->piece == PAWN
+720             && move->torow == (color==WHITE?7:0)
+721             && !move->promotion) {
+722             return NEED_PROMOTION;
+723         }
+724         
+725         move->piece |= color;
+726         if (move->fromfile == POS_UNSPECIFIED
+727             || move->fromrow == POS_UNSPECIFIED) {
+728             return getlocation(gamestate, move);
+729         } else {
+730             return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
+731         }
+732     } else {
+733         return INVALID_MOVE_SYNTAX;
+734     }
+735 }
+736 
+737 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
+738         uint8_t color) {
+739     
+740     Move threats[16];
+741     uint8_t threatcount;
+742     if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
+743         for (int i = 0 ; i < threatcount ; i++) {
+744             if (threats[i].piece != (color|KING)) {
+745                 return 1;
+746             }
+747         }
+748         return 0;
+749     } else {
+750         return 0;
+751     }
+752 }
+753 
+754 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
+755         uint8_t color) {
+756     if (!gameinfo->timecontrol) {
+757         return 0;
+758     }
+759     
+760     if (gamestate->movelist) {
+761         uint16_t time = gameinfo->time;
+762         suseconds_t micros = 0;
+763         
+764         MoveList *movelist = color == WHITE ?
+765             gamestate->movelist : gamestate->movelist->next;
+766         
+767         while (movelist) {
+768             time += gameinfo->addtime;
+769             
+770             struct movetimeval *movetime = &(movelist->move.movetime);
+771             if (movetime->tv_sec >= time) {
+772                 return 0;
+773             }
+774             
+775             time -= movetime->tv_sec;
+776             micros += movetime->tv_usec;
+777             
+778             movelist = movelist->next ? movelist->next->next : NULL;
+779         }
+780         
+781         time_t sec;
+782         movelist = gamestate->lastmove;
+783         if ((movelist->move.piece & COLOR_MASK) != color) {
+784             struct movetimeval *lastmovetstamp = &(movelist->move.timestamp);
+785             struct timeval currenttstamp;
+786             gettimeofday(¤ttstamp, NULL);
+787             micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec;
+788             sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
+789             if (sec >= time) {
+790                 return 0;
+791             }
+792             
+793             time -= sec;
+794         }
+795         
+796         sec = micros / 1e6L;
+797         
+798         if (sec >= time) {
+799             return 0;
+800         }
+801 
+802         time -= sec;
+803         
+804         return time;
+805     } else {
+806         return gameinfo->time;
+807     }
+808 }
+
+ + + diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/golden-master/ctest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/golden-master/ctest.html Mon Oct 03 12:14:53 2022 +0200 @@ -0,0 +1,444 @@ + + + + c2html + + + + +
+  1 /*
+  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+  3  *
+  4  * Copyright 2015 Olaf Wintermann. All rights reserved.
+  5  *
+  6  * Redistribution and use in source and binary forms, with or without
+  7  * modification, are permitted provided that the following conditions are met:
+  8  *
+  9  *   1. Redistributions of source code must retain the above copyright
+ 10  *      notice, this list of conditions and the following disclaimer.
+ 11  *
+ 12  *   2. Redistributions in binary form must reproduce the above copyright
+ 13  *      notice, this list of conditions and the following disclaimer in the
+ 14  *      documentation and/or other materials provided with the distribution.
+ 15  *
+ 16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ 17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ 18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ 19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ 20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ 21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ 22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ 23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ 24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ 25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ 26  * POSSIBILITY OF SUCH DAMAGE.
+ 27  */
+ 28 
+ 29 #include <time.h>
+ 30 #include <stdio.h>
+ 31 #include <stdlib.h>
+ 32 #include <string.h>
+ 33 #include <ucx/string.h>
+ 34 #include <ucx/buffer.h>
+ 35 #include <ucx/utils.h>
+ 36 #include <libxml/tree.h>
+ 37 #include <curl/curl.h>
+ 38 
+ 39 #include <openssl/sha.h>
+ 40 #include <openssl/hmac.h>
+ 41 #include <openssl/evp.h>
+ 42 #include <openssl/bio.h>
+ 43 #include <openssl/buffer.h>
+ 44 #include <openssl/rand.h>
+ 45 
+ 46 #include "utils.h"
+ 47 #include "crypto.h"
+ 48 #include "webdav.h"
+ 49 
+ 50 #define MACRO1337 1337L
+ 51 
+ 52 /* -------------------- This is a testing file. -------------------------- */
+ 53 /*
+ 54 time_t util_parse_creationdate(char *str) {
+ 55     // example: 2012-11-29T21:35:35Z
+ 56     if(!str) {
+ 57         return 0;
+ 58     }
+ 59     // TODO
+ 60     return 0;
+ 61 }
+ 62 */
+ 63 time_t util_parse_lastmodified(char *str) {
+ 64     // example: Thu, 29 Nov 2012 21:35:35 GMT
+ 65     if(!str) {
+ 66         return 0;
+ 67     } else {
+ 68         return curl_getdate(str, NULL);
+ 69     }
+ 70 }
+ 71 
+ 72 int util_getboolean(char *v) {
+ 73     if(v[0] == 'T' || v[0] == 't') {
+ 74         return 1;
+ 75     }
+ 76     return 0;
+ 77 }
+ 78 
+ 79 int util_strtoint(char *str, int64_t *value) {
+ 80     char *end;
+ 81     int64_t val = strtoll(str, &end, 0);
+ 82     if(strlen(end) == 0) {
+ 83         *value = val;
+ 84         return 1;
+ 85     } else {
+ 86         return 0;
+ 87     }
+ 88 }
+ 89 
+ 90 char* util_url_path(char *url) { 
+ 91     char *path = NULL;
+ 92     size_t len = strlen(url);
+ 93     int slashcount = 0;
+ 94     int slmax;
+ 95     if(len > 7 && !strncasecmp(url, "http://", 7)) {
+ 96         slmax = 3;
+ 97     } else if(len > 8 && !strncasecmp(url, "https://", 8)) {
+ 98         slmax = 3;
+ 99     } else {
+100         slmax = 1;
+101     }
+102     char c;
+103     for(int i=0;i<len;i++) {
+104         c = url[i];
+105         if(c == '/') {
+106             slashcount++;
+107             if(slashcount == slmax) {
+108                 path = url + i;
+109                 break;
+110             }
+111         }
+112     } 
+113     return path;
+114 }
+115 
+116 char* util_url_decode(DavSession *sn, char *url) {
+117     char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL);
+118     char *ret = strdup(unesc);
+119     curl_free(unesc);
+120     return ret;
+121 }
+122 
+123 char* util_resource_name(char *url) {
+124     int si = 0;
+125     int osi = 0;
+126     int i = 0;
+127     int p = 0;
+128     char c;
+129     while((c = url[i]) != 0) {
+130         if(c == '/') {
+131             osi = si;
+132             si = i;
+133             p = 1;
+134         }
+135         i++;
+136     }
+137     
+138     char *name = url + si + p;
+139     if(name[0] == 0) {
+140         name = url + osi + p;
+141         if(name[0] == 0) {
+142             return url;
+143         }
+144     }
+145     
+146     return name;
+147 }
+148 
+149 int util_mkdir(char *path, mode_t mode) {
+150 #ifdef _WIN32
+151     return mkdir(path);
+152 #else
+153     return mkdir(path, mode);
+154 #endif
+155 }
+156 
+157 char* util_concat_path(char *url_base, char *p) {
+158     sstr_t base = sstr(url_base);
+159     sstr_t path;
+160     if(p) {
+161         path = sstr(p);
+162     } else {
+163         path = sstrn("", 0);
+164     }
+165     
+166     int add_separator = 0;
+167     if(base.ptr[base.length-1] == '/') {
+168         if(path.ptr[0] == '/') {
+169             base.length--;
+170         }
+171     } else {
+172         if(path.length == 0 || path.ptr[0] != '/') {
+173             add_separator = 1;
+174         }
+175     }
+176     
+177     sstr_t url;
+178     if(add_separator) {
+179         url = sstrcat(3, base, sstr("/"), path);
+180     } else {
+181         url = sstrcat(2, base, path);
+182     }
+183     
+184     return url.ptr;
+185 }
+186 
+187 void util_set_url(DavSession *sn, char *href) {
+188     sstr_t base = sstr(sn->base_url);
+189     sstr_t href_str = sstr(href);
+190     
+191     char *base_path = util_url_path(sn->base_url);
+192     base.length -= strlen(base_path);
+193     
+194     sstr_t url = sstrcat(2, base, href_str);
+195     
+196     curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr);
+197     free(url.ptr);
+198 }
+199 
+200 char* util_path_to_url(DavSession *sn, char *path) {
+201     char *space = malloc(256);
+202     UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND);
+203     
+204     // add base url
+205     ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url);
+206     // remove trailing slash
+207     ucx_buffer_seek(url, -1, SEEK_CUR);
+208     
+209     sstr_t p = sstr(path);
+210     ssize_t ntk = 0;
+211     sstr_t *tks = sstrsplit(p, S("/"), &ntk);
+212     
+213     for(int i=0;i<ntk;i++) {
+214         sstr_t node = tks[i];
+215         if(node.length > 0) {
+216             char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
+217             ucx_buffer_putc(url, '/');
+218             ucx_buffer_write(esc, 1, strlen(esc), url);
+219             curl_free(esc);
+220         }
+221         free(node.ptr);
+222     }
+223     free(tks);
+224     if(path[p.length-1] == '/') {
+225         ucx_buffer_putc(url, '/');
+226     }
+227     ucx_buffer_putc(url, 0);
+228     
+229     space = url->space;
+230     ucx_buffer_free(url);
+231     
+232     return space;
+233 }
+234 
+235 char* util_parent_path(char *path) {
+236     char *name = util_resource_name(path);
+237     size_t namelen = strlen(name);
+238     size_t pathlen = strlen(path);
+239     size_t parentlen = pathlen - namelen;
+240     char *parent = malloc(parentlen + 1);
+241     memcpy(parent, path, parentlen);
+242     parent[parentlen] = '\0';
+243     return parent;
+244 }
+245 
+246 
+247 char* util_xml_get_text(xmlNode *elm) {
+248     xmlNode *node = elm->children;
+249     while(node) {
+250         if(node->type == XML_TEXT_NODE) {
+251             return (char*)node->content;
+252         }
+253         node = node->next;
+254     }
+255     return NULL;
+256 }
+257 
+258 
+259 char* util_base64decode(char *in) {
+260     int len = 0;
+261     return util_base64decode_len(in, &len);
+262 }
+263 
+264 char* util_base64decode_len(char* in, int *outlen) {
+265     size_t len = strlen(in);
+266     char *out = calloc(1, len);
+267     
+268     BIO* b = BIO_new_mem_buf(in, len);
+269     BIO *d = BIO_new(BIO_f_base64());
+270     BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL);
+271     b = BIO_push(d, b);
+272 
+273     *outlen = BIO_read(b, out, len);
+274     BIO_free_all(b);
+275     
+276     return out;
+277 }
+278 
+279 char* util_base64encode(char *in, size_t len) { 
+280     BIO *b;
+281     BIO *e;
+282     BUF_MEM *mem;
+283 
+284     e = BIO_new(BIO_f_base64());
+285     b = BIO_new(BIO_s_mem());
+286     
+287     e = BIO_push(e, b);
+288     BIO_write(e, in, len);
+289     BIO_flush(e);
+290     
+291     BIO_get_mem_ptr(e, &mem);
+292     char *out = malloc(mem->length);
+293     memcpy(out, mem->data, mem->length -1);
+294     out[mem->length - 1] = '\0';
+295 
+296     BIO_free_all(e);
+297 
+298     return out;
+299 }
+300 
+301 char* util_encrypt_str(DavSession *sn, char *str, char *key) {
+302     DavKey *k = dav_context_get_key(sn->context, key);
+303     if(!k) {
+304         // TODO: session error
+305         return NULL;
+306     }
+307     
+308     char *enc_str = aes_encrypt(str, k);
+309     char *ret_str = dav_session_strdup(sn, enc_str);
+310     free(enc_str);
+311     return ret_str;
+312 }
+313 
+314 /* commented out for testing reasons */
+315 /*
+316 char* util_decrypt_str(DavSession *sn, char *str, char *key) {
+317     DavKey *k = dav_context_get_key(sn->context, key);
+318     if(!k) {
+319         // TODO: session error
+320         return NULL;
+321     }
+322     
+323     char *dec_str = aes_decrypt(str, k);
+324     char *ret_str = dav_session_strdup(sn, dec_str);
+325     free(dec_str);
+326     return ret_str;
+327 }
+328 */
+329 char* util_random_str() {
+330     unsigned char *str = malloc(25);
+331     str[24] = '\0';
+332     
+333     sstr_t t = S(
+334             "01234567890"
+335             "abcdefghijklmnopqrstuvwxyz"
+336             "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+337     const unsigned char *table = (const unsigned char*)t.ptr;
+338     
+339     RAND_pseudo_bytes(str, 24);
+340     for(int i=0;i<24;i++) {
+341         int c = str[i] % t.length;
+342         str[i] = table[c];
+343     }
+344     
+345     return (char*)str;
+346 }
+347 
+348 /*
+349  * gets a substring from 0 to the appearance of the token
+350  * tokens are separated by space
+351  * sets sub to the substring and returns the remaining string
+352  */
+353 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {  
+354     int i;
+355     int token_start = -1;
+356     int token_end = -1;
+357     for(i=0;i<=str.length;i++) {
+358         int c;
+359         if(i == str.length) {
+360             c = ' ';
+361         } else {
+362             c = str.ptr[i];
+363         }
+364         if(c < 33) {
+365             if(token_start != -1) {
+366                 token_end = i;
+367                 size_t len = token_end - token_start;
+368                 sstr_t tk = sstrsubsl(str, token_start, len);
+369                 //printf("token: {%.*s}\n", token.length, token.ptr);
+370                 if(!sstrcmp(tk, token)) {
+371                     *sub = sstrtrim(sstrsubsl(str, 0, token_start));
+372                     break;
+373                 }
+374                 token_start = -1;
+375                 token_end = -1;
+376             }
+377         } else {
+378             if(token_start == -1) {
+379                 token_start = i;
+380             }
+381         }
+382     }
+383     
+384     if(i < str.length) {
+385         return sstrtrim(sstrsubs(str, i));
+386     } else {
+387         str.ptr = NULL;
+388         str.length = 0;
+389         return str;
+390     }
+391 }
+
+ + + diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/golden-master/javatest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/golden-master/javatest.html Mon Oct 03 12:14:53 2022 +0200 @@ -0,0 +1,221 @@ + + + + c2html + + + + +
+  1 /*
+  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+  3  *
+  4  * Copyright 2014 Mike Becker. All rights reserved.
+  5  *
+  6  * Redistribution and use in source and binary forms, with or without
+  7  * modification, are permitted provided that the following conditions are met:
+  8  *
+  9  *   1. Redistributions of source code must retain the above copyright
+ 10  *      notice, this list of conditions and the following disclaimer.
+ 11  *
+ 12  *   2. Redistributions in binary form must reproduce the above copyright
+ 13  *      notice, this list of conditions and the following disclaimer in the
+ 14  *      documentation and/or other materials provided with the distribution.
+ 15  *
+ 16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ 17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ 18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ 19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ 20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ 21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ 22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ 23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ 24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ 25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ 26  * POSSIBILITY OF SUCH DAMAGE.
+ 27  *
+ 28  */
+ 29 
+ 30 package de.uapcore.sigred.doc.base;
+ 31 
+ 32 import de.uapcore.sigred.doc.Resources;
+ 33 import de.uapcore.sigrapi.impl.Digraph;
+ 34 import de.uapcore.sigrapi.impl.Graph;
+ 35 import de.uapcore.sigrapi.IGraph;
+ 36 import java.io.IOException;
+ 37 import java.io.InputStream;
+ 38 import java.io.OutputStream;
+ 39 import java.util.concurrent.atomic.AtomicBoolean;
+ 40 import java.util.concurrent.atomic.AtomicReference;
+ 41 import org.apache.xerces.impl.Constants;
+ 42 import org.dom4j.Document;
+ 43 import org.dom4j.DocumentException;
+ 44 import org.dom4j.DocumentHelper;
+ 45 import org.dom4j.Element;
+ 46 import org.dom4j.Namespace;
+ 47 import org.dom4j.QName;
+ 48 import org.dom4j.io.OutputFormat;
+ 49 import org.dom4j.io.SAXReader;
+ 50 import org.dom4j.io.XMLWriter;
+ 51 import org.xml.sax.ErrorHandler;
+ 52 import org.xml.sax.SAXException;
+ 53 import org.xml.sax.SAXParseException;
+ 54 
+ 55 public abstract class AbstractGraphDocument<T extends IGraph>
+ 56         extends FileBackedDocument {
+ 57     
+ 58     protected static final Namespace NAMESPACE = Namespace.get("sigred",
+ 59         "http://develop.uap-core.de/sigred/");
+ 60     
+ 61     private static final
+ 62         QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE);
+ 63     private static final
+ 64         QName TAG_GRAPH = QName.get("graph", NAMESPACE);
+ 65     private static final
+ 66         QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE);
+ 67     private static final
+ 68         QName TAG_METADATA = QName.get("metadata", NAMESPACE);
+ 69     
+ 70     protected final T graph;
+ 71     
+ 72     private final GraphDocumentMetadata metadata;
+ 73     
+ 74     public AbstractGraphDocument(Class<T> graphType) {
+ 75         T g;
+ 76         try {
+ 77             g = graphType.newInstance();
+ 78         } catch (ReflectiveOperationException e) {
+ 79             assert false;
+ 80             g = null; // for the compiler
+ 81         }
+ 82         graph = g;
+ 83         metadata = new GraphDocumentMetadata();
+ 84     }
+ 85 
+ 86     public T getGraph() {
+ 87         return graph;
+ 88     }
+ 89     
+ 90     public GraphDocumentMetadata getMetadata() {
+ 91         return metadata;
+ 92     }
+ 93 
+ 94     protected abstract void writeGraph(Element rootNode) throws IOException;
+ 95     protected abstract void readGraph(Element rootNode) throws IOException;
+ 96 
+ 97     @Override
+ 98     public void writeTo(OutputStream out) throws IOException {
+ 99         Document doc = DocumentHelper.createDocument();
+100 
+101         Element rootNode = doc.addElement(TAG_GRAPHDOC);
+102 
+103         Element metadataNode = rootNode.addElement(TAG_METADATA);
+104 
+105         metadata.write(metadataNode);
+106 
+107         if (graph instanceof Graph) {
+108             writeGraph(rootNode.addElement(TAG_GRAPH));
+109         } else if (graph instanceof Digraph) {
+110             writeGraph(rootNode.addElement(TAG_DIGRAPH));
+111         } else {
+112             throw new IOException("unsupported graph type");
+113         }
+114 
+115         XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
+116         writer.write(doc);
+117         writer.flush();
+118     }
+119 
+120     @Override
+121     public void readFrom(InputStream in) throws IOException {
+122         try {
+123             SAXReader reader = new SAXReader(true);
+124             reader.setStripWhitespaceText(true);
+125             
+126             reader.setFeature(Constants.XERCES_FEATURE_PREFIX+
+127                 Constants.SCHEMA_VALIDATION_FEATURE, true);
+128             reader.setProperty(Constants.XERCES_PROPERTY_PREFIX +
+129                 Constants.SCHEMA_LOCATION, String.format("%s %s",
+130                     NAMESPACE.getURI(), Resources.class.getResource(
+131                         "graph-document.xsd").toExternalForm()));
+132             
+133             final AtomicBoolean passed = new AtomicBoolean(true);
+134             final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>();
+135             // TODO: we should do more detailed error handling here
+136             reader.setErrorHandler(new ErrorHandler() {
+137                 @Override
+138                 public void warning(SAXParseException exception) throws SAXException {
+139                 }
+140 
+141                 @Override
+142                 public void error(SAXParseException exception) throws SAXException {
+143                     xmlerror.set(exception);
+144                     passed.set(false);
+145                 }
+146 
+147                 @Override
+148                 public void fatalError(SAXParseException exception) throws SAXException {
+149                     xmlerror.set(exception);
+150                     passed.set(false);
+151                 }
+152                 
+153             });
+154             Document doc = reader.read(in);
+155             if (!passed.get()) {
+156                 // TODO: provide details (maybe via separate error object?)
+157                 throw xmlerror.get();
+158             }
+159             
+160             doc.normalize();
+161             
+162             Element root = doc.getRootElement();
+163             metadata.read(root.element(TAG_METADATA));
+164             
+165             if (graph instanceof Graph) {
+166                 readGraph(root.element(TAG_GRAPH));
+167             } else if (graph instanceof Digraph) {
+168                 readGraph(root.element(TAG_DIGRAPH));
+169             } else {
+170                 throw new IOException("unsupported graph type");
+171             }
+172         } catch (DocumentException | SAXException ex) {
+173             throw new IOException(ex);
+174         }
+175     }
+176 }
+
+ + + diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/golden-master/plain.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/golden-master/plain.html Mon Oct 03 12:14:53 2022 +0200 @@ -0,0 +1,59 @@ + + + + c2html + + + + +
+1 </body>
+2 </html>
+3 <!c
+4 pblock_free(q);
+5 !>
+6 
+
+ + + diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/v2-regression/bigtest.html --- a/test/v2-regression/bigtest.html Mon Nov 13 14:17:46 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,855 +0,0 @@ - - - - c2html - - - - -
-  1  /*
-  2   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
-  3   *
-  4   * Copyright 2014 Mike Becker. All rights reserved.
-  5   *
-  6   * Redistribution and use in source and binary forms, with or without
-  7   * modification, are permitted provided that the following conditions are met:
-  8   *
-  9   *   1. Redistributions of source code must retain the above copyright
- 10   *      notice, this list of conditions and the following disclaimer.
- 11   *
- 12   *   2. Redistributions in binary form must reproduce the above copyright
- 13   *      notice, this list of conditions and the following disclaimer in the
- 14   *      documentation and/or other materials provided with the distribution.
- 15   *
- 16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- 17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- 18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- 19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- 20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- 21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- 22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- 23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- 24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- 25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- 26   * POSSIBILITY OF SUCH DAMAGE.
- 27   *
- 28   */
- 29  
- 30  #include "rules.h"
- 31  #include "chess.h"
- 32  #include <string.h>
- 33  #include <stdlib.h>
- 34  #include <sys/time.h>
- 35  
- 36  static GameState gamestate_copy_sim(GameState *gamestate) {
- 37      GameState simulation = *gamestate;
- 38      if (simulation.lastmove) {
- 39          MoveList *lastmovecopy = malloc(sizeof(MoveList));
- 40          *lastmovecopy = *(simulation.lastmove);
- 41          simulation.movelist = simulation.lastmove = lastmovecopy;
- 42      }
- 43  
- 44      return simulation;
- 45  }
- 46  
- 47  void gamestate_init(GameState *gamestate) {
- 48      memset(gamestate, 0, sizeof(GameState));
- 49      
- 50      Board initboard = {
- 51          {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK},
- 52          {WPAWN, WPAWN,   WPAWN,   WPAWN,  WPAWN, WPAWN,   WPAWN,   WPAWN},
- 53          {0,     0,       0,       0,      0,     0,       0,       0},
- 54          {0,     0,       0,       0,      0,     0,       0,       0},
- 55          {0,     0,       0,       0,      0,     0,       0,       0},
- 56          {0,     0,       0,       0,      0,     0,       0,       0},
- 57          {BPAWN, BPAWN,   BPAWN,   BPAWN,  BPAWN, BPAWN,   BPAWN,   BPAWN},
- 58          {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK}
- 59      };
- 60      memcpy(gamestate->board, initboard, sizeof(Board));
- 61  }
- 62  
- 63  void gamestate_cleanup(GameState *gamestate) {
- 64      MoveList *elem;
- 65      elem = gamestate->movelist;
- 66      while (elem) {
- 67          MoveList *cur = elem;
- 68          elem = elem->next;
- 69          free(cur);
- 70      };
- 71  }
- 72  
- 73  /* MUST be called IMMEDIATLY after applying a move to work correctly */
- 74  static void format_move(GameState *gamestate, Move *move) {
- 75      char *string = move->string;
- 76      
- 77      /* at least 8 characters should be available, wipe them out */
- 78      memset(string, 0, 8);
- 79      
- 80      /* special formats for castling */
- 81      if ((move->piece&PIECE_MASK) == KING &&
- 82              abs(move->tofile-move->fromfile) == 2) {
- 83          if (move->tofile==fileidx('c')) {
- 84              memcpy(string, "O-O-O", 5);
- 85          } else {
- 86              memcpy(string, "O-O", 3);
- 87          }
- 88      }
- 89  
- 90      /* start by notating the piece character */
- 91      string[0] = getpiecechr(move->piece);
- 92      int idx = string[0] ? 1 : 0;
- 93      
- 94      /* find out how many source information we do need */
- 95      uint8_t piece = move->piece & PIECE_MASK;
- 96      if (piece == PAWN) {
- 97          if (move->capture) {
- 98              string[idx++] = filechr(move->fromfile);
- 99          }
-100      } else if (piece != KING) {
-101          Move threats[16];
-102          uint8_t threatcount;
-103          get_real_threats(gamestate, move->torow, move->tofile,
-104              move->piece&COLOR_MASK, threats, &threatcount);
-105          if (threatcount > 1) {
-106              int ambrows = 0, ambfiles = 0;
-107              for (uint8_t i = 0 ; i < threatcount ; i++) {
-108                  if (threats[i].fromrow == move->fromrow) {
-109                      ambrows++;
-110                  }
-111                  if (threats[i].fromfile == move->fromfile) {
-112                      ambfiles++;
-113                  }
-114              }
-115              /* ambiguous row, name file */
-116              if (ambrows > 1) {
-117                  string[idx++] = filechr(move->fromfile);
-118              }
-119              /* ambiguous file, name row */
-120              if (ambfiles > 1) {
-121                  string[idx++] = filechr(move->fromrow);
-122              }
-123          }
-124      }
-125      
-126      /* capturing? */
-127      if (move->capture) {
-128          string[idx++] = 'x';
-129      }
-130      
-131      /* destination */
-132      string[idx++] = filechr(move->tofile);
-133      string[idx++] = rowchr(move->torow);
-134      
-135      /* promotion? */
-136      if (move->promotion) {
-137          string[idx++] = '=';
-138          string[idx++] = getpiecechr(move->promotion);
-139      }
-140      
-141      /* check? */
-142      if (move->check) {
-143          /* works only, if this function is called when applying the move */
-144          string[idx++] = gamestate->checkmate?'#':'+';
-145      }
-146  }
-147  
-148  static void addmove(GameState* gamestate, Move *move) {
-149      MoveList *elem = malloc(sizeof(MoveList));
-150      elem->next = NULL;
-151      elem->move = *move;
-152      
-153      struct timeval curtimestamp;
-154      gettimeofday(&curtimestamp, NULL);
-155      elem->move.timestamp.tv_sec = curtimestamp.tv_sec;
-156      elem->move.timestamp.tv_usec = curtimestamp.tv_usec;
-157      
-158      if (gamestate->lastmove) {
-159          struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp);
-160          uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec;
-161          suseconds_t micros;
-162          if (curtimestamp.tv_usec < lasttstamp->tv_usec) {
-163              micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec);
-164              sec--;
-165          } else {
-166              micros = curtimestamp.tv_usec - lasttstamp->tv_usec;
-167          }
-168          
-169          elem->move.movetime.tv_sec = sec;
-170          elem->move.movetime.tv_usec = micros;
-171          
-172          gamestate->lastmove->next = elem;
-173          gamestate->lastmove = elem;
-174      } else {
-175          elem->move.movetime.tv_usec = 0;
-176          elem->move.movetime.tv_sec = 0;
-177          gamestate->movelist = gamestate->lastmove = elem;
-178      }
-179  }
-180  
-181  char getpiecechr(uint8_t piece) {
-182      switch (piece & PIECE_MASK) {
-183      case ROOK: return 'R';
-184      case KNIGHT: return 'N';
-185      case BISHOP: return 'B';
-186      case QUEEN: return 'Q';
-187      case KING: return 'K';
-188      default: return '\0';
-189      }
-190  }
-191  
-192  uint8_t getpiece(char c) {
-193      switch (c) {
-194          case 'R': return ROOK;
-195          case 'N': return KNIGHT;
-196          case 'B': return BISHOP;
-197          case 'Q': return QUEEN;
-198          case 'K': return KING;
-199          default: return 0;
-200      }
-201  }
-202  
-203  static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
-204      uint8_t piece = move->piece & PIECE_MASK;
-205      uint8_t color = move->piece & COLOR_MASK;
-206      
-207      /* en passant capture */
-208      if (move->capture && piece == PAWN &&
-209          mdst(gamestate->board, move) == 0) {
-210          gamestate->board[move->fromrow][move->tofile] = 0;
-211      }
-212      
-213      /* remove old en passant threats */
-214      for (uint8_t file = 0 ; file < 8 ; file++) {
-215          gamestate->board[3][file] &= ~ENPASSANT_THREAT;
-216          gamestate->board[4][file] &= ~ENPASSANT_THREAT;
-217      }
-218      
-219      /* add new en passant threat */
-220      if (piece == PAWN && (
-221          (move->fromrow == 1 && move->torow == 3) ||
-222          (move->fromrow == 6 && move->torow == 4))) {
-223          move->piece |= ENPASSANT_THREAT;
-224      }
-225      
-226      /* move (and maybe capture or promote) */
-227      msrc(gamestate->board, move) = 0;
-228      if (move->promotion) {
-229          mdst(gamestate->board, move) = move->promotion;
-230      } else {
-231          mdst(gamestate->board, move) = move->piece;
-232      }
-233      
-234      /* castling */
-235      if (piece == KING && move->fromfile == fileidx('e')) {
-236          
-237          if (move->tofile == fileidx('g')) {
-238              gamestate->board[move->torow][fileidx('h')] = 0;
-239              gamestate->board[move->torow][fileidx('f')] = color|ROOK;
-240          } else if (move->tofile == fileidx('c')) {
-241              gamestate->board[move->torow][fileidx('a')] = 0;
-242              gamestate->board[move->torow][fileidx('d')] = color|ROOK;
-243          }
-244      }
-245  
-246      if (!simulate) {
-247          if (!move->string[0]) {
-248              format_move(gamestate, move);
-249          }
-250      }
-251      /* add move, even in simulation (checkmate test needs it) */
-252      addmove(gamestate, move);
-253  }
-254  
-255  void apply_move(GameState *gamestate, Move *move) {
-256      apply_move_impl(gamestate, move, 0);
-257  }
-258  
-259  static int validate_move_rules(GameState *gamestate, Move *move) {
-260      /* validate indices (don't trust opponent) */
-261      if (!chkidx(move)) {
-262          return INVALID_POSITION;
-263      }
-264      
-265      /* must move */
-266      if (move->fromfile == move->tofile && move->fromrow == move->torow) {
-267          return INVALID_MOVE_SYNTAX;
-268      }
-269      
-270      /* does piece exist */
-271      if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
-272             != (move->piece&(PIECE_MASK|COLOR_MASK))) {
-273          return INVALID_POSITION;
-274      }
-275      
-276      /* can't capture own pieces */
-277      if ((mdst(gamestate->board, move) & COLOR_MASK)
-278              == (move->piece & COLOR_MASK)) {
-279          return RULES_VIOLATED;
-280      }
-281      
-282      /* must capture, if and only if destination is occupied */
-283      if ((mdst(gamestate->board, move) == 0 && move->capture) ||
-284              (mdst(gamestate->board, move) != 0 && !move->capture)) {
-285          return INVALID_MOVE_SYNTAX;
-286      }
-287      
-288      /* validate individual rules */
-289      _Bool chkrules;
-290      switch (move->piece & PIECE_MASK) {
-291      case PAWN:
-292          chkrules = pawn_chkrules(gamestate, move) &&
-293              !pawn_isblocked(gamestate, move);
-294          break;
-295      case ROOK:
-296          chkrules = rook_chkrules(move) &&
-297              !rook_isblocked(gamestate, move);
-298          break;
-299      case KNIGHT:
-300          chkrules = knight_chkrules(move); /* knight is never blocked */
-301          break;
-302      case BISHOP:
-303          chkrules = bishop_chkrules(move) &&
-304              !bishop_isblocked(gamestate, move);
-305          break;
-306      case QUEEN:
-307          chkrules = queen_chkrules(move) &&
-308              !queen_isblocked(gamestate, move);
-309          break;
-310      case KING:
-311          chkrules = king_chkrules(gamestate, move) &&
-312              !king_isblocked(gamestate, move);
-313          break;
-314      default:
-315          return INVALID_MOVE_SYNTAX;
-316      }
-317      
-318      return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
-319  }
-320  
-321  int validate_move(GameState *gamestate, Move *move) {
-322      
-323      int result = validate_move_rules(gamestate, move);
-324      
-325      /* cancel processing to save resources */
-326      if (result != VALID_MOVE_SEMANTICS) {
-327          return result;
-328      }
-329      
-330      /* find kings for check validation */
-331      uint8_t piececolor = (move->piece & COLOR_MASK);
-332      
-333      uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
-334      for (uint8_t row = 0 ; row < 8 ; row++) {
-335          for (uint8_t file = 0 ; file < 8 ; file++) {
-336              if (gamestate->board[row][file] ==
-337                      (piececolor == WHITE?WKING:BKING)) {
-338                  mykingfile = file;
-339                  mykingrow = row;
-340              } else if (gamestate->board[row][file] ==
-341                      (piececolor == WHITE?BKING:WKING)) {
-342                  opkingfile = file;
-343                  opkingrow = row;
-344              }
-345          }
-346      }
-347      
-348      /* simulate move for check validation */
-349      GameState simulation = gamestate_copy_sim(gamestate);
-350      Move simmove = *move;
-351      apply_move_impl(&simulation, &simmove, 1);
-352      
-353      /* don't move into or stay in check position */
-354      if (is_covered(&simulation, mykingrow, mykingfile,
-355          opponent_color(piececolor))) {
-356          
-357          gamestate_cleanup(&simulation);
-358          if ((move->piece & PIECE_MASK) == KING) {
-359              return KING_MOVES_INTO_CHECK;
-360          } else {
-361              /* last move is always not null in this case */
-362              return gamestate->lastmove->move.check ?
-363                  KING_IN_CHECK : PIECE_PINNED;
-364          }
-365      }
-366      
-367      /* correct check and checkmate flags (move is still valid) */
-368      Move threats[16];
-369      uint8_t threatcount;
-370      move->check = get_threats(&simulation, opkingrow, opkingfile,
-371          piececolor, threats, &threatcount);
-372      
-373      if (move->check) {
-374          /* determine possible escape fields */
-375          _Bool canescape = 0;
-376          for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
-377              for (int df = -1 ; df <= 1 && !canescape ; df++) {
-378                  if (!(dr == 0 && df == 0)  &&
-379                          isidx(opkingrow + dr) && isidx(opkingfile + df)) {
-380                      
-381                      /* escape field neither blocked nor covered */
-382                      if ((simulation.board[opkingrow + dr][opkingfile + df]
-383                              & COLOR_MASK) != opponent_color(piececolor)) {
-384                          canescape |= !is_covered(&simulation,
-385                              opkingrow + dr, opkingfile + df, piececolor);
-386                      }
-387                  }
-388              }
-389          }
-390          /* can't escape, can he capture? */
-391          if (!canescape && threatcount == 1) {
-392              canescape = is_attacked(&simulation, threats[0].fromrow,
-393                  threats[0].fromfile, opponent_color(piececolor));
-394          }
-395          
-396          /* can't capture, can he block? */
-397          if (!canescape && threatcount == 1) {
-398              Move *threat = &(threats[0]);
-399              uint8_t threatpiece = threat->piece & PIECE_MASK;
-400              
-401              /* knight, pawns and the king cannot be blocked */
-402              if (threatpiece == BISHOP || threatpiece == ROOK
-403                  || threatpiece == QUEEN) {
-404                  if (threat->fromrow == threat->torow) {
-405                      /* rook aspect (on row) */
-406                      int d = threat->tofile > threat->fromfile ? 1 : -1;
-407                      uint8_t file = threat->fromfile;
-408                      while (!canescape && file != threat->tofile - d) {
-409                          file += d;
-410                          canescape |= is_protected(&simulation,
-411                              threat->torow, file, opponent_color(piececolor));
-412                      }
-413                  } else if (threat->fromfile == threat->tofile) {
-414                      /* rook aspect (on file) */
-415                      int d = threat->torow > threat->fromrow ? 1 : -1;
-416                      uint8_t row = threat->fromrow;
-417                      while (!canescape && row != threat->torow - d) {
-418                          row += d;
-419                          canescape |= is_protected(&simulation,
-420                              row, threat->tofile, opponent_color(piececolor));
-421                      }
-422                  } else {
-423                      /* bishop aspect */
-424                      int dr = threat->torow > threat->fromrow ? 1 : -1;
-425                      int df = threat->tofile > threat->fromfile ? 1 : -1;
-426  
-427                      uint8_t row = threat->fromrow;
-428                      uint8_t file = threat->fromfile;
-429                      while (!canescape && file != threat->tofile - df
-430                          && row != threat->torow - dr) {
-431                          row += dr;
-432                          file += df;
-433                          canescape |= is_protected(&simulation, row, file,
-434                              opponent_color(piececolor));
-435                      }
-436                  }
-437              }
-438          }
-439              
-440          if (!canescape) {
-441              gamestate->checkmate = 1;
-442          }
-443      }
-444      
-445      gamestate_cleanup(&simulation);
-446      
-447      return VALID_MOVE_SEMANTICS;
-448  }
-449  
-450  _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
-451          uint8_t color, Move *threats, uint8_t *threatcount) {
-452      Move candidates[32];
-453      int candidatecount = 0;
-454      for (uint8_t r = 0 ; r < 8 ; r++) {
-455          for (uint8_t f = 0 ; f < 8 ; f++) {
-456              if ((gamestate->board[r][f] & COLOR_MASK) == color) {
-457                  // non-capturing move
-458                  memset(&(candidates[candidatecount]), 0, sizeof(Move));
-459                  candidates[candidatecount].piece = gamestate->board[r][f];
-460                  candidates[candidatecount].fromrow = r;
-461                  candidates[candidatecount].fromfile = f;
-462                  candidates[candidatecount].torow = row;
-463                  candidates[candidatecount].tofile = file;
-464                  candidatecount++;
-465  
-466                  // capturing move
-467                  memcpy(&(candidates[candidatecount]),
-468                      &(candidates[candidatecount-1]), sizeof(Move));
-469                  candidates[candidatecount].capture = 1;
-470                  candidatecount++;
-471              }
-472          }
-473      }
-474  
-475      if (threatcount) {
-476          *threatcount = 0;
-477      }
-478      
-479      
-480      _Bool result = 0;
-481      
-482      for (int i = 0 ; i < candidatecount ; i++) {
-483          if (validate_move_rules(gamestate, &(candidates[i]))
-484                  == VALID_MOVE_SEMANTICS) {
-485              result = 1;
-486              if (threats && threatcount) {
-487                  threats[(*threatcount)++] = candidates[i];
-488              }
-489          }
-490      }
-491      
-492      return result;
-493  }
-494  
-495  _Bool is_pinned(GameState *gamestate, Move *move) {
-496      uint8_t color = move->piece & COLOR_MASK;
-497  
-498      uint8_t kingfile = 0, kingrow = 0;
-499      for (uint8_t row = 0 ; row < 8 ; row++) {
-500          for (uint8_t file = 0 ; file < 8 ; file++) {
-501              if (gamestate->board[row][file] == (color|KING)) {
-502                  kingfile = file;
-503                  kingrow = row;
-504              }
-505          }
-506      }
-507  
-508      GameState simulation = gamestate_copy_sim(gamestate);
-509      Move simmove = *move;
-510      apply_move(&simulation, &simmove);
-511      _Bool covered = is_covered(&simulation,
-512          kingrow, kingfile, opponent_color(color));
-513      gamestate_cleanup(&simulation);
-514      
-515      return covered;
-516  }
-517  
-518  _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
-519          uint8_t color, Move *threats, uint8_t *threatcount) {
-520      
-521      if (threatcount) {
-522          *threatcount = 0;
-523      }
-524  
-525      Move candidates[16];
-526      uint8_t candidatecount;
-527      if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
-528          
-529          _Bool result = 0;
-530          uint8_t kingfile = 0, kingrow = 0;
-531          for (uint8_t row = 0 ; row < 8 ; row++) {
-532              for (uint8_t file = 0 ; file < 8 ; file++) {
-533                  if (gamestate->board[row][file] == (color|KING)) {
-534                      kingfile = file;
-535                      kingrow = row;
-536                  }
-537              }
-538          }
-539  
-540          for (uint8_t i = 0 ; i < candidatecount ; i++) {
-541              GameState simulation = gamestate_copy_sim(gamestate);
-542              Move simmove = candidates[i];
-543              apply_move(&simulation, &simmove);
-544              if (!is_covered(&simulation, kingrow, kingfile,
-545                      opponent_color(color))) {
-546                  result = 1;
-547                  if (threats && threatcount) {
-548                      threats[(*threatcount)++] = candidates[i];
-549                  }
-550              }
-551          }
-552          
-553          return result;
-554      } else {
-555          return 0;
-556      }
-557  }
-558  
-559  static int getlocation(GameState *gamestate, Move *move) {   
-560  
-561      uint8_t color = move->piece & COLOR_MASK;
-562      _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
-563      
-564      Move threats[16], *threat = NULL;
-565      uint8_t threatcount;
-566      
-567      if (get_threats(gamestate, move->torow, move->tofile, color,
-568              threats, &threatcount)) {
-569          
-570          int reason = INVALID_POSITION;
-571          
-572          // find threats for the specified position
-573          for (uint8_t i = 0 ; i < threatcount ; i++) {
-574              if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
-575                      == move->piece &&
-576                      (move->fromrow == POS_UNSPECIFIED ||
-577                      move->fromrow == threats[i].fromrow) &&
-578                      (move->fromfile == POS_UNSPECIFIED ||
-579                      move->fromfile == threats[i].fromfile)) {
-580  
-581                  if (threat) {
-582                      return AMBIGUOUS_MOVE;
-583                  } else {
-584                      // found threat is no real threat
-585                      if (is_pinned(gamestate, &(threats[i]))) {
-586                          reason = incheck?KING_IN_CHECK:PIECE_PINNED;
-587                      } else {
-588                          threat = &(threats[i]);
-589                      }
-590                  }
-591              }
-592          }
-593          
-594          // can't threaten specified position
-595          if (!threat) {
-596              return reason;
-597          }
-598  
-599          memcpy(move, threat, sizeof(Move));
-600          return VALID_MOVE_SYNTAX;
-601      } else {
-602          return INVALID_POSITION;
-603      }
-604  }
-605  
-606  int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) {
-607      memset(move, 0, sizeof(Move));
-608      move->fromfile = POS_UNSPECIFIED;
-609      move->fromrow = POS_UNSPECIFIED;
-610  
-611      size_t len = strlen(mstr);
-612      if (len < 1 || len > 6) {
-613          return INVALID_MOVE_SYNTAX;
-614      }
-615      
-616      /* evaluate check/checkmate flags */
-617      if (mstr[len-1] == '+') {
-618          len--; mstr[len] = '\0';
-619          move->check = 1;
-620      } else if (mstr[len-1] == '#') {
-621          len--; mstr[len] = '\0';
-622          /* ignore - validation should set game state */
-623      }
-624      
-625      /* evaluate promotion */
-626      if (len > 3 && mstr[len-2] == '=') {
-627          move->promotion = getpiece(mstr[len-1]);
-628          if (!move->promotion) {
-629              return INVALID_MOVE_SYNTAX;
-630          } else {
-631              move->promotion |= color;
-632              len -= 2;
-633              mstr[len] = 0;
-634          }
-635      }
-636      
-637      if (len == 2) {
-638          /* pawn move (e.g. "e4") */
-639          move->piece = PAWN;
-640          move->tofile = fileidx(mstr[0]);
-641          move->torow = rowidx(mstr[1]);
-642      } else if (len == 3) {
-643          if (strcmp(mstr, "O-O") == 0) {
-644              /* king side castling */
-645              move->piece = KING;
-646              move->fromfile = fileidx('e');
-647              move->tofile = fileidx('g');
-648              move->fromrow = move->torow = color == WHITE ? 0 : 7;
-649          } else {
-650              /* move (e.g. "Nf3") */
-651              move->piece = getpiece(mstr[0]);
-652              move->tofile = fileidx(mstr[1]);
-653              move->torow = rowidx(mstr[2]);
-654          }
-655      } else if (len == 4) {
-656          move->piece = getpiece(mstr[0]);
-657          if (!move->piece) {
-658              move->piece = PAWN;
-659              move->fromfile = fileidx(mstr[0]);
-660          }
-661          if (mstr[1] == 'x') {
-662              /* capture (e.g. "Nxf3", "dxe5") */
-663              move->capture = 1;
-664          } else {
-665              /* move (e.g. "Ndf3", "N2c3", "e2e4") */
-666              if (isfile(mstr[1])) {
-667                  move->fromfile = fileidx(mstr[1]);
-668                  if (move->piece == PAWN) {
-669                      move->piece = 0;
-670                  }
-671              } else {
-672                  move->fromrow = rowidx(mstr[1]);
-673              }
-674          }
-675          move->tofile = fileidx(mstr[2]);
-676          move->torow = rowidx(mstr[3]);
-677      } else if (len == 5) {
-678          if (strcmp(mstr, "O-O-O") == 0) {
-679              /* queen side castling "O-O-O" */
-680              move->piece = KING;
-681              move->fromfile = fileidx('e');
-682              move->tofile = fileidx('c');
-683              move->fromrow = move->torow = color == WHITE ? 0 : 7;
-684          } else {
-685              move->piece = getpiece(mstr[0]);
-686              if (mstr[2] == 'x') {
-687                  move->capture = 1;
-688                  if (move->piece) {
-689                      /* capture (e.g. "Ndxf3") */
-690                      move->fromfile = fileidx(mstr[1]);
-691                  } else {
-692                      /* long notation capture (e.g. "e5xf6") */
-693                      move->piece = PAWN;
-694                      move->fromfile = fileidx(mstr[0]);
-695                      move->fromrow = rowidx(mstr[1]);
-696                  }
-697              } else {
-698                  /* long notation move (e.g. "Nc5a4") */
-699                  move->fromfile = fileidx(mstr[1]);
-700                  move->fromrow = rowidx(mstr[2]);
-701              }
-702              move->tofile = fileidx(mstr[3]);
-703              move->torow = rowidx(mstr[4]);
-704          }
-705      } else if (len == 6) {
-706          /* long notation capture (e.g. "Nc5xf3") */
-707          if (mstr[3] == 'x') {
-708              move->capture = 1;
-709              move->piece = getpiece(mstr[0]);
-710              move->fromfile = fileidx(mstr[1]);
-711              move->fromrow = rowidx(mstr[2]);
-712              move->tofile = fileidx(mstr[4]);
-713              move->torow = rowidx(mstr[5]);
-714          }
-715      }
-716  
-717      
-718      if (move->piece) {
-719          if (move->piece == PAWN
-720              && move->torow == (color==WHITE?7:0)
-721              && !move->promotion) {
-722              return NEED_PROMOTION;
-723          }
-724          
-725          move->piece |= color;
-726          if (move->fromfile == POS_UNSPECIFIED
-727              || move->fromrow == POS_UNSPECIFIED) {
-728              return getlocation(gamestate, move);
-729          } else {
-730              return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
-731          }
-732      } else {
-733          return INVALID_MOVE_SYNTAX;
-734      }
-735  }
-736  
-737  _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
-738          uint8_t color) {
-739      
-740      Move threats[16];
-741      uint8_t threatcount;
-742      if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
-743          for (int i = 0 ; i < threatcount ; i++) {
-744              if (threats[i].piece != (color|KING)) {
-745                  return 1;
-746              }
-747          }
-748          return 0;
-749      } else {
-750          return 0;
-751      }
-752  }
-753  
-754  uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
-755          uint8_t color) {
-756      if (!gameinfo->timecontrol) {
-757          return 0;
-758      }
-759      
-760      if (gamestate->movelist) {
-761          uint16_t time = gameinfo->time;
-762          suseconds_t micros = 0;
-763          
-764          MoveList *movelist = color == WHITE ?
-765              gamestate->movelist : gamestate->movelist->next;
-766          
-767          while (movelist) {
-768              time += gameinfo->addtime;
-769              
-770              struct movetimeval *movetime = &(movelist->move.movetime);
-771              if (movetime->tv_sec >= time) {
-772                  return 0;
-773              }
-774              
-775              time -= movetime->tv_sec;
-776              micros += movetime->tv_usec;
-777              
-778              movelist = movelist->next ? movelist->next->next : NULL;
-779          }
-780          
-781          time_t sec;
-782          movelist = gamestate->lastmove;
-783          if ((movelist->move.piece & COLOR_MASK) != color) {
-784              struct movetimeval *lastmovetstamp = &(movelist->move.timestamp);
-785              struct timeval currenttstamp;
-786              gettimeofday(¤ttstamp, NULL);
-787              micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec;
-788              sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
-789              if (sec >= time) {
-790                  return 0;
-791              }
-792              
-793              time -= sec;
-794          }
-795          
-796          sec = micros / 1e6L;
-797          
-798          if (sec >= time) {
-799              return 0;
-800          }
-801  
-802          time -= sec;
-803          
-804          return time;
-805      } else {
-806          return gameinfo->time;
-807      }
-808  }
-
- - - diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/v2-regression/ctest.html --- a/test/v2-regression/ctest.html Mon Nov 13 14:17:46 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,438 +0,0 @@ - - - - c2html - - - - -
-  1  /*
-  2   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
-  3   *
-  4   * Copyright 2015 Olaf Wintermann. All rights reserved.
-  5   *
-  6   * Redistribution and use in source and binary forms, with or without
-  7   * modification, are permitted provided that the following conditions are met:
-  8   *
-  9   *   1. Redistributions of source code must retain the above copyright
- 10   *      notice, this list of conditions and the following disclaimer.
- 11   *
- 12   *   2. Redistributions in binary form must reproduce the above copyright
- 13   *      notice, this list of conditions and the following disclaimer in the
- 14   *      documentation and/or other materials provided with the distribution.
- 15   *
- 16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- 17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- 18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- 19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- 20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- 21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- 22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- 23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- 24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- 25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- 26   * POSSIBILITY OF SUCH DAMAGE.
- 27   */
- 28  
- 29  #include <time.h>
- 30  #include <stdio.h>
- 31  #include <stdlib.h>
- 32  #include <string.h>
- 33  #include <ucx/string.h>
- 34  #include <ucx/buffer.h>
- 35  #include <ucx/utils.h>
- 36  #include <libxml/tree.h>
- 37  #include <curl/curl.h>
- 38  
- 39  #include <openssl/sha.h>
- 40  #include <openssl/hmac.h>
- 41  #include <openssl/evp.h>
- 42  #include <openssl/bio.h>
- 43  #include <openssl/buffer.h>
- 44  #include <openssl/rand.h>
- 45  
- 46  #include "utils.h"
- 47  #include "crypto.h"
- 48  #include "webdav.h"
- 49  
- 50  #define MACRO1337 1337L
- 51  
- 52  /* -------------------- This is a testing file. -------------------------- */
- 53  /*
- 54  time_t util_parse_creationdate(char *str) {
- 55      // example: 2012-11-29T21:35:35Z
- 56      if(!str) {
- 57          return 0;
- 58      }
- 59      // TODO
- 60      return 0;
- 61  }
- 62  */
- 63  time_t util_parse_lastmodified(char *str) {
- 64      // example: Thu, 29 Nov 2012 21:35:35 GMT
- 65      if(!str) {
- 66          return 0;
- 67      } else {
- 68          return curl_getdate(str, NULL);
- 69      }
- 70  }
- 71  
- 72  int util_getboolean(char *v) {
- 73      if(v[0] == 'T' || v[0] == 't') {
- 74          return 1;
- 75      }
- 76      return 0;
- 77  }
- 78  
- 79  int util_strtoint(char *str, int64_t *value) {
- 80      char *end;
- 81      int64_t val = strtoll(str, &end, 0);
- 82      if(strlen(end) == 0) {
- 83          *value = val;
- 84          return 1;
- 85      } else {
- 86          return 0;
- 87      }
- 88  }
- 89  
- 90  char* util_url_path(char *url) { 
- 91      char *path = NULL;
- 92      size_t len = strlen(url);
- 93      int slashcount = 0;
- 94      int slmax;
- 95      if(len > 7 && !strncasecmp(url, "http://", 7)) {
- 96          slmax = 3;
- 97      } else if(len > 8 && !strncasecmp(url, "https://", 8)) {
- 98          slmax = 3;
- 99      } else {
-100          slmax = 1;
-101      }
-102      char c;
-103      for(int i=0;i<len;i++) {
-104          c = url[i];
-105          if(c == '/') {
-106              slashcount++;
-107              if(slashcount == slmax) {
-108                  path = url + i;
-109                  break;
-110              }
-111          }
-112      } 
-113      return path;
-114  }
-115  
-116  char* util_url_decode(DavSession *sn, char *url) {
-117      char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL);
-118      char *ret = strdup(unesc);
-119      curl_free(unesc);
-120      return ret;
-121  }
-122  
-123  char* util_resource_name(char *url) {
-124      int si = 0;
-125      int osi = 0;
-126      int i = 0;
-127      int p = 0;
-128      char c;
-129      while((c = url[i]) != 0) {
-130          if(c == '/') {
-131              osi = si;
-132              si = i;
-133              p = 1;
-134          }
-135          i++;
-136      }
-137      
-138      char *name = url + si + p;
-139      if(name[0] == 0) {
-140          name = url + osi + p;
-141          if(name[0] == 0) {
-142              return url;
-143          }
-144      }
-145      
-146      return name;
-147  }
-148  
-149  int util_mkdir(char *path, mode_t mode) {
-150  #ifdef _WIN32
-151      return mkdir(path);
-152  #else
-153      return mkdir(path, mode);
-154  #endif
-155  }
-156  
-157  char* util_concat_path(char *url_base, char *p) {
-158      sstr_t base = sstr(url_base);
-159      sstr_t path;
-160      if(p) {
-161          path = sstr(p);
-162      } else {
-163          path = sstrn("", 0);
-164      }
-165      
-166      int add_separator = 0;
-167      if(base.ptr[base.length-1] == '/') {
-168          if(path.ptr[0] == '/') {
-169              base.length--;
-170          }
-171      } else {
-172          if(path.length == 0 || path.ptr[0] != '/') {
-173              add_separator = 1;
-174          }
-175      }
-176      
-177      sstr_t url;
-178      if(add_separator) {
-179          url = sstrcat(3, base, sstr("/"), path);
-180      } else {
-181          url = sstrcat(2, base, path);
-182      }
-183      
-184      return url.ptr;
-185  }
-186  
-187  void util_set_url(DavSession *sn, char *href) {
-188      sstr_t base = sstr(sn->base_url);
-189      sstr_t href_str = sstr(href);
-190      
-191      char *base_path = util_url_path(sn->base_url);
-192      base.length -= strlen(base_path);
-193      
-194      sstr_t url = sstrcat(2, base, href_str);
-195      
-196      curl_easy_setopt(sn->handle, CURLOPT_URL, url.ptr);
-197      free(url.ptr);
-198  }
-199  
-200  char* util_path_to_url(DavSession *sn, char *path) {
-201      char *space = malloc(256);
-202      UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND);
-203      
-204      // add base url
-205      ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url);
-206      // remove trailing slash
-207      ucx_buffer_seek(url, -1, SEEK_CUR);
-208      
-209      sstr_t p = sstr(path);
-210      ssize_t ntk = 0;
-211      sstr_t *tks = sstrsplit(p, S("/"), &ntk);
-212      
-213      for(int i=0;i<ntk;i++) {
-214          sstr_t node = tks[i];
-215          if(node.length > 0) {
-216              char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
-217              ucx_buffer_putc(url, '/');
-218              ucx_buffer_write(esc, 1, strlen(esc), url);
-219              curl_free(esc);
-220          }
-221          free(node.ptr);
-222      }
-223      free(tks);
-224      if(path[p.length-1] == '/') {
-225          ucx_buffer_putc(url, '/');
-226      }
-227      ucx_buffer_putc(url, 0);
-228      
-229      space = url->space;
-230      ucx_buffer_free(url);
-231      
-232      return space;
-233  }
-234  
-235  char* util_parent_path(char *path) {
-236      char *name = util_resource_name(path);
-237      size_t namelen = strlen(name);
-238      size_t pathlen = strlen(path);
-239      size_t parentlen = pathlen - namelen;
-240      char *parent = malloc(parentlen + 1);
-241      memcpy(parent, path, parentlen);
-242      parent[parentlen] = '\0';
-243      return parent;
-244  }
-245  
-246  
-247  char* util_xml_get_text(xmlNode *elm) {
-248      xmlNode *node = elm->children;
-249      while(node) {
-250          if(node->type == XML_TEXT_NODE) {
-251              return (char*)node->content;
-252          }
-253          node = node->next;
-254      }
-255      return NULL;
-256  }
-257  
-258  
-259  char* util_base64decode(char *in) {
-260      int len = 0;
-261      return util_base64decode_len(in, &len);
-262  }
-263  
-264  char* util_base64decode_len(char* in, int *outlen) {
-265      size_t len = strlen(in);
-266      char *out = calloc(1, len);
-267      
-268      BIO* b = BIO_new_mem_buf(in, len);
-269      BIO *d = BIO_new(BIO_f_base64());
-270      BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL);
-271      b = BIO_push(d, b);
-272  
-273      *outlen = BIO_read(b, out, len);
-274      BIO_free_all(b);
-275      
-276      return out;
-277  }
-278  
-279  char* util_base64encode(char *in, size_t len) { 
-280      BIO *b;
-281      BIO *e;
-282      BUF_MEM *mem;
-283  
-284      e = BIO_new(BIO_f_base64());
-285      b = BIO_new(BIO_s_mem());
-286      
-287      e = BIO_push(e, b);
-288      BIO_write(e, in, len);
-289      BIO_flush(e);
-290      
-291      BIO_get_mem_ptr(e, &mem);
-292      char *out = malloc(mem->length);
-293      memcpy(out, mem->data, mem->length -1);
-294      out[mem->length - 1] = '\0';
-295  
-296      BIO_free_all(e);
-297  
-298      return out;
-299  }
-300  
-301  char* util_encrypt_str(DavSession *sn, char *str, char *key) {
-302      DavKey *k = dav_context_get_key(sn->context, key);
-303      if(!k) {
-304          // TODO: session error
-305          return NULL;
-306      }
-307      
-308      char *enc_str = aes_encrypt(str, k);
-309      char *ret_str = dav_session_strdup(sn, enc_str);
-310      free(enc_str);
-311      return ret_str;
-312  }
-313  
-314  /* commented out for testing reasons */
-315  /*
-316  char* util_decrypt_str(DavSession *sn, char *str, char *key) {
-317      DavKey *k = dav_context_get_key(sn->context, key);
-318      if(!k) {
-319          // TODO: session error
-320          return NULL;
-321      }
-322      
-323      char *dec_str = aes_decrypt(str, k);
-324      char *ret_str = dav_session_strdup(sn, dec_str);
-325      free(dec_str);
-326      return ret_str;
-327  }
-328  */
-329  char* util_random_str() {
-330      unsigned char *str = malloc(25);
-331      str[24] = '\0';
-332      
-333      sstr_t t = S(
-334              "01234567890"
-335              "abcdefghijklmnopqrstuvwxyz"
-336              "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-337      const unsigned char *table = (const unsigned char*)t.ptr;
-338      
-339      RAND_pseudo_bytes(str, 24);
-340      for(int i=0;i<24;i++) {
-341          int c = str[i] % t.length;
-342          str[i] = table[c];
-343      }
-344      
-345      return (char*)str;
-346  }
-347  
-348  /*
-349   * gets a substring from 0 to the appearance of the token
-350   * tokens are separated by space
-351   * sets sub to the substring and returns the remaining string
-352   */
-353  sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {  
-354      int i;
-355      int token_start = -1;
-356      int token_end = -1;
-357      for(i=0;i<=str.length;i++) {
-358          int c;
-359          if(i == str.length) {
-360              c = ' ';
-361          } else {
-362              c = str.ptr[i];
-363          }
-364          if(c < 33) {
-365              if(token_start != -1) {
-366                  token_end = i;
-367                  size_t len = token_end - token_start;
-368                  sstr_t tk = sstrsubsl(str, token_start, len);
-369                  //printf("token: {%.*s}\n", token.length, token.ptr);
-370                  if(!sstrcmp(tk, token)) {
-371                      *sub = sstrtrim(sstrsubsl(str, 0, token_start));
-372                      break;
-373                  }
-374                  token_start = -1;
-375                  token_end = -1;
-376              }
-377          } else {
-378              if(token_start == -1) {
-379                  token_start = i;
-380              }
-381          }
-382      }
-383      
-384      if(i < str.length) {
-385          return sstrtrim(sstrsubs(str, i));
-386      } else {
-387          str.ptr = NULL;
-388          str.length = 0;
-389          return str;
-390      }
-391  }
-
- - - diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/v2-regression/javatest.html --- a/test/v2-regression/javatest.html Mon Nov 13 14:17:46 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ - - - - c2html - - - - -
-  1  /*
-  2   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
-  3   *
-  4   * Copyright 2014 Mike Becker. All rights reserved.
-  5   *
-  6   * Redistribution and use in source and binary forms, with or without
-  7   * modification, are permitted provided that the following conditions are met:
-  8   *
-  9   *   1. Redistributions of source code must retain the above copyright
- 10   *      notice, this list of conditions and the following disclaimer.
- 11   *
- 12   *   2. Redistributions in binary form must reproduce the above copyright
- 13   *      notice, this list of conditions and the following disclaimer in the
- 14   *      documentation and/or other materials provided with the distribution.
- 15   *
- 16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- 17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- 18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- 19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- 20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- 21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- 22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- 23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- 24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- 25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- 26   * POSSIBILITY OF SUCH DAMAGE.
- 27   *
- 28   */
- 29  
- 30  package de.uapcore.sigred.doc.base;
- 31  
- 32  import de.uapcore.sigred.doc.Resources;
- 33  import de.uapcore.sigrapi.impl.Digraph;
- 34  import de.uapcore.sigrapi.impl.Graph;
- 35  import de.uapcore.sigrapi.IGraph;
- 36  import java.io.IOException;
- 37  import java.io.InputStream;
- 38  import java.io.OutputStream;
- 39  import java.util.concurrent.atomic.AtomicBoolean;
- 40  import java.util.concurrent.atomic.AtomicReference;
- 41  import org.apache.xerces.impl.Constants;
- 42  import org.dom4j.Document;
- 43  import org.dom4j.DocumentException;
- 44  import org.dom4j.DocumentHelper;
- 45  import org.dom4j.Element;
- 46  import org.dom4j.Namespace;
- 47  import org.dom4j.QName;
- 48  import org.dom4j.io.OutputFormat;
- 49  import org.dom4j.io.SAXReader;
- 50  import org.dom4j.io.XMLWriter;
- 51  import org.xml.sax.ErrorHandler;
- 52  import org.xml.sax.SAXException;
- 53  import org.xml.sax.SAXParseException;
- 54  
- 55  public abstract class AbstractGraphDocument<T extends IGraph>
- 56          extends FileBackedDocument {
- 57      
- 58      protected static final Namespace NAMESPACE = Namespace.get("sigred",
- 59          "http://develop.uap-core.de/sigred/");
- 60      
- 61      private static final
- 62          QName TAG_GRAPHDOC = QName.get("graph-document", NAMESPACE);
- 63      private static final
- 64          QName TAG_GRAPH = QName.get("graph", NAMESPACE);
- 65      private static final
- 66          QName TAG_DIGRAPH = QName.get("digraph", NAMESPACE);
- 67      private static final
- 68          QName TAG_METADATA = QName.get("metadata", NAMESPACE);
- 69      
- 70      protected final T graph;
- 71      
- 72      private final GraphDocumentMetadata metadata;
- 73      
- 74      public AbstractGraphDocument(Class<T> graphType) {
- 75          T g;
- 76          try {
- 77              g = graphType.newInstance();
- 78          } catch (ReflectiveOperationException e) {
- 79              assert false;
- 80              g = null; // for the compiler
- 81          }
- 82          graph = g;
- 83          metadata = new GraphDocumentMetadata();
- 84      }
- 85  
- 86      public T getGraph() {
- 87          return graph;
- 88      }
- 89      
- 90      public GraphDocumentMetadata getMetadata() {
- 91          return metadata;
- 92      }
- 93  
- 94      protected abstract void writeGraph(Element rootNode) throws IOException;
- 95      protected abstract void readGraph(Element rootNode) throws IOException;
- 96  
- 97      @Override
- 98      public void writeTo(OutputStream out) throws IOException {
- 99          Document doc = DocumentHelper.createDocument();
-100  
-101          Element rootNode = doc.addElement(TAG_GRAPHDOC);
-102  
-103          Element metadataNode = rootNode.addElement(TAG_METADATA);
-104  
-105          metadata.write(metadataNode);
-106  
-107          if (graph instanceof Graph) {
-108              writeGraph(rootNode.addElement(TAG_GRAPH));
-109          } else if (graph instanceof Digraph) {
-110              writeGraph(rootNode.addElement(TAG_DIGRAPH));
-111          } else {
-112              throw new IOException("unsupported graph type");
-113          }
-114  
-115          XMLWriter writer = new XMLWriter(out, OutputFormat.createPrettyPrint());
-116          writer.write(doc);
-117          writer.flush();
-118      }
-119  
-120      @Override
-121      public void readFrom(InputStream in) throws IOException {
-122          try {
-123              SAXReader reader = new SAXReader(true);
-124              reader.setStripWhitespaceText(true);
-125              
-126              reader.setFeature(Constants.XERCES_FEATURE_PREFIX+
-127                  Constants.SCHEMA_VALIDATION_FEATURE, true);
-128              reader.setProperty(Constants.XERCES_PROPERTY_PREFIX +
-129                  Constants.SCHEMA_LOCATION, String.format("%s %s",
-130                      NAMESPACE.getURI(), Resources.class.getResource(
-131                          "graph-document.xsd").toExternalForm()));
-132              
-133              final AtomicBoolean passed = new AtomicBoolean(true);
-134              final AtomicReference<SAXParseException> xmlerror = new AtomicReference<>();
-135              // TODO: we should do more detailed error handling here
-136              reader.setErrorHandler(new ErrorHandler() {
-137                  @Override
-138                  public void warning(SAXParseException exception) throws SAXException {
-139                  }
-140  
-141                  @Override
-142                  public void error(SAXParseException exception) throws SAXException {
-143                      xmlerror.set(exception);
-144                      passed.set(false);
-145                  }
-146  
-147                  @Override
-148                  public void fatalError(SAXParseException exception) throws SAXException {
-149                      xmlerror.set(exception);
-150                      passed.set(false);
-151                  }
-152                  
-153              });
-154              Document doc = reader.read(in);
-155              if (!passed.get()) {
-156                  // TODO: provide details (maybe via separate error object?)
-157                  throw xmlerror.get();
-158              }
-159              
-160              doc.normalize();
-161              
-162              Element root = doc.getRootElement();
-163              metadata.read(root.element(TAG_METADATA));
-164              
-165              if (graph instanceof Graph) {
-166                  readGraph(root.element(TAG_GRAPH));
-167              } else if (graph instanceof Digraph) {
-168                  readGraph(root.element(TAG_DIGRAPH));
-169              } else {
-170                  throw new IOException("unsupported graph type");
-171              }
-172          } catch (DocumentException | SAXException ex) {
-173              throw new IOException(ex);
-174          }
-175      }
-176  }
-
- - - diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/v2-regression/plain.html --- a/test/v2-regression/plain.html Mon Nov 13 14:17:46 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ - - - - c2html - - - - -
-1  </body>
-2  </html>
-3  <!c
-4  pblock_free(q);
-5  !>
-6  
-
- - -