-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmap_fline
99 lines (94 loc) · 3.25 KB
/
map_fline
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
89
90
91
92
93
94
95
96
97
98
99
#! .desc:
# Process each populated file line with a function
#! .params:
# <$1> - read_method(
# '-cat' - use `cat`
# '-shell' - use shell input redirection
# .
# )
# <"$2"> - file path
# <$3> - function name
# ["$4"]+ - function arguments
#! .uses (<2+>):
# [&9]
# [()]
# [<<]
# [<]
#! .uses.util:
# [cat] ('--' '$2');
# concatenate and print files
#! .rc:
# (0) success
# (*) error
# (255) bad input / bad usage / error
#! .rc.fn:
# ($2) (*) error
#! .rc.util
# (cat) (*) error
#.
map_fline() {
# Assert $2 is a file.
[ -f "$2" ] || return 255
# Read the file with the specified method.
#
# Use `cat` to read the file if it is a potential specification of a file
# in `/dev`, `/proc`, or `/sys` (kernel-created virtual file).
#
# `cat` will read the file in one `read` syscall. This is important because
# certain kernel files can only be read() once, returning EOF on subsequent
# read() syscalls. This behavior is prevalent for certain files in `/proc`
# which do not support seeking.
#
# `cat` reads a file in large chunks, accommodating the requirement for a
# single read(). This contrasts with a POSIX shell, which reads files one
# byte at a time and may only return the first byte of data from certain
# kernel files.
#
# Use the shell to read the file if it is a small (< ~10000XX lines),
# user-created file.
case "$1" in
'-cat')
# Store the output of `cat` in $_file using command substitution.
# The custom `x` character at the end and its subsequent removal
# outside the command substitution preserves any trailing <newline>
# characters stripped by command substitution.
#
# For more information, refer to:
# > "POSIX.1-2024, Volume: Shell & Utilities, Section: Shell
# Command Language, Subsection: Command Substitution".
_file=$(cat -- "$2" && printf "%s" 'x') || return "$?"
_file="${_file%?}"
# Iterate over each populated line of $_file using heredoc.
#
# For more information, refer to:
# > "POSIX.1-2024, Volume: Shell & Utilities, Section: Shell
# Command Language, Subsection: Here-Document".
shift 2; while IFS= read -r _line; do
[ "$_line" ] || continue
"$@" "$_line" || return "$?"
done \
<<_FILE
$_file
_FILE
;;
'-shell')
# Open the file and assign it to file descriptor `9`. The usage of
# a file descriptor simplifies the error handling. Additionally,
# TOCTOU flaws are completely avoided this way.
{ exec 9< "$2"; } || return "$?"
# Pass each populated line to $3 (the function). If an error is
# received, file descriptor `9` will be released and the error
# signaled.
shift 2; while IFS= read -r _line <&9 || [ "$_line" ]; do
[ "$_line" ] || continue
"$@" "$_line" || { set -- "$?"; exec 9<&-; return "$1"; }
done
# Release file descriptor `9` to close the associated file.
exec 9<&-
;;
*)
return 255
;;
esac
return 0
}