diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -54,8 +54,12 @@ ) install( FILES kdevplatform_shell_environment.sh DESTINATION bin PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) -install( FILES kdev_format_source DESTINATION bin PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) +########### kdev_format_source executable ############### + +add_executable(kdev_format_source kdev_format_source.cpp) +target_link_libraries(kdev_format_source Qt5::Core) +install(TARGETS kdev_format_source DESTINATION bin) ########### install files ############### diff --git a/util/kdev_format_source b/util/kdev_format_source deleted file mode 100644 --- a/util/kdev_format_source +++ /dev/null @@ -1,132 +0,0 @@ -#!/bin/bash - -# Author: David Nolden -# This script is made available under the GPLv2 licence. -# -# This script formats a given source-file automatically by -# using formatting scripts as defined in specific meta-information -# "format_sources" files contained in the file system hierarchy. -# -# The rules within a "format_sources" file apply to all subdirectories, -# and follow this syntax: -# Each line defines a list of wildcards followed by a colon and the formatting-command. -# Example: "*.cpp *.h : my_custom_formatting_script.sh $TMPFILE" -# -# The file must be terminated by a trailing newline. -# -# If no colon and no wildcards are given, the command is -# used for everything, equivalently to the "*" wildcard. -# -# The contents is processed in linear order, and the first matching command is used. - -ORIGFILE=$1 -TMPFILE=$2 - -if ! [ "$ORIGFILE" ]; then - echo "Usage: $(basename $0) [FILE] [TEMPFILE]" - echo "" - echo "Where FILE represents the original location of the formatted contents," - echo "and TEMPFILE is used as the actual, potentially different," - echo "contents of the file." - exit -fi - -ORIGFILE=$(readlink -f $ORIGFILE) - -if ! [ $TMPFILE ]; then - echo "No tempfile given, formatting the original file" - TMPFILE=$ORIGFILE -else - TMPFILE=$(readlink -f $TMPFILE) -fi - -# Helper: Returns the relative path from a given source directory to a target path -function relativePath { - source=$1 - target=$2 - - common_part=$source - back= - while [ "${target#$common_part}" = "${target}" ]; do - common_part=$(dirname $common_part) - back="../${back}" - done - - echo ${back}${target#$common_part/} -} - -# Go to the directory of the original file, and start searching for "format_sources" files upwards -cd $(dirname $ORIGFILE) - -while ! [ "$(pwd)" == "/" ]; do - - if [ -e "format_sources" ]; then - echo "found $(readlink -f format_sources)" - - # Read line by line - while read line - do -# echo "Line: $line" - # Split by the ":" which is the delimiter between wildcards - IFS="\:" - array= - pos=0 - - # remove leading whitespace - line="${line#"${line%%[![:space:]]*}"}" - - if [[ "$line" == \#* ]] || ! [ "$line" ]; then - # Ignore lines starting with # - # Those can be used for comments. - # Also ignore empty lines - continue - fi - - for item in $line; - do - array[$pos]=$item - pos=$(($pos+1)) - done - - if [ $pos == "2" ]; then - # We found the correct syntax with "wildcards : command" - WILDCARDS=${array[0]} - COMMAND=${array[1]} - - MATCHED=0 - -# echo "wildcards: $WILDCARDS" - - RELATIVE_ORIGFILE=$(relativePath $(pwd) $ORIGFILE) -# echo "relative path: $RELATIVE_ORIGFILE" - - IFS=" " - set -f # This disables the wildcard expansion, we don't want it - for WILDCARD in $WILDCARDS; do - set +f - # This if-command does wildcard matching -# echo "matching $RELATIVE_ORIGFILE and $WILDCARD" - if [[ "$RELATIVE_ORIGFILE" == $WILDCARD ]]; then - echo "matched $RELATIVE_ORIGFILE with wildcard $WILDCARD, using command \"$COMMAND\"" - eval $COMMAND - exit - fi - set -f - done - set +f - fi - - if [ $pos == "1" ]; then - # We found the simple syntax without wildcards, and only with the command - COMMAND=${array[0]} - echo "matched without wildcard, using command $COMMAND" - eval $COMMAND - exit - fi - - done < format_sources - fi - - - cd .. -done diff --git a/util/kdev_format_source.cpp b/util/kdev_format_source.cpp new file mode 100644 --- /dev/null +++ b/util/kdev_format_source.cpp @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include + +static const QString formatFileName = QStringLiteral("format_sources"); + +inline QTextStream& qStdOut() +{ + static QTextStream r{stdout}; + return r; +} + +struct KDevFormatLine +{ + QStringList wildcards; + QString command; +}; + +class KDevFormatFile +{ +public: + KDevFormatFile(const QString& origFilePath, const QString& tempFilePath); + + bool find(); + bool read(); + bool apply(); + +private: + bool executeCommand(QString command); + + QString m_origFilePath; + QString m_tempFilePath; + + QVector m_formatLines; +}; + +int main(int argc, char** argv) +{ + static const QString formatFileName = QStringLiteral("format_sources"); + + if (argc == 1) { + qStdOut() << "Usage:" << argv[0] << "ORIGFILE [TMPFILE]\n\n"; + qStdOut() << "Where ORIGFILE represents the original location of the formatted contents,\n"; + qStdOut() << "and TMPFILE is used as the actual, potentially different,\n"; + qStdOut() << "contents of the file.\n"; + return EXIT_FAILURE; + } + + QFileInfo origFileInfo(argv[1]); + if (!origFileInfo.exists()) { + qStdOut() << "orig file \"" << origFileInfo.absoluteFilePath() << "\" does not exits\n"; + return EXIT_FAILURE; + } + + QString origFilePath = origFileInfo.canonicalFilePath(); + QString tempFilePath; + + if (argc > 2) + tempFilePath = QFileInfo(argv[2]).canonicalFilePath(); + else { + tempFilePath = origFilePath; + qStdOut() << "no temp file given, formatting the original file\n"; + } + + KDevFormatFile formatFile(origFilePath, tempFilePath); + + if (!formatFile.find()) + return EXIT_FAILURE; + + if (!formatFile.read()) + return EXIT_FAILURE; + + if (!formatFile.apply()) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +KDevFormatFile::KDevFormatFile(const QString& origFilePath, const QString& tempFilePath) + : m_origFilePath(origFilePath) + , m_tempFilePath(tempFilePath) +{ +} + +bool KDevFormatFile::find() +{ + QDir srcDir(QFileInfo(m_origFilePath).canonicalPath()); + + while (!srcDir.isRoot()) { + if (srcDir.exists(formatFileName)) { + QDir::setCurrent(srcDir.canonicalPath()); + + qStdOut() << "found \"" + << QFileInfo(srcDir.canonicalPath() + "/" + formatFileName).canonicalFilePath() + << "\"\n"; + return true; + } + + srcDir.cdUp(); + } + + return false; +} + +bool KDevFormatFile::read() +{ + QFile formatFile(formatFileName); + if (!formatFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qStdOut() << "unable to open \"" << formatFileName << "\"\n"; + return false; + } + + int lineNumber = 0; + QString line; + QStringList wildcards; + QString command; + + while (!formatFile.atEnd()) { + ++lineNumber; + + line = formatFile.readLine().trimmed(); + if (line.isEmpty() || line.startsWith('#')) + continue; + + QStringList elems = line.split(':'); + + // We found the simple syntax without wildcards, and only with the command + if (elems.size() == 1) { + wildcards.clear(); + command = elems[0].trimmed(); + + m_formatLines.append({wildcards, command}); + } + + // We found the correct syntax with "wildcards : command" + else if (elems.size() == 2) { + wildcards = elems[0].split(' ', QString::SkipEmptyParts); + command = elems[1].trimmed(); + + if (wildcards.isEmpty()) { + qStdOut() << formatFileName << ":" << lineNumber + << ": error: empty wildcard, skip the line\n"; + continue; + } + + m_formatLines.append({wildcards, command}); + } + + // We found the incorrect correct syntax + else { + qStdOut() << formatFileName << ":" << lineNumber + << ": error: too many delimiters (':'), skip the line\n"; + continue; + } + } + + if (m_formatLines.isEmpty()) { + qStdOut() << formatFileName << ": error: no commands are found\n"; + return false; + } + + return true; +} + +bool KDevFormatFile::apply() +{ + foreach (const KDevFormatLine& formatLine, m_formatLines) { + if (formatLine.wildcards.isEmpty()) { + qStdOut() << "matched \"" << m_origFilePath << "\" without wildcard"; + return executeCommand(formatLine.command); + } + + foreach(const QString& wildcard, formatLine.wildcards) { + if (QDir::match(QDir::current().canonicalPath() + "/" + wildcard.trimmed(), m_origFilePath)) { + qStdOut() << "matched \"" << m_origFilePath << "\" with wildcard \"" << wildcard << "\""; + return executeCommand(formatLine.command); + } + } + } + + qStdOut() << formatFileName << ": error: no commands applicable to \"" << m_origFilePath << "\"\n"; + return false; +} + +bool KDevFormatFile::executeCommand(QString command) +{ + qStdOut() << ", using command \"" << command << "\"\n"; + + command.replace(QStringLiteral("$ORIGFILE"), m_origFilePath); + command.replace(QStringLiteral("$TMPFILE"), m_tempFilePath); + + // Do force flush for stream before running command. + // Otherwise command's output can be shown before text, + // printed in previous code. + qStdOut().flush(); + + int execResult = QProcess::execute(command); + + if (execResult == -2) { + qStdOut() << "command \"" + command + "\" failed to start\n"; + return false; + } + + if (execResult == -1) { + qStdOut() << "command \"" + command + "\" crashed\n"; + return false; + } + + return true; +}