# HG changeset patch # User Mike Becker # Date 1682362901 -7200 # Node ID 5da2cb5aea6bdb346614c30d63d029deefc22877 # Parent 1b12cf799fee3da4ce7e21e5e27aa27eb198ca75# Parent 7dd4fd1e70711d479364c9f241019e9ddbe10a35 merge upstream changes diff -r 7dd4fd1e7071 -r 5da2cb5aea6b .hgignore diff -r 7dd4fd1e7071 -r 5da2cb5aea6b Makefile --- a/Makefile Mon Oct 03 12:56:28 2022 +0200 +++ b/Makefile Mon Apr 24 21:01:41 2023 +0200 @@ -44,39 +44,23 @@ build/%$(OBJ_EXT): src/%.c $(CC) -o $@ $(CFLAGS) -c $< - + build: $(MKDIR) $@ - -test-c: all - for f in ctest bigtest empty ; do \ - echo "test/$$f.c" ; \ - ./build/$(BIN) -o "build/$$f.html" \ - -H test/header.html \ - -F test/footer.html \ - "test/$$f.c" && \ - diff "build/$$f.html" "test/golden-master/$$f.html" ; \ - done - -test-java: all - for f in javatest ; do \ - ./build/$(BIN) -j -o "build/$$f.html" \ - -H test/jheader.html \ - -F test/footer.html \ - "test/$$f.java" && \ - diff "build/$$f.html" "test/golden-master/$$f.html" ; \ - done - -test-plain: all - for f in plain ; do \ - ./build/$(BIN) -p -o "build/$$f.html" \ - -H test/header.html \ - -F test/footer.html \ - "test/$$f.txt" && \ - diff "build/$$f.html" "test/golden-master/$$f.html" ; \ - done - -test: test-c test-java test-plain + +test: all + ./build/$(BIN) test/ctest.c -o build/ctest.html \ + -H test/header.html -F test/footer.html + ./build/$(BIN) -j test/javatest.java -o build/javatest.html \ + -H test/jheader.html -F test/footer.html + ./build/$(BIN) test/bigtest.c -o build/bigtest.html \ + -H test/header.html -F test/footer.html + ./build/$(BIN) -p test/plain.txt -o build/plain.html \ + -H test/header.html -F test/footer.html + diff build/ctest.html test/gs/ctest.html && \ + diff build/javatest.html test/gs/javatest.html && \ + diff build/bigtest.html test/gs/bigtest.html && \ + diff build/plain.html test/gs/plain.html @echo "Tests successful." clean: diff -r 7dd4fd1e7071 -r 5da2cb5aea6b src/c2html.c --- a/src/c2html.c Mon Oct 03 12:56:28 2022 +0200 +++ b/src/c2html.c Mon Apr 24 21:01:41 2023 +0200 @@ -29,131 +29,93 @@ #include "c2html.h" -#include -#include +#include +#include -#define try_write(wfnc, str, n, buf, written, maxlen) \ - { \ - size_t m = maxlen-written; \ - written += wfnc(str, 1, n > m ? m : n, buf); \ +size_t c2html_format( + CxList const *lines, + void *outbuf, + cx_write_func wfnc, + c2html_highlighter_func highlighter, + int showln +) { + /* total written bytes */ + size_t written = 0; + + /* compute width of line numbering */ + int lnw = 0; + if (showln) { + size_t no_lines = cxListSize(lines); + for (size_t p = 1; p < no_lines; p *= 10) lnw++; } -static size_t formatlines(c2html_highlighter_func highlighter, UcxList *in, - void *outbuf, write_func wfnc, size_t maxlen, int showlineno) { - /* total written bytes */ - size_t written = 0; - - /* compute width of line numbering */ - int lnw = 0; - if (showlineno) { - size_t lines = ucx_list_size(in); - for (size_t p = 1; p < lines ; p*=10) lnw++; - } - - /* start monospace formatting */ - try_write(wfnc, "
\n", 6, outbuf, written, maxlen);
+    /* start code formatting */
+    written += wfnc("
\n", 1, 26, outbuf); /* process lines */ - size_t lineno = 0; - c2html_highlighter_data* hd = malloc(sizeof(c2html_highlighter_data)); - hd->multiline_comment = 0; - hd->primary_buffer = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); - hd->secondary_buffer = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND); - UcxBuffer *line = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - - UCX_FOREACH(sourceline, in) { + int lineno = 0; + c2html_highlighter_data hd; + hd.multiline_comment = 0; + cxBufferInit(&hd.primary_buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&hd.secondary_buffer, NULL, 32, NULL, CX_BUFFER_AUTO_EXTEND); + CxBuffer out_line; + cxBufferInit(&out_line, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND); + + CxIterator in_lines = cxListIterator(lines); + cx_foreach(char*, in_line, in_lines) { /* increase line number and clean line buffer */ lineno++; - ucx_buffer_clear(line); - + cxBufferClear(&out_line); + /* write line number */ - if (showlineno) { - ucx_bprintf(line, "%*d ", - lineno, lineno, lnw, lineno); + if (showln) { + cx_bprintf(&out_line, "%*d ", + lineno, lineno, lnw, lineno); } - + /* process code line */ - highlighter(sourceline->data, line, hd); - + highlighter(in_line, &out_line, &hd); + /* write code line */ - try_write(wfnc, line->space, line->size, outbuf, written, maxlen); - - if (written == maxlen) break; + written += wfnc(out_line.space, 1, out_line.size, outbuf); } - - /* end monospace formatting */ - try_write(wfnc, "
\n", 7, outbuf, written, maxlen); - + + /* end code formatting */ + written += wfnc("\n", 1, 7, outbuf); + /* cleanup and return */ - ucx_buffer_free(hd->primary_buffer); - ucx_buffer_free(hd->secondary_buffer); - free(hd); - ucx_buffer_free(line); - + cxBufferDestroy(&hd.primary_buffer); + cxBufferDestroy(&hd.secondary_buffer); + cxBufferDestroy(&out_line); + return written; } -size_t c2html_formatn(void* inputbuffer, read_func rfnc, - char* ibuf, size_t ibuflen, void* outputbuffer, write_func wfnc, - size_t maxlen, c2html_highlighter_func hltr, int showln) { - - UcxBuffer *content = ucx_buffer_new(NULL, ibuflen*2, UCX_BUFFER_AUTOEXTEND); - ucx_stream_bcopy(inputbuffer, content, rfnc, (write_func) ucx_buffer_write, - ibuf, ibuflen); - - size_t n = c2html_bformatn(content->space, content->size, - outputbuffer, wfnc, maxlen, hltr, showln); - - ucx_buffer_free(content); - return n; -} +size_t c2html_bformat( + char const *inputbuffer, + size_t inputbuflen, + void *outbuf, + cx_write_func wfnc, + c2html_highlighter_func highlighter, + int showln +) { + /* a rough estimate for the number of lines */ + size_t est_cap = 16 + inputbuflen / 40; -size_t c2html_format(void* inputbuffer, read_func rfnc, - char* ibuf, size_t ibuflen, void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln) { - return c2html_formatn(inputbuffer, rfnc, ibuf, ibuflen, - outputbuffer, wfnc, (size_t)-1, hltr, showln); -} - -size_t c2html_fformat(FILE* inputfile, char *ibuf, size_t ibuflen, - void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln) { - return c2html_format(inputfile, (read_func) fread, ibuf, ibuflen, - outputbuffer, wfnc, hltr, showln); -} - -void c2html_fformatf(FILE *inputfile, char *ibuf, size_t ibuflen, - FILE* outputfile, c2html_highlighter_func hltr, int showln) { - c2html_format(inputfile, (read_func) fread, ibuf, ibuflen, - outputfile, (write_func) fwrite, hltr, showln); -} - -size_t c2html_bformatn(const char* inputbuffer, size_t inputbuflen, - void* outputbuffer, write_func wfnc, - size_t maxlen, c2html_highlighter_func hltr, int showln) { - UcxList *lines = ucx_list_append(NULL, (char*)inputbuffer); - for (size_t i = 1 ; i < inputbuflen ; i++) { - if (inputbuffer[i] == '\n' && i+1 < inputbuflen) { - ucx_list_append(lines, (char*)inputbuffer+i+1); + /* create the line pointer array */ + CxList *lines = cxArrayListCreateSimple(CX_STORE_POINTERS, est_cap); + cxListAdd(lines, inputbuffer); + for (size_t i = 1; i < inputbuflen; i++) { + if (inputbuffer[i] == '\n' && i + 1 < inputbuflen) { + cxListAdd(lines, inputbuffer + i + 1); } } - - size_t n = formatlines(hltr, lines, outputbuffer, wfnc, maxlen, showln); - - ucx_list_free(lines); - return n; -} + + /* invoke the other function */ + size_t n = c2html_format(lines, outbuf, wfnc, highlighter, showln); -size_t c2html_bformat(const char* inputbuffer, size_t inputbuflen, - void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln) { - return c2html_bformatn(inputbuffer, inputbuflen, outputbuffer, wfnc, - (size_t)-1, hltr, showln); -} - -void c2html_bformatf(const char* inputbuffer, size_t inputbuflen, - FILE* outputfile, c2html_highlighter_func hltr, int showln) { - c2html_bformatn(inputbuffer, inputbuflen, outputfile, - (write_func) fwrite, (size_t)-1, hltr, showln); -} + /* cleanup and return */ + cxListDestroy(lines); + return n; +} \ No newline at end of file diff -r 7dd4fd1e7071 -r 5da2cb5aea6b src/c2html.h --- a/src/c2html.h Mon Oct 03 12:56:28 2022 +0200 +++ b/src/c2html.h Mon Apr 24 21:01:41 2023 +0200 @@ -31,90 +31,23 @@ #define C2HTML_H #include +#include #include "highlighter.h" #ifdef __cplusplus extern "C" { #endif -#define VERSION_MAJOR 2 +#define VERSION_MAJOR 3 #define VERSION_MINOR 0 #define VERSION_DEVELOP 0 /* set this to zero for release version */ /** - * Reads a source file from the input buffer and writes at most - * maxlen bytes of the formatted output to the output buffer. - * - * The output buffer must either be large enough to hold maxlen - * bytes or the write function must trigger an automatic extension of the - * buffer. - * - * The input is copied via an intermediate buffer to an internal buffer. - * - * @param inputbuffer the input buffer - * @param rfnc a read function operating for the input buffer - * @param ibuf intermediate processing buffer - * @param ibuflen length of intermediate processing buffer - * @param outputbuffer the output buffer - * @param wfnc a write function for the output buffer - * @param maxlen the maximum amount bytes which will be written to the - * output buffer - * @param hltr the highlighter function - * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * - * @return total amount of bytes written to the output buffer - * - * @see c2html_plain_highlighter() - * @see c2html_c_highlighter() - * @see c2html_java_highlighter() - */ -size_t c2html_formatn(void* inputbuffer, read_func rfnc, - char* ibuf, size_t ibuflen, void* outputbuffer, write_func wfnc, - size_t maxlen, c2html_highlighter_func hltr, int showln); - -/** - * Reads a source file from the input buffer and writes the formatted output - * to an output buffer. - * - * The output buffer must either be large enough to hold the formatted data - * or the write function must trigger an automatic extension of the buffer. + * Writes the formatted source data to the output buffer. * - * The input is copied via an intermediate buffer to an internal buffer. - * - * @param inputbuffer the input buffer - * @param rfnc a read function operating for the input buffer - * @param ibuf intermediate processing buffer - * @param ibuflen length of intermediate processing buffer - * @param outputbuffer the output buffer - * @param wfnc a write function for the output buffer - * @param hltr the highlighter function - * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * - * @return total amount of bytes written to the output buffer - * - * @see c2html_plain_highlighter() - * @see c2html_c_highlighter() - * @see c2html_java_highlighter() - */ -size_t c2html_format(void* inputbuffer, read_func rfnc, - char* ibuf, size_t ibuflen, void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln); - -/** - * Reads a source file from the specified input file stream and writes the - * formatted output to an output buffer. - * - * The output buffer must either be large enough to hold the formatted data - * or the write function must trigger an automatic extension of the buffer. - * - * The input is copied via an intermediate buffer to an internal buffer. - * For files, the recommended intermediate buffer length is the file system - * block size. - * - * @param inputfile the input file stream - * @param ibuf intermediate processing buffer - * @param ibuflen length of intermediate processing buffer - * @param outputbuffer the output buffer + * @param inputbuffer the source file data as string + * @param inputbuflen the length of the source file + * @param outbuf the output buffer * @param wfnc a write function for the output buffer * @param hltr the highlighter function * @param showln zero, if line numbers shall be omitted, nonzero otherwise @@ -125,92 +58,32 @@ * @see c2html_c_highlighter() * @see c2html_java_highlighter() */ -size_t c2html_fformat(FILE* inputfile, char *ibuf, size_t ibuflen, - void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln); +size_t c2html_bformat(char const* inputbuffer, size_t inputbuflen, + void* outbuf, cx_write_func wfnc, + c2html_highlighter_func hltr, int showln); /** - * Reads a source file from the specified input file stream and directly writes - * the formatted output to the output file stream. - * - * For files, the recommended intermediate buffer length is the file system - * block size. - * - * @param inputfile the input file stream - * @param ibuf intermediate processing buffer - * @param ibuflen length of intermediate processing buffer (recommended: 4 KB) - * @param outputfile the output file stream + * Writes the formatted source data to the output buffer. + * + * This function takes a list of \c char* that point to the beginning of each + * line. These pointers may point directly into the source text and the strings + * do not need to be zero-terminated, but the line-breaks must be included. + * + * @param lines a list of pointers to the beginning of each line + * @param outbuf the output buffer + * @param wfnc a write function for the output buffer * @param hltr the highlighter function * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * + * + * @return total amount of bytes written to the output buffer + * * @see c2html_plain_highlighter() * @see c2html_c_highlighter() * @see c2html_java_highlighter() */ -void c2html_fformatf(FILE *inputfile, char *ibuf, size_t ibuflen, - FILE* outputfile, c2html_highlighter_func hltr, int showln); - - -/** - * Writes at most maxlen bytes of formatted source data to the - * output buffer. - * - * @param inputbuffer the source file data as string - * @param inputbuflen the length of the source file - * @param outputbuffer the output buffer - * @param wfnc a write function for the output buffer - * @param maxlen the maximum amount bytes which will be written to the - * output buffer - * @param hltr the highlighter function - * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * - * @return total amount of bytes written to the output buffer - * - * @see c2html_plain_highlighter() - * @see c2html_c_highlighter() - * @see c2html_java_highlighter() - */ -size_t c2html_bformatn(const char* inputbuffer, size_t inputbuflen, - void* outputbuffer, write_func wfnc, - size_t maxlen, c2html_highlighter_func hltr, int showln); - - -/** - * Writes the formatted source data to the output buffer. - * - * @param inputbuffer the source file data as string - * @param inputbuflen the length of the source file - * @param outputbuffer the output buffer - * @param wfnc a write function for the output buffer - * @param hltr the highlighter function - * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * - * @return total amount of bytes written to the output buffer - * - * @see c2html_plain_highlighter() - * @see c2html_c_highlighter() - * @see c2html_java_highlighter() - */ -size_t c2html_bformat(const char* inputbuffer, size_t inputbuflen, - void* outputbuffer, write_func wfnc, - c2html_highlighter_func hltr, int showln); - - -/** - * Writes the formatted source data directly to the specified file stream. - * - * @param inputbuffer the source file data as string - * @param inputbuflen the length of the source file - * @param outputfile the output file stream - * @param hltr the highlighter function - * @param showln zero, if line numbers shall be omitted, nonzero otherwise - * - * @see c2html_plain_highlighter() - * @see c2html_c_highlighter() - * @see c2html_java_highlighter() - */ -void c2html_bformatf(const char* inputbuffer, size_t inputbuflen, - FILE* outputfile, c2html_highlighter_func hltr, int showln); +size_t c2html_format(CxList const* lines, + void* outbuf, cx_write_func wfnc, + c2html_highlighter_func hltr, int showln); #ifdef __cplusplus } diff -r 7dd4fd1e7071 -r 5da2cb5aea6b src/frontend.c --- a/src/frontend.c Mon Oct 03 12:56:28 2022 +0200 +++ b/src/frontend.c Mon Apr 24 21:01:41 2023 +0200 @@ -33,9 +33,7 @@ #include #include "c2html.h" -#include - -#define FILEBUF_SIZE 4096 +#include typedef struct { char* outfilename; @@ -45,20 +43,17 @@ int showlinenumbers; } Settings; -static int appendfile(const char *filename, FILE *fout, - char *copybuf, size_t copybuflen, const char *errmsg) { - FILE *headerfile = fopen(filename, "r"); - if (!headerfile) { +static int appendfile(const char *filename, FILE *fout, const char *errmsg) { + FILE *fin = fopen(filename, "r"); + if (!fin) { perror(errmsg); if (fout != stdout) { fclose(fout); } return 1; } - ucx_stream_bncopy(headerfile, fout, - (read_func) fread, (write_func) fwrite, - copybuf, copybuflen, (size_t)-1); - fclose(headerfile); + cx_stream_copy(fin, fout, (cx_read_func) fread, (cx_write_func) fwrite); + fclose(fin); return 0; } @@ -86,7 +81,7 @@ c2html_highlighter_func hltr = c2html_c_highlighter; /* Parse command line */ - char optc; + int optc; while ((optc = getopt(argc, argv, "hljo:pH:F:vV")) != -1) { switch (optc) { case 'o': @@ -142,15 +137,8 @@ fout = stdout; } - /* Allocate file buffer */ - char *filebuf = malloc(FILEBUF_SIZE); - if (!filebuf) { - perror("Error allocating file buffer"); - return EXIT_FAILURE; - } - /* Prepend header file */ - if (appendfile(settings.headerfile, fout, filebuf, FILEBUF_SIZE, + if (appendfile(settings.headerfile, fout, "Error opening header file")) { return EXIT_FAILURE; } @@ -158,11 +146,17 @@ /* Process input file */ FILE *inputfile = fopen(settings.infilename, "r"); if (inputfile) { - c2html_fformatf( - inputfile, filebuf, FILEBUF_SIZE, - fout, hltr, settings.showlinenumbers + CxBuffer fbuf; + cxBufferInit(&fbuf, NULL, 4096, NULL, CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(inputfile, &fbuf, (cx_read_func) fread, + (cx_write_func) cxBufferWrite); + fclose(inputfile); + c2html_bformat( + fbuf.space, fbuf.size, + fout, (cx_write_func ) fwrite, hltr, + settings.showlinenumbers ); - fclose(inputfile); + cxBufferDestroy(&fbuf); } else { perror("Error opening input file"); if (fout != stdout) { @@ -172,12 +166,10 @@ } /* Append footer file */ - if (appendfile(settings.footerfile, fout, filebuf, FILEBUF_SIZE, + if (appendfile(settings.footerfile, fout, "Error opening footer file")) { return EXIT_FAILURE; } - - free(filebuf); return EXIT_SUCCESS; } diff -r 7dd4fd1e7071 -r 5da2cb5aea6b src/highlighter.c --- a/src/highlighter.c Mon Oct 03 12:56:28 2022 +0200 +++ b/src/highlighter.c Mon Apr 24 21:01:41 2023 +0200 @@ -33,35 +33,35 @@ #include #include #include -#include -#include -static void put_htmlescaped(UcxBuffer *dest, char c) { +#include + +static void put_htmlescaped(CxBuffer *dest, char c) { if (c == '>') { - ucx_buffer_puts(dest, ">"); + cxBufferPutString(dest, ">"); } else if (c == '<') { - ucx_buffer_puts(dest, "<"); + cxBufferPutString(dest, "<"); } else if (c) { - ucx_buffer_putc(dest, c); + cxBufferPut(dest, c); } } -static void put_htmlescapedstr(UcxBuffer *dest, sstr_t s) { +static void put_htmlescapedstr(CxBuffer *dest, cxstring s) { for (int i = 0 ; i < s.length ; i++) { put_htmlescaped(dest, s.ptr[i]); } } -static int check_keyword(sstr_t word, const char** keywords) { +static int check_keyword(cxstring word, const char** keywords) { for (int i = 0 ; keywords[i] ; i++) { - if (sstrcmp(word, sstr((char*)keywords[i])) == 0) { + if (cx_strcmp(word, cx_str(keywords[i])) == 0) { return 1; } } return 0; } -static int check_capsonly(sstr_t word) { +static int check_capsonly(cxstring word) { for (size_t i = 0 ; i < word.length ; i++) { if (!isupper(word.ptr[i]) && !isdigit(word.ptr[i]) && word.ptr[i] != '_') { @@ -73,7 +73,7 @@ /* Plaintext Highlighter */ -void c2html_plain_highlighter(char *src, UcxBuffer *dest, +void c2html_plain_highlighter(char const *src, CxBuffer *dest, c2html_highlighter_data *hd) { while (*src && *src != '\n') { if (*src != '\r') { @@ -81,7 +81,7 @@ } src++; } - ucx_buffer_putc(dest, '\n'); + cxBufferPut(dest, '\n'); } /* C Highlighter */ @@ -94,15 +94,15 @@ "while", NULL }; -void c2html_c_highlighter(char *src, UcxBuffer *dest, +void c2html_c_highlighter(char const *src, CxBuffer *dest, c2html_highlighter_data *hd) { /* reset buffers without clearing them */ - hd->primary_buffer->size = hd->primary_buffer->pos = 0; - hd->secondary_buffer->size = hd->secondary_buffer->pos = 0; + hd->primary_buffer.size = hd->primary_buffer.pos = 0; + hd->secondary_buffer.size = hd->secondary_buffer.pos = 0; /* alias the buffers for better handling */ - UcxBuffer *wbuf = hd->primary_buffer; - UcxBuffer *ifilebuf = hd->secondary_buffer; + CxBuffer *wbuf = &hd->primary_buffer; + CxBuffer *ifilebuf = &hd->secondary_buffer; /* local information */ size_t sp = (size_t)-1; @@ -113,7 +113,7 @@ /* continue a multi line comment highlighting */ if (hd->multiline_comment) { iscomment = 1; - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } char c; @@ -126,42 +126,42 @@ if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') { iscomment = 0; hd->multiline_comment = 0; - ucx_buffer_puts(dest, "/"); + cxBufferPutString(dest, "/"); continue; } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) { iscomment = 1; hd->multiline_comment = (src[sp+1] == '*'); - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } } if (iscomment) { if (c == '\n') { - ucx_buffer_puts(dest, "\n"); + cxBufferPutString(dest, "\n"); } else { put_htmlescaped(dest, c); } } else if (isinclude) { if (c == '<') { - ucx_buffer_puts(dest, + cxBufferPutString(dest, "<"); } else if (c == '\"') { if (parseinclude) { - ucx_buffer_puts(dest, "\">"); - ucx_buffer_write(ifilebuf->space, 1, ifilebuf->size, dest); - ucx_buffer_puts(dest, "\""); + cxBufferPutString(dest, "\">"); + cxBufferWrite(ifilebuf->space, 1, ifilebuf->size, dest); + cxBufferPutString(dest, "\""); parseinclude = 0; } else { - ucx_buffer_puts(dest, + cxBufferPutString(dest, "') { - ucx_buffer_puts(dest, ">"); + cxBufferPutString(dest, ">"); } else { if (parseinclude) { - ucx_buffer_putc(ifilebuf, c); + cxBufferPut(ifilebuf, c); } put_htmlescaped(dest, c); } @@ -172,14 +172,14 @@ put_htmlescaped(dest, c); if (c == quote) { isstring = 0; - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } else { put_htmlescaped(dest, c); } } else { isstring = 1; quote = c; - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); put_htmlescaped(dest, c); } } else { @@ -187,32 +187,32 @@ put_htmlescaped(dest, c); } else if (isalnum(c) || c == '_' || c == '#') { /* buffer the current word */ - ucx_buffer_putc(wbuf, c); + cxBufferPut(wbuf, c); } else { /* write buffered word, if any */ if (wbuf->size > 0) { - sstr_t word = sstrn(wbuf->space, wbuf->size); + cxstring word = cx_strn(wbuf->space, wbuf->size); int closespan = 1; - sstr_t typesuffix = ST("_t"); + cxstring typesuffix = CX_STR("_t"); if (check_keyword(word, ckeywords)) { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); - } else if (sstrsuffix(word, typesuffix)) { - ucx_buffer_puts(dest, + } else if (cx_strsuffix(word, typesuffix)) { + cxBufferPutString(dest, ""); } else if (word.ptr[0] == '#') { - isinclude = !sstrcmp(word, S("#include")); - ucx_buffer_puts(dest, + isinclude = !cx_strcmp(word, CX_STR("#include")); + cxBufferPutString(dest, ""); } else if (check_capsonly(word)) { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); } else { closespan = 0; } put_htmlescapedstr(dest, word); if (closespan) { - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } } wbuf->pos = wbuf->size = 0; /* reset word buffer */ @@ -239,14 +239,14 @@ "volatile", "const", "float", "native", "super", "while", NULL }; -void c2html_java_highlighter(char *src, UcxBuffer *dest, +void c2html_java_highlighter(char const *src, CxBuffer *dest, c2html_highlighter_data *hd) { /* reset buffers without clearing them */ - hd->primary_buffer->size = hd->primary_buffer->pos = 0; - hd->secondary_buffer->size = hd->secondary_buffer->pos = 0; + hd->primary_buffer.size = hd->primary_buffer.pos = 0; + hd->secondary_buffer.size = hd->secondary_buffer.pos = 0; /* alias the buffers for better handling */ - UcxBuffer *wbuf = hd->primary_buffer; + CxBuffer *wbuf = &hd->primary_buffer; /* local information */ size_t sp = (size_t)-1; @@ -256,7 +256,7 @@ if (hd->multiline_comment) { iscomment = 1; - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } char c; @@ -269,18 +269,18 @@ if (hd->multiline_comment && sp > 0 && src[sp-1] == '*') { iscomment = 0; hd->multiline_comment = 0; - ucx_buffer_puts(dest, "/"); + cxBufferPutString(dest, "/"); continue; } else if (!iscomment && (src[sp+1] == '/' || src[sp+1] == '*')) { iscomment = 1; hd->multiline_comment = (src[sp+1] == '*'); - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } } if (iscomment) { if (c == '\n') { - ucx_buffer_puts(dest, "\n"); + cxBufferPutString(dest, "\n"); } else { put_htmlescaped(dest, c); } @@ -293,14 +293,14 @@ put_htmlescaped(dest, c); if (c == quote) { isstring = 0; - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } else { put_htmlescaped(dest, c); } } else { isstring = 1; quote = c; - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); put_htmlescaped(dest, c); } @@ -309,23 +309,23 @@ put_htmlescaped(dest, c); } else if (isalnum(c) || c == '_' || c == '@') { /* buffer the current word */ - ucx_buffer_putc(wbuf, c); + cxBufferPut(wbuf, c); } else { /* write buffered word, if any */ if (wbuf->size > 0) { - sstr_t word = sstrn(wbuf->space, wbuf->size); + cxstring word = cx_strn(wbuf->space, wbuf->size); int closespan = 1; if (check_keyword(word, jkeywords)) { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); } else if (isupper(word.ptr[0])) { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); } else if (word.ptr[0] == '@') { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); } else if (check_capsonly(word)) { - ucx_buffer_puts(dest, + cxBufferPutString(dest, ""); } else { closespan = 0; @@ -333,7 +333,7 @@ put_htmlescapedstr(dest, word); if (closespan) { - ucx_buffer_puts(dest, ""); + cxBufferPutString(dest, ""); } } wbuf->pos = wbuf->size = 0; /* reset buffer */ diff -r 7dd4fd1e7071 -r 5da2cb5aea6b src/highlighter.h --- a/src/highlighter.h Mon Oct 03 12:56:28 2022 +0200 +++ b/src/highlighter.h Mon Apr 24 21:01:41 2023 +0200 @@ -30,7 +30,7 @@ #ifndef C2HTML_HIGHLIGHTER_H #define C2HTML_HIGHLIGHTER_H -#include +#include #ifdef __cplusplus extern "C" { @@ -38,12 +38,12 @@ typedef struct { int multiline_comment; - UcxBuffer* primary_buffer; - UcxBuffer* secondary_buffer; + CxBuffer primary_buffer; + CxBuffer secondary_buffer; } c2html_highlighter_data; #define C2HTML_HIGHLIGHTER_SIGNATURE \ -char*,UcxBuffer*, c2html_highlighter_data* +char const*, CxBuffer*, c2html_highlighter_data* typedef void(*c2html_highlighter_func)(C2HTML_HIGHLIGHTER_SIGNATURE); diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/ctest.c --- a/test/ctest.c Mon Oct 03 12:56:28 2022 +0200 +++ b/test/ctest.c Mon Apr 24 21:01:41 2023 +0200 @@ -199,7 +199,7 @@ char* util_path_to_url(DavSession *sn, char *path) { char *space = malloc(256); - UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND); + UcxBuffer *url = ucx_buffer_new(space, 256, CX_BUFFER_AUTO_EXTEND); // add base url ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/empty.c diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/golden-master/bigtest.html --- a/test/golden-master/bigtest.html Mon Oct 03 12:56:28 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,861 +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 7dd4fd1e7071 -r 5da2cb5aea6b test/golden-master/ctest.html --- a/test/golden-master/ctest.html Mon Oct 03 12:56:28 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,444 +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 7dd4fd1e7071 -r 5da2cb5aea6b test/golden-master/empty.html --- a/test/golden-master/empty.html Mon Oct 03 12:56:28 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ - - - - c2html - - - - -
-1 
- - - diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/golden-master/javatest.html --- a/test/golden-master/javatest.html Mon Oct 03 12:56:28 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +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 7dd4fd1e7071 -r 5da2cb5aea6b test/golden-master/plain.html --- a/test/golden-master/plain.html Mon Oct 03 12:56:28 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ - - - - c2html - - - - -
-1 </body>
-2 </html>
-3 <!c
-4 pblock_free(q);
-5 !>
-6 
-
- - - diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/gs/bigtest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gs/bigtest.html Mon Apr 24 21:01:41 2023 +0200 @@ -0,0 +1,865 @@ + + + + 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 7dd4fd1e7071 -r 5da2cb5aea6b test/gs/ctest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gs/ctest.html Mon Apr 24 21:01:41 2023 +0200 @@ -0,0 +1,448 @@ + + + + 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, CX_BUFFER_AUTO_EXTEND); +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 7dd4fd1e7071 -r 5da2cb5aea6b test/gs/javatest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gs/javatest.html Mon Apr 24 21:01:41 2023 +0200 @@ -0,0 +1,225 @@ + + + + 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 7dd4fd1e7071 -r 5da2cb5aea6b test/gs/plain.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gs/plain.html Mon Apr 24 21:01:41 2023 +0200 @@ -0,0 +1,63 @@ + + + + c2html + + + + +
+1 </body> +2 </html> +3 <!c +4 pblock_free(q); +5 !> +6 +
+ + + diff -r 7dd4fd1e7071 -r 5da2cb5aea6b test/header.html --- a/test/header.html Mon Oct 03 12:56:28 2022 +0200 +++ b/test/header.html Mon Apr 24 21:01:41 2023 +0200 @@ -3,6 +3,10 @@ c2html