ds4mac – переключатель каталогов для macOS / Linux: механизм тегов

у меня есть эта программа в GitHub. Используется для перехода в каталоги через настраиваемые теги. Вот механизм тегов:

com::github::coderodde::ds4mac::DirectoryTagEntry.h:

//// /////////////////////////////////////////////  ////
 // Created by Rodion "rodde" Efremov on 19.11.2020. //
//// ////////////////////////////////////////////// ////
#ifndef COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_H
#define COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_H

#include <fstream>
#include <iostream>
#include <string>

namespace com::github::coderodde::ds4mac {

    class DirectoryTagEntry {
    private:
        std::string tagName;
        std::string directoryName;

    public:

        DirectoryTagEntry(std::string tagName, std::string directoryName);

        const std::string& getTagName() const;
        const std::string& getDirectoryName() const;

        void setTagName(const std::string& newTagName);
        void setDirectoryName(const std::string& newDirectoryName);

        size_t getLevenshteinDistance(std::string str) const;

        friend void operator>>(std::ifstream& ifs, DirectoryTagEntry& dte) {
            std::string readTagName;
            std::string readDirectoryName;

            std::getline(ifs, readDirectoryName);
            dte.setTagName(readTagName);
            dte.setDirectoryName(readDirectoryName);
        }

        friend void operator<<(std::ofstream& ofs, const DirectoryTagEntry& dte) {
            ofs << dte.getTagName() << " " << dte.getDirectoryName();
        }

        static struct {
            bool operator()(DirectoryTagEntry const& a, DirectoryTagEntry const& b) {
                return a.getTagName() < b.getTagName();
            }
        } tagComparator;

        static struct {
            bool operator()(DirectoryTagEntry const& a, DirectoryTagEntry const& b) {
                return a.getDirectoryName() < b.getDirectoryName();
            }
        } directoryComparator;
    };
}

#endif // COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_H

com::github::coderodde::ds4mac::DirectoryTagEntry.cpp:

//// ////////////////////////////////////////////// ////
 // Created by Rodion "rodde" Efremov on 19.11.2020. //
//// ////////////////////////////////////////////// ////

#include "DirectoryTagEntry.h"
#include <utility>
#include <vector>

namespace com::github::coderodde::ds4mac {

    using std::size_t;

    DirectoryTagEntry::DirectoryTagEntry(
        std::string tagName_,
        std::string directoryName_) :
        tagName{std::move(tagName_)},
        directoryName{std::move(directoryName_)} {}

    const std::string& DirectoryTagEntry::getTagName() const {
        return tagName;
    }

    const std::string& DirectoryTagEntry::getDirectoryName() const {
        return directoryName;
    }      

    void DirectoryTagEntry::setTagName(const std::string& newTagName) {
        tagName = newTagName;
    }

    void DirectoryTagEntry::setDirectoryName(const std::string& newDirectoryName) {
        directoryName = newDirectoryName;
    }

    size_t DirectoryTagEntry::getLevenshteinDistance(const std::string str) const {
        const size_t len1 = str.length() + 1;
        const size_t len2 = tagName.length() + 1;

        std::vector<std::vector<size_t>> distanceMatrix(len1);

        for (size_t i = 0; i < len1; i++) {
            std::vector<size_t> row(len2);
            distanceMatrix[i] = row;
        }

        for (size_t i = 1; i < len1; i++) {
            distanceMatrix[i][0] = i;
        }

        for (size_t i = 1; i < len2; i++) {
            distanceMatrix[0][i] = i;
        }

        for (size_t i1 = 1; i1 < len1; i1++) {
            for (size_t i2 = 1; i2 < len2; i2++) {
                size_t cost = (str[i1 - 1] == tagName[i2 - 1] ? 0 : 1);

                distanceMatrix[i1][i2] =
                       std::min(
                                std::min(distanceMatrix[i1 - 1][i2] + 1,
                                         distanceMatrix[i1][i2 - 1] + 1),
                                         distanceMatrix[i1 - 1][i2 - 1] + cost);
            }
        }      

        return distanceMatrix[len1 - 1][len2 - 1];
    }
}

com::github::coderodde::ds4mac::DirectoryTagEntryList.h:

//// /////////////////////////////////////////////  ////
 // Created by Rodion "rodde" Efremov on 20.11.2020. //
//// ////////////////////////////////////////////// ////

#ifndef COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_LIST_H
#define COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_LIST_H

#include "DirectoryTagEntry.h"
#include <vector>

namespace com::github::coderodde::ds4mac {

    class DirectoryTagEntryList {
    private:
        std::vector<DirectoryTagEntry> entries;

    public:
        const size_t size() const;
        DirectoryTagEntryList& operator<<(DirectoryTagEntry const& directoryTagEntry);
        DirectoryTagEntry at(size_t index) const;
        DirectoryTagEntry* operator[](std::string const& targetDirectoryName);
        void operator<<(std::ifstream& ifs);
        void sortByTags();
        void sortByDirectories();
        void listTags();
        void listTagsAndDirectories();
        friend void operator>>(std::ifstream&, DirectoryTagEntryList&);
        friend void operator>>(DirectoryTagEntryList const&, 
                               std::ofstream&);
    };
}

#endif // COM_GITHUB_CODERODDE_DS4MAC_DIRECTORY_TAG_ENTRY_LIST_H

com::github::coderodde::ds4mac::DirectoryTagEntryList.cpp:

//// ////////////////////////////////////////////// ////
 // Created by Rodion "rodde" Efremov on 20.11.2020. //
//// ////////////////////////////////////////////// ////

#include "DirectoryTagEntry.h"
#include "DirectoryTagEntryList.h"
#include <algorithm>
#include <limits>
#include <string>
//#include <linux/limits.h>

using com::github::coderodde::ds4mac::DirectoryTagEntry;
using std::getline;
using std::string;

namespace com::github::coderodde::ds4mac {

    const size_t DirectoryTagEntryList::size() const {
        return entries.size();
    }

    DirectoryTagEntryList& DirectoryTagEntryList::operator<<(
        const DirectoryTagEntry &directoryTagEntry) {
    
        entries.push_back(directoryTagEntry);
        return *this;
    }

    DirectoryTagEntry DirectoryTagEntryList::at(size_t index) const {
        return entries.at(index);
    }

    DirectoryTagEntry* DirectoryTagEntryList::operator[](
        const std::string& targetDirectoryName) {
    
        DirectoryTagEntry* ptrBestDirectoryEntry;
        size_t bestLevenshteinDistance = std::numeric_limits<size_t>::max();

        for (DirectoryTagEntry& dte : entries) {
            size_t levenshteinDistance = dte.getLevenshteinDistance(targetDirectoryName);

            if (levenshteinDistance == 0) {
                return &dte;
            }

            if (bestLevenshteinDistance > levenshteinDistance) {
                bestLevenshteinDistance = levenshteinDistance;
                ptrBestDirectoryEntry = &dte;
            }
        }

        return ptrBestDirectoryEntry;
    }

    void DirectoryTagEntryList::operator<<(std::ifstream& ifs) {

        using std::string;

    }

    void DirectoryTagEntryList::sortByTags() {
        std::stable_sort(entries.begin(), entries.end(), DirectoryTagEntry::tagComparator);
    }

    void DirectoryTagEntryList::sortByDirectories() {
        std::stable_sort(entries.begin(), entries.end(),  DirectoryTagEntry::directoryComparator);
    }

    // trim from start (in place)
    static inline void ltrim(std::string &s) {
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
            return !std::isspace(ch);
        }));
    }

    // trim from end (in place)
    static inline void rtrim(std::string &s) {
        s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
            return !std::isspace(ch);
        }).base(), s.end());
    }

    // trim from both ends (in place)
    static inline void trim(std::string &s) {
        ltrim(s);
        rtrim(s);
    }

    void operator>>(
        std::ifstream& ifs,
        DirectoryTagEntryList& directoryTagEntryList) {

        while (ifs.good() && !ifs.eof()) {

            string tag;
            ifs >> tag;
            trim(tag);

            // Grab the rest of the line. 
            // We need this isntead of >> in order to obtain 
            // the space characters in the directory names.
            string dir;
            getline(ifs, dir);
            trim(dir);

            DirectoryTagEntry newDirectoryEntry(tag, dir);
            directoryTagEntryList << newDirectoryEntry;
        }
    }

    void operator>>(DirectoryTagEntryList const& directoryTagEntryList,
                    std::ofstream& ofs) {
        for (size_t i = 0, sz = directoryTagEntryList.size();
            i < sz;
            i++) {
            DirectoryTagEntry const& dte = directoryTagEntryList.at(i);
            ofs << dte.getTagName() << " " << dte.getDirectoryName() << "n";
        }
    }
}

main.cpp:

#include "DirectoryTagEntry.h"
#include "DirectoryTagEntryList.h"
#include <iomanip>
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#include <cstring>
#include <pwd.h>
#include <unistd.h>
#include <vector>

using com::github::coderodde::ds4mac::DirectoryTagEntry;
using com::github::coderodde::ds4mac::DirectoryTagEntryList;
using std::cerr;
using std::cout;
using std::ifstream;
using std::ofstream;
using std::setw;
using std::string;

//// ///////////////////
 // Operation names: //
/////////////////// ////
const string OPERATION_SWITCH_DIRECTORY = "switch_directory";
const string OPERATION_DESCRIPTOR_SHOW_TAG_ENTRY_LIST =
        "show_tag_entry_list";

//// /////////////////
 // All the flags: //
///////////////// ////
const string FLAG_LIST_TAGS = "-l";
const string FLAG_LIST_BOTH = "-L";
const string FLAG_LIST_TAGS_SORTED = "-s";
const string FLAG_LIST_BOTH_SORTED = "-S";
const string FLAG_LIST_BOTH_SORTED_DIRS = "-d";
const string FLAG_UPDATE_PREVIOUS = "--update-previous";
const string PREV_TAG_NAME = "__dt_previous";

// The path to the tag file, relative to the home directory:
const string RELATIVE_TAG_FILE_PATH = "/.ds/tags";

//// ///////////////////////////////////////////
 // Returns the entire path to the tag file. //
/////////////////////////////////////////// ////
static const string getTagFilePath() {
    char* c_home_directory = getpwuid(getuid())->pw_dir;
    size_t str_len = strlen(c_home_directory) + 1;
    char* c_home_directory_copy = (char*) std::malloc(str_len);
    std::strncpy(c_home_directory_copy,
                 c_home_directory,
                 str_len);

    string path(c_home_directory_copy);
    return path += RELATIVE_TAG_FILE_PATH;
}

//// /////////////////////////////////////////
 // Returns the current working directory: //
///////////////////////////////////////// ////
static string getCurrentWorkingDirectory() {
    char* working_dir = new char[PATH_MAX];
    working_dir = getcwd(working_dir, PATH_MAX);
    string rv = working_dir;
    delete[] working_dir;
    return rv;
}

//// //////////////////////////////////////
 // Returns the maximum length of tags: //
////////////////////////////////////// ////
static const size_t getMaximumTagLength(DirectoryTagEntryList const& dtel) {
    size_t maximumTagLength = 0;

    for (size_t index = 0, sz = dtel.size(); index < sz; index++) {
        maximumTagLength = 
            std::max(maximumTagLength,
                     dtel.at(index).getTagName().length());
    }

    return maximumTagLength;
}

//// //////////////////////////////////
 // Updates the previous directory: //
////////////////////////////////// ////
static string updatePreviousDirectory(
        DirectoryTagEntryList& directoryTagEntryList,
        string& newDirectoryName) {

    DirectoryTagEntry* dte = directoryTagEntryList[PREV_TAG_NAME];

    if (dte->getTagName().compare(PREV_TAG_NAME) == 0) {
        string rv = dte->getDirectoryName();
        dte->setDirectoryName(newDirectoryName);
        return rv;
    } else {
        DirectoryTagEntry ndte(PREV_TAG_NAME, newDirectoryName);
        directoryTagEntryList << ndte;
        return getCurrentWorkingDirectory();
    }
}

//// //////////////////////////////////////////////////
 // Returns the home directory of the current user: //
////////////////////////////////////////////////// ////
static string getHomeDirectory() {
    return string(getpwuid(getuid())->pw_dir);
}

//// //////////////////////////////////////////////////////////////////////
 // Changes the leading tilde to the name of the user's home directory: //
////////////////////////////////////////////////////////////////////// ////
static string convertDirectoryNameToExactDirectoryName(string dir) {
    if (dir.size() == 0) {
        throw "The directory name is empty. This should not happen.";
    }

    if (dir[0] != '~') {
        // Nothing to do.
        return dir;
    }

    string homeDirectory = getHomeDirectory();
    auto iter = dir.cbegin();
    std::advance(iter, 1);

    for (auto it = iter; it != dir.cend(); ++it) {
        homeDirectory.push_back(*it);
    }

    return homeDirectory;
}

static void checkIfstream(ifstream& ifs) {
    if (!ifs.is_open()) {
        throw "Could not open the tag file for reading.";
    }

    if (!ifs.good()) {
        throw "The tag file input stream is not good.";
    }
}

static void checkOfstream(ofstream& ofs) {
    if (!ofs.is_open()) {
        throw "Could not open the tag file for writing.";
    }

    if (!ofs.good()) {
        throw "The tag file output stream is not good.";
    }
}

//// //////////////////////////////////////////////////////////////////
 // Jumps to the directory to which dt was switching most recently: //
////////////////////////////////////////////////////////////////// ////
static void jumpToPreviousDirectory() {
    string tagFilePath = getTagFilePath();
    ifstream ifs;
    ifs.open(tagFilePath, std::ifstream::in);
    checkIfstream(ifs);

    DirectoryTagEntryList directoryTagEntryList;
    ifs >> directoryTagEntryList;

    DirectoryTagEntry* directoryTagEntry = directoryTagEntryList[PREV_TAG_NAME];
    string nextPath;
    string currentWorkingDirectory = getCurrentWorkingDirectory();

    if (directoryTagEntry->getTagName().compare(PREV_TAG_NAME) == 0)  {
        nextPath = updatePreviousDirectory(directoryTagEntryList, 
                                           currentWorkingDirectory);
    } else {
        DirectoryTagEntry prevTagEntry(PREV_TAG_NAME,
                                       currentWorkingDirectory);

        directoryTagEntryList << prevTagEntry;
        nextPath = currentWorkingDirectory;
    }

    ofstream ofs;
    ofs.open(tagFilePath, ofstream::out);
    checkOfstream(ofs);

    directoryTagEntryList >> ofs;
    ofs.close();

    cout << OPERATION_SWITCH_DIRECTORY
         << 'n'
         << convertDirectoryNameToExactDirectoryName(nextPath)
         << std::flush;
}

//// /////////////////////////////
 // Switches to the directory: //
///////////////////////////// ////
static void switchDirectory(std::string const& tag) {
    DirectoryTagEntryList directoryTagEntryList;
    std::ifstream ifs;
    std::string tagFilePath = getTagFilePath();
    ifs.open(tagFilePath, std::ifstream::in);
    checkIfstream(ifs);

    ifs >> directoryTagEntryList;

    string currentWorkingDirectory = getCurrentWorkingDirectory();

    if (directoryTagEntryList.size() == 0) {
        string nextDirectory = 
            updatePreviousDirectory(
                directoryTagEntryList,
                currentWorkingDirectory);

        std::ofstream ofs;
        ofs.open(getTagFilePath(), std::ofstream::out);
        checkOfstream(ofs);

        directoryTagEntryList >> ofs;
        ofs.close();

        cout << OPERATION_SWITCH_DIRECTORY
             << 'n'
             << convertDirectoryNameToExactDirectoryName(nextDirectory);
    }

    if (directoryTagEntryList.size() == 1) {
        cout << OPERATION_SWITCH_DIRECTORY
             << "n"
             << directoryTagEntryList[0]->getDirectoryName();

        updatePreviousDirectory(directoryTagEntryList,
                                currentWorkingDirectory)  ;

        std::ofstream ofs;
        ofs.open(getTagFilePath(), std::ofstream::out);
        checkOfstream(ofs);

        directoryTagEntryList >> ofs;
        ofs.close();
    }

    DirectoryTagEntry* bestMatch = directoryTagEntryList[tag];

    std::ofstream ofs;
    ofs.open(getTagFilePath(), std::ofstream::out);
    checkOfstream(ofs);

    updatePreviousDirectory(directoryTagEntryList, 
                            currentWorkingDirectory);

    directoryTagEntryList >> ofs;
    ofs.close();

    // New line?
    cout << OPERATION_SWITCH_DIRECTORY
         << 'n'
         << convertDirectoryNameToExactDirectoryName(
                bestMatch->getDirectoryName());
}

//// //////////////////////////////////////
 // Lists taga without the directories: //
////////////////////////////////////// ////
static void listTagsOnly(
    DirectoryTagEntryList const& directoryTagEntryList) {

    cout << OPERATION_DESCRIPTOR_SHOW_TAG_ENTRY_LIST << 'n';

    for (size_t index = 0, sz = directoryTagEntryList.size();
         index < sz;
         index++) {
        cout << directoryTagEntryList.at(index).getTagName() << "n";
    }
}

//// //////////////////////////////
 // Lists taga and directories: //
////////////////////////////// ////
static void listTagsAndDirectories(
        DirectoryTagEntryList const& directoryTagEntryList) {
    cout << OPERATION_DESCRIPTOR_SHOW_TAG_ENTRY_LIST << 'n';

    size_t maxTagLength = getMaximumTagLength(directoryTagEntryList);

    for (size_t index = 0, sz = directoryTagEntryList.size(); 
         index < sz; 
         index++) {
        DirectoryTagEntry const& directoryTagEntry =
            directoryTagEntryList.at(index);

        std::cout << std::setw(maxTagLength + 1)
                  << directoryTagEntry.getTagName() 
                  << ' '
                  << directoryTagEntry.getDirectoryName()
                  << 'n';
    }
}

//// /////////////////////////////////
 // Decided how to print the tags: //
///////////////////////////////// ////
static void listTags(std::string const& flag) {
    std::ifstream ifs;
    ifs.open(getTagFilePath(), std::ifstream::in);
    checkIfstream(ifs);

    DirectoryTagEntryList directoryTagEntryList;
    ifs >> directoryTagEntryList;

    if (flag == FLAG_LIST_TAGS_SORTED 
        || flag == FLAG_LIST_BOTH_SORTED) {
        directoryTagEntryList.sortByTags();
    } else if (flag == FLAG_LIST_BOTH_SORTED_DIRS) {
        directoryTagEntryList.sortByDirectories();
    }

    if (flag == FLAG_LIST_TAGS_SORTED
        || flag == FLAG_LIST_TAGS) {
        listTagsOnly(directoryTagEntryList);
    } else if (flag == FLAG_LIST_BOTH 
        || flag == FLAG_LIST_BOTH_SORTED
        || flag == FLAG_LIST_BOTH_SORTED_DIRS) {
        listTagsAndDirectories(directoryTagEntryList);
    }
}

//// ///////////////////////////////
 // Processes a single flag/tag: //
/////////////////////////////// ////
static void processSingleFlag(std::string const& arg) {
    if (arg == FLAG_LIST_BOTH
        || arg == FLAG_LIST_TAGS
        || arg == FLAG_LIST_TAGS_SORTED
        || arg == FLAG_LIST_BOTH_SORTED
        || arg == FLAG_LIST_BOTH_SORTED_DIRS) {
        listTags(arg);
    } else {
        switchDirectory(arg);
    }
}

static void processUpdatePrevious(string& dir) {
    string tagFilePath = getTagFilePath();
    DirectoryTagEntryList directoryTagEntryList;

    ifstream ifs;
    ifs.open(tagFilePath, ifstream::in);
    checkIfstream(ifs);

    ifs >> directoryTagEntryList;
    ifs.close();

    DirectoryTagEntry* previousEntryCandidate = 
        directoryTagEntryList[PREV_TAG_NAME];

    string nextPath;

    if (previousEntryCandidate->getTagName().compare(PREV_TAG_NAME) == 0) {
        nextPath = previousEntryCandidate->getDirectoryName();
        previousEntryCandidate->setDirectoryName(dir);
    } else {
        DirectoryTagEntry prevDirectoryTagEntry(PREV_TAG_NAME, dir);
        nextPath = getCurrentWorkingDirectory();
        directoryTagEntryList << prevDirectoryTagEntry;
    }

    ofstream ofs;
    ofs.open(getTagFilePath(), ofstream::out);
    checkOfstream(ofs);
    directoryTagEntryList >> ofs;
    ofs.close();

    // return nextPath;
}

int main(int argc, char *argv[]) {
    try {
        if (argc == 1) {
            jumpToPreviousDirectory();
        } else if (argc == 2) {
            processSingleFlag(argv[1]);
        } else if (argc == 3) {
            string flag = argv[1];

            if (flag.compare(FLAG_UPDATE_PREVIOUS) != 0) {
                string errorMsg = "Flag ";
                errorMsg = errorMsg.append(flag);
                errorMsg = errorMsg.append(" not recognized.");
                throw errorMsg.data();
            }

            string dir = argv[2];
            processUpdatePrevious(dir);
        }
    } catch (char *const msg) {
        cerr << msg << 'n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Makefile

all: DirectoryTagEntry.cpp DirectoryTagEntryList.cpp main.cpp
    g++ -std=c++17 -Wall -Werror -pedantic -o ds_engine DirectoryTagEntry.cpp DirectoryTagEntryList.cpp main.cpp

install:
    chmod 500 installer.sh
    installer.sh

Смотрите также

  1. Скрипт bash
  2. Скрипт установщика

Запрос на критику

Особенно, main.cpp мне кажется, что это дерьмо. Как я мог С ++: понять это?

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *