8ac29cc825
Test compilation performed with warnings. Author and license added. Test for failing grep on ancient OSX versions. Replaced the test image with something less noisy (which compresses better).
125 lines
2.8 KiB
Bash
Executable File
125 lines
2.8 KiB
Bash
Executable File
#!/bin/sh -e
|
|
|
|
# Tool to bundle multiple C/C++ source files, inlining any includes.
|
|
#
|
|
# Note: this POSIX-compliant script is many times slower than the original bash
|
|
# implementation (due to the grep calls) but it runs and works everywhere.
|
|
#
|
|
# TODO: ROOTS and FOUND as arrays (since they fail on paths with spaces)
|
|
#
|
|
# Author: Carl Woffenden, Numfum GmbH (released under a CC0 license)
|
|
|
|
# Common file roots
|
|
ROOTS="./"
|
|
|
|
# Files previously visited
|
|
FOUND=""
|
|
|
|
# Optional destination file (empty string to write to stdout)
|
|
DESTN=""
|
|
|
|
# Prints the script usage then exits
|
|
usage() {
|
|
echo "Usage: $0 [-r <path>] [-o <outfile>] infile"
|
|
echo " -r file root search paths"
|
|
echo " -o output file (otherwise stdout)"
|
|
echo "Example: $0 -r ../my/path - r ../other/path -o out.c in.c"
|
|
exit 1
|
|
}
|
|
|
|
# Tests that the grep implementation works as expected (older OSX grep fails)
|
|
test_grep() {
|
|
if ! echo '#include "foo"' | grep -Eq '^\s*#\s*include\s*".+"'; then
|
|
echo "Aborting: the grep implementation fails to parse include lines"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Tests if list $1 has item $2 (returning zero on a match)
|
|
list_has_item() {
|
|
if echo "$1" | grep -Eq "(^|\s*)$2(\$|\s*)"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Adds a new line with the supplied arguments to $DESTN (or stdout)
|
|
write_line() {
|
|
if [ -n "$DESTN" ]; then
|
|
printf '%s\n' "$@" >> "$DESTN"
|
|
else
|
|
printf '%s\n' "$@"
|
|
fi
|
|
}
|
|
|
|
# Adds the contents of $1 with any of its includes inlined
|
|
add_file() {
|
|
# Match the path
|
|
local file=
|
|
if [ -f "$1" ]; then
|
|
file="$1"
|
|
else
|
|
for root in $ROOTS; do
|
|
if test -f "$root/$1"; then
|
|
file="$root/$1"
|
|
fi
|
|
done
|
|
fi
|
|
if [ -n "$file" ]; then
|
|
# Read the file
|
|
local line=
|
|
while IFS= read -r line; do
|
|
if echo "$line" | grep -Eq '^\s*#\s*include\s*".+"'; then
|
|
# We have an include directive so strip the (first) file
|
|
local inc=$(echo "$line" | grep -Eo '".*"' | grep -Eo '\w*(\.?\w+)+' | head -1)
|
|
if ! list_has_item "$FOUND" "$inc"; then
|
|
# And we've not previously encountered it
|
|
FOUND="$FOUND $inc"
|
|
write_line "/**** start inlining $inc ****/"
|
|
add_file "$inc"
|
|
write_line "/**** ended inlining $inc ****/"
|
|
else
|
|
write_line "/**** skipping file: $inc ****/"
|
|
fi
|
|
else
|
|
# Otherwise write the source line
|
|
write_line "$line"
|
|
fi
|
|
done < "$file"
|
|
else
|
|
write_line "#error Unable to find \"$1\""
|
|
fi
|
|
}
|
|
|
|
while getopts ":r:o:" opts; do
|
|
case $opts in
|
|
r)
|
|
ROOTS="$OPTARG $ROOTS"
|
|
;;
|
|
o)
|
|
DESTN="$OPTARG"
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND-1))
|
|
|
|
if [ -n "$1" ]; then
|
|
if [ -f "$1" ]; then
|
|
if [ -n "$DESTN" ]; then
|
|
printf "" > "$DESTN"
|
|
fi
|
|
test_grep
|
|
add_file $1
|
|
else
|
|
echo "Input file not found: \"$1\""
|
|
exit 1
|
|
fi
|
|
else
|
|
usage
|
|
fi
|
|
exit 0
|