Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / github.com / prometheus / procfs / ttar
1 #!/usr/bin/env bash
2
3 # Purpose: plain text tar format
4 # Limitations: - only suitable for text files, directories, and symlinks
5 #              - stores only filename, content, and mode
6 #              - not designed for untrusted input
7 #
8 # Note: must work with bash version 3.2 (macOS)
9
10 # Copyright 2017 Roger Luethi
11 #
12 # Licensed under the Apache License, Version 2.0 (the "License");
13 # you may not use this file except in compliance with the License.
14 # You may obtain a copy of the License at
15 #
16 # http://www.apache.org/licenses/LICENSE-2.0
17 #
18 # Unless required by applicable law or agreed to in writing, software
19 # distributed under the License is distributed on an "AS IS" BASIS,
20 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 # See the License for the specific language governing permissions and
22 # limitations under the License.
23
24 set -o errexit -o nounset
25
26 # Sanitize environment (for instance, standard sorting of glob matches)
27 export LC_ALL=C
28
29 path=""
30 CMD=""
31 ARG_STRING="$*"
32
33 #------------------------------------------------------------------------------
34 # Not all sed implementations can work on null bytes. In order to make ttar
35 # work out of the box on macOS, use Python as a stream editor.
36
37 USE_PYTHON=0
38
39 PYTHON_CREATE_FILTER=$(cat << 'PCF'
40 #!/usr/bin/env python
41
42 import re
43 import sys
44
45 for line in sys.stdin:
46     line = re.sub(r'EOF', r'\EOF', line)
47     line = re.sub(r'NULLBYTE', r'\NULLBYTE', line)
48     line = re.sub('\x00', r'NULLBYTE', line)
49     sys.stdout.write(line)
50 PCF
51 )
52
53 PYTHON_EXTRACT_FILTER=$(cat << 'PEF'
54 #!/usr/bin/env python
55
56 import re
57 import sys
58
59 for line in sys.stdin:
60     line = re.sub(r'(?<!\\)NULLBYTE', '\x00', line)
61     line = re.sub(r'\\NULLBYTE', 'NULLBYTE', line)
62     line = re.sub(r'([^\\])EOF', r'\1', line)
63     line = re.sub(r'\\EOF', 'EOF', line)
64     sys.stdout.write(line)
65 PEF
66 )
67
68 function test_environment {
69     if [[ "$(echo "a" | sed 's/a/\x0/' | wc -c)" -ne 2 ]]; then
70         echo "WARNING sed unable to handle null bytes, using Python (slow)."
71         if ! which python >/dev/null; then
72             echo "ERROR Python not found. Aborting."
73             exit 2
74         fi
75         USE_PYTHON=1
76     fi
77 }
78
79 #------------------------------------------------------------------------------
80
81 function usage {
82     bname=$(basename "$0")
83     cat << USAGE
84 Usage:   $bname [-C <DIR>] -c -f <ARCHIVE> <FILE...> (create archive)
85          $bname            -t -f <ARCHIVE>           (list archive contents)
86          $bname [-C <DIR>] -x -f <ARCHIVE>           (extract archive)
87
88 Options:
89          -C <DIR>                                    (change directory)
90          -v                                          (verbose)
91
92 Example: Change to sysfs directory, create ttar file from fixtures directory
93          $bname -C sysfs -c -f sysfs/fixtures.ttar fixtures/
94 USAGE
95 exit "$1"
96 }
97
98 function vecho {
99     if [ "${VERBOSE:-}" == "yes" ]; then
100         echo >&7 "$@"
101     fi
102 }
103
104 function set_cmd {
105     if [ -n "$CMD" ]; then
106         echo "ERROR: more than one command given"
107         echo
108         usage 2
109     fi
110     CMD=$1
111 }
112
113 unset VERBOSE
114
115 while getopts :cf:htxvC: opt; do
116     case $opt in
117         c)
118             set_cmd "create"
119             ;;
120         f)
121             ARCHIVE=$OPTARG
122             ;;
123         h)
124             usage 0
125             ;;
126         t)
127             set_cmd "list"
128             ;;
129         x)
130             set_cmd "extract"
131             ;;
132         v)
133             VERBOSE=yes
134             exec 7>&1
135             ;;
136         C)
137             CDIR=$OPTARG
138             ;;
139         *)
140             echo >&2 "ERROR: invalid option -$OPTARG"
141             echo
142             usage 1
143             ;;
144     esac
145 done
146
147 # Remove processed options from arguments
148 shift $(( OPTIND - 1 ));
149
150 if [ "${CMD:-}" == "" ]; then
151     echo >&2 "ERROR: no command given"
152     echo
153     usage 1
154 elif [ "${ARCHIVE:-}" == "" ]; then
155     echo >&2 "ERROR: no archive name given"
156     echo
157     usage 1
158 fi
159
160 function list {
161     local path=""
162     local size=0
163     local line_no=0
164     local ttar_file=$1
165     if [ -n "${2:-}" ]; then
166         echo >&2 "ERROR: too many arguments."
167         echo
168         usage 1
169     fi
170     if [ ! -e "$ttar_file" ]; then
171         echo >&2 "ERROR: file not found ($ttar_file)"
172         echo
173         usage 1
174     fi
175     while read -r line; do
176         line_no=$(( line_no + 1 ))
177         if [ $size -gt 0 ]; then
178             size=$(( size - 1 ))
179             continue
180         fi
181         if [[ $line =~ ^Path:\ (.*)$ ]]; then
182             path=${BASH_REMATCH[1]}
183         elif [[ $line =~ ^Lines:\ (.*)$ ]]; then
184             size=${BASH_REMATCH[1]}
185             echo "$path"
186         elif [[ $line =~ ^Directory:\ (.*)$ ]]; then
187             path=${BASH_REMATCH[1]}
188             echo "$path/"
189         elif [[ $line =~ ^SymlinkTo:\ (.*)$ ]]; then
190             echo  "$path -> ${BASH_REMATCH[1]}"
191         fi
192     done < "$ttar_file"
193 }
194
195 function extract {
196     local path=""
197     local size=0
198     local line_no=0
199     local ttar_file=$1
200     if [ -n "${2:-}" ]; then
201         echo >&2 "ERROR: too many arguments."
202         echo
203         usage 1
204     fi
205     if [ ! -e "$ttar_file" ]; then
206         echo >&2 "ERROR: file not found ($ttar_file)"
207         echo
208         usage 1
209     fi
210     while IFS= read -r line; do
211         line_no=$(( line_no + 1 ))
212         local eof_without_newline
213         if [ "$size" -gt 0 ]; then
214             if [[ "$line" =~ [^\\]EOF ]]; then
215                 # An EOF not preceeded by a backslash indicates that the line
216                 # does not end with a newline
217                 eof_without_newline=1
218             else
219                 eof_without_newline=0
220             fi
221             # Replace NULLBYTE with null byte if at beginning of line
222             # Replace NULLBYTE with null byte unless preceeded by backslash
223             # Remove one backslash in front of NULLBYTE (if any)
224             # Remove EOF unless preceeded by backslash
225             # Remove one backslash in front of EOF
226             if [ $USE_PYTHON -eq 1 ]; then
227                 echo -n "$line" | python -c "$PYTHON_EXTRACT_FILTER" >> "$path"
228             else
229                 # The repeated pattern makes up for sed's lack of negative
230                 # lookbehind assertions (for consecutive null bytes).
231                 echo -n "$line" | \
232                     sed -e 's/^NULLBYTE/\x0/g;
233                             s/\([^\\]\)NULLBYTE/\1\x0/g;
234                             s/\([^\\]\)NULLBYTE/\1\x0/g;
235                             s/\\NULLBYTE/NULLBYTE/g;
236                             s/\([^\\]\)EOF/\1/g;
237                             s/\\EOF/EOF/g;
238                     ' >> "$path"
239             fi
240             if [[ "$eof_without_newline" -eq 0 ]]; then
241                 echo >> "$path"
242             fi
243             size=$(( size - 1 ))
244             continue
245         fi
246         if [[ $line =~ ^Path:\ (.*)$ ]]; then
247             path=${BASH_REMATCH[1]}
248             if [ -e "$path" ] || [ -L "$path" ]; then
249                 rm "$path"
250             fi
251         elif [[ $line =~ ^Lines:\ (.*)$ ]]; then
252             size=${BASH_REMATCH[1]}
253             # Create file even if it is zero-length.
254             touch "$path"
255             vecho "    $path"
256         elif [[ $line =~ ^Mode:\ (.*)$ ]]; then
257             mode=${BASH_REMATCH[1]}
258             chmod "$mode" "$path"
259             vecho "$mode"
260         elif [[ $line =~ ^Directory:\ (.*)$ ]]; then
261             path=${BASH_REMATCH[1]}
262             mkdir -p "$path"
263             vecho "    $path/"
264         elif [[ $line =~ ^SymlinkTo:\ (.*)$ ]]; then
265             ln -s "${BASH_REMATCH[1]}" "$path"
266             vecho "    $path -> ${BASH_REMATCH[1]}"
267         elif [[ $line =~ ^# ]]; then
268             # Ignore comments between files
269             continue
270         else
271             echo >&2 "ERROR: Unknown keyword on line $line_no: $line"
272             exit 1
273         fi
274     done < "$ttar_file"
275 }
276
277 function div {
278     echo "# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" \
279          "- - - - - -"
280 }
281
282 function get_mode {
283     local mfile=$1
284     if [ -z "${STAT_OPTION:-}" ]; then
285         if stat -c '%a' "$mfile" >/dev/null 2>&1; then
286             # GNU stat
287             STAT_OPTION='-c'
288             STAT_FORMAT='%a'
289         else
290             # BSD stat
291             STAT_OPTION='-f'
292             # Octal output, user/group/other (omit file type, sticky bit)
293             STAT_FORMAT='%OLp'
294         fi
295     fi
296     stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile"
297 }
298
299 function _create {
300     shopt -s nullglob
301     local mode
302     local eof_without_newline
303     while (( "$#" )); do
304         file=$1
305         if [ -L "$file" ]; then
306             echo "Path: $file"
307             symlinkTo=$(readlink "$file")
308             echo "SymlinkTo: $symlinkTo"
309             vecho "    $file -> $symlinkTo"
310             div
311         elif [ -d "$file" ]; then
312             # Strip trailing slash (if there is one)
313             file=${file%/}
314             echo "Directory: $file"
315             mode=$(get_mode "$file")
316             echo "Mode: $mode"
317             vecho "$mode $file/"
318             div
319             # Find all files and dirs, including hidden/dot files
320             for x in "$file/"{*,.[^.]*}; do
321                 _create "$x"
322             done
323         elif [ -f "$file" ]; then
324             echo "Path: $file"
325             lines=$(wc -l "$file"|awk '{print $1}')
326             eof_without_newline=0
327             if [[ "$(wc -c "$file"|awk '{print $1}')" -gt 0 ]] && \
328                     [[ "$(tail -c 1 "$file" | wc -l)" -eq 0 ]]; then
329                 eof_without_newline=1
330                 lines=$((lines+1))
331             fi
332             echo "Lines: $lines"
333             # Add backslash in front of EOF
334             # Add backslash in front of NULLBYTE
335             # Replace null byte with NULLBYTE
336             if [ $USE_PYTHON -eq 1 ]; then
337                 < "$file" python -c "$PYTHON_CREATE_FILTER"
338             else
339                 < "$file" \
340                     sed 's/EOF/\\EOF/g;
341                          s/NULLBYTE/\\NULLBYTE/g;
342                          s/\x0/NULLBYTE/g;
343                     '
344             fi
345             if [[ "$eof_without_newline" -eq 1 ]]; then
346                 # Finish line with EOF to indicate that the original line did
347                 # not end with a linefeed
348                 echo "EOF"
349             fi
350             mode=$(get_mode "$file")
351             echo "Mode: $mode"
352             vecho "$mode $file"
353             div
354         else
355             echo >&2 "ERROR: file not found ($file in $(pwd))"
356             exit 2
357         fi
358         shift
359     done
360 }
361
362 function create {
363     ttar_file=$1
364     shift
365     if [ -z "${1:-}" ]; then
366         echo >&2 "ERROR: missing arguments."
367         echo
368         usage 1
369     fi
370     if [ -e "$ttar_file" ]; then
371         rm "$ttar_file"
372     fi
373     exec > "$ttar_file"
374     echo "# Archive created by ttar $ARG_STRING"
375     _create "$@"
376 }
377
378 test_environment
379
380 if [ -n "${CDIR:-}" ]; then
381     if [[ "$ARCHIVE" != /* ]]; then
382         # Relative path: preserve the archive's location before changing
383         # directory
384         ARCHIVE="$(pwd)/$ARCHIVE"
385     fi
386     cd "$CDIR"
387 fi
388
389 "$CMD" "$ARCHIVE" "$@"