~nabijaczleweli/voreutils

305ccd6fec5d0ea41829cb943908798c9efebcfc — наб 25 days ago 55c2e29
Add stdbuf

Untestable, unfortunately, I think?
M .gitignore => .gitignore +2 -0
@@ 15,3 15,5 @@
!man/**
!cmd
!cmd/**
!lib
!lib/**

M Makefile => Makefile +24 -13
@@ 17,11 17,15 @@ LDFLAGS ?=
OUTDIR ?= out/
OBJDIR ?= $(OUTDIR)obj/
CMDDIR ?= $(OUTDIR)cmd/
LIBDIR ?= $(OUTDIR)lib/
MANDIR ?= $(OUTDIR)man/
HTMLMANDIR ?= $(OUTDIR)man-html/
VOREUTILS_LIB_PREFIX ?= /usr/lib/voreutils/
VOREUTILS_LIB_PREFIX ?= /usr/lib/x86_64-linux-gnu/voreutils/


AS_NEEDED := -Wl,--as-needed
SO := .so
SONAME := -soname
SYSTEM := $(shell uname -s)
ifeq "$(SYSTEM)" "NetBSD"


@@ 31,6 35,7 @@ else ifeq "$(SYSTEM)" "OpenBSD"
else ifeq "$(SYSTEM)" "Darwin"
	AS_NEEDED :=
	SONAME := -install_name
	SO := .dylib
else ifeq "$(SYSTEM)" "Linux"
	CXXSPECIFICCC += -D_FILE_OFFSET_BITS=64
else


@@ 63,31 68,33 @@ ifeq "$(SYMLINK)" "y"
SYMFLAG := s
endif

CCAR := -g -O3 -pipe -Wall -Wextra $(CXXSPECIFICCC) $(CFLAGS)  $(shell pkg-config --cflags libselinux 2>/dev/null && echo -DVOREUTILS_LIBSELINUX) $(shell for l in b2 crypto; do pkg-config --cflags "lib$$l" 2>/dev/null; done)
LDAR := $(AS_NEEDED) -L$(OUTDIR)   $(CXXSPECIFICLD) $(LDFLAGS) $(shell pkg-config --libs   libselinux 2>/dev/null                               ) $(shell for l in b2 crypto; do pkg-config --libs "lib$$l" 2>/dev/null || echo "-l$$l"; done)
CPPAR := -Iinclude/ -DVOREUTILS_VERSION='"$(VOREUTILS_VERSION)"' -DVOREUTILS_DATE='"$(VOREUTILS_DATE)"' -include vore-id.h $(CPPFLAGS)
CCAR := -g -O3 -pipe -Wall -Wextra -fPIC $(CXXSPECIFICCC) $(CFLAGS)  $(shell pkg-config --cflags libselinux 2>/dev/null && echo -DVOREUTILS_LIBSELINUX) $(shell for l in b2 crypto; do pkg-config --cflags "lib$$l" 2>/dev/null; done)
LDAR := $(AS_NEEDED) -L$(OUTDIR)         $(CXXSPECIFICLD) $(LDFLAGS) $(shell pkg-config --libs   libselinux 2>/dev/null                               ) $(shell for l in b2 crypto; do pkg-config --libs "lib$$l" 2>/dev/null || echo "-l$$l"; done)
CPPAR := -Iinclude/ -DVOREUTILS_VERSION='"$(VOREUTILS_VERSION)"' -DVOREUTILS_DATE='"$(VOREUTILS_DATE)"' -DVOREUTILS_SO='"$(SO)"' -DVOREUTILS_LIB_PREFIX='"$(VOREUTILS_LIB_PREFIX)"' -include vore-id.h $(CPPFLAGS)
BINARIES := $(filter-out cmd/aliases,$(wildcard cmd/*))
LIBRARIES := $(wildcard lib/*)
INCLUDES := $(wildcard include/*)
MANPAGES := $(wildcard man/*.[18])
MANPAGES := $(wildcard man/*.[138])


.PHONY : all binaries manpages htmlpages nomandoc clean test
.PHONY : all binaries libraries manpages htmlpages nomandoc clean test
.SECONDARY:

all : binaries manpages htmlpages
all : binaries libraries manpages htmlpages
allpages : manpages htmlpages
clean:
	rm -rf $(OUTDIR) $(OBJDIR)

test : binaries
test : binaries libraries
	CMDDIR=$(realpath $(CMDDIR))/ find "tests/" -mindepth 1 -maxdepth 1 -type f -print -execdir ./{} \; 3>$(OBJDIR)testpsko
	CMDDIR=$(realpath $(CMDDIR))/ find "tests/" -mindepth 2 -maxdepth 2 -type f -name test -print -execdir ./{} \; 3>>$(OBJDIR)testpsko
	@cat $(OBJDIR)testpsko >&2
	@! [ -s $(OBJDIR)testpsko ]

binaries : $(patsubst %.c,%,$(patsubst %.cpp,%,$(patsubst cmd/%,$(CMDDIR)%,$(BINARIES)))) $(OBJDIR)aliases
manpages : $(patsubst %,$(MANDIR)%,$(patsubst %.8,man8/%.8,$(patsubst %.1,man1/%.1,$(patsubst man/%,%,$(MANPAGES))))) $(OBJDIR)man/aliases
htmlpages : $(patsubst %,$(HTMLMANDIR)%.html,$(patsubst %.8,man8/%.8,$(patsubst %.1,man1/%.1,$(patsubst man/%,%,$(MANPAGES))))) $(OBJDIR)man/aliases $(HTMLMANDIR)style.css
libraries : $(patsubst %.c,%$(SO),$(patsubst %.cpp,%$(SO),$(patsubst lib/%,$(LIBDIR)%,$(LIBRARIES))))
manpages : $(patsubst %,$(MANDIR)%,$(patsubst %.8,man8/%.8,$(patsubst %.3,man3/%.3,$(patsubst %.1,man1/%.1,$(patsubst man/%,%,$(MANPAGES)))))) $(OBJDIR)man/aliases
htmlpages : $(patsubst %,$(HTMLMANDIR)%.html,$(patsubst %.8,man8/%.8,$(patsubst %.3,man3/%.3,$(patsubst %.1,man1/%.1,$(patsubst man/%,%,$(MANPAGES)))))) $(OBJDIR)man/aliases $(HTMLMANDIR)style.css

nomandoc : $(MANPAGES) $(OBJDIR)man/aliases
	sh -xc 'for p; do cp "man/$$p" "$(MANDIR)man$${p##*.}/"; done' sh $(patsubst man/%,%,$(MANPAGES))


@@ 117,19 124,19 @@ $(OBJDIR)aliases : cmd/aliases $(patsubst %.c,%,$(patsubst %.cpp,%,$(patsubst cm

$(OBJDIR)man/% : man/%
	@mkdir -p $(dir $@)
	$(SED) 's/^\.Dd/.Dd $(VOREUTILS_DATE)/' $< > $@
	$(SED) -e 's/^\.Dd/.Dd $(VOREUTILS_DATE)/' -e 's:@VOREUTILS_LIB_PREFIX@:$(VOREUTILS_LIB_PREFIX):g' -e 's:@VOREUTILS_SO@:$(SO):g' $< > $@
	! $(MANDOC) -I os="voreutils $(VOREUTILS_VERSION)" -Tlint $@ 2>&1 | grep -vE -e 'mandoc: outdated mandoc.db' -e 'STYLE: referenced manual not found' -e 'WARNING: cross reference to self' -e 'WARNING: undefined string, using "": doc-'

# https://twitter.com/nabijaczleweli/status/1411351513193238530
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901636#52
$(MANDIR)man1/% $(MANDIR)man8/% : $(OBJDIR)man/%
$(MANDIR)man1/% $(MANDIR)man3/% $(MANDIR)man8/% : $(OBJDIR)man/%
	@mkdir -p $(dir $@)
	$(AWK) 'BEGIN { ints=0; tsc=0 }  /^\.TS/,/^\.TE/ { if(!ints) { ints=1; ++tsc; print "_VO_TS" tsc } print > ("$<-TS" tsc); next }  { ints=0; print }' $< | \
		$(MANDOC) -I os="voreutils $(VOREUTILS_VERSION)" -Tman | \
		grep -vF 'Automatically generated from an mdoc input file.' | \
		$(AWK) '/^_VO_TS/ {tsc=substr($$0, 7); while(getline < ("$<-TS" tsc)) print; next}  {print}' > $@

$(HTMLMANDIR)man1/%.html $(HTMLMANDIR)man8/%.html : $(OBJDIR)man/%
$(HTMLMANDIR)man1/%.html $(HTMLMANDIR)man3/%.html $(HTMLMANDIR)man8/%.html : $(OBJDIR)man/%
	@mkdir -p $(dir $@)
	$(MANDOC) -I os="voreutils $(VOREUTILS_VERSION)" -Thtml -Ostyle="../style.css",man="../man%S/%N.%S.html" $< | \
		$(AWK) '/^<h1/ {in_syn = $$0 ~ /id="SYNOPSIS"/}  /^<br/ {if(in_syn) next}  {print}' | \


@@ 142,7 149,11 @@ $(HTMLMANDIR)style.css : man/style.css

$(CMDDIR)% : $(OBJDIR)cmd/%.o
	@mkdir -p $(dir $@)
	$(CXX) $(CCAR) -std=c++17 -fno-exceptions -o $@ $^ $(LDAR)
	$(CXX) $(CCAR) -fno-exceptions -o $@ $^ $(LDAR)

$(LIBDIR)%$(SO) : $(OBJDIR)lib/%.o
	@mkdir -p $(dir $@)
	$(CXX) $(CCAR) -shared $< -o $@ $(LDAR) -Wl,$(SONAME),$(subst $(OUTDIR),,$@).$(VOREUTILS_VERSION)

$(OBJDIR)%.o : %.c
	@mkdir -p $(dir $@)

M README.md => README.md +1 -1
@@ 88,7 88,7 @@ GNU coreutils provide the following 105 binaries, according to `dpkg -L coreutil
  [ ] /usr/bin/sort
  [ ] /usr/bin/split
  [ ] /usr/bin/stat
  [ ] /usr/bin/stdbuf
  [x] /usr/bin/stdbuf
  [x] /usr/bin/sum
  [x] /usr/bin/tac
  [ ] /usr/bin/tail

A cmd/stdbuf.cpp => cmd/stdbuf.cpp +94 -0
@@ 0,0 1,94 @@
// SPDX-License-Identifier: 0BSD


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <string>
#include <strings.h>
#include <unistd.h>
#include <vore-getopt>
#include <vore-optarg>
#include <vore-size>


#if __APPLE__
#define LD_PRELOAD "DYLD_INSERT_LIBRARIES"
#else
#define LD_PRELOAD "LD_PRELOAD"
#endif

#define LIBSTDBUF_SO VOREUTILS_LIB_PREFIX "libstdbuf" VOREUTILS_SO


#define USAGE "usage: %s [-i size] [-o L|size] [-e L|size] program [argument]...\n"


int main(int argc, char * const * argv) {
	const char * o_stdin{};
	const char * o_stdout{};
	const char * o_stderr{};

	const char ** os[] = {&o_stdin, &o_stdout, &o_stderr};
	for(auto && [arg, val] : vore::opt::get{argc,
	                                        argv,
	                                        "+i:o:e:",
	                                        {{"input", required_argument, nullptr, 'i'},  //
	                                         {"output", required_argument, nullptr, 'o'},
	                                         {"error", required_argument, nullptr, 'e'}}}) {
		std::uint8_t idx{};
		switch(arg) {
			case 'e':
				++idx;
				[[fallthrough]];
			case 'o':
				++idx;
				[[fallthrough]];
			case 'i':
				if(!strcasecmp(val, "L")) {
					if(os[idx] == &o_stdin) {
						std::fprintf(stderr, "%s: -i: L meaningless\n", argv[0]);
						return 125;
					}
				} else {
					if(!vore::parse_size<std::size_t>(argv[0], val))
						return 125;
				}
				*os[idx] = val;
				break;
			default:
				std::fprintf(stderr, USAGE, argv[0]);
				return 125;
		}
	}
	if(!argv[optind]) {
		std::fprintf(stderr, USAGE, argv[0]);
		return 125;
	}


	// TODO?: should probably search for libstdbuf a bit harder
	bool env_err{};
	if(auto preload = std::getenv(LD_PRELOAD))
		env_err = env_err || setenv(LD_PRELOAD, (std::string{preload} + ":" LIBSTDBUF_SO).c_str(), true);
	else
		env_err = env_err || putenv(const_cast<char *>(LD_PRELOAD "=" LIBSTDBUF_SO));

	env_err = env_err || (o_stdin && setenv("_STDBUF_I", o_stdin, true));
	env_err = env_err || (o_stdout && setenv("_STDBUF_O", o_stdout, true));
	env_err = env_err || (o_stderr && setenv("_STDBUF_E", o_stderr, true));

#if __APPLE__
	env_err = env_err || putenv(const_cast<char *>("DYLD_FORCE_FLAT_NAMESPACE=1"));
#endif
	if(env_err) {
		std::fprintf(stderr, "%s: %s\n", argv[0], std::strerror(errno));
		return 125;
	}

	execvp(argv[optind], argv + optind);
	int exec_err = errno;
	std::fprintf(stderr, "%s: %s: %s\n", argv[0], argv[optind], std::strerror(exec_err));
	return exec_err == ENOENT ? 127 : 126;
}

M cmd/truncate.cpp => cmd/truncate.cpp +8 -73
@@ 2,18 2,17 @@


#include <algorithm>
#include <cctype>
#include <cinttypes>
#include <cmath>
#include <cctype>
#include <cstring>
#include <optional>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vore-file>
#include <vore-getopt>
#include <vore-optarg>
#include <vore-size>


#define USAGE(self)                    \


@@ 43,82 42,18 @@ int main(int argc, char * const * argv) {
				create_ok = false;
				break;
			case 's': {
				double unit = 1024;
				std::uint8_t power{};
				std::string_view v{val};
				if(v.back() == 'b' || v.back() == 'B') {
					unit = 1000;
					v    = v.substr(0, v.size() - 1);
				}
				if(!v.empty() && !std::isdigit(v.back()))
					switch(v.back()) {
						case 'y':
						case 'Y':
							power += 1;
							[[fallthrough]];
						case 'z':
						case 'Z':
							power += 1;
							[[fallthrough]];
						case 'e':
						case 'E':
							power += 1;
							[[fallthrough]];
						case 'p':
						case 'P':
							power += 1;
							[[fallthrough]];
						case 't':
						case 'T':
							power += 1;
							[[fallthrough]];
						case 'g':
						case 'G':
							power += 1;
							[[fallthrough]];
						case 'm':
						case 'M':
							power += 1;
							[[fallthrough]];
						case 'k':
						case 'K':
							power += 1;
							v = v.substr(0, v.size() - 1);
							break;
						default:
							std::fprintf(stderr, "%s: %s: unrecognised suffix %c; must be one of: KMGTPEZY\n", argv[0], val, v.back());
							return 1;
					}
				if(!v.empty() && !std::isdigit(v.front())) {
					if(auto m = std::find(std::begin(size_mode_s), std::end(size_mode_s), v.front()); m != std::end(size_mode_s)) {
				auto v = val;
				if(!std::isdigit(v[0])) {
					if(auto m = std::find(std::begin(size_mode_s), std::end(size_mode_s), v[0]); m != std::end(size_mode_s)) {
						size_mode = static_cast<size_mode_t>(m - std::begin(size_mode_s));
						v         = v.substr(1);
						++v;
					} else {
						std::fprintf(stderr, "%s: %s: unrecognised size mode %c; must be one of: %s\n", argv[0], val, v.front(), size_mode_s);
						std::fprintf(stderr, "%s: %s: unrecognised size mode %c; must be one of: %s\n", argv[0], v, v[0], size_mode_s);
						return 1;
					}
				}

				// libstdc++ in Buster doesn't have std::from_chars for floating-point types! Very cool!
				std::string v_s{v};
				char * v_se{};
				double base = std::strtod(v_s.c_str(), &v_se);
				if(base == 0 && v_se == v_s.c_str()) {
					std::fprintf(stderr, "%s: %s: invalid size\n", argv[0], v_s.c_str());
				if(!(size = vore::parse_size<off_t>(argv[0], v)))
					return 1;
				}
				if(std::isnan(base)) {
					std::fprintf(stderr, "%s: %s: invalid size: not a number\n", argv[0], v_s.c_str());
					return 1;
				}

				for(auto p = power; p--;)
					base *= unit;
				if(base > static_cast<double>(std::numeric_limits<off_t>::max())) {
					std::fprintf(stderr, "%s: %s*(%g^%" PRIu8 "): %s\n", argv[0], v_s.c_str(), unit, power, std::strerror(ERANGE));
					return 1;
				}
				size = base;
			} break;
			case 'o':
				size_in_blocks = true;

M include/vore-chrono => include/vore-chrono +2 -2
@@ 12,8 12,8 @@
#include <string_view>


namespace {
	namespace vore {
namespace vore {
	namespace {
		std::optional<double> parse_duration(const char * self, const char * val) {
			std::string_view delay = val;


M include/vore-file => include/vore-file +2 -2
@@ 14,8 14,8 @@
#include <vector>


namespace {
	namespace vore::file {
namespace vore::file {
	namespace {
		template <bool allow_stdio>
		class fd {
		public:

M include/vore-getopt => include/vore-getopt +2 -2
@@ 8,8 8,8 @@
#include <initializer_list>


namespace {
	namespace vore::opt {
namespace vore::opt {
	namespace {
		template <std::size_t N>
		struct get_iter;


M include/vore-numeric => include/vore-numeric +2 -2
@@ 8,8 8,8 @@
#include <limits>


namespace {
	namespace vore {
namespace vore {
	namespace {
		template <class T>
		bool parse_uint(const char * val, T & out) {
			if(val[0] == '\0') {

M include/vore-optarg => include/vore-optarg +5 -3
@@ 6,8 6,8 @@
#include <string_view>  // rbegin/rend


namespace {
	namespace vore {
namespace vore {
	namespace {
		template <class T>
		struct backward {
			T & c;


@@ 21,8 21,10 @@ namespace {
		template <class T>
		backward(const T &) -> backward<const T>;
	}
}

	namespace vore::opt {
namespace vore::opt {
	namespace {
		class args {
		public:
			using iterator = const char * const *;

M include/vore-path => include/vore-path +2 -2
@@ 6,8 6,8 @@
#include <string_view>


namespace {
	namespace vore {
namespace vore {
	namespace {
		template <class CharT, class Traits>
		constexpr std::basic_string_view<CharT, Traits> basename(const std::basic_string_view<CharT, Traits> & str) noexcept {
			if(size_t idx = str.rfind('/'); idx != std::basic_string_view<CharT, Traits>::npos)

M include/vore-print => include/vore-print +2 -2
@@ 9,8 9,8 @@
#include <string_view>


namespace {
	namespace vore {
namespace vore {
	namespace {
		namespace detail {
			template <class T>
			static const char * c_str(const T * str) {

M include/vore-signal => include/vore-signal +2 -2
@@ 13,8 13,8 @@
#endif


namespace {
	namespace vore::signal {
namespace vore::signal {
	namespace {
#if __linux__
		std::optional<int> from_name(const char * name) {
			for(auto && [k, v] : std::initializer_list<std::pair<const char *, int>> {

A include/vore-size => include/vore-size +91 -0
@@ 0,0 1,91 @@
// SPDX-License-Identifier: 0BSD


#pragma once

#include <cctype>
#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <optional>
#include <string>
#include <string_view>


namespace vore {
	namespace {
		template <class T>
		std::optional<T> parse_size(const char * self, const char * val) {
			double unit = 1024;
			std::uint8_t power{};
			std::string_view v{val};
			if(v.back() == 'b' || v.back() == 'B') {
				unit = 1000;
				v    = v.substr(0, v.size() - 1);
			}
			if(!v.empty() && !std::isdigit(v.back()))
				switch(v.back()) {
					case 'y':
					case 'Y':
						power += 1;
						[[fallthrough]];
					case 'z':
					case 'Z':
						power += 1;
						[[fallthrough]];
					case 'e':
					case 'E':
						power += 1;
						[[fallthrough]];
					case 'p':
					case 'P':
						power += 1;
						[[fallthrough]];
					case 't':
					case 'T':
						power += 1;
						[[fallthrough]];
					case 'g':
					case 'G':
						power += 1;
						[[fallthrough]];
					case 'm':
					case 'M':
						power += 1;
						[[fallthrough]];
					case 'k':
					case 'K':
						power += 1;
						v = v.substr(0, v.size() - 1);
						break;
					default:
						std::fprintf(stderr, "%s: %s: unrecognised suffix %c; must be one of: KMGTPEZY\n", self, val, v.back());
						return {};
				}

			// libstdc++ in Buster doesn't have std::from_chars for floating-point types! Very cool!
			std::string v_s{v};
			char * v_se{};
			double base = std::strtod(v_s.c_str(), &v_se);
			if(base == 0 && v_se == v_s.c_str()) {
				std::fprintf(stderr, "%s: %s: invalid size\n", self, v_s.c_str());
				return {};
			}
			if(std::isnan(base)) {
				std::fprintf(stderr, "%s: %s: invalid size: not a number\n", self, v_s.c_str());
				return {};
			}

			for(auto p = power; p--;)
				base *= unit;
			if(base > static_cast<double>(std::numeric_limits<T>::max())) {
				std::fprintf(stderr, "%s: %s*(%g^%" PRIu8 "): %s\n", self, v_s.c_str(), unit, power, std::strerror(ERANGE));
				return {};
			}

			return base;
		}
	}
}

M include/vore-span => include/vore-span +2 -2
@@ 4,8 4,8 @@
#pragma once


namespace {
	namespace vore {
namespace vore {
	namespace {
		template <class T>
		struct span {
			using iterator = T *;

M include/vore-token => include/vore-token +2 -2
@@ 8,8 8,8 @@
#include <string>


namespace {
	namespace vore {
namespace vore {
	namespace {
		struct tokenise_iter {
			const char * delim;
			char * saveptr = nullptr;

M include/vore-visit => include/vore-visit +2 -2
@@ 4,8 4,8 @@
#pragma once


namespace {
	namespace vore {
namespace vore {
	namespace {
		template <class... Ts>
		struct overload : Ts... {
			using Ts::operator()...;

A lib/libstdbuf.cpp => lib/libstdbuf.cpp +49 -0
@@ 0,0 1,49 @@
// SPDX-License-Identifier: 0BSD


#include <cstdlib>
#include <initializer_list>
#include <strings.h>
#include <tuple>
#include <vore-size>


#if __APPLE__
#define LIBSTDBUF_SO "libstdbuf.dylib"
#else
#define LIBSTDBUF_SO "libstdbuf.so"
#endif


namespace vore {
	static void * libstdbuf = [] {
		for(auto [stream, env, name] : std::initializer_list<std::tuple<FILE *, const char *, const char *>>{
		        {stderr, "_STDBUF_E", "stderr"},  // stderr first since we print diagnostics there, and setvbuf() after output is UB
		        {stdout, "_STDBUF_O", "stdout"},
		        {stdin, "_STDBUF_I", "stdin"},
		    })
			if(auto o_val = std::getenv(env)) {
				auto mode = _IONBF;
				char * buf{};
				std::size_t bufsize{};
				if(!strcasecmp(o_val, "L"))
					mode = _IOLBF;
				else {
					if(auto s = parse_size<std::size_t>(LIBSTDBUF_SO, o_val)) {
						if(*s) {
							mode    = _IOFBF;
							bufsize = *s;
							buf     = static_cast<char *>(std::malloc(bufsize));  // libc will allocate something itself if it gets NULL here
						}
					} else
						continue;
				}
				if(std::setvbuf(stream, buf, mode, bufsize)) {
					std::fprintf(stderr, "%s: %s=%s failed\n", LIBSTDBUF_SO, name, o_val);  // neither glibc nor musl set errno
					std::free(buf);
				}
			}

		return nullptr;
	}();
}

M man/env.1 => man/env.1 +1 -1
@@ 149,7 149,7 @@ is searched when requested, confer
.El
.
.Sh EXIT STATUS
.Bl -tag -compact -width "all others"
.Bl -tag -compact -width "All others"
.It Sy 127
.Ar program
wasn't found.

A man/libstdbuf.3 => man/libstdbuf.3 +50 -0
@@ 0,0 1,50 @@
.\" SPDX-License-Identifier: 0BSD
.\"
.Dd
.Dt LIBSTDBUF 3
.Os
.
.Sh NAME
.Nm libstdbuf
.Nd configure I/O buffers
.Sh SYNOPSIS
.Ev _STDBUF_ Ns Oo Ar IOE Oc Ns = Ns Sy L Ns | Ns Ar size
.Ev LD_PRELOAD Ns = Ns Pa @VOREUTILS_LIB_PREFIX@ Ns Nm Ns Pa @VOREUTILS_SO@
.
.Sh DESCRIPTION
If any of
.Ev _STDBUF_I , _STDBUF_O , _STDBUF_E
environment variables are set, sets buffering for
.Sy stdin , stdout , stderr
.Xr stdio 3
streams, respectively, when loaded.
.Pp
If the value is
.Sy L Pq Sy l ,
sets line buffering; otherwise it's taken to be in the case-insensitive
.Dl Ar base Ns Oo Sy KMGTPEZY Oc Ns Op Sy B
format, where
.Em base
is a floating-point amount of bytes, which is then optionally multiplied by the relevant unit.
.Sy B
sets the unit multiplier to
.Em 1000 Pq from Em 1024 .
The buffer size
is equal to
.Em base Ns Sy \(pc Ns Em unit Ns Sy ^ Ns Em mult ,
if any, or
.Em base .
If it works out to
.Em 0 ,
buffering is disabled.
.\" Copied from truncate.1, TODO: clumsy
.
.Sh DIAGNOSTICS
Diagnostics are issued to the standard error stream under the
.Nm Ns Pa @VOREUTILS_SO@
identifier if the buffer specifier is in an invalid format or the buffering couldn't be set.
.
.Sh SEE ALSO
.Xr stdbuf 1 ,
.Xr setvbuf 3 ,
.Xr stdio 3

A man/stdbuf.1 => man/stdbuf.1 +107 -0
@@ 0,0 1,107 @@
.\" SPDX-License-Identifier: 0BSD
.\"
.Dd
.Dt STDBUF 1
.Os
.
.Sh NAME
.Nm stdbuf
.Nd configure I/O buffers
.Sh SYNOPSIS
.Nm
.Op Fl i Ar size
.Op Fl o Sy L Ns | Ns Ar size
.Op Fl e Sy L Ns | Ns Ar size
.Ar program
.Oo Ar args Oc Ns …
.
.Sh DESCRIPTION
Configures
.Xr libstdbuf 3
then executes
.Ar program args
with it loaded.
.Pp
.Ar size
is in the case-insensitive format:
.Dl Ar base Ns Oo Sy KMGTPEZY Oc Ns Op Sy B
Where
.Em base
is a floating-point amount of bytes, which is then optionally multiplied by the relevant unit.
.Sy B
sets the unit multiplier to
.Em 1000 Pq from Em 1024 .
.Ar size
is equal to
.Em base Ns Sy \(pc Ns Em unit Ns Sy ^ Ns Em mult ,
if any, or
.Em base .
.\" Copied from truncate.1, TODO: clumsy
.Pp
If
.Ar size
works out to
.Em 0 ,
the stream is unbuffered,
If
.Sy L Pq Sy l
is specified, the stream is line-buffered.
This is forbidden for the standard input stream as it's meaningless.
.
.Sh OPTIONS
.Bl -tag -compact -width "-i, --ignore-garbage"
.It Fl i , -input Ns = Ns Ar size
Configures buffering for the standard input stream.
.It Fl o , -output Ns = Ns Sy L Ns | Ns Ar size
Configures buffering for the standard output stream.
.It Fl e , -error Ns = Ns Sy L Ns | Ns Ar size
Configures buffering for the standard error stream.
.El
.
.Sh EXIT STATUS
.Bl -tag -compact -width "All others"
.It Sy 127
.Ar program
wasn't found.
.It Sy 126
.Ar program
exists, but couldn't be executed for a different reason.
.It Sy 125
an error occurred in
.Nm .
.It All others
returned by
.Ar program .
.El
.
.Sh EXAMPLES
Normally,
.Nm grep
would buffer incoming CIFS connections because the output leads into a pipe.
By employing
.Nm ,
each connection is printed as soon as it lands in the log:
.Dl Nm tail Fl f Pa /var/log/messages | Nm Fl o Ns Ar L Nm grep Li 'DPT=445' | Nm cat Fl n
.
.Sh SEE ALSO
.Xr libstdbuf 3
.
.Sh HISTORY
Originates from the GNU system, also available in
.Fx 8.4 .
.Pp
The GNU system disallows lowercase
.Sy L ,
.Ar size
with
.Sy B
but without a unit, as well as lowercase
.Sy B ,
and only supports integer
.Ar base Ns s .
.Pp
.Fx
supports a simplified
.Sy 0 Ns | Ns Sy L Ns | Ns Sy B Ns | Ns Ar size Ns Op Sy kmG
buffer size spec
.Pq where Sy B No means Dv BUFSIZ .

M man/truncate.1 => man/truncate.1 +4 -4
@@ 36,17 36,17 @@ With
is in the case-insensitive format:
.Dl Oo Sy +-<>/% Oc Ns Ar base Ns Oo Sy KMGTPEZY Oc Ns Op Sy B
Where
.Ar base
.Em base
is a floating-point amount of bytes, which is then optionally multiplied by the relevant unit.
.Sy B
sets the unit multiplier to
.Em 1000 Pq from Em 1024 .
.Ar size
is equal to
.Em base Ns Sy \(pc Ns Ar unit Ns Sy ^ Ns Ar mult ,
.Em base Ns Sy \(pc Ns Em unit Ns Sy ^ Ns Em mult ,
if any, or
.Em base .
.\" TODO: this is clumsy
.\" TODO: this is clumsy, copied into stdbuf.1, libstdbuf.3
.Pp
If
.Fl o


@@ 171,7 171,7 @@ filesystem created with
.Xr stat 2
.
.Sh HISTORY
Оriginates from the GNU system, also available on
Оriginates from the GNU system, also available in
.Fx 4.2 .
.Pp
The GNU system disallows

M voreutils.sublime-project => voreutils.sublime-project +1 -1
@@ 17,7 17,7 @@
			"name": "Source",
			"path": ".",
			"file_include_patterns": ["*.c", "*.cpp", "*.h", "*.hpp", "vore-*", "README*", "Makefile*", "aliases"],
			"folder_include_patterns": ["cmd", "include"]
			"folder_include_patterns": ["cmd", "lib", "include"]
		},
		{
			"follow_symlinks": true,