Vendor ttar from github.com/ideaship/ttar

pull/843/head
Tobias Schmidt 7 years ago
parent 002c1ca029
commit 7a1a512c8a

133
ttar

@ -1,11 +1,26 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Purpose: plain text tar format # Purpose: plain text tar format
# Limitations: - only suitable for text files, directories, and symlinks # Limitations: - only suitable for text files, directories, and symlinks
# - stores only filename, content, and mode # - stores only filename, content, and mode
# - not designed for untrusted input # - not designed for untrusted input
#
# Note: must work with bash version 3.2 (macOS) # Note: must work with bash version 3.2 (macOS)
# Copyright 2017 Roger Luethi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit -o nounset set -o errexit -o nounset
# Sanitize environment (for instance, standard sorting of glob matches) # Sanitize environment (for instance, standard sorting of glob matches)
@ -13,7 +28,55 @@ export LC_ALL=C
path="" path=""
CMD="" CMD=""
ARG_STRING="$@" ARG_STRING="$*"
#------------------------------------------------------------------------------
# Not all sed implementations can work on null bytes. In order to make ttar
# work out of the box on macOS, use Python as a stream editor.
USE_PYTHON=0
PYTHON_CREATE_FILTER=$(cat << 'PCF'
#!/usr/bin/env python
import re
import sys
for line in sys.stdin:
line = re.sub(r'EOF', r'\EOF', line)
line = re.sub(r'NULLBYTE', r'\NULLBYTE', line)
line = re.sub('\x00', r'NULLBYTE', line)
sys.stdout.write(line)
PCF
)
PYTHON_EXTRACT_FILTER=$(cat << 'PEF'
#!/usr/bin/env python
import re
import sys
for line in sys.stdin:
line = re.sub(r'(?<!\\)NULLBYTE', '\x00', line)
line = re.sub(r'\\NULLBYTE', 'NULLBYTE', line)
line = re.sub(r'([^\\])EOF', r'\1', line)
line = re.sub(r'\\EOF', 'EOF', line)
sys.stdout.write(line)
PEF
)
function test_environment {
if [[ "$(echo "a" | sed 's/a/\x0/' | wc -c)" -ne 2 ]]; then
echo "WARNING sed unable to handle null bytes, using Python (slow)."
if ! which python >/dev/null; then
echo "ERROR Python not found. Aborting."
exit 2
fi
USE_PYTHON=1
fi
}
#------------------------------------------------------------------------------
function usage { function usage {
bname=$(basename "$0") bname=$(basename "$0")
@ -24,6 +87,7 @@ Usage: $bname [-C <DIR>] -c -f <ARCHIVE> <FILE...> (create archive)
Options: Options:
-C <DIR> (change directory) -C <DIR> (change directory)
-v (verbose)
Example: Change to sysfs directory, create ttar file from fixtures directory Example: Change to sysfs directory, create ttar file from fixtures directory
$bname -C sysfs -c -f sysfs/fixtures.ttar fixtures/ $bname -C sysfs -c -f sysfs/fixtures.ttar fixtures/
@ -46,6 +110,8 @@ function set_cmd {
CMD=$1 CMD=$1
} }
unset VERBOSE
while getopts :cf:htxvC: opt; do while getopts :cf:htxvC: opt; do
case $opt in case $opt in
c) c)
@ -143,8 +209,37 @@ function extract {
fi fi
while IFS= read -r line; do while IFS= read -r line; do
line_no=$(( line_no + 1 )) line_no=$(( line_no + 1 ))
local eof_without_newline
if [ "$size" -gt 0 ]; then if [ "$size" -gt 0 ]; then
echo "$line" >> "$path" if [[ "$line" =~ [^\\]EOF ]]; then
# An EOF not preceeded by a backslash indicates that the line
# does not end with a newline
eof_without_newline=1
else
eof_without_newline=0
fi
# Replace NULLBYTE with null byte if at beginning of line
# Replace NULLBYTE with null byte unless preceeded by backslash
# Remove one backslash in front of NULLBYTE (if any)
# Remove EOF unless preceeded by backslash
# Remove one backslash in front of EOF
if [ $USE_PYTHON -eq 1 ]; then
echo -n "$line" | python -c "$PYTHON_EXTRACT_FILTER" >> "$path"
else
# The repeated pattern makes up for sed's lack of negative
# lookbehind assertions (for consecutive null bytes).
echo -n "$line" | \
sed -e 's/^NULLBYTE/\x0/g;
s/\([^\\]\)NULLBYTE/\1\x0/g;
s/\([^\\]\)NULLBYTE/\1\x0/g;
s/\\NULLBYTE/NULLBYTE/g;
s/\([^\\]\)EOF/\1/g;
s/\\EOF/EOF/g;
' >> "$path"
fi
if [[ "$eof_without_newline" -eq 0 ]]; then
echo >> "$path"
fi
size=$(( size - 1 )) size=$(( size - 1 ))
continue continue
fi fi
@ -188,11 +283,14 @@ function get_mode {
local mfile=$1 local mfile=$1
if [ -z "${STAT_OPTION:-}" ]; then if [ -z "${STAT_OPTION:-}" ]; then
if stat -c '%a' "$mfile" >/dev/null 2>&1; then if stat -c '%a' "$mfile" >/dev/null 2>&1; then
# GNU stat
STAT_OPTION='-c' STAT_OPTION='-c'
STAT_FORMAT='%a' STAT_FORMAT='%a'
else else
# BSD stat
STAT_OPTION='-f' STAT_OPTION='-f'
STAT_FORMAT='%A' # Octal output, user/group/other (omit file type, sticky bit)
STAT_FORMAT='%OLp'
fi fi
fi fi
stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile" stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile"
@ -201,6 +299,7 @@ function get_mode {
function _create { function _create {
shopt -s nullglob shopt -s nullglob
local mode local mode
local eof_without_newline
while (( "$#" )); do while (( "$#" )); do
file=$1 file=$1
if [ -L "$file" ]; then if [ -L "$file" ]; then
@ -224,8 +323,30 @@ function _create {
elif [ -f "$file" ]; then elif [ -f "$file" ]; then
echo "Path: $file" echo "Path: $file"
lines=$(wc -l "$file"|awk '{print $1}') lines=$(wc -l "$file"|awk '{print $1}')
eof_without_newline=0
if [[ "$(wc -c "$file"|awk '{print $1}')" -gt 0 ]] && \
[[ "$(tail -c 1 "$file" | wc -l)" -eq 0 ]]; then
eof_without_newline=1
lines=$((lines+1))
fi
echo "Lines: $lines" echo "Lines: $lines"
cat "$file" # Add backslash in front of EOF
# Add backslash in front of NULLBYTE
# Replace null byte with NULLBYTE
if [ $USE_PYTHON -eq 1 ]; then
< "$file" python -c "$PYTHON_CREATE_FILTER"
else
< "$file" \
sed 's/EOF/\\EOF/g;
s/NULLBYTE/\\NULLBYTE/g;
s/\x0/NULLBYTE/g;
'
fi
if [[ "$eof_without_newline" -eq 1 ]]; then
# Finish line with EOF to indicate that the original line did
# not end with a linefeed
echo "EOF"
fi
mode=$(get_mode "$file") mode=$(get_mode "$file")
echo "Mode: $mode" echo "Mode: $mode"
vecho "$mode $file" vecho "$mode $file"
@ -254,6 +375,8 @@ function create {
_create "$@" _create "$@"
} }
test_environment
if [ -n "${CDIR:-}" ]; then if [ -n "${CDIR:-}" ]; then
if [[ "$ARCHIVE" != /* ]]; then if [[ "$ARCHIVE" != /* ]]; then
# Relative path: preserve the archive's location before changing # Relative path: preserve the archive's location before changing

Loading…
Cancel
Save