2021-11-13 20:56:19 -06:00
#!/usr/bin/env bash
# "Import" our needed functions from our library
LIBRARY_PATH = "/usr/local/lib/Custom-Scripts"
# shellcheck source=/usr/local/lib/
2021-11-13 21:16:57 -06:00
if ! source " ${ LIBRARY_PATH } /Logging-RGB.bash " ; then
echo " Unable to source Logging-RGB.bash at ${ LIBRARY_PATH } "
exit 1
fi
2021-11-13 20:56:19 -06:00
# shellcheck source=/usr/local/lib/
2021-11-13 21:16:57 -06:00
if ! source " ${ LIBRARY_PATH } /Trim.bash " ; then
log "error" " Unable to source Trim.bash at ${ LIBRARY_PATH } "
exit 1
fi
2021-11-13 20:56:19 -06:00
# Tmp file used because most distros have this dir exist in RAM, use PID of our process to write to
TEMP_FILE = " /tmp/ $$ "
# Set the Contents directory to the env variable, script set, or to their .contents file in the user home
CONTENTS_DIRECTORY = " ${ CONTENTS_DIRECTORY : = \
$( mkdir -p " ${ HOME } /.contents " && echo " ${ HOME } /.contents " ) } "
EXIT_ON_ERROR = 0
REPEAT = 1
WRITE_TEMPLATE_MANUALLY_SET = 1
WRITE_TEMPLATE_TO = ""
declare -A template_handlers
template_handlers = (
[ "FILE" ] = "file_handler"
[ "WRITE-TO" ] = "write_to_handler"
[ "TEMPLATE" ] = "template_handler"
)
write_content( ) {
local content
local leading_spaces
content = " ${ 1 } "
leading_spaces = " ${ 2 } "
local string_builder
string_builder = ""
for ( ( i = 1; i<= leading_spaces; i++) ) ; do
string_builder = " ${ string_builder } "
done
string_builder = " ${ string_builder } ${ content } "
_write_to_buffer " ${ string_builder } "
}
_write_to_buffer( ) {
log "debug" " Received content to write to buffer:
${ 1 } "
printf "%s\n" " ${ 1 } " >> " ${ TEMP_FILE } "
log "debug" "Finished writing content to buffer"
}
template_handler( ) {
local template_file
template_file = " ${ 1 } "
local leading_spaces
leading_spaces = " ${ 2 } "
# Check that the given file is not an absolute path
if [ [ ! " ${ template_file : 0 : 1 } " = = "/" ] ] ; then
template_file = " ${ CONTENTS_DIRECTORY } / ${ template_file } "
fi
if [ [ ! -f " ${ template_file } " ] ] ; then
log "error" " The given template file \" ${ template_file } \" does not exist "
return 1
fi
file_handler " ${ template_file } " " ${ leading_spaces } "
REPEAT = 0
}
file_handler( ) {
local file
file = " ${ 1 } "
local leading_spaces
leading_spaces = " ${ 2 } "
# Check that the given file is not an absolute path
if [ [ ! " ${ file : 0 : 1 } " = = "/" ] ] && [ [ ! -f " ${ file } " ] ] ; then
file = " ${ CONTENTS_DIRECTORY } / ${ file } "
fi
if [ [ ! -f " ${ file } " ] ] ; then
log "error" " The given file \" ${ file } \" does not exist "
return 1
fi
while IFS = "\n" read -r line; do
write_content " ${ line } " " ${ leading_spaces } "
done < " ${ file } "
}
write_to_handler( ) {
local write_path
write_path = ${ 1 }
log "debug" " Received write path \" ${ write_path } \" "
if [ [ ! " ${ write_path : 0 : 1 } " = = "/" ] ] ; then
log "error" " Given write path was invalid, received \" ${ write_path } \", the path must be an absolute path "
return 1
fi
if [ [ -d " ${ write_path } " ] ] ; then
log "error" " Given write path was invalid, received \" ${ write_path } \", the path must be a path to an output file (doesn't have to exist), not a directory "
return 1
fi
# This means that it was NOT manually set, 0 is true in shell
if ( ( " ${ WRITE_TEMPLATE_MANUALLY_SET } " = = 1) ) ; then
WRITE_TEMPLATE_TO = ${ write_path }
fi
}
parse_template_extracted( ) {
local tmpl_extracted
tmpl_extracted = " ${ 1 } "
local full_line
full_line = " ${ 2 } "
local num_leading_spaces
num_leading_spaces = $( awk -F"[ ]" '{for(i=1;i<=NF && ($i=="");i++);print ""i-1""}' <<< " ${ full_line } " )
# Basic checks to ensure template is correct, first not empty, second that an '=' was passed to give type specifier
# third is to ensure only one equals sign is passed
if [ [ -z " ${ tmpl_extracted } " ] ] ; then
log "error" "Template passed was empty... ignoring"
return 1
elif [ [ ! " ${ tmpl_extracted } " = *"=" * ] ] ; then
log "error" "Template line passed was of an invalid format, missing = specifier, assumed type is missing as well"
return 1
elif [ " $( echo " ${ tmpl_extracted } " | grep -o "=" | wc -l) " -gt 1 ] ; then
log "error" "Template line passed had too many \"=\", there should only be 1"
return 1
fi
local var_type
var_type = " $( trim " $( echo " ${ tmpl_extracted } " | cut -d "=" -f1) " ) "
# Shellcheck incorrectly reads the below line
# shellcheck disable=SC2116
var_type = " $( echo " ${ var_type ^^ } " ) "
local var_arg
var_arg = " $( trim " $( echo " ${ tmpl_extracted } " | cut -d "=" -f2) " ) "
local handler_to_use
handler_to_use = " ${ template_handlers [ ${ var_type } ] } "
if [ [ -z " ${ handler_to_use } " ] ] ; then
log "error" " Invalid handler found, no such handler \" ${ var_type } \" exists as a handler "
return 1
else
log "debug" " Using handler \" ${ handler_to_use } \" "
if ! eval " ${ handler_to_use } " " ${ var_arg } " " ${ num_leading_spaces } " ; then
log "error" " Arguments passed for \" ${ var_type } \" were invalid, check the argument "
return 1
fi
log "debug" " Finished using handler \" ${ handler_to_use } \" "
fi
}
output_finished_template( ) {
log "debug" "Outputting finished template"
mkdir -p " $( dirname " ${ WRITE_TEMPLATE_TO } " ) "
# We want globbing here
# shellcheck disable=SC2086
if [ [ -z " ${ WRITE_TEMPLATE_TO } " ] ] ; then
log "error" "Was never given a path to write the template to, check your template file or explicitly specify an output location"
exit 1
else
mv " ${ TEMP_FILE } " ${ WRITE_TEMPLATE_TO }
fi
}
parse_template_line( ) {
local line
line = " ${ 1 } "
2021-11-15 14:33:21 -06:00
local tmpl_extracted
2021-11-13 20:56:19 -06:00
if [ [ " ${ line } " = *\{ #%*%#\}* ]]; then
2021-11-15 14:33:21 -06:00
tmpl_extracted = " ${ line #* \{ #% } "
2021-11-13 20:56:19 -06:00
tmpl_extracted = " ${ tmpl_extracted % \% # \} * } "
tmpl_extracted = " $( trim " ${ tmpl_extracted } " ) "
log "debug" " Extracted a template: ${ tmpl_extracted } "
# Check for non zero status codes and parse the extracted template
if ! parse_template_extracted " ${ tmpl_extracted } " " ${ line } " ; then
return 1
fi
log "debug" " Finished extracting template: ${ tmpl_extracted } "
else
write_content " ${ line } " "0"
fi
}
read_template( ) {
local read_file
local line
local line_num
line_num = 1
read_file = " ${ 1 } "
log "info" " Parsing template file \" ${ read_file } \" "
while IFS = "\n" read -r line; do
# Check for non zero status code
if ! parse_template_line " ${ line } " ; then
log "error" " Invalid template passed from \" ${ read_file } \", check line ${ line_num } "
if ( ( " ${ EXIT_ON_ERROR } " = = 0 ) ) ; then
exit 1
fi
fi
line_num = $(( line_num + 1 ))
done <" ${ read_file } "
}
templater( ) {
local read_file
read_file = " ${ 1 } "
[ [ ! -f " ${ read_file } " ] ] &&
log "error" " $( important " ${ read_file } " ) does not exist! " &&
return 1
read_template " ${ read_file } "
if ( ( " ${ REPEAT } " = = 0 ) ) ; then
REPEAT = 1
log "debug" " Repeat set to 0, repeating on the finished file located at ${ TEMP_FILE } "
mv " ${ TEMP_FILE } " " ${ TEMP_FILE } -repeat "
main "-t" " ${ TEMP_FILE } -repeat "
else
log "info" " Finished parsing template file \" ${ read_file } \" "
log "info" "Writing finished template..."
output_finished_template
log "info" " Finished writing template for \" ${ read_file } \" to \" ${ WRITE_TEMPLATE_TO } \" "
fi
}
2021-11-13 21:16:57 -06:00
arg_required( ) {
echo_rgb " ${ 1 } " 255 183 0
}
arg_optional( ) {
echo_rgb " ${ 1 } " 117 255 255
}
arg_description( ) {
echo_rgb " ${ @ } " 220 190 255
}
2021-11-13 20:56:19 -06:00
usage( ) {
# Print out usage instructions for the local script
#
# Arguments:
# None
#
# Usage:
# usage
#
# POSIX Compliant:
# Yes
#
printf "Usage: %s\n" \
2021-11-13 21:16:57 -06:00
" $( basename ${ 0 } ) [OPTIONS]
$( arg_required "REQUIRED" )
$( arg_optional "OPTIONAL" )
$( arg_required "--template-file" ) <templateFile: string> | $( arg_required "-t" ) <templateFile: string>
$( arg_description " A template file to parse and apply rules for
Example:
--template-file example.tmpl" )
$( arg_optional "--contents-dir" ) <contentsDirectory: string> | $( arg_optional "-c" ) <contentsDirectory: string>
$( arg_description " The files that contain contents read by the templates.
By default this is set to ~/.contents/ if unset, to set via variable you can either use this argument or pass
export CONTENTS_DIRECTORY = \` your contents directory\`
2021-11-13 20:56:19 -06:00
Example:
2021-11-13 21:16:57 -06:00
--contents-dir ~/Desktop/contents" )
$( arg_optional "--output-to" ) <outputFile: string> | $( arg_optional "-o" ) <outputFile: string>
$( arg_description " The file to output the applied template to, this can be set within a template, but can be overriden with this option
2021-11-13 20:56:19 -06:00
Example:
2021-11-13 21:16:57 -06:00
--output-to myfile.out" )
$( arg_optional "--no-exit" ) | $( arg_optional "-n" )
$( arg_description " Do not exit on any errors, continue executing
Example:
--no-exit")"
2021-11-13 20:56:19 -06:00
}
main( ) {
# Parse input arguments
#
# Arguments:
# Consult the `usage` function
#
# Usage:
# parse_args "$@"
# - All arguments should be ingested by parse_args first for variable setting
#
# POSIX Compliant:
# Yes
#
local template_file
while :; do
case ${ 1 } in
-h | -\? | --help)
usage # Display a usage synopsis.
exit
; ;
--) # End of all options.
break
; ;
-t | --template-file)
shift
template_file = " ${ 1 } "
[ [ ! -f " ${ template_file } " ] ] &&
log "error" " The given template file \" ${ template_file } \" does not exist " &&
exit 1
log "info" " Set template file to \" ${ template_file } \" "
; ;
-c | --contents-dir)
shift
CONTENTS_DIRECTORY = " ${ 1 } "
[ [ ! -d " ${ CONTENTS_DIRECTORY } " ] ] &&
log "error" " \" ${ CONTENTS_DIRECTORY } \" is an invalid path, contents directory must be an absolute path " &&
exit 1
log "info" " Set contents directory to \" ${ CONTENTS_DIRECTORY } \" "
; ;
-o | --output-to)
shift
WRITE_TEMPLATE_MANUALLY_SET = 0
WRITE_TEMPLATE_TO = " ${ 1 } "
; ;
-n | --no-exit)
EXIT_ON_ERROR = 1
log "info" "No longer exiting on errors."
; ;
-?*)
printf 'Unknown option: %s\n' " $1 " >& 2
usage
exit 1
; ;
*) # Default case: No more options, so break out of the loop.
break ; ;
esac
shift
done
[ [ -z " ${ template_file } " ] ] &&
log "error" "No template file provided, exiting" &&
exit 1
templater " ${ template_file } "
}
main " $@ "