diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fead66..70dd7e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,39 +75,50 @@ find_package(PCRE) # List sources and headers set(SOURCE_FILES - src/abort.c - src/abort.h - src/event.c - src/event.h - src/report.c - src/report.h - src/runner.c - src/runner.h - src/process.c - src/process.h - src/stats.c - src/stats.h + src/core/abort.c + src/core/abort.h + src/core/report.c + src/core/report.h + src/core/runner.c + src/core/runner.h + src/core/worker.c + src/core/worker.h + src/core/stats.c + src/core/stats.h + src/core/ordered-set.c + src/core/theories.c + src/compat/internal.h + src/compat/pipe.c + src/compat/pipe.h + src/compat/section.c + src/compat/section.h + src/compat/process.c + src/compat/process.h + src/compat/basename.c + src/compat/basename.h + src/compat/mockfile.c + src/compat/time.c + src/compat/time.h + src/compat/posix.h + src/io/redirect.c + src/io/event.c + src/io/event.h + src/io/asprintf.c + src/io/file.c src/log/logging.c src/log/tap.c src/log/normal.c - src/options.c - src/timer.c - src/timer.h - src/i18n.c - src/i18n.h - src/ordered-set.c - src/posix-compat.c - src/theories.c - src/asprintf.c - src/file.c - src/main.c - src/entry.c + src/string/i18n.c + src/string/i18n.h + src/entry/options.c + src/entry/main.c + src/entry/entry.c ) if (PCRE_FOUND) set (SOURCE_FILES ${SOURCE_FILES} - src/extmatch.c - src/extmatch.h + src/string/extmatch.c + src/string/extmatch.h ) set(HAVE_PCRE 1) endif () diff --git a/po/POTFILES.in b/po/POTFILES.in index e93e6cd..bec1563 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,3 @@ # List of source files which contain translatable strings. src/log/normal.c -src/i18n.c +src/string/i18n.c diff --git a/po/fr.po b/po/fr.po index 26354e1..d9b7f11 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: criterion 2.0.0\n" "Report-Msgid-Bugs-To: franklinmathieu+criterion@gmail.com\n" -"POT-Creation-Date: 2015-09-14 04:12+0200\n" +"POT-Creation-Date: 2015-09-16 21:18+0200\n" "PO-Revision-Date: 2015-04-03 17:58+0200\n" "Last-Translator: \n" "Language-Team: French\n" @@ -109,68 +109,68 @@ msgstr "" "%1$sSynthèse: Testés: %2$s%3$lu%4$s | Validés: %5$s%6$lu%7$s | Échoués: %8$s" "%9$lu%10$s | Plantages: %11$s%12$lu%13$s %14$s\n" -#: src/i18n.c:13 +#: src/string/i18n.c:13 msgid "The conditions for this assertion were not met." msgstr "Les conditions de cette assertion n'ont pas été remplies." -#: src/i18n.c:14 +#: src/string/i18n.c:14 #, c-format msgid "The expression %s is false." msgstr "L'expression %s est fausse." -#: src/i18n.c:15 +#: src/string/i18n.c:15 #, c-format msgid "The expression (as strings) %s is false." msgstr "L'expression (en tant que chaînes de caractères) %s est fausse." -#: src/i18n.c:16 +#: src/string/i18n.c:16 #, c-format msgid "%s is null." msgstr "%s est nul." -#: src/i18n.c:17 +#: src/string/i18n.c:17 #, c-format msgid "%s is not null." msgstr "%s n'est pas nul." -#: src/i18n.c:18 +#: src/string/i18n.c:18 #, c-format msgid "%s is empty." msgstr "%s est vide." -#: src/i18n.c:19 +#: src/string/i18n.c:19 #, c-format msgid "%s is not empty." msgstr "%s n'est pas vide." -#: src/i18n.c:20 +#: src/string/i18n.c:20 #, fuzzy, c-format msgid "The statement `%s` did not throw any exception." msgstr "L'instruction `%s` n'a pas levé d'exception." -#: src/i18n.c:21 +#: src/string/i18n.c:21 #, fuzzy, c-format msgid "The statement `%s` threw some exception." msgstr "L'instruction `%1$s` a levé une exception." -#: src/i18n.c:24 +#: src/string/i18n.c:24 #, c-format msgid "The file contents of %1$s does not match the string \"%2$s\"." msgstr "" "Le contenu du fichier %1$s ne correspond pas à la chaine de caractères \"%2$s" "\"." -#: src/i18n.c:25 +#: src/string/i18n.c:25 #, fuzzy, c-format msgid "The file contents of %1$s does not match the contents of %2$s." msgstr "Le contenu du fichier %1$s ne correspond pas au contenu de %2$s." -#: src/i18n.c:26 +#: src/string/i18n.c:26 #, c-format msgid "The statement `%1$s` did throw an instance of the `%2$s` exception." msgstr "L'instruction `%1$s` a levé une instance de l'exception `%2$s`." -#: src/i18n.c:27 +#: src/string/i18n.c:27 #, c-format msgid "The statement `%1$s` did not throw an instance of the `%2$s` exception." msgstr "L'instruction `%1$s` n'a pas levé d'instance de l'exception `%2$s`." diff --git a/src/compat/basename.c b/src/compat/basename.c new file mode 100644 index 0000000..ed388ba --- /dev/null +++ b/src/compat/basename.c @@ -0,0 +1,32 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "basename.h" + +const char *basename_compat(const char *str) { + const char *start = str; + for (const char *c = str; *c; ++c) + if ((*c == '/' || *c == '\\') && c[1]) + start = c + 1; + return start; +} diff --git a/src/compat/basename.h b/src/compat/basename.h new file mode 100644 index 0000000..5a1521b --- /dev/null +++ b/src/compat/basename.h @@ -0,0 +1,29 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef BASENAME_H_ +# define BASENAME_H_ + +const char *basename_compat(const char *str); + +#endif /* !BASENAME_H_ */ diff --git a/src/compat/internal.h b/src/compat/internal.h new file mode 100644 index 0000000..04f257e --- /dev/null +++ b/src/compat/internal.h @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef INTERNAL_H_ +# define INTERNAL_H_ + +# include "posix.h" + +# ifdef VANILLA_WIN32 +# define VC_EXTRALEAN +# define WIN32_LEAN_AND_MEAN +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0502 +# include +# include +# include +# include +# include +# include +# else +# include +# include +# include +# include +# endif + +#endif /* !INTERNAL_H_ */ diff --git a/src/compat/mockfile.c b/src/compat/mockfile.c new file mode 100644 index 0000000..8687767 --- /dev/null +++ b/src/compat/mockfile.c @@ -0,0 +1,182 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include "internal.h" +#include "criterion/redirect.h" + +#if defined(BSD) || defined(__unix__) + +# ifdef BSD +typedef int cr_count; +typedef int cr_retcount; +typedef fpos_t cr_off; +# else +typedef size_t cr_count; +typedef ssize_t cr_retcount; +typedef off64_t cr_off; +# endif + +struct memfile { + size_t size; + size_t region_size; + size_t cur; + size_t max_size; + char *mem; +}; + +static inline size_t size_safe_add(size_t size, size_t cur, cr_off off) { + cur = cur < SIZE_MAX - off ? cur + off : SIZE_MAX; + return cur < size ? cur : size; +} + +static inline size_t off_safe_add(size_t size, size_t cur, cr_off off) { + if (off >= 0) + cur = cur < SIZE_MAX - off ? cur + off : SIZE_MAX; + else + cur = cur > (size_t) -off ? cur + off : 0; + return cur < size ? cur : size; +} + +# define errno_return(Errno, Val) \ + do { \ + errno = (Errno); \ + return (Val); \ + } while (0) + +static cr_retcount mock_file_read(void *cookie, char *buf, cr_count count) { + struct memfile *mf = cookie; +# ifdef BSD + if (count < 0) + errno_return(EINVAL, (cr_retcount) -1); +# endif + if (mf->cur >= mf->size || count == 0) + return 0; + + size_t end = size_safe_add(mf->size, mf->cur, count); + count = end - mf->cur; + memcpy(buf, mf->mem + mf->cur, count); + mf->cur = end; + return count; +} + +static cr_retcount mock_file_write(void *cookie, const char *buf, cr_count count) { + struct memfile *mf = cookie; +# ifdef BSD + if (count < 0) + errno_return(EINVAL, (cr_retcount) -1); +# endif + if (count == 0) + return 0; + + if (mf->cur >= mf->max_size) + errno_return(EIO, (cr_retcount) -1); + + size_t end = size_safe_add(mf->max_size, mf->cur, count); + if (mf->size < end) + mf->size = end; + count = end - mf->cur; + + if (mf->size > mf->region_size) { + while (mf->size > mf->region_size) + mf->region_size = mf->region_size * 3 / 2; + char *newptr = realloc(mf->mem, mf->region_size); + if (!newptr) + errno_return(EIO, (cr_retcount) -1); + mf->mem = newptr; + } + memcpy(mf->mem + mf->cur, buf, count); + mf->cur = end; + return count; +} + +# ifdef BSD +static cr_off mock_file_seek(void *cookie, cr_off off, int whence) { + struct memfile *mf = cookie; + switch (whence) { + case SEEK_SET: return (mf->cur = off); + case SEEK_CUR: return (mf->cur = off_safe_add(mf->size, mf->cur, off)); + case SEEK_END: return (mf->cur = off_safe_add(mf->size, mf->size, off)); + default: break; + } + errno = EINVAL; + return (off_t) -1; +} +# else +static int mock_file_seek(void *cookie, cr_off *off, int whence) { + struct memfile *mf = cookie; + switch (whence) { + case SEEK_SET: mf->cur = *off; break; + case SEEK_CUR: *off = (mf->cur = off_safe_add(mf->size, mf->cur, *off)); break; + case SEEK_END: *off = (mf->cur = off_safe_add(mf->size, mf->size, *off)); break; + default: errno = EINVAL; return -1; + } + return 0; +} +# endif + +static int mock_file_close(void *cookie) { + struct memfile *mf = cookie; + free(mf->mem); + free(cookie); + return 0; +} +#endif + +FILE *cr_mock_file_size(size_t max_size) { +#if defined(__unix__) || defined(BSD) + struct memfile *cookie = malloc(sizeof (struct memfile)); + *cookie = (struct memfile) { + .max_size = max_size, + .region_size = 4096, + .mem = malloc(4096), + }; + + FILE *f; +# ifdef __unix__ + f = fopencookie(cookie, "w+", (cookie_io_functions_t) { + .read = mock_file_read, + .write = mock_file_write, + .seek = mock_file_seek, + .close = mock_file_close, + }); +# else + f = funopen(cookie, + mock_file_read, + mock_file_write, + mock_file_seek, + mock_file_close); +# endif + return f; +#else + (void) max_size; + + // fallback to tmpfile() + return tmpfile(); +#endif +} diff --git a/src/compat/pipe-internal.h b/src/compat/pipe-internal.h new file mode 100644 index 0000000..54a5242 --- /dev/null +++ b/src/compat/pipe-internal.h @@ -0,0 +1,38 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef PIPE_INTERNAL_H_ +# define PIPE_INTERNAL_H_ + +# include "internal.h" +# include "pipe.h" + +struct pipe_handle { +#ifdef VANILLA_WIN32 + HANDLE fhs[2]; +#else + int fds[2]; +#endif +}; + +#endif /* !PIPE_INTERNAL_H_ */ diff --git a/src/compat/pipe.c b/src/compat/pipe.c new file mode 100644 index 0000000..0a7e4e9 --- /dev/null +++ b/src/compat/pipe.c @@ -0,0 +1,181 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include + +#include "criterion/assert.h" +#include "pipe-internal.h" + +FILE *pipe_in(s_pipe_handle *p, int do_close) { +#ifdef VANILLA_WIN32 + if (do_close) + CloseHandle(p->fhs[1]); + int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY); + if (fd == -1) + return NULL; + FILE *in = _fdopen(fd, "r"); +#else + if (do_close) + close(p->fds[1]); + FILE *in = fdopen(p->fds[0], "r"); +#endif + if (!in) + return NULL; + + setvbuf(in, NULL, _IONBF, 0); + return in; +} + +FILE *pipe_out(s_pipe_handle *p, int do_close) { +#ifdef VANILLA_WIN32 + if (do_close) + CloseHandle(p->fhs[0]); + int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY); + if (fd == -1) + return NULL; + FILE *out = _fdopen(fd, "w"); +#else + if (do_close) + close(p->fds[0]); + FILE *out = fdopen(p->fds[1], "w"); +#endif + if (!out) + return NULL; + + setvbuf(out, NULL, _IONBF, 0); + return out; +} + +int stdpipe_stack(s_pipe_handle *out) { +#ifdef VANILLA_WIN32 + HANDLE fhs[2]; + SECURITY_ATTRIBUTES attr = { + .nLength = sizeof (SECURITY_ATTRIBUTES), + .bInheritHandle = TRUE + }; + if (!CreatePipe(fhs, fhs + 1, &attr, 0)) + return -1; + *out = (s_pipe_handle) {{ fhs[0], fhs[1] }}; +#else + int fds[2] = { -1, -1 }; + if (pipe(fds) == -1) + return -1; + *out = (s_pipe_handle) {{ fds[0], fds[1] }}; +#endif + return 0; +} + +s_pipe_handle *stdpipe() { + s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); + if (stdpipe_stack(handle) < 0) + return NULL; + return handle; +} + +int stdpipe_options(s_pipe_handle *handle, int id, int noblock) { +#ifdef VANILLA_WIN32 + HANDLE fhs[2]; + SECURITY_ATTRIBUTES attr = { + .nLength = sizeof (SECURITY_ATTRIBUTES), + .bInheritHandle = TRUE + }; + char pipe_name[256] = {0}; + snprintf(pipe_name, sizeof (pipe_name), + "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id); + fhs[0] = CreateNamedPipe(pipe_name, + PIPE_ACCESS_INBOUND, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE + | (noblock ? PIPE_NOWAIT : PIPE_WAIT), + 1, + 4096 * 4, + 4096 * 4, + 0, + &attr); + + if (fhs[0] == INVALID_HANDLE_VALUE) + return 0; + + fhs[1] = CreateFile(pipe_name, + GENERIC_WRITE, + 0, + &attr, + OPEN_EXISTING, + 0, + NULL); + + if (fhs[1] == INVALID_HANDLE_VALUE) { + CloseHandle(fhs[0]); + return 0; + } + + *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; +#else + (void) id; + + int fds[2] = { -1, -1 }; + if (pipe(fds) == -1) + return 0; + + if (noblock) + for (int i = 0; i < 2; ++i) + fcntl(fds[i], F_SETFL, fcntl(fds[i], F_GETFL) | O_NONBLOCK); + + *handle = (s_pipe_handle) {{ fds[0], fds[1] }}; +#endif + return 1; +} + +void pipe_std_redirect(s_pipe_handle *pipe, enum criterion_std_fd fd) { + enum pipe_end end = fd == CR_STDIN ? PIPE_READ : PIPE_WRITE; +#ifdef VANILLA_WIN32 + int stdfd = _open_osfhandle((intptr_t) pipe->fhs[end], end == PIPE_READ ? _O_RDONLY : _O_WRONLY); + if (stdfd == -1) + cr_assert_fail("Could not redirect standard file descriptor."); + + _close(fd); + _dup2(stdfd, fd); + _close(stdfd); + + setvbuf(get_std_file(fd), NULL, _IONBF, 0); + + static int handles[] = { + [CR_STDIN] = STD_INPUT_HANDLE, + [CR_STDOUT] = STD_OUTPUT_HANDLE, + [CR_STDERR] = STD_ERROR_HANDLE, + }; + SetStdHandle(handles[fd], pipe->fhs[end]); +#else + close(fd); + dup2(pipe->fds[end], fd); + close(pipe->fds[end]); +#endif +} + +static s_pipe_handle stdout_redir_; +static s_pipe_handle stderr_redir_; +static s_pipe_handle stdin_redir_; + +s_pipe_handle *stdout_redir = &stdout_redir_; +s_pipe_handle *stderr_redir = &stderr_redir_; +s_pipe_handle *stdin_redir = &stdin_redir_; diff --git a/src/compat/pipe.h b/src/compat/pipe.h new file mode 100644 index 0000000..2aa9d8d --- /dev/null +++ b/src/compat/pipe.h @@ -0,0 +1,63 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef PIPE_H_ +# define PIPE_H_ + +# include "common.h" + +struct pipe_handle; +typedef struct pipe_handle s_pipe_handle; + +enum pipe_end { + PIPE_READ = 0, + PIPE_WRITE = 1, +}; + +enum criterion_std_fd { + CR_STDIN = 0, + CR_STDOUT = 1, + CR_STDERR = 2, +}; + +s_pipe_handle *stdpipe(); +FILE *pipe_in(s_pipe_handle *p, int do_close); +FILE *pipe_out(s_pipe_handle *p, int do_close); + +int stdpipe_options(s_pipe_handle *pipe, int id, int noblock); +void pipe_std_redirect(s_pipe_handle *pipe, enum criterion_std_fd fd); + +INLINE FILE* get_std_file(int fd_kind) { + switch (fd_kind) { + case CR_STDIN: return stdin; + case CR_STDOUT: return stdout; + case CR_STDERR: return stderr; + } + return NULL; +} + +extern s_pipe_handle *stdout_redir; +extern s_pipe_handle *stderr_redir; +extern s_pipe_handle *stdin_redir; + +#endif /* !PIPE_H_ */ diff --git a/src/compat/posix.h b/src/compat/posix.h new file mode 100644 index 0000000..a001bbb --- /dev/null +++ b/src/compat/posix.h @@ -0,0 +1,59 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef POSIX_COMPAT_H_ +# define POSIX_COMPAT_H_ + +#if defined(_WIN32) && !defined(__CYGWIN__) +# define VANILLA_WIN32 +#endif + +# if !defined(_POSIX_SOURCE) +# define _POSIX_SOURCE 1 +# define TMP_POSIX +# endif +# include +# ifdef TMP_POSIX +# undef _POSIX_SOURCE +# undef TMP_POSIX +# endif + +# ifdef VANILLA_WIN32 +# define WEXITSTATUS(Status) (((Status) & 0xFF00) >> 8) +# define WTERMSIG(Status) ((Status) & 0x7F) +# define WIFEXITED(Status) (WTERMSIG(Status) == 0) +# define WIFSIGNALED(Status) (((signed char) (WTERMSIG(Status) + 1) >> 1) > 0) + +# define SIGPROF 27 +# define CR_EXCEPTION_TIMEOUT 0xC0001042 +# else +# include +# include +# endif + +# include "compat/pipe.h" +# include "compat/section.h" +# include "compat/process.h" +# include "compat/basename.h" + +#endif /* !POSIX_COMPAT_H_ */ diff --git a/src/compat/process.c b/src/compat/process.c new file mode 100644 index 0000000..5204e3e --- /dev/null +++ b/src/compat/process.c @@ -0,0 +1,252 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "core/worker.h" +#include "process.h" +#include "internal.h" +#include "pipe-internal.h" + +#ifdef VANILLA_WIN32 +# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ + CreateProcessW(Filename, \ + CmdLine, \ + NULL, \ + NULL, \ + TRUE, \ + CREATE_SUSPENDED, \ + NULL, \ + NULL, \ + &(StartupInfo), \ + &(Info)) + +# define WRITE_PROCESS_(Proc, What, Size) \ + WriteProcessMemory(Proc, &What, &What, Size, NULL); +#endif + +struct proc_handle { +#ifdef VANILLA_WIN32 + HANDLE handle; +#else + pid_t pid; +#endif +}; + +struct worker_context g_worker_context = {.test = NULL}; + +#ifdef VANILLA_WIN32 +struct full_context { + struct criterion_test test; + struct criterion_test_extra_data test_data; + struct criterion_suite suite; + struct criterion_test_extra_data suite_data; + f_worker_func func; + struct pipe_handle pipe; + volatile int resumed; +}; + +static TCHAR g_mapping_name[] = TEXT("WinCriterionWorker"); +#define MAPPING_SIZE sizeof (struct full_context) + +static struct full_context local_ctx; +#endif + +int resume_child(void) { +#ifdef VANILLA_WIN32 + HANDLE sharedMem = OpenFileMapping( + FILE_MAP_ALL_ACCESS, + FALSE, + g_mapping_name); + + if (sharedMem == NULL) + return 0; + + struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, + FILE_MAP_ALL_ACCESS, + 0, + 0, + MAPPING_SIZE); + + if (ctx == NULL) + exit(-1); + + local_ctx = *ctx; + g_worker_context = (struct worker_context) { + &local_ctx.test, + &local_ctx.suite, + local_ctx.func, + &local_ctx.pipe + }; + + local_ctx.test.data = &local_ctx.test_data; + local_ctx.suite.data = &local_ctx.suite_data; + + ctx->resumed = 1; + + UnmapViewOfFile(ctx); + CloseHandle(sharedMem); + + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + + run_worker(&g_worker_context); + return 1; +#else + return 0; +#endif +} + +s_proc_handle *fork_process() { +#ifdef VANILLA_WIN32 + PROCESS_INFORMATION info; + STARTUPINFOW si = { .cb = sizeof (STARTUPINFOW) }; + + ZeroMemory(&info, sizeof (info)); + + // Create the suspended child process + wchar_t filename[MAX_PATH]; + GetModuleFileNameW(NULL, filename, MAX_PATH); + + if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) + return (void *) -1; + + // Copy context over + HANDLE sharedMem = CreateFileMapping( + INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + MAPPING_SIZE, + g_mapping_name); + + if (sharedMem == NULL) + return (void *) -1; + + struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, + FILE_MAP_ALL_ACCESS, + 0, + 0, + MAPPING_SIZE); + + if (ctx == NULL) { + CloseHandle(sharedMem); + return (void *) -1; + } + + *ctx = (struct full_context) { + .test = *g_worker_context.test, + .test_data = *g_worker_context.test->data, + .suite = *g_worker_context.suite, + .func = g_worker_context.func, + .pipe = *g_worker_context.pipe, + .resumed = 0, + }; + + if (g_worker_context.suite->data) + ctx->suite_data = *g_worker_context.suite->data; + + ResumeThread(info.hThread); + CloseHandle(info.hThread); + + while (!ctx->resumed); // wait until the child has initialized itself + + UnmapViewOfFile(ctx); + CloseHandle(sharedMem); + + s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); + *handle = (s_proc_handle) { info.hProcess }; + return handle; +#else + pid_t pid = fork(); + if (pid == -1) + return (void *) -1; + if (pid == 0) + return NULL; + + s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); + *handle = (s_proc_handle) { pid }; + return handle; +#endif +} + +void wait_process(s_proc_handle *handle, int *status) { +#ifdef VANILLA_WIN32 + WaitForSingleObject(handle->handle, INFINITE); + DWORD exit_code; + GetExitCodeProcess(handle->handle, &exit_code); + CloseHandle(handle->handle); + + unsigned int sig = 0; + switch (exit_code) { + case STATUS_FLOAT_DENORMAL_OPERAND: + case STATUS_FLOAT_DIVIDE_BY_ZERO: + case STATUS_FLOAT_INEXACT_RESULT: + case STATUS_FLOAT_INVALID_OPERATION: + case STATUS_FLOAT_OVERFLOW: + case STATUS_FLOAT_STACK_CHECK: + case STATUS_FLOAT_UNDERFLOW: + case STATUS_INTEGER_DIVIDE_BY_ZERO: + case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break; + + case STATUS_ILLEGAL_INSTRUCTION: + case STATUS_PRIVILEGED_INSTRUCTION: + case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break; + + case CR_EXCEPTION_TIMEOUT: sig = SIGPROF; break; + + case STATUS_ACCESS_VIOLATION: + case STATUS_DATATYPE_MISALIGNMENT: + case STATUS_ARRAY_BOUNDS_EXCEEDED: + case STATUS_GUARD_PAGE_VIOLATION: + case STATUS_IN_PAGE_ERROR: + case STATUS_NO_MEMORY: + case STATUS_INVALID_DISPOSITION: + case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break; + + case STATUS_CONTROL_C_EXIT: sig = SIGINT; break; + + default: break; + } + + *status = sig ? sig : exit_code << 8; +#else + waitpid(handle->pid, status, 0); +#endif +} + +s_proc_handle *get_current_process() { + s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); +#ifdef VANILLA_WIN32 + *handle = (s_proc_handle) { GetCurrentProcess() }; +#else + *handle = (s_proc_handle) { getpid() }; +#endif + return handle; +} + +bool is_current_process(s_proc_handle *proc) { +#ifdef VANILLA_WIN32 + return GetProcessId(proc->handle) == GetProcessId(GetCurrentProcess()); +#else + return proc->pid == getpid(); +#endif +} diff --git a/src/compat/process.h b/src/compat/process.h new file mode 100644 index 0000000..2d5d2ef --- /dev/null +++ b/src/compat/process.h @@ -0,0 +1,49 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef COMPAT_PROCESS_H_ +# define COMPAT_PROCESS_H_ + +# include "criterion/types.h" + +struct proc_handle; +typedef struct proc_handle s_proc_handle; + +struct worker_context { + struct criterion_test *test; + struct criterion_suite *suite; + f_worker_func func; + struct pipe_handle *pipe; +}; + +extern struct worker_context g_worker_context; + +int resume_child(void); + +s_proc_handle *fork_process(); +void wait_process(s_proc_handle *handle, int *status); + +s_proc_handle *get_current_process(); +bool is_current_process(s_proc_handle *proc); + +#endif /* !COMPAT_PROCESS_H_ */ diff --git a/src/compat/section.c b/src/compat/section.c new file mode 100644 index 0000000..fb63def --- /dev/null +++ b/src/compat/section.c @@ -0,0 +1,88 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "section.h" +#include "internal.h" + +#ifdef _WIN32 +void *get_win_section_start(const char *section) { + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL); + PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew)); + + assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE); + assert(ntHeader->Signature == IMAGE_NT_SIGNATURE); + + PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader); + for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) { + if (!strncmp((char*) pSecHeader->Name, section, 8)) { + return (char*) dosHeader + pSecHeader->VirtualAddress; + } + } + return NULL; +} + +void *get_win_section_end(const char *section) { + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL); + PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew)); + + assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE); + assert(ntHeader->Signature == IMAGE_NT_SIGNATURE); + + PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader); + for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) { + if (!strncmp((char*) pSecHeader->Name, section, 8)) { + return (char*) dosHeader + (size_t) pSecHeader->VirtualAddress + pSecHeader->SizeOfRawData; + } + } + return NULL; +} +#endif + +#ifdef __APPLE__ +# include +# include + +# define BASE_IMAGE_INDEX 0 + +static inline void *get_real_address(void *addr) { + if (!addr) + return NULL; + + // We need to slide the section address to get a valid pointer + // because ASLR will shift the image by a random offset + return addr + _dyld_get_image_vmaddr_slide(BASE_IMAGE_INDEX); +} + +void *get_osx_section_start(const char *section) { + unsigned long secsize; + return get_real_address(getsectdata("__DATA", section, &secsize)); +} + +void *get_osx_section_end(const char *section) { + unsigned long secsize; + char *section_start = getsectdata("__DATA", section, &secsize); + return get_real_address(section_start) + secsize; +} +#endif + diff --git a/src/posix-compat.h b/src/compat/section.h similarity index 58% rename from src/posix-compat.h rename to src/compat/section.h index a0c86e7..9f6359e 100644 --- a/src/posix-compat.h +++ b/src/compat/section.h @@ -21,64 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef POSIX_COMPAT_H_ -# define POSIX_COMPAT_H_ - -#if defined(_WIN32) && !defined(__CYGWIN__) -# define VANILLA_WIN32 -#endif - -# if !defined(_POSIX_SOURCE) -# define _POSIX_SOURCE 1 -# define TMP_POSIX -# endif -# include -# ifdef TMP_POSIX -# undef _POSIX_SOURCE -# undef TMP_POSIX -# endif - -# ifdef VANILLA_WIN32 -# define WEXITSTATUS(Status) (((Status) & 0xFF00) >> 8) -# define WTERMSIG(Status) ((Status) & 0x7F) -# define WIFEXITED(Status) (WTERMSIG(Status) == 0) -# define WIFSIGNALED(Status) (((signed char) (WTERMSIG(Status) + 1) >> 1) > 0) - -# define SIGPROF 27 -# define CR_EXCEPTION_TIMEOUT 0xC0001042 -# else -# include -# include -# endif - -#include - -struct proc_handle; -typedef struct proc_handle s_proc_handle; - -struct pipe_handle; -typedef struct pipe_handle s_pipe_handle; - -struct worker_context { - struct criterion_test *test; - struct criterion_suite *suite; - f_worker_func func; - struct pipe_handle *pipe; -}; - -extern struct worker_context g_worker_context; - -int resume_child(void); - -s_pipe_handle *stdpipe(); -FILE *pipe_in(s_pipe_handle *p, int do_close); -FILE *pipe_out(s_pipe_handle *p, int do_close); - -s_proc_handle *fork_process(); -void wait_process(s_proc_handle *handle, int *status); - -s_proc_handle *get_current_process(); -bool is_current_process(s_proc_handle *proc); +#ifndef SECTION_H_ +# define SECTION_H_ # ifdef _WIN32 void *get_win_section_start(const char *section); @@ -99,6 +43,4 @@ void *get_osx_section_end(const char *section); # define GET_SECTION_END(Name) SECTION_END(Name) # endif -const char *basename_compat(const char *str); - -#endif /* !POSIX_COMPAT_H_ */ +#endif /* !SECTION_H_ */ diff --git a/src/timer.c b/src/compat/time.c similarity index 98% rename from src/timer.c rename to src/compat/time.c index 8007afa..14de484 100644 --- a/src/timer.c +++ b/src/compat/time.c @@ -1,9 +1,9 @@ #include #include #include -#include "timer.h" #include "criterion/common.h" -#include "posix-compat.h" +#include "compat/time.h" +#include "compat/posix.h" #define GIGA 1000000000 diff --git a/src/timer.h b/src/compat/time.h similarity index 100% rename from src/timer.h rename to src/compat/time.h diff --git a/src/abort.c b/src/core/abort.c similarity index 100% rename from src/abort.c rename to src/core/abort.c diff --git a/src/abort.h b/src/core/abort.h similarity index 100% rename from src/abort.h rename to src/core/abort.h diff --git a/src/ordered-set.c b/src/core/ordered-set.c similarity index 100% rename from src/ordered-set.c rename to src/core/ordered-set.c diff --git a/src/report.c b/src/core/report.c similarity index 99% rename from src/report.c rename to src/core/report.c index 9a6f80e..025d484 100644 --- a/src/report.c +++ b/src/core/report.c @@ -31,7 +31,7 @@ #include "criterion/ordered-set.h" #include "report.h" #include "config.h" -#include "posix-compat.h" +#include "compat/posix.h" static inline void nothing() {} diff --git a/src/report.h b/src/core/report.h similarity index 100% rename from src/report.h rename to src/core/report.h diff --git a/src/runner.c b/src/core/runner.c similarity index 98% rename from src/runner.c rename to src/core/runner.c index c752d86..f085921 100644 --- a/src/runner.c +++ b/src/core/runner.c @@ -28,20 +28,20 @@ #include "criterion/options.h" #include "criterion/ordered-set.h" #include "criterion/logging.h" +#include "compat/time.h" +#include "compat/posix.h" +#include "string/i18n.h" +#include "io/event.h" #include "stats.h" #include "runner.h" #include "report.h" -#include "event.h" -#include "process.h" -#include "timer.h" -#include "posix-compat.h" +#include "worker.h" #include "abort.h" #include "config.h" -#include "i18n.h" #include "common.h" #ifdef HAVE_PCRE -#include "extmatch.h" +#include "string/extmatch.h" #endif #ifdef _MSC_VER diff --git a/src/runner.h b/src/core/runner.h similarity index 98% rename from src/runner.h rename to src/core/runner.h index e78fd96..75e12dc 100644 --- a/src/runner.h +++ b/src/core/runner.h @@ -25,7 +25,6 @@ # define CRITERION_RUNNER_H_ # include "criterion/types.h" -# include "posix-compat.h" DECL_SECTION_LIMITS(struct criterion_test, cr_tst); DECL_SECTION_LIMITS(struct criterion_suite, cr_sts); diff --git a/src/stats.c b/src/core/stats.c similarity index 100% rename from src/stats.c rename to src/core/stats.c diff --git a/src/stats.h b/src/core/stats.h similarity index 98% rename from src/stats.h rename to src/core/stats.h index e024282..d13ee83 100644 --- a/src/stats.h +++ b/src/core/stats.h @@ -25,7 +25,7 @@ # define STATS_H_ # include "criterion/stats.h" -# include "event.h" +# include "io/event.h" struct criterion_global_stats *stats_init(void); struct criterion_test_stats *test_stats_init(struct criterion_test *t); diff --git a/src/theories.c b/src/core/theories.c similarity index 100% rename from src/theories.c rename to src/core/theories.c diff --git a/src/process.c b/src/core/worker.c similarity index 98% rename from src/process.c rename to src/core/worker.c index 561665c..f6833f8 100644 --- a/src/process.c +++ b/src/core/worker.c @@ -28,9 +28,9 @@ #include "criterion/types.h" #include "criterion/options.h" #include "criterion/redirect.h" -#include "process.h" -#include "event.h" -#include "posix-compat.h" +#include "io/event.h" +#include "compat/posix.h" +#include "worker.h" struct process { s_proc_handle *proc; diff --git a/src/process.h b/src/core/worker.h similarity index 98% rename from src/process.h rename to src/core/worker.h index 5e0a6c3..2f63406 100644 --- a/src/process.h +++ b/src/core/worker.h @@ -25,7 +25,7 @@ # define PROCESS_H_ # include -# include "posix-compat.h" +# include "compat/process.h" struct process; diff --git a/src/entry.c b/src/entry/entry.c similarity index 100% rename from src/entry.c rename to src/entry/entry.c diff --git a/src/main.c b/src/entry/main.c similarity index 99% rename from src/main.c rename to src/entry/main.c index 3aa9584..5551886 100644 --- a/src/main.c +++ b/src/entry/main.c @@ -29,7 +29,7 @@ #include "criterion/criterion.h" #include "criterion/options.h" #include "criterion/ordered-set.h" -#include "runner.h" +#include "core/runner.h" #include "config.h" #include "common.h" diff --git a/src/options.c b/src/entry/options.c similarity index 100% rename from src/options.c rename to src/entry/options.c diff --git a/src/asprintf.c b/src/io/asprintf.c similarity index 100% rename from src/asprintf.c rename to src/io/asprintf.c diff --git a/src/event.c b/src/io/event.c similarity index 100% rename from src/event.c rename to src/io/event.c diff --git a/src/event.h b/src/io/event.h similarity index 100% rename from src/event.h rename to src/io/event.h diff --git a/src/file.c b/src/io/file.c similarity index 100% rename from src/file.c rename to src/io/file.c diff --git a/src/io/redirect.c b/src/io/redirect.c new file mode 100644 index 0000000..a9d80d0 --- /dev/null +++ b/src/io/redirect.c @@ -0,0 +1,77 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include "criterion/assert.h" +#include "criterion/redirect.h" +#include "compat/pipe.h" + +void cr_redirect(enum criterion_std_fd fd_kind, s_pipe_handle *pipe) { + fflush(get_std_file(fd_kind)); + if (!stdpipe_options(pipe, fd_kind, fd_kind == CR_STDIN ? 0 : 1)) + cr_assert_fail("Could not redirect standard file descriptor."); + + pipe_std_redirect(pipe, fd_kind); +} + +void cr_redirect_stdout(void) { + cr_redirect(CR_STDOUT, stdout_redir); +} + +void cr_redirect_stderr(void) { + cr_redirect(CR_STDERR, stderr_redir); +} + +void cr_redirect_stdin(void) { + cr_redirect(CR_STDIN, stdin_redir); +} + +FILE* cr_get_redirected_stdout(void) { + static FILE *f; + if (!f) { + f = pipe_in(stdout_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdout read end."); + } + return f; +} + +FILE* cr_get_redirected_stderr(void) { + static FILE *f; + if (!f) { + f = pipe_in(stderr_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stderr read end."); + } + return f; +} + +FILE* cr_get_redirected_stdin(void) { + static FILE *f; + if (!f) { + f = pipe_out(stdin_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdin write end."); + } + return f; +} diff --git a/src/log/logging.c b/src/log/logging.c index b527e71..7ef465b 100644 --- a/src/log/logging.c +++ b/src/log/logging.c @@ -27,7 +27,7 @@ #include #include "criterion/logging.h" #include "criterion/options.h" -#include "i18n.h" +#include "string/i18n.h" #ifdef ENABLE_NLS # define LOG_FORMAT "[%1$s%2$s%3$s] %4$s" diff --git a/src/log/normal.c b/src/log/normal.c index 6e3b1db..834c182 100644 --- a/src/log/normal.c +++ b/src/log/normal.c @@ -30,10 +30,10 @@ #include "criterion/logging.h" #include "criterion/options.h" #include "criterion/ordered-set.h" -#include "timer.h" +#include "compat/posix.h" +#include "compat/time.h" +#include "string/i18n.h" #include "config.h" -#include "i18n.h" -#include "posix-compat.h" #include "common.h" #ifdef VANILLA_WIN32 diff --git a/src/log/tap.c b/src/log/tap.c index 9a87b78..4615f70 100644 --- a/src/log/tap.c +++ b/src/log/tap.c @@ -29,9 +29,9 @@ #include "criterion/logging.h" #include "criterion/options.h" #include "criterion/ordered-set.h" -#include "timer.h" +#include "compat/posix.h" +#include "compat/time.h" #include "config.h" -#include "posix-compat.h" #include "common.h" #ifdef _MSC_VER diff --git a/src/posix-compat.c b/src/posix-compat.c deleted file mode 100644 index a8061da..0000000 --- a/src/posix-compat.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright © 2015 Franklin "Snaipe" Mathieu - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#define _GNU_SOURCE 1 -#include -#include -#include -#include "posix-compat.h" -#include "process.h" -#include "criterion/assert.h" -#include "criterion/redirect.h" - -#ifdef VANILLA_WIN32 -# define VC_EXTRALEAN -# define WIN32_LEAN_AND_MEAN -# undef _WIN32_WINNT -# define _WIN32_WINNT 0x0502 -# include -# include -# include -# include -# include - -# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ - CreateProcessW(Filename, \ - CmdLine, \ - NULL, \ - NULL, \ - TRUE, \ - CREATE_SUSPENDED, \ - NULL, \ - NULL, \ - &(StartupInfo), \ - &(Info)) - -# define WRITE_PROCESS_(Proc, What, Size) \ - WriteProcessMemory(Proc, &What, &What, Size, NULL); - -# include - -#else -# include -# include -# include -# include -#endif - -#include - -struct proc_handle { -#ifdef VANILLA_WIN32 - HANDLE handle; -#else - pid_t pid; -#endif -}; - -struct pipe_handle { -#ifdef VANILLA_WIN32 - HANDLE fhs[2]; -#else - int fds[2]; -#endif -}; - -struct worker_context g_worker_context = {.test = NULL}; - -#ifdef VANILLA_WIN32 -struct full_context { - struct criterion_test test; - struct criterion_test_extra_data test_data; - struct criterion_suite suite; - struct criterion_test_extra_data suite_data; - f_worker_func func; - struct pipe_handle pipe; - volatile int resumed; -}; - -static TCHAR g_mapping_name[] = TEXT("WinCriterionWorker"); -#define MAPPING_SIZE sizeof (struct full_context) - -static struct full_context local_ctx; -#endif - -int resume_child(void) { -#ifdef VANILLA_WIN32 - HANDLE sharedMem = OpenFileMapping( - FILE_MAP_ALL_ACCESS, - FALSE, - g_mapping_name); - - if (sharedMem == NULL) - return 0; - - struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, - FILE_MAP_ALL_ACCESS, - 0, - 0, - MAPPING_SIZE); - - if (ctx == NULL) - exit(-1); - - local_ctx = *ctx; - g_worker_context = (struct worker_context) { - &local_ctx.test, - &local_ctx.suite, - local_ctx.func, - &local_ctx.pipe - }; - - local_ctx.test.data = &local_ctx.test_data; - local_ctx.suite.data = &local_ctx.suite_data; - - ctx->resumed = 1; - - UnmapViewOfFile(ctx); - CloseHandle(sharedMem); - - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); - - run_worker(&g_worker_context); - return 1; -#else - return 0; -#endif -} - -s_proc_handle *fork_process() { -#ifdef VANILLA_WIN32 - PROCESS_INFORMATION info; - STARTUPINFOW si = { .cb = sizeof (STARTUPINFOW) }; - - ZeroMemory(&info, sizeof (info)); - - // Create the suspended child process - wchar_t filename[MAX_PATH]; - GetModuleFileNameW(NULL, filename, MAX_PATH); - - if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) - return (void *) -1; - - // Copy context over - HANDLE sharedMem = CreateFileMapping( - INVALID_HANDLE_VALUE, - NULL, - PAGE_READWRITE, - 0, - MAPPING_SIZE, - g_mapping_name); - - if (sharedMem == NULL) - return (void *) -1; - - struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem, - FILE_MAP_ALL_ACCESS, - 0, - 0, - MAPPING_SIZE); - - if (ctx == NULL) { - CloseHandle(sharedMem); - return (void *) -1; - } - - *ctx = (struct full_context) { - .test = *g_worker_context.test, - .test_data = *g_worker_context.test->data, - .suite = *g_worker_context.suite, - .func = g_worker_context.func, - .pipe = *g_worker_context.pipe, - .resumed = 0, - }; - - if (g_worker_context.suite->data) - ctx->suite_data = *g_worker_context.suite->data; - - ResumeThread(info.hThread); - CloseHandle(info.hThread); - - while (!ctx->resumed); // wait until the child has initialized itself - - UnmapViewOfFile(ctx); - CloseHandle(sharedMem); - - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); - *handle = (s_proc_handle) { info.hProcess }; - return handle; -#else - pid_t pid = fork(); - if (pid == -1) - return (void *) -1; - if (pid == 0) - return NULL; - - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); - *handle = (s_proc_handle) { pid }; - return handle; -#endif -} - -void wait_process(s_proc_handle *handle, int *status) { -#ifdef VANILLA_WIN32 - WaitForSingleObject(handle->handle, INFINITE); - DWORD exit_code; - GetExitCodeProcess(handle->handle, &exit_code); - CloseHandle(handle->handle); - - unsigned int sig = 0; - switch (exit_code) { - case STATUS_FLOAT_DENORMAL_OPERAND: - case STATUS_FLOAT_DIVIDE_BY_ZERO: - case STATUS_FLOAT_INEXACT_RESULT: - case STATUS_FLOAT_INVALID_OPERATION: - case STATUS_FLOAT_OVERFLOW: - case STATUS_FLOAT_STACK_CHECK: - case STATUS_FLOAT_UNDERFLOW: - case STATUS_INTEGER_DIVIDE_BY_ZERO: - case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break; - - case STATUS_ILLEGAL_INSTRUCTION: - case STATUS_PRIVILEGED_INSTRUCTION: - case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break; - - case CR_EXCEPTION_TIMEOUT: sig = SIGPROF; break; - - case STATUS_ACCESS_VIOLATION: - case STATUS_DATATYPE_MISALIGNMENT: - case STATUS_ARRAY_BOUNDS_EXCEEDED: - case STATUS_GUARD_PAGE_VIOLATION: - case STATUS_IN_PAGE_ERROR: - case STATUS_NO_MEMORY: - case STATUS_INVALID_DISPOSITION: - case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break; - - case STATUS_CONTROL_C_EXIT: sig = SIGINT; break; - - default: break; - } - - *status = sig ? sig : exit_code << 8; -#else - waitpid(handle->pid, status, 0); -#endif -} - -FILE *pipe_in(s_pipe_handle *p, int do_close) { -#ifdef VANILLA_WIN32 - if (do_close) - CloseHandle(p->fhs[1]); - int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY); - if (fd == -1) - return NULL; - FILE *in = _fdopen(fd, "r"); -#else - if (do_close) - close(p->fds[1]); - FILE *in = fdopen(p->fds[0], "r"); -#endif - if (!in) - return NULL; - - setvbuf(in, NULL, _IONBF, 0); - return in; -} - -FILE *pipe_out(s_pipe_handle *p, int do_close) { -#ifdef VANILLA_WIN32 - if (do_close) - CloseHandle(p->fhs[0]); - int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY); - if (fd == -1) - return NULL; - FILE *out = _fdopen(fd, "w"); -#else - if (do_close) - close(p->fds[0]); - FILE *out = fdopen(p->fds[1], "w"); -#endif - if (!out) - return NULL; - - setvbuf(out, NULL, _IONBF, 0); - return out; -} - -int stdpipe_stack(s_pipe_handle *out) { -#ifdef VANILLA_WIN32 - HANDLE fhs[2]; - SECURITY_ATTRIBUTES attr = { - .nLength = sizeof (SECURITY_ATTRIBUTES), - .bInheritHandle = TRUE - }; - if (!CreatePipe(fhs, fhs + 1, &attr, 0)) - return -1; - *out = (s_pipe_handle) {{ fhs[0], fhs[1] }}; -#else - int fds[2] = { -1, -1 }; - if (pipe(fds) == -1) - return -1; - *out = (s_pipe_handle) {{ fds[0], fds[1] }}; -#endif - return 0; -} - -s_pipe_handle *stdpipe() { - s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); - if (stdpipe_stack(handle) < 0) - return NULL; - return handle; -} - -s_proc_handle *get_current_process() { - s_proc_handle *handle = smalloc(sizeof (s_proc_handle)); -#ifdef VANILLA_WIN32 - *handle = (s_proc_handle) { GetCurrentProcess() }; -#else - *handle = (s_proc_handle) { getpid() }; -#endif - return handle; -} - -bool is_current_process(s_proc_handle *proc) { -#ifdef VANILLA_WIN32 - return GetProcessId(proc->handle) == GetProcessId(GetCurrentProcess()); -#else - return proc->pid == getpid(); -#endif -} - -#ifdef _WIN32 -void *get_win_section_start(const char *section) { - PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL); - PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew)); - - assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE); - assert(ntHeader->Signature == IMAGE_NT_SIGNATURE); - - PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader); - for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) { - if (!strncmp((char*) pSecHeader->Name, section, 8)) { - return (char*) dosHeader + pSecHeader->VirtualAddress; - } - } - return NULL; -} - -void *get_win_section_end(const char *section) { - PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL); - PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew)); - - assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE); - assert(ntHeader->Signature == IMAGE_NT_SIGNATURE); - - PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader); - for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) { - if (!strncmp((char*) pSecHeader->Name, section, 8)) { - return (char*) dosHeader + (size_t) pSecHeader->VirtualAddress + pSecHeader->SizeOfRawData; - } - } - return NULL; -} -#endif - -#ifdef __APPLE__ -# include -# include - -# define BASE_IMAGE_INDEX 0 - -static inline void *get_real_address(void *addr) { - if (!addr) - return NULL; - - // We need to slide the section address to get a valid pointer - // because ASLR will shift the image by a random offset - return addr + _dyld_get_image_vmaddr_slide(BASE_IMAGE_INDEX); -} - -void *get_osx_section_start(const char *section) { - unsigned long secsize; - return get_real_address(getsectdata("__DATA", section, &secsize)); -} - -void *get_osx_section_end(const char *section) { - unsigned long secsize; - char *section_start = getsectdata("__DATA", section, &secsize); - return get_real_address(section_start) + secsize; -} -#endif - -const char *basename_compat(const char *str) { - const char *start = str; - for (const char *c = str; *c; ++c) - if ((*c == '/' || *c == '\\') && c[1]) - start = c + 1; - return start; -} - -#ifdef VANILLA_WIN32 -typedef DWORD cr_std_fd; -#else -typedef int cr_std_fd; -#endif - -static s_pipe_handle stdout_redir; -static s_pipe_handle stderr_redir; -static s_pipe_handle stdin_redir; - -enum criterion_std_fd { - CR_STDIN = 0, - CR_STDOUT = 1, - CR_STDERR = 2, -}; - -enum criterion_pipe_end { - PIPE_READ = 0, - PIPE_WRITE = 1, -}; - -cr_std_fd get_std_fd(int fd_kind) { - static int kinds[] = { -#ifdef VANILLA_WIN32 - [CR_STDIN] = STD_INPUT_HANDLE, - [CR_STDOUT] = STD_OUTPUT_HANDLE, - [CR_STDERR] = STD_ERROR_HANDLE, -#else - [CR_STDIN] = STDIN_FILENO, - [CR_STDOUT] = STDOUT_FILENO, - [CR_STDERR] = STDERR_FILENO, -#endif - }; - - return kinds[fd_kind]; -} - -FILE* get_std_file(int fd_kind) { - switch (fd_kind) { - case CR_STDIN: return stdin; - case CR_STDOUT: return stdout; - case CR_STDERR: return stderr; - } - return NULL; -} - -int make_redirect_pipe(s_pipe_handle *handle, int id, int noblock) { -#ifdef VANILLA_WIN32 - HANDLE fhs[2]; - SECURITY_ATTRIBUTES attr = { - .nLength = sizeof (SECURITY_ATTRIBUTES), - .bInheritHandle = TRUE - }; - char pipe_name[256] = {0}; - snprintf(pipe_name, sizeof (pipe_name), - "\\\\.\\pipe\\criterion_%lu_%d", GetCurrentProcessId(), id); - fhs[0] = CreateNamedPipe(pipe_name, - PIPE_ACCESS_INBOUND, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE - | (noblock ? PIPE_NOWAIT : PIPE_WAIT), - 1, - 4096 * 4, - 4096 * 4, - 0, - &attr); - - if (fhs[0] == INVALID_HANDLE_VALUE) - return 0; - - fhs[1] = CreateFile(pipe_name, - GENERIC_WRITE, - 0, - &attr, - OPEN_EXISTING, - 0, - NULL); - - if (fhs[1] == INVALID_HANDLE_VALUE) { - CloseHandle(fhs[0]); - return 0; - } - - *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; -#else - (void) id; - - int fds[2] = { -1, -1 }; - if (pipe(fds) == -1) - return 0; - - if (noblock) - for (int i = 0; i < 2; ++i) - fcntl(fds[i], F_SETFL, fcntl(fds[i], F_GETFL) | O_NONBLOCK); - - *handle = (s_pipe_handle) {{ fds[0], fds[1] }}; -#endif - return 1; -} - -void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index, int noblock) { - fflush(get_std_file(fd_kind)); - if (!make_redirect_pipe(pipe, fd_kind, noblock)) - cr_assert_fail("Could not redirect standard file descriptor."); - - cr_std_fd fd = get_std_fd(fd_kind); -#ifdef VANILLA_WIN32 - int stdfd = _open_osfhandle((intptr_t) pipe->fhs[fd_index], fd_kind == 0 ? _O_RDONLY : _O_WRONLY); - if (stdfd == -1) - cr_assert_fail("Could not redirect standard file descriptor."); - - fflush(get_std_file(fd_kind)); - - _close(fd_kind); - SetStdHandle(fd, pipe->fhs[fd_index]); - - _dup2(stdfd, fd_kind); - _close(stdfd); - - setvbuf(get_std_file(fd_kind), NULL, _IONBF, 0); -#else - close(fd); - dup2(pipe->fds[fd_index], fd); - close(pipe->fds[fd_index]); -#endif -} - -void cr_redirect_stdout(void) { - cr_redirect(CR_STDOUT, &stdout_redir, PIPE_WRITE, 1); -} - -void cr_redirect_stderr(void) { - cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE, 1); -} - -void cr_redirect_stdin(void) { - cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ, 0); -} - -FILE* cr_get_redirected_stdout(void) { - static FILE *f; - if (!f) { - f = pipe_in(&stdout_redir, 0); - if (!f) - cr_assert_fail("Could not get redirected stdout read end."); - } - return f; -} - -FILE* cr_get_redirected_stderr(void) { - static FILE *f; - if (!f) { - f = pipe_in(&stderr_redir, 0); - if (!f) - cr_assert_fail("Could not get redirected stderr read end."); - } - return f; -} - -FILE* cr_get_redirected_stdin(void) { - static FILE *f; - if (!f) { - f = pipe_out(&stdin_redir, 0); - if (!f) - cr_assert_fail("Could not get redirected stdin write end."); - } - return f; -} - -#if defined(BSD) || defined(__unix__) - -# ifdef BSD -typedef int cr_count; -typedef int cr_retcount; -typedef fpos_t cr_off; -# else -typedef size_t cr_count; -typedef ssize_t cr_retcount; -typedef off64_t cr_off; -# endif - -struct memfile { - size_t size; - size_t region_size; - size_t cur; - size_t max_size; - char *mem; -}; - -static inline size_t size_safe_add(size_t size, size_t cur, cr_off off) { - cur = cur < SIZE_MAX - off ? cur + off : SIZE_MAX; - return cur < size ? cur : size; -} - -static inline size_t off_safe_add(size_t size, size_t cur, cr_off off) { - if (off >= 0) - cur = cur < SIZE_MAX - off ? cur + off : SIZE_MAX; - else - cur = cur > (size_t) -off ? cur + off : 0; - return cur < size ? cur : size; -} - -# define errno_return(Errno, Val) \ - do { \ - errno = (Errno); \ - return (Val); \ - } while (0) - -static cr_retcount mock_file_read(void *cookie, char *buf, cr_count count) { - struct memfile *mf = cookie; -# ifdef BSD - if (count < 0) - errno_return(EINVAL, (cr_retcount) -1); -# endif - if (mf->cur >= mf->size || count == 0) - return 0; - - size_t end = size_safe_add(mf->size, mf->cur, count); - count = end - mf->cur; - memcpy(buf, mf->mem + mf->cur, count); - mf->cur = end; - return count; -} - -static cr_retcount mock_file_write(void *cookie, const char *buf, cr_count count) { - struct memfile *mf = cookie; -# ifdef BSD - if (count < 0) - errno_return(EINVAL, (cr_retcount) -1); -# endif - if (count == 0) - return 0; - - if (mf->cur >= mf->max_size) - errno_return(EIO, (cr_retcount) -1); - - size_t end = size_safe_add(mf->max_size, mf->cur, count); - if (mf->size < end) - mf->size = end; - count = end - mf->cur; - - if (mf->size > mf->region_size) { - while (mf->size > mf->region_size) - mf->region_size = mf->region_size * 3 / 2; - char *newptr = realloc(mf->mem, mf->region_size); - if (!newptr) - errno_return(EIO, (cr_retcount) -1); - mf->mem = newptr; - } - memcpy(mf->mem + mf->cur, buf, count); - mf->cur = end; - return count; -} - -# ifdef BSD -static cr_off mock_file_seek(void *cookie, cr_off off, int whence) { - struct memfile *mf = cookie; - switch (whence) { - case SEEK_SET: return (mf->cur = off); - case SEEK_CUR: return (mf->cur = off_safe_add(mf->size, mf->cur, off)); - case SEEK_END: return (mf->cur = off_safe_add(mf->size, mf->size, off)); - default: break; - } - errno = EINVAL; - return (off_t) -1; -} -# else -static int mock_file_seek(void *cookie, cr_off *off, int whence) { - struct memfile *mf = cookie; - switch (whence) { - case SEEK_SET: mf->cur = *off; break; - case SEEK_CUR: *off = (mf->cur = off_safe_add(mf->size, mf->cur, *off)); break; - case SEEK_END: *off = (mf->cur = off_safe_add(mf->size, mf->size, *off)); break; - default: errno = EINVAL; return -1; - } - return 0; -} -# endif - -static int mock_file_close(void *cookie) { - struct memfile *mf = cookie; - free(mf->mem); - free(cookie); - return 0; -} -#endif - -FILE *cr_mock_file_size(size_t max_size) { -#if defined(__unix__) || defined(BSD) - struct memfile *cookie = malloc(sizeof (struct memfile)); - *cookie = (struct memfile) { - .max_size = max_size, - .region_size = 4096, - .mem = malloc(4096), - }; - - FILE *f; -# ifdef __unix__ - f = fopencookie(cookie, "w+", (cookie_io_functions_t) { - .read = mock_file_read, - .write = mock_file_write, - .seek = mock_file_seek, - .close = mock_file_close, - }); -# else - f = funopen(cookie, - mock_file_read, - mock_file_write, - mock_file_seek, - mock_file_close); -# endif - return f; -#else - (void) max_size; - - // fallback to tmpfile() - return tmpfile(); -#endif -} diff --git a/src/extmatch.c b/src/string/extmatch.c similarity index 100% rename from src/extmatch.c rename to src/string/extmatch.c diff --git a/src/extmatch.h b/src/string/extmatch.h similarity index 100% rename from src/extmatch.h rename to src/string/extmatch.h diff --git a/src/i18n.c b/src/string/i18n.c similarity index 100% rename from src/i18n.c rename to src/string/i18n.c diff --git a/src/i18n.h b/src/string/i18n.h similarity index 100% rename from src/i18n.h rename to src/string/i18n.h diff --git a/test/asprintf.c b/test/asprintf.c index 26630fd..dd191aa 100644 --- a/test/asprintf.c +++ b/test/asprintf.c @@ -60,6 +60,6 @@ Theory((struct format_test *fmt), asprintf, valid) { Test(asprintf, invalid) { char *actual; - cr_expect_eq(cr_asprintf(&actual, "%"), -1); + cr_expect_lt(cr_asprintf(&actual, "%"), 0); } #endif