Files

71 lines
3.1 KiB
Bash
Raw Permalink Normal View History

2025-10-19 11:55:24 +03:00
#!/bin/sh
# lshar.sh - Luxferre's shell archiver
# Outputs to stdout only (for GNU and BSD shar compatibility)
# No external deps for .shars (sh commands required: mkdir, printf)
# Archive creation also depends on sort (Busybox, Toybox or GNU Coreutils)
# Works with any text or binary files and directory structures
# Created by Luxferre in 2023, released into public domain
# a portable, compact, no-deps serializer of any file into printf sequence
f2p() {
c=0 # init counter
l="$2" # get chunk length
[ -z "$l" ] && l=4096 # 4096-byte chunks by default (shouldn't be a problem)
cprefix="printf '%b' '" # pattern to prepend to every chunk
csuffix="' >> '$1'\n" # pattern to append to every chunk
printf '%b' "$cprefix" # always start with the prefix
IFS='' # separator
while read -r -s -d '' -n1 b; do # convert every byte
if [ "$c" == "$l" ]; then # roll over and output suffix+prefix
c=0 # reinit counter
printf '%b' "$csuffix" # end the chunk
printf '%b' "$cprefix" # begin a new chunk
else
c="$(( c + 1 ))" # increment c
fi
bcode="$(printf '%d' "'$b")" # get the ASCII code of the byte
if [ "$bcode" -gt 32 ] && [ "$bcode" -lt 127 ] &&
[ "$bcode" -ne 39 ] && [ "$bcode" -ne 92 ]
then # printable and not a single quote or a backslash
printf '%b' "$b" # codes from 34 to 126 are safe to print
else # subject to escaping, print the hex representation
printf '\\x%02x' "$bcode"
fi
done <"$1"
printf '%b' "$csuffix" # always end with the suffix
}
# main code
LF=$'\n' # cache the linefeed character
flist='' # pre-build the file list from args
dlist='' # pre-build the directory list from args
for i; do # iterate over args
bname="${i##*/}" # get the basename
if [ -d "$i" ]; then # it's a directory, we skip . and ..
[ "$bname" == '.' ] || [ "$bname" == '..' ] && continue
dlist="${dlist}${i%/}${LF}" # append the directory
else # it's a file
dname="${i%%$bname}" # get the basedir
dlist="${dlist}${dname%/}${LF}" # append the basedir
flist="${flist}${i}${LF}" # append the file
fi
done
flist="$(printf '%s' "$flist" | sort -u)" # remove file duplicates
dlist="$(printf '%s' "$dlist" | sort -u)" # remove directory duplicates
printf '%s\n' '#!/bin/sh' # output the shebang, just in case
IFS="$LF" # update the separator
for indir in $dlist; do # create the directory structure first
[ -z "$indir" ] && continue # skip empty names
printf '# dir: %s\n' "$indir"
printf "printf 'c - %%s\\\n' '%s'\n" "$indir" # announce creation
printf "mkdir -p '%s' > /dev/null 2>&1\n" "$indir" # create the dir
done
for infile in $flist; do # iterate over the file list afterwards
[ -z "$infile" ] && continue # skip empty names
printf '# file: %s\n' "$infile"
printf "printf 'x - %%s\\\n' '%s'\n" "$infile" # announce unrolling
printf "printf '' > '%s'\n" "$infile" # clear the file
f2p "$infile" # run the serializer
IFS="$LF" # update the separator
done