diff --git a/CMakeLists.txt b/CMakeLists.txt index f0a58a828f..a7efecfc1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,7 @@ add_custom_target(nvim ALL) add_dependencies(nvim nvim_bin nvim_runtime_deps nvim_runtime) add_subdirectory(src/nvim) +add_subdirectory(src/tee) add_subdirectory(cmake.config) add_subdirectory(runtime) add_subdirectory(test) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index d254b1d4ac..83dbce44d4 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -423,6 +423,7 @@ list(SORT NVIM_SOURCES) list(SORT NVIM_HEADERS) list(APPEND UNCRUSTIFY_NVIM_SOURCES ${NVIM_SOURCES} ${NVIM_HEADERS}) +list(APPEND UNCRUSTIFY_NVIM_SOURCES ${PROJECT_SOURCE_DIR}/src/tee/tee.c) foreach(sfile ${NVIM_SOURCES}) get_filename_component(f ${sfile} NAME) @@ -454,6 +455,7 @@ foreach(hfile ${NVIM_HEADERS}) endforeach() list(APPEND LINT_NVIM_SOURCES ${NVIM_SOURCES} ${NVIM_HEADERS}) +list(APPEND LINT_NVIM_SOURCES ${PROJECT_SOURCE_DIR}/src/tee/tee.c) # Log level (NVIM_LOG_DEBUG in log.h) if(CI_BUILD) diff --git a/src/tee/CMakeLists.txt b/src/tee/CMakeLists.txt new file mode 100644 index 0000000000..1395c513ae --- /dev/null +++ b/src/tee/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(tee tee.c) + +# tee has to be in the same directory as the nvim executable +set_target_properties(tee PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +) + +install(TARGETS tee + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) diff --git a/src/tee/tee.c b/src/tee/tee.c new file mode 100644 index 0000000000..d8ef4c8aac --- /dev/null +++ b/src/tee/tee.c @@ -0,0 +1,130 @@ +// tee.c - pipe fitting +// +// Copyright (c) 1996, Paul Slootman +// +// Author: Paul Slootman +// (paul@wurtel.hobby.nl, paul@murphy.nl, paulS@toecompst.nl) +// Modifications for MSVC: Yasuhiro Matsumoto +// Modifications for Neovim: https://github.com/neovim/neovim/pull/36363 +// +// This source code is released into the public domain. It is provided on an +// as-is basis and no responsibility is accepted for its failure to perform +// as expected. It is worth at least as much as you paid for it! +// +// +// tee reads stdin, and writes what it reads to each of the specified +// files. The primary reason of existence for this version is a quick +// and dirty implementation to distribute with Vim, to make one of the +// most useful features of Vim possible on OS/2: quickfix. +// +// Of course, not using tee but instead redirecting make's output directly +// into a temp file and then processing that is possible, but if we have a +// system capable of correctly piping (unlike DOS, for example), why not +// use it as well as possible? This tee should also work on other systems, +// but it's not been tested there, only on OS/2. +// +// tee is also available in the GNU shellutils package, which is available +// precompiled for OS/2. That one probably works better. + +#ifndef _MSC_VER +# include +#endif +#include +#include +#include +#include + +#ifdef _WIN32 +# define sysconf(x) - 1 +#endif + +void usage(void) +{ + fprintf(stderr, + "Neotee: a web-scale fork of tee\n" + "Usage:\n" + "\ttee [-a] file ... file_n\n" + "\n" + "\t-a\tappend to files instead of truncating\n" + "\n" + "Tee reads its input, and writes to each of the specified files,\n" + "as well as to the standard output.\n" + "\n" + "Shipped with Nvim 0.12+ for use with :make, :grep, :!, etc.\n"); +} + +int main(int argc, char *argv[]) +{ + int append = 0; + size_t numfiles; + int maxfiles; + FILE **filepointers; + int i; + char buf[65536]; + int n; + int optnr = 1; + + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + break; + } + if (!strcmp(argv[i], "-a")) { + append++; + } else { + usage(); + exit(2); + } + optnr++; + } + + numfiles = argc - optnr; + + if (numfiles == 0) { + usage(); + exit(2); + } + + maxfiles = sysconf(_SC_OPEN_MAX); // or fill in 10 or so + if (maxfiles < 0) { + maxfiles = 10; + } + if (numfiles + 3 > maxfiles) { // +3 accounts for stdin, out, err + fprintf(stderr, "There is a limit of max %d files.\n", maxfiles - 3); + exit(1); + } + filepointers = calloc(numfiles, sizeof(FILE *)); // NOLINT + if (filepointers == NULL) { + fprintf(stderr, "Error allocating memory for %ld files\n", (long)numfiles); + exit(1); + } + for (i = 0; i < numfiles; i++) { + filepointers[i] = fopen(argv[i + optnr], append ? "ab" : "wb"); + if (filepointers[i] == NULL) { + fprintf(stderr, "Can't open \"%s\"\n", argv[i + optnr]); + exit(1); + } + } +#ifdef _WIN32 + setmode(fileno(stdin), O_BINARY); + fflush(stdout); // needed for _fsetmode(stdout) + setmode(fileno(stdout), O_BINARY); + setvbuf(stdout, NULL, _IONBF, 0); // unbuffered for immediate output +#endif + + while ((n = fread(buf, 1, sizeof(buf), stdin)) > 0) { + fwrite(buf, 1, n, stdout); + for (i = 0; i < numfiles; i++) { + if (filepointers[i]) { + fwrite(buf, 1, n, filepointers[i]); + } + } + fflush(stdout); + } + for (i = 0; i < numfiles; i++) { + if (filepointers[i]) { + fclose(filepointers[i]); + } + } + + exit(0); +}