~nabijaczleweli/voreutils

ref: 72087d46a1d3f95f4e58bc8d4803aa05ab64b9f0 voreutils/cmd/tee.cpp -rw-r--r-- 2.3 KiB
72087d46наб OpenBSD port 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// SPDX-License-Identifier: 0BSD


#include <algorithm>
#include <cstdio>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <vector>
#include <vore-getopt>
#include <vore-file>
#include <vore-optarg>
#include <vore-token>


int main(int argc, char * const * argv) {
	bool fatal_errors{};
	int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
	for(auto && [arg, val] : vore::opt::get{argc,
	                                        argv,
	                                        "aiep",
	                                        {{"append", no_argument, nullptr, 'a'},  //
	                                         {"ignore-interrupt", no_argument, nullptr, 'i'}}})
		switch(arg) {
			case 'a':
				flags &= ~O_TRUNC;
				flags |= O_APPEND;
				break;
			case 'i': {
				const struct sigaction act { .sa_handler = SIG_IGN };
				sigaction(SIGINT, &act, nullptr);
			} break;
			case 'e':
				fatal_errors = true;
				break;
			case 'p':
				break;
			default:
				return std::fprintf(stderr, "usage: %s [-aie] [file]...\n", argv[0]), 1;
		}


	std::vector<std::pair<const char *, int>> files;
	for(auto path : vore::opt::args{argv + optind}) {
		vore::file::fd<false> fd{path, flags, 0666};
		if(fd == -1) {
			std::fprintf(stderr, "%s: %s: %s\n", argv[0], path, std::strerror(errno));
			if(fatal_errors)
				return 1;
		}
		files.emplace_back(path, std::move(fd).take());
	}

	for(;;) {
		std::uint8_t buf[64 * 1024];
		ssize_t rd;
		while((rd = read(0, buf, sizeof(buf))) == -1 && errno == EINTR)
			;
		if(rd == -1)
			return std::fprintf(stderr, "%s: %s\n", argv[0], std::strerror(errno)), 1;
		if(!rd)
			break;

		auto commit_write = [&](auto path, auto & fd, auto hard_error) {
			for(ssize_t off{}; fd != -1 && off != rd;) {
				auto wr = write(fd, buf + off, rd - off);
				if(wr == -1 && errno == EINTR)
					continue;
				if(wr == -1) {
					fd = -1;
					std::fprintf(stderr, "%s: %s: %s\n", argv[0], path, std::strerror(errno));
					if(hard_error)
						return true;
				}
				off += wr;
			}
			return false;
		};

		if(int stdout_ = 1; commit_write("stdout", stdout_, true))
			return 1;
		for(auto && [path, fd] : files)
			if(commit_write(path, fd, fatal_errors))
				return 1;
	}

	return std::any_of(std::begin(files), std::end(files), [](auto && f) { return f.second == -1; });
}