commit 68ee57cdfca08c14944c0be75d1efed54fbdf671 Author: bakkeby Date: Wed Oct 16 21:21:25 2019 +0200 Adding flexipatch-finalizer diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..2d34caf --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Stein Gunnar Bakkeby +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..390fb5a --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +Most [suckless.org](https://suckless.org) software comes with basic functionality which can be _extended_ by applying code _patches_. This typically involves a fair deal of tinkering on behalf of the end user especially when applying more than one patch. + +The _flexipatch_ builds have a different take on patching where preprocessor directives are used to decide whether or not to include a patch during build time. This means, for better or worse, that the code contains both the patched and the original code. The aim being that you can pick and mix your patches from a configuration file and just compile. + +The _flexipatch-finalizer_ is a custom pre-processor that uses the same configuration file and strips a flexipatch build of any unused code, leaving a build of the software with the selected patches applied. + +Example flexipatch builds this finalizer can be used with: + + - [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) + - [dmenu-flexipatch](https://github.com/bakkeby/dmenu-flexipatch) + - [st-flexipatch](https://github.com/bakkeby/st-flexipatch) + +:warning: Do make sure that you make a backup of your flexipatch build and your `patches.h` configuration file before running this script. + +:warning: This script alters and removes files within the given source directory. + +:warning: This process is irreversible. + + +Example usage: + +```bash +$ ./flexipatch-finalizer.sh +Usage: flexipatch-finalizer.sh [OPTION?] + +This is a custom pre-processor designed to remove unused flexipatch patches and create a final build. + + -r, --run include this flag to confirm that you really do want to run this script + + -d, --directory the flexipatch directory to process (defaults to current directory) + -h, --help display this help section + -k, --keep keep temporary files and do not replace the original ones + -e, --echo echo commands that will be run rather than running them + --debug prints additional debug information to stderr + +Warning! This script alters and removes files within the source directory. +Warning! This process is irreversible! Use with care. Do make a backup before running this. + +$ ./flexipatch-finalizer.sh -r -d /path/to/dwm-flexipatch +$ +``` + +Example end diff having VERTCENTER_PATCH enabled for st. + +```diff +... +/* Purely graphic info */ /* Purely graphic info */ +typedef struct { typedef struct { + int tw, th; /* tty width and height */ int tw, th; /* tty width and height */ + int w, h; /* window width and height */ int w, h; /* window width and height */ + #if ANYSIZE_PATCH < + int hborderpx, vborderpx; < + #endif // ANYSIZE_PATCH < + int ch; /* char height */ int ch; /* char height */ + int cw; /* char width */ int cw; /* char width */ + #if VERTCENTER_PATCH < + int cyo; /* char y offset */ int cyo; /* char y offset */ + #endif // VERTCENTER_PATCH < + int mode; /* window state/mode flags */ int mode; /* window state/mode flags */ + int cursor; /* cursor style */ int cursor; /* cursor style */ + #if VISUALBELL_2_PATCH || VISUALBELL_3_PATCH < + int vbellset; /* 1 during visual bell, 0 otherwise */ < + struct timespec lastvbell; < + #endif // VISUALBELL_2_PATCH < +} TermWindow; } TermWindow; +... +``` \ No newline at end of file diff --git a/flexipatch-finalizer.sh b/flexipatch-finalizer.sh new file mode 100755 index 0000000..e139236 --- /dev/null +++ b/flexipatch-finalizer.sh @@ -0,0 +1,285 @@ +#!/bin/bash + +KEEP_FILES=0 +ECHO_COMMANDS=0 +RUN_SCRIPT=0 +DIRECTORY=. +DEBUG=0 + +if [[ $# = 0 ]]; then + set -- '-h' +fi + +while (( $# )); do + case "$1" in + -d|--directory) + shift + DIRECTORY=$1 + shift + ;; + --debug) + shift + DEBUG=1 + ;; + -r|--run) + shift + RUN_SCRIPT=1 + ;; + -e|--echo) + shift + ECHO_COMMANDS=1 + ;; + -k|--keep) + shift + KEEP_FILES=1 + ;; + -h|--help) + shift + fmt=" %-31s%s\n" + + printf "%s" "Usage: $(basename ${BASH_SOURCE[0]}) [OPTION?]" + printf "\n" + printf "\nThis is a custom pre-processor designed to remove unused flexipatch patches and create a final build." + printf "\n\n" + printf "$fmt" "-r, --run" "include this flag to confirm that you really do want to run this script" + printf "\n" + printf "$fmt" "-d, --directory " "the flexipatch directory to process (defaults to current directory)" + printf "$fmt" "-h, --help" "display this help section" + printf "$fmt" "-k, --keep" "keep temporary files and do not replace the original ones" + printf "$fmt" "-e, --echo" "echo commands that will be run rather than running them" + printf "$fmt" " --debug" "prints additional debug information to stderr" + printf "\nWarning! This script alters and removes files within the source directory." + printf "\nWarning! This process is irreversible! Use with care. Do make a backup before running this." + printf "\n\n" + exit + ;; + *) + echo "Ignoring unknown argument ($1)" + shift + ;; + esac +done + +if [[ $RUN_SCRIPT = 0 ]]; then + echo "Re-run this command with the --run option to confirm that you really want to run this script." + echo "The changes this script makes are irreversible." + exit 1 +fi + +if [[ ! -e ${DIRECTORY}/patches.h ]]; then + printf "No patches.h file found. Make sure you run this script within a flexipatch source directory." + exit 1 +fi + + +FILES_TO_DELETE=$(find $DIRECTORY -name "*.c" -o -name "*.h" | awk -v DEBUG="$DEBUG" -v DIRECTORY="$DIRECTORY" ' +function istrue(f) { + ret = 0 + for ( i = 2; i <= length(f); i++ ) { + if ( f[i] == "||" ) { + if ( ret == -1 ) { + ret = 0 + } else if ( ret == 1 ) { + break + } + continue + } else if ( f[i] == "&&" ) { + if ( ret == 0 ) { + ret = -1 + } + continue + } else if ( ret == -1 ) { + continue + } else if ( f[i] !~ /_(PATCH|LAYOUT)$/ ) { + ret = 1 + } else if ( f[i] ~ /^!/ ) { + ret = !patches[substr(f[i],2)] + } else { + ret = patches[f[i]] + } + } + + if ( ret == -1 ) { + ret = 0 + } + + return ret +} + +function schedule_delete(file) { + /* Skip duplicates */ + for ( i = 1; i < length(files_to_delete); i++ ) { + if ( files_to_delete[i] == file) { + return + } + } + if (DEBUG) { + print "Scheduling file " file " for deletion." > "/dev/stderr" + } + files_to_delete[length(files_to_delete)] = file +} + +function is_flexipatch(patch) { + return patch ~ /_(PATCH|LAYOUT)$/ +} + +BEGIN { + /* Read patches.h and store patch settings in the patches associative array */ + if (DEBUG) { + print "Reading file " DIRECTORY "/patches.h" > "/dev/stderr" + } + while (( getline line < (DIRECTORY"/patches.h") ) > 0 ) { + split(line,f) + if ( f[1] ~ /^#define$/ ) { + patches[f[2]] = f[3] + if (DEBUG) { + print "Found " f[2] " = " f[3] > "/dev/stderr" + } + } + } + files_to_delete[0] = "" +} + +{ + level = 0 + do_print[level] = 1 + has_printed[level] = 0 + condition[level] = "" + + while (( getline line < $0) > 0 ) { + split(line,f) + if ( f[1] ~ /^#if$/ ) { + do_print[++level] = do_print[level] + has_printed[level] = 0 + condition[level] = f[2] + if ( do_print[level] ) { + if ( istrue(f) ) { + has_printed[level] = 1 + do_print[level] = 1 + } else { + do_print[level] = 0 + } + } + if ( is_flexipatch(condition[level]) ) { + continue + } + } else if ( f[1] ~ /^#ifdef$/ || f[1] ~ /^#ifndef$/ ) { + do_print[++level] = do_print[level] + has_printed[level] = 0 + condition[level] = f[2] + if ( do_print[level] ) { + has_printed[level] = 1 + do_print[level] = 1 + } + if ( is_flexipatch(condition[level]) ) { + continue + } + } else if ( f[1] ~ /^#elif$/ ) { + if ( has_printed[level] == 0 && do_print[level-1] == 1 ) { + if ( istrue(f) ) { + has_printed[level] = 1 + do_print[level] = 1 + } else { + do_print[level] = 0 + } + } else { + do_print[level] = 0 + } + if ( is_flexipatch(f[2]) ) { + continue + } + } else if ( f[1] ~ /^#else$/ ) { + if ( has_printed[level] == 0 && do_print[level-1] == 1 ) { + has_printed[level] = 1 + do_print[level] = 1 + } else { + do_print[level] = 0 + } + if ( is_flexipatch(condition[level]) ) { + continue + } + } else if ( f[1] ~ /^#include$/ && f[2] ~ /^"/ && (do_print[level] == 0 || f[2] == "\"patches.h\"") ) { + dir = "" + if ( $0 ~ /\// ) { + dir = $0 + sub("/[^/]+$", "/", dir) + } + schedule_delete(dir substr(f[2], 2, length(f[2]) - 2)) + continue + } else if ( f[1] ~ /^#endif$/ ) { + if ( is_flexipatch(condition[level]) ) { + level-- + continue + } + level-- + } + + if ( do_print[level] ) { + print line > $0 ".~" + } + } +} + +END { + for ( i = 1; i < length(files_to_delete); i++ ) { + print files_to_delete[i] + } +} +') + +# Chmod and replace files +for FILE in $(find $DIRECTORY -name "*.~"); do + chmod --reference=${FILE%%.~} ${FILE} + if [[ $KEEP_FILES = 0 ]] || [[ $ECHO_COMMANDS = 1 ]]; then + if [[ $ECHO_COMMANDS = 1 ]]; then + echo "mv ${FILE} ${FILE%%.~}" + else + mv ${FILE} ${FILE%%.~} + fi + fi +done + +# Delete unnecessary files +if [[ $KEEP_FILES = 0 ]] || [[ $ECHO_COMMANDS = 1 ]]; then + for FILE in $FILES_TO_DELETE; do + if [[ $ECHO_COMMANDS = 1 ]]; then + echo "rm $FILE" + else + rm "$FILE" + fi + done + + if [[ -f $DIRECTORY/README.md ]]; then + if [[ $ECHO_COMMANDS = 1 ]]; then + echo "rm $DIRECTORY/README.md" + else + rm $DIRECTORY/README.md + fi + fi + + # Remove empty include files + INCLUDE_RE='*patch/*include.[hc]' + if [[ $ECHO_COMMANDS = 1 ]]; then + INCLUDE_RE='*patch/*include.[hc][.]~' + fi + for FILE in $(find $DIRECTORY -path $INCLUDE_RE); do + if [[ $(grep -c "#include " $FILE) = 0 ]]; then + if [[ $ECHO_COMMANDS = 1 ]]; then + echo "rm ${FILE%%.~}" + else + rm "$FILE" + fi + + for LINE in $(grep -Ern "#include \"patch/$(basename ${FILE%%.~})\"" $DIRECTORY | grep -v '.~:' | awk -F":" '{print $1 ":" $2 }'); do + INCFILE=$(echo $LINE | cut -d":" -f1) + LINE_NO=$(echo $LINE | cut -d":" -f2) + if [[ $ECHO_COMMANDS = 1 ]]; then + echo "sed -i \"${LINE_NO}d\" ${INCFILE}" + else + sed -i "${LINE_NO}d" ${INCFILE} + fi + done + fi + + done +fi \ No newline at end of file