~nabijaczleweli/voreutils

5c3e7885e5d9d8fa8b0eebe7e5fd37de42e17ebf — наб 4 months ago 520c52d
Add tac. Add tests
136 files changed, 1261 insertions(+), 8 deletions(-)

M .builds/freebsd-latest.yml
M .builds/openbsd-latest.yml
M .builds/sid.yml
M .gitignore
M Makefile
M README.md
A cmd/aliases
A cmd/tac.cpp
M cmd/true.cpp
A include/vore-file
A include/vore-getopt
M include/vore-id.h
A include/vore-optarg
R include/{vore-string_view => vore-path}
A include/vore-visit
A man/tac.1
A tests/tac/data/seb
A tests/tac/data/seb.d/seb
A tests/tac/data/seb.d/seb-b
A tests/tac/data/seb.d/seb-br
A tests/tac/data/seb.d/seb-brs'  '
A tests/tac/data/seb.d/seb-brs' '
A tests/tac/data/seb.d/seb-brs''
A tests/tac/data/seb.d/seb-brs''.error
A tests/tac/data/seb.d/seb-brs'.'
A tests/tac/data/seb.d/seb-brs'..'
A tests/tac/data/seb.d/seb-brs'\s'
A tests/tac/data/seb.d/seb-brs'a $'
A tests/tac/data/seb.d/seb-brs'a '
A tests/tac/data/seb.d/seb-bs'  '
A tests/tac/data/seb.d/seb-bs' '
A tests/tac/data/seb.d/seb-bs''
A tests/tac/data/seb.d/seb-bs'.'
A tests/tac/data/seb.d/seb-bs'..'
A tests/tac/data/seb.d/seb-bs'\s'
A tests/tac/data/seb.d/seb-bs'a $'
A tests/tac/data/seb.d/seb-bs'a '
A tests/tac/data/seb.d/seb-r
A tests/tac/data/seb.d/seb-rs'  '
A tests/tac/data/seb.d/seb-rs' '
A tests/tac/data/seb.d/seb-rs''
A tests/tac/data/seb.d/seb-rs''.error
A tests/tac/data/seb.d/seb-rs'.'
A tests/tac/data/seb.d/seb-rs'..'
A tests/tac/data/seb.d/seb-rs'\s'
A tests/tac/data/seb.d/seb-rs'a $'
A tests/tac/data/seb.d/seb-rs'a '
A tests/tac/data/seb.d/seb-s'  '
A tests/tac/data/seb.d/seb-s' '
A tests/tac/data/seb.d/seb-s''
A tests/tac/data/seb.d/seb-s'.'
A tests/tac/data/seb.d/seb-s'..'
A tests/tac/data/seb.d/seb-s'\s'
A tests/tac/data/seb.d/seb-s'a $'
A tests/tac/data/seb.d/seb-s'a '
A tests/tac/data/sep
A tests/tac/data/sep.d/sep
A tests/tac/data/sep.d/sep-b
A tests/tac/data/sep.d/sep-br
A tests/tac/data/sep.d/sep-brs'  '
A tests/tac/data/sep.d/sep-brs' '
A tests/tac/data/sep.d/sep-brs''
A tests/tac/data/sep.d/sep-brs''.error
A tests/tac/data/sep.d/sep-brs'.'
A tests/tac/data/sep.d/sep-brs'..'
A tests/tac/data/sep.d/sep-brs'\s'
A tests/tac/data/sep.d/sep-brs'a $'
A tests/tac/data/sep.d/sep-brs'a '
A tests/tac/data/sep.d/sep-bs'  '
A tests/tac/data/sep.d/sep-bs' '
A tests/tac/data/sep.d/sep-bs''
A tests/tac/data/sep.d/sep-bs'.'
A tests/tac/data/sep.d/sep-bs'..'
A tests/tac/data/sep.d/sep-bs'\s'
A tests/tac/data/sep.d/sep-bs'a $'
A tests/tac/data/sep.d/sep-bs'a '
A tests/tac/data/sep.d/sep-r
A tests/tac/data/sep.d/sep-rs'  '
A tests/tac/data/sep.d/sep-rs' '
A tests/tac/data/sep.d/sep-rs''
A tests/tac/data/sep.d/sep-rs''.error
A tests/tac/data/sep.d/sep-rs'.'
A tests/tac/data/sep.d/sep-rs'..'
A tests/tac/data/sep.d/sep-rs'\s'
A tests/tac/data/sep.d/sep-rs'a $'
A tests/tac/data/sep.d/sep-rs'a '
A tests/tac/data/sep.d/sep-s'  '
A tests/tac/data/sep.d/sep-s' '
A tests/tac/data/sep.d/sep-s''
A tests/tac/data/sep.d/sep-s'.'
A tests/tac/data/sep.d/sep-s'..'
A tests/tac/data/sep.d/sep-s'\s'
A tests/tac/data/sep.d/sep-s'a $'
A tests/tac/data/sep.d/sep-s'a '
A tests/tac/data/true.cpp
A tests/tac/data/true.cpp.d/true.cpp
A tests/tac/data/true.cpp.d/true.cpp-b
A tests/tac/data/true.cpp.d/true.cpp-br
A tests/tac/data/true.cpp.d/true.cpp-brs'  '
A tests/tac/data/true.cpp.d/true.cpp-brs' '
A tests/tac/data/true.cpp.d/true.cpp-brs''
A tests/tac/data/true.cpp.d/true.cpp-brs''.error
A tests/tac/data/true.cpp.d/true.cpp-brs'.'
A tests/tac/data/true.cpp.d/true.cpp-brs'..'
A tests/tac/data/true.cpp.d/true.cpp-brs'\s'
A tests/tac/data/true.cpp.d/true.cpp-brs'a $'
A tests/tac/data/true.cpp.d/true.cpp-brs'a '
A tests/tac/data/true.cpp.d/true.cpp-bs'  '
A tests/tac/data/true.cpp.d/true.cpp-bs' '
A tests/tac/data/true.cpp.d/true.cpp-bs''
A tests/tac/data/true.cpp.d/true.cpp-bs'.'
A tests/tac/data/true.cpp.d/true.cpp-bs'..'
A tests/tac/data/true.cpp.d/true.cpp-bs'\s'
A tests/tac/data/true.cpp.d/true.cpp-bs'a $'
A tests/tac/data/true.cpp.d/true.cpp-bs'a '
A tests/tac/data/true.cpp.d/true.cpp-r
A tests/tac/data/true.cpp.d/true.cpp-rs'  '
A tests/tac/data/true.cpp.d/true.cpp-rs' '
A tests/tac/data/true.cpp.d/true.cpp-rs''
A tests/tac/data/true.cpp.d/true.cpp-rs''.error
A tests/tac/data/true.cpp.d/true.cpp-rs'.'
A tests/tac/data/true.cpp.d/true.cpp-rs'..'
A tests/tac/data/true.cpp.d/true.cpp-rs'\s'
A tests/tac/data/true.cpp.d/true.cpp-rs'a $'
A tests/tac/data/true.cpp.d/true.cpp-rs'a '
A tests/tac/data/true.cpp.d/true.cpp-s'  '
A tests/tac/data/true.cpp.d/true.cpp-s' '
A tests/tac/data/true.cpp.d/true.cpp-s''
A tests/tac/data/true.cpp.d/true.cpp-s'.'
A tests/tac/data/true.cpp.d/true.cpp-s'..'
A tests/tac/data/true.cpp.d/true.cpp-s'\s'
A tests/tac/data/true.cpp.d/true.cpp-s'a $'
A tests/tac/data/true.cpp.d/true.cpp-s'a '
A tests/tac/test
A tests/true
M voreutils.sublime-project
M .builds/freebsd-latest.yml => .builds/freebsd-latest.yml +1 -0
@@ 5,3 5,4 @@ tasks:
  - build: |
      cd voreutils
      gmake
      gmake test

M .builds/openbsd-latest.yml => .builds/openbsd-latest.yml +1 -0
@@ 6,3 6,4 @@ tasks:
  - build: |
      cd voreutils
      gmake CXX=c++ LTO=n
      gmake CXX=c++ LTO=n test

M .builds/sid.yml => .builds/sid.yml +2 -0
@@ 11,10 11,12 @@ tasks:
  - build-gcc: |
      cd voreutils
      make
      make test
      make clean
  - build-clang: |
      cd voreutils
      CC=clang CXX=clang++ make
      CC=clang CXX=clang++ make test
      CC=clang CXX=clang++ make clean
  - push-manpages: |
      git -C voreutils/ worktree add ../voreutils-man man

M .gitignore => .gitignore +2 -0
@@ 9,6 9,8 @@
!*.sublime-project
!include
!include/**
!tests
!tests/**
!man
!man/**
!cmd

M Makefile => Makefile +20 -4
@@ 7,6 7,7 @@ CPP ?= cpp
MANDOC ?= mandoc
AWK ?= awk
SED ?= sed
SYMLINK ?= n
LTO ?= y
VOREUTILS_VERSION ?= $(shell git describe --always)
VOREUTILS_DATE ?= $(shell date $(shell date --version > /dev/null 2>&1 && echo "--date @" || echo "-r ")$(shell git log -1 --no-show-signature --format=%at) +"%B %e, %Y")


@@ 56,16 57,19 @@ else
	CXXSPECIFICLD :=
endif

ifeq "$(SYMLINK)" "y"
SYMFLAG := s
endif

CCAR := -g -O3 -pipe -Wall -Wextra $(CXXSPECIFICCC) $(CFLAGS)
LDAR := $(AS_NEEDED) -L$(OUTDIR) $(CXXSPECIFICLD) $(LDFLAGS)
CPPAR := -Iinclude/ -DVOREUTILS_VERSION='"$(VOREUTILS_VERSION)"' -DVOREUTILS_DATE='"$(VOREUTILS_DATE)"' -include vore-id.h $(CPPFLAGS)
BINARIES := $(wildcard cmd/*)
BINARIES := $(filter-out cmd/aliases,$(wildcard cmd/*))
INCLUDES := $(wildcard include/*)
MANPAGES := $(wildcard man/*.[18])


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

all : binaries manpages htmlpages


@@ 73,7 77,13 @@ allpages : manpages htmlpages
clean:
	rm -rf $(OUTDIR) $(OBJDIR)

binaries : $(patsubst %.c,%,$(patsubst %.cpp,%,$(patsubst cmd/%,$(CMDDIR)%,$(BINARIES))))
test : binaries
	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



@@ 88,10 98,16 @@ $(OBJDIR)man/aliases : man/aliases
		sec2 = substr($$2, length($$2));                        \
		rel = (sec1 == sec2) ? "" : "../man" sec1 "/";          \
		print "(mkdir -p man" sec1 " man" sec2 " && cd man" sec2 " && set -x && ln -fs " rel $$1 "$$suff " $$2 "$$suff)"; \
	}' man/aliases > $@
	}' $^ > $@
	{ cd $(MANDIR)     && suff=        sh; } < $@
	{ cd $(HTMLMANDIR) && suff=".html" sh; } < $@

# This ordering is suboptimal
$(OBJDIR)aliases : cmd/aliases $(patsubst %.c,%,$(patsubst %.cpp,%,$(patsubst cmd/%,$(CMDDIR)%,$(BINARIES))))
	@mkdir -p $(dir $@) $(CMDDIR)
	$(AWK) '!/^$$/ {print "ln -f$(SYMFLAG) " $$1 " " $$2}' $< > $@
	{ cd $(CMDDIR) && sh -x; } < $@

$(OBJDIR)man/% : man/%
	@mkdir -p $(dir $@)
	$(SED) 's/^\.Dd/.Dd $(VOREUTILS_DATE)/' $< > $@

M README.md => README.md +3 -2
@@ 90,7 90,7 @@ GNU coreutils provide the following 105 binaries, according to `dpkg -L coreutil
  [ ] /usr/bin/stat
  [ ] /usr/bin/stdbuf
  [ ] /usr/bin/sum
  [ ] /usr/bin/tac
  [x] /usr/bin/tac
  [ ] /usr/bin/tail
  [ ] /usr/bin/tee
  [ ] /usr/bin/test


@@ 116,7 116,8 @@ You'll need a non-ancient C[++] toolchain, a POSIX AWK, GNU make, and mandoc (op
Run GNU make. See the head of the [Makefile](Makefile) for tunables,
notably `VOREUTILS_{VERSION,DATE}`, which are derived from git HEAD,
`OUTDIR` (and `{CMD,MAN,HTMLMAN}DIR`) where artifacts land, and
`OBJDIR` where intermediate objects land; these can all be set independently.
`OBJDIR` where intermediate objects land; these can all be set independently,
`SYMLINK`, if set to "y", will link binary altnames together symbolically.

## Organisation
Who knows yet!

A cmd/aliases => cmd/aliases +1 -0
@@ 0,0 1,1 @@
true  false

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


#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <getopt.h>
#include <numeric>
#include <optional>
#include <regex.h>
#include <string_view>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utility>
#include <variant>
#include <vector>
#include <vore-file>
#include <vore-getopt>
#include <vore-optarg>
#include <vore-visit>

using namespace std::literals;


enum class separator_position : bool { before, after };

const char * default_files[] = {"-", nullptr};


int main(int argc, char * const * argv) {
	std::variant<std::string_view, regex_t, std::monostate> separator = "\n"sv;
	auto is_regex                                                     = false;
	auto position                                                     = separator_position::after;

	for(auto && [arg, val] : vore::opt::get{argc,
	                                        argv,
	                                        "s:rb",
	                                        {{"separator", required_argument, nullptr, 's'},  //
	                                         {"regex", no_argument, nullptr, 'r'},            //
	                                         {"before", no_argument, nullptr, 'b'}}})
		switch(arg) {
			case 's':
				separator = val;
				break;
			case 'r':
				is_regex = true;
				break;
			case 'b':
				position = separator_position::before;
				break;
			default:
				std::fprintf(stderr, "usage: %s [-rb] [-s eparator] [file]...\n", argv[0]);
				return 1;
		}

	if(std::get<std::string_view>(separator).empty()) {
		if(is_regex) {
			std::fprintf(stderr, "%s: invalid regular expression: %s\n", argv[0], std::strerror(ENODATA));
			return 1;
		} else
			separator = std::monostate{};
	}

	if(is_regex) {
		regex_t r;
		if(int err = regcomp(&r, std::get<std::string_view>(separator).data(), REG_NEWLINE); err) {
			char errbuf[256];
			regerror(err, &r, errbuf, sizeof(errbuf));
			std::fprintf(stderr, "%s: invalid regular expression: %s\n", argv[0], errbuf);
			return 1;
		}
		separator = std::move(r);
	}


	std::vector<char> filbuf;
	std::vector<std::pair<std::string_view, std::string_view>> shards;
	for(auto file : vore::opt::args(*(argv + optind) ? (argv + optind) : default_files)) {
		vore::file::mapping mapped;
		{
			vore::file::fd<true> fd{file, O_RDONLY | O_CLOEXEC};
			if(fd == -1) {
				std::fprintf(stderr, "%s: couldn't open %s: %s\n", argv[0], file, std::strerror(errno));
				continue;
			}

			struct stat sb;
			(void)fstat(fd, &sb);

			if(sb.st_size) {
				mapped = {nullptr, static_cast<std::size_t>(sb.st_size) + 1, PROT_READ, MAP_PRIVATE, fd, 0};
				if(!mapped)
					goto done;
			}

			if(vore::file::slurp(filbuf, fd))
				std::fprintf(stderr, "%s: read %s: %s\n", argv[0], file, std::strerror(errno));
			filbuf.emplace_back('\0');  // for regexec(), like the overmap above

		done:;
		}


		bool ever_matched = false;
		for(auto fildat = mapped ? std::string_view{filbuf.data(), filbuf.size() - 1} : mapped->substr(0, mapped->size() - 1);;) {
			for(std::size_t substart = fildat.size() - 1; substart != static_cast<std::size_t>(-1); --substart) {
				auto matched = std::visit(
				    vore::overload{[&](const std::string_view & sep) -> std::optional<std::pair<std::size_t, std::size_t>> {
					                   if(auto idx = fildat.substr(substart).rfind(sep, 0) /* starts_with() – we're going backward */; idx != std::string_view::npos) {
						                   return {{idx, idx + sep.size()}};
					                   } else
						                   return {};
				                   },
				                   [&](const regex_t & sep) -> std::optional<std::pair<std::size_t, std::size_t>> {
					                   regmatch_t mch;
					                   int fl = 0;
					                   if(substart != 0)
						                   fl |= REG_NOTBOL;
					                   if(ever_matched)
						                   fl |= REG_NOTEOL;
					                   if(regexec(&sep, fildat.data() + substart, 1, &mch, fl) == 0 && static_cast<std::size_t>(mch.rm_eo) <= fildat.size() - substart) {
						                   return {{mch.rm_so, mch.rm_eo}};
					                   } else
						                   return {};
				                   },
				                   [&](const std::monostate &) -> std::optional<std::pair<std::size_t, std::size_t>> { return {}; }},
				    separator);

				if(matched) {
					auto [start, end] = *matched;
					start += substart;
					end += substart;
					shards.emplace_back(fildat.substr(end), fildat.substr(start, end - start));
					fildat       = fildat.substr(0, substart);
					ever_matched = true;
					goto continue2;
				}
			}

			shards.emplace_back(fildat, "");
			break;

		continue2:;
		}

		switch(position) {
			case separator_position::before:
				break;
			case separator_position::after:
				for(std::size_t i = shards.size() - 1; i > 0; --i)
					std::swap(shards[i - 1].second, shards[i].second);
				break;
		}

		for(auto && [seg, sep] : shards)
			switch(position) {
				case separator_position::before:
					std::swap(seg, sep);
					[[fallthrough]];
				case separator_position::after:
					std::fwrite(seg.data(), 1, seg.size(), stdout);
					std::fwrite(sep.data(), 1, sep.size(), stdout);
					break;
			}

		filbuf.clear();
		shards.clear();
	}
}

M cmd/true.cpp => cmd/true.cpp +1 -1
@@ 2,7 2,7 @@


#include <string_view>
#include <vore-string_view>
#include <vore-path>


int main(int, char ** argv) {

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


#pragma once


#include <errno.h>
#include <fcntl.h>
#include <string_view>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>


namespace vore::file {
	template <bool allow_stdio>
	class fd {
	public:
		fd(const char * path, int flags) noexcept {
			using namespace std::literals;

			if(allow_stdio && path == "-"sv) {
				switch(flags & (O_RDONLY | O_WRONLY)) {
					case O_RDONLY:
						this->desc = 0;
						return;
					case O_WRONLY:
						this->desc = 1;
						return;
					default:
						errno = EINVAL;
						return;
				}
			}

			while((this->desc = open(path, flags)) == -1 && errno == EINTR)
				;
			opened = this->desc != -1;
		}

		fd(const fd &) = delete;
		constexpr fd(fd && oth) noexcept : desc(oth.desc), opened(oth.opened) { oth.opened = false; }

		~fd() {
			if(opened)
				close(this->desc);
		}

		constexpr operator int() const noexcept { return this->desc; }

	private:
		int desc    = -1;
		bool opened = false;
	};


	template <class C = char>
	class mapping {
	public:
		constexpr mapping() noexcept {}

		mapping(void * addr, size_t length, int prot, int flags, int fd, off_t offset) noexcept {
			void * ret = mmap(addr, length, prot, flags, fd, offset);
			if(ret != MAP_FAILED) {
				map    = {static_cast<C *>(ret), length};
				opened = true;
			}
		}

		mapping(const mapping &) = delete;
		constexpr mapping(mapping && oth) noexcept : map(oth.map), opened(oth.opened) { oth.opened = false; }

		constexpr mapping& operator=(mapping && oth) noexcept {
			this->map     = oth.map;
			this->opened  = oth.opened;
			oth.opened    = false;
			return *this;
		}

		~mapping() {
			if(opened)
				munmap(const_cast<C *>(this->map.data()), this->map.size());
		}

		constexpr operator bool() const noexcept { return !this->map.empty(); }
		constexpr operator std::basic_string_view<C>() const noexcept { return this->map; }
		constexpr const std::basic_string_view<C> & operator*() const noexcept { return this->map; }
		constexpr const std::basic_string_view<C> * operator->() const noexcept { return &this->map; }


	private:
		std::basic_string_view<C> map = {};
		bool opened                   = false;
	};


	template <class B>
	int slurp(std::vector<B> & obuf, int fd) {
		static_assert(sizeof(B) == 1);

		errno = 0;
		for(B buf[4096]; ssize_t ret = read(fd, buf, sizeof(buf));) {
			if(ret == -1) {
				if(errno == EINTR)
					continue;
				else
					break;
			}

			if(ret == 0)
				break;

			obuf.insert(std::end(obuf), buf, buf + ret);
		}

		return errno;
	}
}

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


#pragma once

#include <cstdint>
#include <getopt.h>
#include <initializer_list>
#include <iterator>


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

	struct get_opt {
		int ret       = -1;
		char * optarg = nullptr;
	};


	// TODO: this may want to set/reset optind?
	template <std::size_t N>
	struct get {
		using iterator = get_iter<N>;


		int argc;
		char * const * argv;

		const char * optstring;
		struct ::option options[N + 1];


		constexpr iterator begin() const noexcept { return ++iterator{this}; }
		constexpr iterator end() const noexcept { return {}; }
	};

	template <std::size_t N>
	get(int, const char * const *, const char *, const struct ::option (&)[N]) -> get<N>;


	template <std::size_t N>
	struct get_iter {
		const get<N> * opts;
		get_opt last = {};


		get_iter & operator++() noexcept {
			this->last.ret    = getopt_long(opts->argc, opts->argv, opts->optstring, opts->options, nullptr);
			this->last.optarg = optarg;
			return *this;
		}

		get_iter operator++(int) noexcept {
			const auto ret = *this;
			++(*this);
			return ret;
		}

		constexpr bool operator==(const get_iter & rhs) const noexcept { return this->last.ret == -1 && rhs.opts == nullptr; }
		constexpr bool operator!=(const get_iter & rhs) const noexcept { return !(*this == rhs); }

		constexpr const get_opt & operator*() const noexcept { return this->last; }
	};
}

M include/vore-id.h => include/vore-id.h +2 -0
@@ 1,5 1,7 @@
// SPDX-License-Identifier: 0BSD

#pragma once


extern const char vore_id[];
const char vore_id[] __attribute__((section(".note.version"))) = "voreutils " VOREUTILS_VERSION " (" VOREUTILS_DATE ")";

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


#pragma once

#include <iterator>


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

		constexpr auto begin() const noexcept { return std::rbegin(c); }
		constexpr auto end() const noexcept { return std::rend(c); }
	};

	template <class T>
	backward(T &) -> backward<T>;
	template <class T>
	backward(const T &) -> backward<const T>;
}

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

		constexpr args(const char * const * argv) noexcept : b(argv), e(argv) {
			while(*e)
				++e;
		}

		constexpr args(const args &) noexcept = default;
		constexpr args(args &&) noexcept      = default;

		constexpr iterator begin() const noexcept { return b; }
		constexpr iterator end() const noexcept { return e; }

		constexpr auto rbegin() const noexcept { return std::reverse_iterator{e}; }
		constexpr auto rend() const noexcept { return std::reverse_iterator{b}; }

	private:
		const char * const * b;
		const char * const * e;
	};
}

R include/vore-string_view => include/vore-path +2 -0
@@ 1,6 1,8 @@
// SPDX-License-Identifier: 0BSD


#pragma once

#include <string_view>



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


#pragma once


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

A man/tac.1 => man/tac.1 +35 -0
@@ 0,0 1,35 @@
.\" SPDX-License-Identifier: 0BSD
.\"
.Dd
.Dt TAC 1
.Os
.
.Sh NAME
.Nm tac
.Nd concatenate reversed files
.Sh SYNOPSIS
.Nm
.Op Fl rb
.Op Fl s Ar eparator
.Oo Ar file Oc Ns …
.
.Sh DESCRIPTION
Read each
.Ar file Pq or standard input if Dq - ,
split it on the separator expression, matching from the end, and print it back out, starting with the final match.
.Ar file No defaults to Dq - .
.
.Sh OPTIONS
.Bl -tag -compact -width "-s, --separator=sep"
.It Fl s , -separator Ns = Ns Ar sep
Separate with
.Ar sep
instead of a single newline.
An empty separator matches nothing
.Pq and the invocation is equivalent to Nm cat .
.It Fl r , -regex
Treat separator as regular expression.
An empty separator is refused.
.It Fl b , -before
Print separator before its line.
.El

A tests/tac/data/seb => tests/tac/data/seb +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb => tests/tac/data/seb.d/seb +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-b => tests/tac/data/seb.d/seb-b +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-br => tests/tac/data/seb.d/seb-br +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'  ' => tests/tac/data/seb.d/seb-brs'  ' +1 -0
@@ 0,0 1,1 @@
  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a1
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs' ' => tests/tac/data/seb.d/seb-brs' ' +1 -0
@@ 0,0 1,1 @@
 a a16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a 1
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'' => tests/tac/data/seb.d/seb-brs'' +0 -0
A tests/tac/data/seb.d/seb-brs''.error => tests/tac/data/seb.d/seb-brs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/seb.d/seb-brs'.' => tests/tac/data/seb.d/seb-brs'.' +1 -0
@@ 0,0 1,1 @@
a 61a 51a 41a 31a 21a 11a 01a 9a 8a a a 5a 4 a  1
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'..' => tests/tac/data/seb.d/seb-brs'..' +1 -0
@@ 0,0 1,1 @@
 a16 a15 a14 a13 a12 a11 a10 aa98  a a aa54 a   1
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'\s' => tests/tac/data/seb.d/seb-brs'\s' +1 -0
@@ 0,0 1,1 @@
 a a16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a 1
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'a $' => tests/tac/data/seb.d/seb-brs'a $' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-brs'a ' => tests/tac/data/seb.d/seb-brs'a ' +1 -0
@@ 0,0 1,1 @@
a a8 a9 a10 a11 a12 a13 a14 a15 a16 aa a 4 a5 1  
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'  ' => tests/tac/data/seb.d/seb-bs'  ' +1 -0
@@ 0,0 1,1 @@
  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a1
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs' ' => tests/tac/data/seb.d/seb-bs' ' +1 -0
@@ 0,0 1,1 @@
 a a16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a 1
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'' => tests/tac/data/seb.d/seb-bs'' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'.' => tests/tac/data/seb.d/seb-bs'.' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'..' => tests/tac/data/seb.d/seb-bs'..' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'\s' => tests/tac/data/seb.d/seb-bs'\s' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'a $' => tests/tac/data/seb.d/seb-bs'a $' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-bs'a ' => tests/tac/data/seb.d/seb-bs'a ' +1 -0
@@ 0,0 1,1 @@
a a8 a9 a10 a11 a12 a13 a14 a15 a16 aa a 4 a5 1  
\ No newline at end of file

A tests/tac/data/seb.d/seb-r => tests/tac/data/seb.d/seb-r +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'  ' => tests/tac/data/seb.d/seb-rs'  ' +1 -0
@@ 0,0 1,1 @@
a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a1  
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs' ' => tests/tac/data/seb.d/seb-rs' ' +1 -0
@@ 0,0 1,1 @@
aa16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a  1 
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'' => tests/tac/data/seb.d/seb-rs'' +0 -0
A tests/tac/data/seb.d/seb-rs''.error => tests/tac/data/seb.d/seb-rs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/seb.d/seb-rs'.' => tests/tac/data/seb.d/seb-rs'.' +1 -0
@@ 0,0 1,1 @@
a 61a 51a 41a 31a 21a 11a 01a 9a 8a a a 5a 4 a  1
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'..' => tests/tac/data/seb.d/seb-rs'..' +1 -0
@@ 0,0 1,1 @@
 a16 a15 a14 a13 a12 a11 a10 aa98  a a aa54 a 1  
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'\s' => tests/tac/data/seb.d/seb-rs'\s' +1 -0
@@ 0,0 1,1 @@
aa16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a  1 
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'a $' => tests/tac/data/seb.d/seb-rs'a $' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-rs'a ' => tests/tac/data/seb.d/seb-rs'a ' +1 -0
@@ 0,0 1,1 @@
a8 a9 a10 a11 a12 a13 a14 a15 a16 aa 4 a5 a 1  a 
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'  ' => tests/tac/data/seb.d/seb-s'  ' +1 -0
@@ 0,0 1,1 @@
a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a1  
\ No newline at end of file

A tests/tac/data/seb.d/seb-s' ' => tests/tac/data/seb.d/seb-s' ' +1 -0
@@ 0,0 1,1 @@
aa16 a15 a14 a13 a12 a11 a10 a9 a8 a a a5 4 a  1 
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'' => tests/tac/data/seb.d/seb-s'' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'.' => tests/tac/data/seb.d/seb-s'.' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'..' => tests/tac/data/seb.d/seb-s'..' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'\s' => tests/tac/data/seb.d/seb-s'\s' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'a $' => tests/tac/data/seb.d/seb-s'a $' +1 -0
@@ 0,0 1,1 @@
1  a 4 a5 a a a8 a9 a10 a11 a12 a13 a14 a15 a16 a
\ No newline at end of file

A tests/tac/data/seb.d/seb-s'a ' => tests/tac/data/seb.d/seb-s'a ' +1 -0
@@ 0,0 1,1 @@
a8 a9 a10 a11 a12 a13 a14 a15 a16 aa 4 a5 a 1  a 
\ No newline at end of file

A tests/tac/data/sep => tests/tac/data/sep +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep => tests/tac/data/sep.d/sep +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-b => tests/tac/data/sep.d/sep-b +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-br => tests/tac/data/sep.d/sep-br +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'  ' => tests/tac/data/sep.d/sep-brs'  ' +1 -0
@@ 0,0 1,1 @@
  8 9 10 11 12 13 14 15 16   4 5 1 
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs' ' => tests/tac/data/sep.d/sep-brs' ' +1 -0
@@ 0,0 1,1 @@
  16 15 14 13 12 11 10 9 8   5 4  1
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'' => tests/tac/data/sep.d/sep-brs'' +0 -0
A tests/tac/data/sep.d/sep-brs''.error => tests/tac/data/sep.d/sep-brs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/sep.d/sep-brs'.' => tests/tac/data/sep.d/sep-brs'.' +1 -0
@@ 0,0 1,1 @@
 61 51 41 31 21 11 01 9 8   5 4   1
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'..' => tests/tac/data/sep.d/sep-brs'..' +1 -0
@@ 0,0 1,1 @@
6  1154  1132  1110  1 9 8   5 4  1
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'\s' => tests/tac/data/sep.d/sep-brs'\s' +1 -0
@@ 0,0 1,1 @@
  16 15 14 13 12 11 10 9 8   5 4  1
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'a $' => tests/tac/data/sep.d/sep-brs'a $' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-brs'a ' => tests/tac/data/sep.d/sep-brs'a ' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'  ' => tests/tac/data/sep.d/sep-bs'  ' +1 -0
@@ 0,0 1,1 @@
  8 9 10 11 12 13 14 15 16   4 5 1 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs' ' => tests/tac/data/sep.d/sep-bs' ' +1 -0
@@ 0,0 1,1 @@
  16 15 14 13 12 11 10 9 8   5 4  1
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'' => tests/tac/data/sep.d/sep-bs'' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'.' => tests/tac/data/sep.d/sep-bs'.' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'..' => tests/tac/data/sep.d/sep-bs'..' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'\s' => tests/tac/data/sep.d/sep-bs'\s' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'a $' => tests/tac/data/sep.d/sep-bs'a $' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-bs'a ' => tests/tac/data/sep.d/sep-bs'a ' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-r => tests/tac/data/sep.d/sep-r +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'  ' => tests/tac/data/sep.d/sep-rs'  ' +1 -0
@@ 0,0 1,1 @@
8 9 10 11 12 13 14 15 16 4 5   1   
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs' ' => tests/tac/data/sep.d/sep-rs' ' +1 -0
@@ 0,0 1,1 @@
16 15 14 13 12 11 10 9 8   5 4   1 
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'' => tests/tac/data/sep.d/sep-rs'' +0 -0
A tests/tac/data/sep.d/sep-rs''.error => tests/tac/data/sep.d/sep-rs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/sep.d/sep-rs'.' => tests/tac/data/sep.d/sep-rs'.' +1 -0
@@ 0,0 1,1 @@
 61 51 41 31 21 11 01 9 8   5 4   1
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'..' => tests/tac/data/sep.d/sep-rs'..' +1 -0
@@ 0,0 1,1 @@
6  1154  1132  1110  1 9 8   5 41  
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'\s' => tests/tac/data/sep.d/sep-rs'\s' +1 -0
@@ 0,0 1,1 @@
16 15 14 13 12 11 10 9 8   5 4   1 
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'a $' => tests/tac/data/sep.d/sep-rs'a $' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-rs'a ' => tests/tac/data/sep.d/sep-rs'a ' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'  ' => tests/tac/data/sep.d/sep-s'  ' +1 -0
@@ 0,0 1,1 @@
8 9 10 11 12 13 14 15 16 4 5   1   
\ No newline at end of file

A tests/tac/data/sep.d/sep-s' ' => tests/tac/data/sep.d/sep-s' ' +1 -0
@@ 0,0 1,1 @@
16 15 14 13 12 11 10 9 8   5 4   1 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'' => tests/tac/data/sep.d/sep-s'' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'.' => tests/tac/data/sep.d/sep-s'.' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'..' => tests/tac/data/sep.d/sep-s'..' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'\s' => tests/tac/data/sep.d/sep-s'\s' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'a $' => tests/tac/data/sep.d/sep-s'a $' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/sep.d/sep-s'a ' => tests/tac/data/sep.d/sep-s'a ' +1 -0
@@ 0,0 1,1 @@
1   4 5   8 9 10 11 12 13 14 15 16 
\ No newline at end of file

A tests/tac/data/true.cpp => tests/tac/data/true.cpp +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp => tests/tac/data/true.cpp.d/true.cpp +16 -0
@@ 0,0 1,16 @@
}
		return 2;
	else
		return 1;
	else if(self == "false")
		return 0;
	if(self == "true")
	auto self = vore::basename(argv[0] ?: "(?)");
int main(int, char ** argv) {


#include <vore-string_view>
#include <string_view>


// SPDX-License-Identifier: 0BSD

A tests/tac/data/true.cpp.d/true.cpp-b => tests/tac/data/true.cpp.d/true.cpp-b +17 -0
@@ 0,0 1,17 @@


}
		return 2;
	else
		return 1;
	else if(self == "false")
		return 0;
	if(self == "true")
	auto self = vore::basename(argv[0] ?: "(?)");
int main(int, char ** argv) {


#include <vore-string_view>
#include <string_view>

// SPDX-License-Identifier: 0BSD
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-br => tests/tac/data/true.cpp.d/true.cpp-br +17 -0
@@ 0,0 1,17 @@


}
		return 2;
	else
		return 1;
	else if(self == "false")
		return 0;
	if(self == "true")
	auto self = vore::basename(argv[0] ?: "(?)");
int main(int, char ** argv) {


#include <vore-string_view>
#include <string_view>

// SPDX-License-Identifier: 0BSD
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-brs'  ' => tests/tac/data/true.cpp.d/true.cpp-brs'  ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-brs' ' => tests/tac/data/true.cpp.d/true.cpp-brs' ' +17 -0
@@ 0,0 1,17 @@
 2;
}
 1;
	else
		return "false")
		return == if(self 0;
	else "true")
		return == "(?)");
	if(self ?: vore::basename(argv[0] = self {
	auto argv) ** char main(int, <vore-string_view>


int <string_view>
#include 0BSD


#include SPDX-License-Identifier://
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-brs'' => tests/tac/data/true.cpp.d/true.cpp-brs'' +0 -0
A tests/tac/data/true.cpp.d/true.cpp-brs''.error => tests/tac/data/true.cpp.d/true.cpp-brs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/true.cpp.d/true.cpp-brs'.' => tests/tac/data/true.cpp.d/true.cpp-brs'.' +17 -0
@@ 0,0 1,17 @@
}
;
2 nruter		e
sle	;
1 nruter		)
"eslaf" == fles(fi esle	;
0 nruter		)
"eurt" == fles(fi	;
)")?(" :? ]0[vgra(emanesab::erov = fles otua	{
 )vgra ** rahc ,tni(niam tni>


weiv_gnirts-erov< edulcni#>
weiv_gnirts< edulcni#D


SB0 :reifitnedI-esneciL-XDPS //
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-brs'..' => tests/tac/data/true.cpp.d/true.cpp-brs'..' +17 -0
@@ 0,0 1,17 @@
2;
}
n uret	rse
	el1;
	n uret	r")
	seal"f=  =lfsef( iseel0;
	n uret	r")
	uetr "==f el(sif);
	)"(? "?:] [0gvare(amenas:be:or v =lfseo ut	a {
v)rg a**r ha ct,inn(ai mntw>


iie_vngriste-or<ve udclinw>
#ie_vngrist <delunc#iSD


0B: erfitienIde-nsceLiX-PD S//
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-brs'\s' => tests/tac/data/true.cpp.d/true.cpp-brs'\s' +17 -0
@@ 0,0 1,17 @@


} 2;	return	
	else
 1;	return	
 "false") == if(self	else
 0;	return	
 "true") ==	if(self
 "(?)"); ?: vore::basename(argv[0] = self	auto
 { argv) ** char main(int,
int

 <vore-string_view>
#include <string_view>
#include

 0BSD SPDX-License-Identifier://
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-brs'a $' => tests/tac/data/true.cpp.d/true.cpp-brs'a $' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-brs'a ' => tests/tac/data/true.cpp.d/true.cpp-brs'a ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'  ' => tests/tac/data/true.cpp.d/true.cpp-bs'  ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs' ' => tests/tac/data/true.cpp.d/true.cpp-bs' ' +17 -0
@@ 0,0 1,17 @@
 2;
}
 1;
	else
		return "false")
		return == if(self 0;
	else "true")
		return == "(?)");
	if(self ?: vore::basename(argv[0] = self {
	auto argv) ** char main(int, <vore-string_view>


int <string_view>
#include 0BSD


#include SPDX-License-Identifier://
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-bs'' => tests/tac/data/true.cpp.d/true.cpp-bs'' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'.' => tests/tac/data/true.cpp.d/true.cpp-bs'.' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'..' => tests/tac/data/true.cpp.d/true.cpp-bs'..' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'\s' => tests/tac/data/true.cpp.d/true.cpp-bs'\s' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'a $' => tests/tac/data/true.cpp.d/true.cpp-bs'a $' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-bs'a ' => tests/tac/data/true.cpp.d/true.cpp-bs'a ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-r => tests/tac/data/true.cpp.d/true.cpp-r +16 -0
@@ 0,0 1,16 @@
}
		return 2;
	else
		return 1;
	else if(self == "false")
		return 0;
	if(self == "true")
	auto self = vore::basename(argv[0] ?: "(?)");
int main(int, char ** argv) {


#include <vore-string_view>
#include <string_view>


// SPDX-License-Identifier: 0BSD

A tests/tac/data/true.cpp.d/true.cpp-rs'  ' => tests/tac/data/true.cpp.d/true.cpp-rs'  ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-rs' ' => tests/tac/data/true.cpp.d/true.cpp-rs' ' +17 -0
@@ 0,0 1,17 @@
2;
}
1;
	else
		return "false")
		return == if(self 0;
	else "true")
		return == "(?)");
	if(self ?: vore::basename(argv[0] = self {
	auto argv) ** char main(int, <vore-string_view>


int <string_view>
#include 0BSD


#include SPDX-License-Identifier: // 
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-rs'' => tests/tac/data/true.cpp.d/true.cpp-rs'' +0 -0
A tests/tac/data/true.cpp.d/true.cpp-rs''.error => tests/tac/data/true.cpp.d/true.cpp-rs''.error +1 -0
@@ 0,0 1,1 @@
yes

A tests/tac/data/true.cpp.d/true.cpp-rs'.' => tests/tac/data/true.cpp.d/true.cpp-rs'.' +17 -0
@@ 0,0 1,17 @@


};2 nruter	
	esle
	;1 nruter	
	)"eslaf" == fles(fi esle
	;0 nruter	
	)"eurt" == fles(fi
	;)")?(" :? ]0[vgra(emanesab::erov = fles otua
	{ )vgra ** rahc ,tni(niam tn


i>weiv_gnirts-erov< edulcni
#>weiv_gnirts< edulcni


#DSB0 :reifitnedI-esneciL-XDPS //
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-rs'..' => tests/tac/data/true.cpp.d/true.cpp-rs'..' +17 -0
@@ 0,0 1,17 @@

}
2;n uret
		rse
	el1;n uret
		r")seal"f=  =lfsef( ise
	el0;n uret
		r")uetr "==f el(s
	if);)"(? "?:] [0gvare(amenas:be:or v =lfseo ut
	a {v)rg a**r ha ct,inn(ai m


intw>ie_vngriste-or<ve udcl
#inw>ie_vngrist <delunc


#iSD0B: erfitienIde-nsceLiX-PD S//
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-rs'\s' => tests/tac/data/true.cpp.d/true.cpp-rs'\s' +17 -0
@@ 0,0 1,17 @@
}
2;
return 		else
	1;
return 		"false")
== if(self else 	0;
return 		"true")
== if(self 	"(?)");
?: vore::basename(argv[0] = self auto 	{
argv) ** char main(int, int 

<vore-string_view>
#include <string_view>
#include 

0BSD
SPDX-License-Identifier: // 
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-rs'a $' => tests/tac/data/true.cpp.d/true.cpp-rs'a $' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-rs'a ' => tests/tac/data/true.cpp.d/true.cpp-rs'a ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'  ' => tests/tac/data/true.cpp.d/true.cpp-s'  ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s' ' => tests/tac/data/true.cpp.d/true.cpp-s' ' +17 -0
@@ 0,0 1,17 @@
2;
}
1;
	else
		return "false")
		return == if(self 0;
	else "true")
		return == "(?)");
	if(self ?: vore::basename(argv[0] = self {
	auto argv) ** char main(int, <vore-string_view>


int <string_view>
#include 0BSD


#include SPDX-License-Identifier: // 
\ No newline at end of file

A tests/tac/data/true.cpp.d/true.cpp-s'' => tests/tac/data/true.cpp.d/true.cpp-s'' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'.' => tests/tac/data/true.cpp.d/true.cpp-s'.' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'..' => tests/tac/data/true.cpp.d/true.cpp-s'..' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'\s' => tests/tac/data/true.cpp.d/true.cpp-s'\s' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'a $' => tests/tac/data/true.cpp.d/true.cpp-s'a $' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/data/true.cpp.d/true.cpp-s'a ' => tests/tac/data/true.cpp.d/true.cpp-s'a ' +16 -0
@@ 0,0 1,16 @@
// SPDX-License-Identifier: 0BSD


#include <string_view>
#include <vore-string_view>


int main(int, char ** argv) {
	auto self = vore::basename(argv[0] ?: "(?)");
	if(self == "true")
		return 0;
	else if(self == "false")
		return 1;
	else
		return 2;
}

A tests/tac/test => tests/tac/test +97 -0
@@ 0,0 1,97 @@
#!/bin/sh
# SPDX-License-Identifier: 0BSD

IFS="
"

tmpdir="$(mktemp -dt "tac.XXXXXXXXXX")/"
tac="${CMDDIR}tac"
# tac="tac"
data="$(realpath data)/"
files="$(for f in "$data"*; do [ -f "$f" ] && echo "${f##*/}"; done)"

mkdir "${tmpdir}singlef/"
for f in $files; do
	cp "$data$f" "${tmpdir}singlef/"
done
cp -r "${tmpdir}singlef/" "${tmpdir}withnull/"
cp -r "${tmpdir}singlef/" "${tmpdir}with-/"
cp -r "${tmpdir}singlef/" "${tmpdir}just-/"
cp -r "${tmpdir}singlef/" "${tmpdir}nothing/"

cd "${tmpdir}singlef/" && for f in $files; do
  mkdir "$f.d"
  "$tac"     "$f" > "$f.d/$f"    2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
  "$tac" -b  "$f" > "$f.d/$f-b"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
  "$tac" -r  "$f" > "$f.d/$f-r"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
  "$tac" -br "$f" > "$f.d/$f-br" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  for s in '' ' ' '  ' 'a ' ' ' '\s' '.' '..' 'a $'; do
    "$tac" -s   "$s" "$f" > "$f.d/$f-s'$s'"   2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
    "$tac" -bs  "$s" "$f" > "$f.d/$f-bs'$s'"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
    "$tac" -rs  "$s" "$f" > "$f.d/$f-rs'$s'"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
    "$tac" -brs "$s" "$f" > "$f.d/$f-brs'$s'" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  done
done
diff -r -u "$data" "${tmpdir}singlef/" >&3 2>&1 && echo "Single-file ok"

cd "${tmpdir}withnull/" && for f in $files; do
  mkdir "$f.d"
  "$tac"     /dev/null "$f" /dev/null > "$f.d/$f"    2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
  "$tac" -b  /dev/null "$f" /dev/null > "$f.d/$f-b"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
  "$tac" -r  /dev/null "$f" /dev/null > "$f.d/$f-r"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
  "$tac" -br /dev/null "$f" /dev/null > "$f.d/$f-br" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  for s in '' ' ' '  ' 'a ' ' ' '\s' '.' '..' 'a $'; do
    "$tac" -s   "$s" /dev/null "$f" /dev/null > "$f.d/$f-s'$s'"   2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
    "$tac" -bs  "$s" /dev/null "$f" /dev/null > "$f.d/$f-bs'$s'"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
    "$tac" -rs  "$s" /dev/null "$f" /dev/null > "$f.d/$f-rs'$s'"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
    "$tac" -brs "$s" /dev/null "$f" /dev/null > "$f.d/$f-brs'$s'" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  done
done
diff -r -u "$data" "${tmpdir}withnull/" >&3 2>&1 && echo "Interposed null ok"

cd "${tmpdir}with-/" && for f in $files; do
  mkdir "$f.d"
  printf "" | "$tac"     - "$f" - > "$f.d/$f"    2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
  printf "" | "$tac" -b  - "$f" - > "$f.d/$f-b"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
  printf "" | "$tac" -r  - "$f" - > "$f.d/$f-r"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
  printf "" | "$tac" -br - "$f" - > "$f.d/$f-br" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  for s in '' ' ' '  ' 'a ' ' ' '\s' '.' '..' 'a $'; do
    printf "" | "$tac" -s   "$s" - "$f" - > "$f.d/$f-s'$s'"   2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
    printf "" | "$tac" -bs  "$s" - "$f" - > "$f.d/$f-bs'$s'"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
    printf "" | "$tac" -rs  "$s" - "$f" - > "$f.d/$f-rs'$s'"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
    printf "" | "$tac" -brs "$s" - "$f" - > "$f.d/$f-brs'$s'" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  done
done
diff -r -u "$data" "${tmpdir}with-/" >&3 2>&1 && echo "Interposed - ok"

cd "${tmpdir}just-/" && for f in $files; do
  mkdir "$f.d"
  "$tac"     - < "$f" > "$f.d/$f"    2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
  "$tac" -b  - < "$f" > "$f.d/$f-b"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
  "$tac" -r  - < "$f" > "$f.d/$f-r"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
  "$tac" -br - < "$f" > "$f.d/$f-br" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  for s in '' ' ' '  ' 'a ' ' ' '\s' '.' '..' 'a $'; do
    "$tac" -s   "$s" - < "$f" > "$f.d/$f-s'$s'"   2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
    "$tac" -bs  "$s" - < "$f" > "$f.d/$f-bs'$s'"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
    "$tac" -rs  "$s" - < "$f" > "$f.d/$f-rs'$s'"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
    "$tac" -brs "$s" - < "$f" > "$f.d/$f-brs'$s'" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  done
done
diff -r -u "$data" "${tmpdir}just-/" >&3 2>&1 && echo "stdin ok"

cd "${tmpdir}nothing/" && for f in $files; do
  mkdir "$f.d"
  "$tac"     < "$f" > "$f.d/$f"    2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
  "$tac" -b  < "$f" > "$f.d/$f-b"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
  "$tac" -r  < "$f" > "$f.d/$f-r"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
  "$tac" -br < "$f" > "$f.d/$f-br" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  for s in '' ' ' '  ' 'a ' ' ' '\s' '.' '..' 'a $'; do
    "$tac" -s   "$s" < "$f" > "$f.d/$f-s'$s'"   2>/dev/null || echo yes > "$f.d/$f-s'$s'.error"
    "$tac" -bs  "$s" < "$f" > "$f.d/$f-bs'$s'"  2>/dev/null || echo yes > "$f.d/$f-bs'$s'.error"
    "$tac" -rs  "$s" < "$f" > "$f.d/$f-rs'$s'"  2>/dev/null || echo yes > "$f.d/$f-rs'$s'.error"
    "$tac" -brs "$s" < "$f" > "$f.d/$f-brs'$s'" 2>/dev/null || echo yes > "$f.d/$f-brs'$s'.error"
  done
done
diff -r -u "$data" "${tmpdir}nothing/" >&3 2>&1 && echo "Default ok"

rm -rf "$tmpdir" >&3

A tests/true => tests/true +24 -0
@@ 0,0 1,24 @@
#!/bin/sh
# SPDX-License-Identifier: 0BSD

tmpdir="$(mktemp -dt "true.XXXXXXXXXX")/"
true="${CMDDIR}true"

altname="${tmpdir%/}"
altname="${altname##*/}"

ln -fs "$true" "${tmpdir}true"
ln -fs "$true" "${tmpdir}false"
ln -fs "$true" "${tmpdir}something-else"
ln -fs "$true" "${tmpdir}$altname"

"${tmpdir}true"; err=$?
[ $err -eq 0 ] || echo "true: got $err instead of 0 for true" >&3
"${tmpdir}false"; err=$?
[ $err -eq 1 ] || echo "true: got $err instead of 1 for false" >&3
"${tmpdir}something-else"; err=$?
[ $err -eq 2 ] || echo "true: got $err instead of 2 for something-else" >&3
"${tmpdir}$altname"; err=$?
[ $err -eq 2 ] || echo "true: got $err instead of 2 for $altname" >&3

rm -rf "$tmpdir" >&3

M voreutils.sublime-project => voreutils.sublime-project +6 -1
@@ 16,11 16,16 @@
			"follow_symlinks": true,
			"name": "Source",
			"path": ".",
			"file_include_patterns": ["*.c", "*.cpp", "*.h", "*.hpp", "vore-*", "README*", "Makefile*"],
			"file_include_patterns": ["*.c", "*.cpp", "*.h", "*.hpp", "vore-*", "README*", "Makefile*", "aliases"],
			"folder_include_patterns": ["cmd", "include"]
		},
		{
			"follow_symlinks": true,
			"name": "Tests",
			"path": "tests"
		},
		{
			"follow_symlinks": true,
			"name": "Manpages",
			"path": "man"
		},