1#!/usr/bin/env python3
2#
3# tee, but for pipes
4#
5# Example:
6# ./scripts/tee.py in_pipe out_pipe1 out_pipe2
7#
8# Copyright (c) 2022, The littlefs authors.
9# SPDX-License-Identifier: BSD-3-Clause
10#
11
12import os
13import io
14import time
15import sys
16
17
18def openio(path, mode='r', buffering=-1):
19    # allow '-' for stdin/stdout
20    if path == '-':
21        if mode == 'r':
22            return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering)
23        else:
24            return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering)
25    else:
26        return open(path, mode, buffering)
27
28def main(in_path, out_paths, *, keep_open=False):
29    out_pipes = [openio(p, 'wb', 0) for p in out_paths]
30    try:
31        with openio(in_path, 'rb', 0) as f:
32            while True:
33                buf = f.read(io.DEFAULT_BUFFER_SIZE)
34                if not buf:
35                    if not keep_open:
36                        break
37                    # don't just flood reads
38                    time.sleep(0.1)
39                    continue
40
41                for p in out_pipes:
42                    try:
43                        p.write(buf)
44                    except BrokenPipeError:
45                        pass
46    except FileNotFoundError as e:
47        print("error: file not found %r" % in_path)
48        sys.exit(-1)
49    except KeyboardInterrupt:
50        pass
51
52
53if __name__ == "__main__":
54    import sys
55    import argparse
56    parser = argparse.ArgumentParser(
57        description="tee, but for pipes.",
58        allow_abbrev=False)
59    parser.add_argument(
60        'in_path',
61        help="Path to read from.")
62    parser.add_argument(
63        'out_paths',
64        nargs='+',
65        help="Path to write to.")
66    parser.add_argument(
67        '-k', '--keep-open',
68        action='store_true',
69        help="Reopen the pipe on EOF, useful when multiple "
70            "processes are writing.")
71    sys.exit(main(**{k: v
72        for k, v in vars(parser.parse_intermixed_args()).items()
73        if v is not None}))
74