B Thibault 7 жил өмнө
parent
commit
de04676976
10 өөрчлөгдсөн 607 нэмэгдсэн , 1 устгасан
  1. 3 0
      .gitignore
  2. 27 0
      Makefile
  3. 6 1
      README.md
  4. 96 0
      appContext.cpp
  5. 48 0
      appContext.hpp
  6. 9 0
      exception.cpp
  7. 17 0
      exception.hpp
  8. 216 0
      history.cpp
  9. 72 0
      history.hpp
  10. 113 0
      main.cpp

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+*.o
+/cd
+

+ 27 - 0
Makefile

@@ -0,0 +1,27 @@
+
+OUTPUT= cd
+
+SRC=	main.cpp		\
+		appContext.cpp	\
+		history.cpp		\
+		exception.cpp
+
+OBJ=	$(SRC:.cpp=.o)
+
+CXXFLAGS=	-std=c++17 -g3
+
+$(OUTPUT):	all
+
+all:	$(OBJ)
+	$(CXX) $(OBJ) -o $(OUTPUT) $(LDDFLAGS)
+
+fclean:	clean
+	$(RM) $(OUTPUT)
+
+clean:
+	$(RM) $(OBJ)
+
+re:		fclean all
+
+.PHONY:	all fclean clean all
+

+ 6 - 1
README.md

@@ -1,2 +1,7 @@
-# cd
+
+## install
+function cd(){
+    CD_ARGS=`/home/isundil/projects/cd/cd "$@"`
+    if [[ $CD_ARGS != "" ]]; then builtin cd $CD_ARGS; fi
+}
 

+ 96 - 0
appContext.cpp

@@ -0,0 +1,96 @@
+#include <unistd.h>
+#include <sstream>
+#include <string>
+#include <vector>
+#include "appContext.hpp"
+#include "exception.hpp"
+
+using namespace std;
+using namespace knacki::cd;
+
+static vector<string> GetArgs(char **arr)
+{
+    std::vector<string> result;
+    for (char *i =*arr; i; i=*(++arr))
+        result.push_back(i);
+    return result;
+}
+
+std::string AppContext::GetAbsolutePath(const std::string &dir, const std::string &file)
+{
+    stringstream ss;
+    if (file[0] == '/')
+        return file;
+    if (dir[0] != '/')
+    {
+        char *cwd = get_current_dir_name();
+        ss << cwd << '/';
+        free(cwd);
+    }
+    ss << dir << '/' << file;
+    return ss.str();
+}
+
+AppContext::AppContext(char **av, char **en)
+{
+    const std::string appName = *av;
+    vector<string> args = ::GetArgs(av +1);
+    string path;
+    bool inArgs = true;
+#ifdef __DEBUG
+    bool usageOnly = false;
+#else
+    bool usageOnly = isatty(1);
+#endif
+    bool historyOnly = false;
+    bool flush = false;
+    bool getHistoryFile = false;
+    string histDir;
+    std::stringstream ss ("cd_");
+    ss << "cd_" << getppid() << ".hist";
+    string defaultHistFile = ss.str();
+    string histFile;
+
+    for (vector<string>::const_iterator i =args.cbegin(); i != args.cend(); i++)
+    {
+        if (!inArgs)
+            if (path.empty())
+                path = *i;
+            else throw ArgumentException(*i);
+        else if (*i == "--")
+            inArgs = false;
+        else if (*i == "--histdir")
+            histDir = *(++i);
+        else if (*i == "--histfile")
+            histFile = *(++i);
+        else if (*i == "--gethistfile")
+            getHistoryFile = true;
+        else if (*i == "--history")
+            historyOnly = true;
+        else if (*i == "--flush" || *i == "-F")
+            flush = true;
+        else if (*i == "--help")
+            usageOnly = true;
+        else if (path.empty())
+            path = *i;
+        else
+            throw ArgumentException(*i);
+    }
+    if (!historyOnly && !flush && path.empty())
+    {
+        const char *home = getenv("HOME");
+        if (home)
+            path = getenv("HOME");
+        // FIXME else wtf
+    }
+    fArgs = AppContext::Args(usageOnly, historyOnly, getHistoryFile, flush, histDir.empty() ? "/tmp/" : histDir, histFile.empty() ? defaultHistFile : histFile, path);
+}
+
+bool AppContext::IsReadOnly() const
+{
+    return GetArgs().historyOnly || GetArgs().getHistoryFile;
+}
+
+const AppContext::Args &AppContext::GetArgs() const
+{ return fArgs; }
+

+ 48 - 0
appContext.hpp

@@ -0,0 +1,48 @@
+#pragma once
+
+#define __DEBUG 1
+
+#include <string>
+
+namespace knacki::cd{
+class ArgumentException;
+
+class AppContext
+{
+    public:
+        AppContext(char **, char **);
+        static std::string GetAbsolutePath(const std::string &, const std::string &);
+
+        struct Args {
+            public:
+                bool usageOnly;
+                bool historyOnly;
+                bool getHistoryFile;
+                bool flush;
+                std::string histDir;
+                std::string histFile;
+                std::string histAbsolutePath;
+                std::string pushArg;
+
+                Args(){};
+                Args(bool _usageOnly, bool _historyOnly, bool _getHistoryFile, bool _flush, const std::string &_histDir, const std::string &_histFile, const std::string &_pushArg):
+                    usageOnly(_usageOnly),
+                    historyOnly(_historyOnly),
+                    getHistoryFile(_getHistoryFile),
+                    flush(_flush),
+                    histDir(_histDir),
+                    histFile(_histFile),
+                    histAbsolutePath(GetAbsolutePath(histDir, histFile)),
+                    pushArg(_pushArg)
+                {};
+        };
+        const Args &GetArgs() const;
+        bool IsReadOnly() const;
+
+    protected:
+        Args fArgs;
+
+    private:
+        AppContext();
+};
+}

+ 9 - 0
exception.cpp

@@ -0,0 +1,9 @@
+#include "exception.hpp"
+
+using namespace knacki::cd;
+
+ArgumentException::ArgumentException(const std::string &_arg): arg(_arg)
+{}
+
+const std::string &ArgumentException::GetWrongArg() const
+{ return arg; }

+ 17 - 0
exception.hpp

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <exception>
+#include <string>
+
+namespace knacki::cd {
+
+class ArgumentException: public std::exception
+{
+    public:
+        ArgumentException(const std::string &arg);
+        const std::string &GetWrongArg() const;
+
+    protected:
+        const std::string arg;
+};
+}

+ 216 - 0
history.cpp

@@ -0,0 +1,216 @@
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <algorithm>
+#include <fstream>
+#include <sstream>
+#include "history.hpp"
+#include "appContext.hpp"
+
+using namespace std;
+using namespace knacki::cd;
+
+static std::string RelativePathToAbsolute(const string &path)
+{
+    if (path[0] == '/')
+        return path;
+    char *p = realpath(path.c_str(), nullptr);
+    string pathResult(p);
+    free(p);
+    return pathResult;
+}
+
+HistoryEntry::HistoryEntry(PTR_TYPE i, const string &e): path(e), index(i)
+{}
+
+HistoryEntry::HistoryEntry(const HistoryEntry &o): path(o.path), index(o.index)
+{}
+
+HistoryEntry &HistoryEntry::operator=(const HistoryEntry &o)
+{
+    path = o.path;
+    index = o.index;
+    return *this;
+}
+
+const std::string &HistoryEntry::GetPath() const
+{ return path; }
+
+PTR_TYPE HistoryEntry::GetIndex() const
+{ return index; }
+
+History::History(const string &path): current(-1), prev(-1)
+{
+    std::ifstream file(path, fstream::in | fstream::binary);
+    Read(file);
+}
+
+HistoryRW::HistoryRW(const string &_path): History(_path), path(_path)
+{}
+
+const std::vector<HistoryEntry> &History::GetEntries() const
+{ return hist; }
+
+const HistoryEntry *History::GetHistoryEntry(PTR_TYPE index) const
+{
+    for (const HistoryEntry &i: GetEntries())
+        if (i.GetIndex() == index)
+            return &i;
+    return nullptr;
+}
+
+void History::Read(std::istream &file)
+{
+    PTR_TYPE current = -1;
+    PTR_TYPE prev = -1;
+    unsigned int histIndex =0;
+    size_t size;
+    char *buf = nullptr;
+    size_t bufSize = 0;
+
+    hist.clear();
+    if (!file.good() && errno != ENOENT)
+        throw IOError(strerror(errno));
+    file.seekg(0);
+    if (sizeof(prev) != file.readsome((char *)(&prev), sizeof(prev)))
+        return;
+    if (sizeof(current) != file.readsome((char *)(&current), sizeof(current)))
+        return;
+    while (file.good())
+    {
+        if (sizeof(size) != file.readsome((char*)(&size), sizeof(size)))
+            break;
+        if (size > bufSize)
+        {
+            delete[] buf;
+            buf = new char[size]();
+            bufSize = size;
+        }
+        if (size != file.readsome(buf, size))
+            break;
+        const size_t lastIndex = ++histIndex;
+        hist.push_back(HistoryEntry(lastIndex, string(buf, size)));
+        if (current != -1)
+            if (!--current)
+                this->current = lastIndex;
+        if (prev != -1)
+            if (!--prev)
+                this->prev = lastIndex;
+    }
+    delete []buf;
+    if (this->current == -1)
+        this->current = hist.back().GetIndex();
+}
+
+void HistoryRW::Write()
+{
+    size_t written = 0;
+
+    std::ofstream file(path, fstream::in | fstream::out | fstream::binary | fstream::trunc);
+    if (!file.good())
+        throw IOError(strerror(errno));
+    file.write((char*)(&prev), sizeof(prev));
+    file.write((char*)(&current), sizeof(current));
+    for (HistoryEntry i: hist)
+    {
+        const size_t pathLen = i.GetPath().length();
+        file.write((char*)(&pathLen), sizeof(pathLen));
+        written += sizeof(pathLen);
+        file.write(i.GetPath().c_str(), sizeof(*(i.GetPath().c_str())) * i.GetPath().size());
+        written += sizeof(*(i.GetPath().c_str())) * i.GetPath().size();
+    }
+}
+
+bool HistoryRW::AppendCwd()
+{
+    char *cwd = get_current_dir_name();
+    const bool result = Append(cwd);
+    free(cwd);
+    return result;
+}
+
+bool HistoryRW::Append(const HistoryEntry &ent)
+{
+    const HistoryEntry *e = GetHistoryEntry(ent.GetIndex());
+    if (e)
+    {
+        // Juste move ptr
+        if (current != e->GetIndex())
+        {
+            prev = current;
+            current = e->GetIndex();
+            return true;
+        }
+        else
+            return false;
+    }
+    return Append(ent.GetPath());
+}
+
+void HistoryRW::TruncateAfter(PTR_TYPE pos)
+{
+    while (!hist.empty() && hist.back().GetIndex() > pos)
+        hist.pop_back();
+}
+
+bool HistoryRW::Append(const std::string &path)
+{
+    if (current != -1)
+    {
+        const HistoryEntry *ent = GetHistoryEntry(current);
+        if (ent && ent->GetPath() == path)
+            return false;
+        TruncateAfter(current);
+    }
+    const size_t lastIndex = hist.size() +1;
+    hist.push_back(HistoryEntry(lastIndex, path));
+    prev = current;
+    current = lastIndex;
+    return true;
+}
+
+bool HistoryRW::Flush()
+{
+    if (hist.empty())
+        return false;
+    hist.clear();
+    current = prev = -1;
+    return true;
+}
+
+bool History::IsPrevious(const HistoryEntry &e) const
+{ return prev != -1 && prev == e.GetIndex(); }
+
+bool History::IsCurrent(const HistoryEntry &e) const
+{ return current != -1 && current == e.GetIndex(); }
+
+const HistoryEntry *History::GetNextWorkingDirectory(const AppContext &app) const
+{
+    const string dest = app.GetArgs().pushArg;
+    if (dest == "-")
+    {
+        const char *buf = nullptr;
+        if (prev != -1)
+        {
+            const HistoryEntry *e = GetHistoryEntry(prev);
+            if (e)
+                return new HistoryEntry(*e);
+        }
+        if (buf = getenv("OLDPWD"))
+            return new HistoryEntry(hist.size() +1, buf);
+        else
+            return nullptr;
+    }
+    else if (dest[0] == '-' || dest[0] == '+')
+    {
+        stringstream eventId(dest.c_str() +1);
+        long eventIdLong = 0;
+        eventId >> eventIdLong;
+        eventIdLong = (current > -1 ? current : 0) +(eventIdLong * (dest[0] == '-' ? -1 : 1));
+        const HistoryEntry *ent = GetHistoryEntry(eventIdLong);
+        return ent ? new HistoryEntry(*ent) : nullptr;
+    }
+    const string absDest = RelativePathToAbsolute(dest);
+    return new HistoryEntry(hist.size() +1, absDest);
+}
+

+ 72 - 0
history.hpp

@@ -0,0 +1,72 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+namespace knacki::cd{
+    class AppContext;
+    typedef char PTR_TYPE;
+
+    class IOError
+    {
+        public:
+            IOError(const std::string &e): what(e)
+            {}
+
+            const std::string what;
+    };
+
+    class HistoryEntry
+    {
+        public:
+            HistoryEntry(PTR_TYPE index, const std::string &e);
+            HistoryEntry(const HistoryEntry &);
+            HistoryEntry &operator=(const HistoryEntry &o);
+
+            const std::string &GetPath() const;
+            PTR_TYPE GetIndex() const;
+
+        protected:
+            std::string path;
+            PTR_TYPE index;
+    };
+
+    class History
+    {
+        public:
+            History(const std::string &file);
+
+            const std::vector<HistoryEntry> &GetEntries() const;
+            const HistoryEntry *GetNextWorkingDirectory(const AppContext &app) const;
+
+            bool IsPrevious(const HistoryEntry &) const;
+            bool IsCurrent(const HistoryEntry &) const;
+
+        protected:
+            History() {};
+            std::vector<HistoryEntry> hist;
+            PTR_TYPE current;
+            PTR_TYPE prev;
+
+            void Read(std::istream &file);
+            const HistoryEntry *GetHistoryEntry(PTR_TYPE index) const;
+    };
+
+    class HistoryRW: public History
+    {
+        public:
+            HistoryRW(const std::string &file);
+            bool AppendCwd();
+            bool Append(const std::string &);
+            bool Append(const HistoryEntry &);
+            bool Flush();
+            void Write();
+
+        protected:
+            void TruncateAfter(PTR_TYPE pos);
+
+        private:
+            const std::string path;
+    };
+}
+

+ 113 - 0
main.cpp

@@ -0,0 +1,113 @@
+#include <unistd.h>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <cmath>
+#include "appContext.hpp"
+#include "history.hpp"
+#include "exception.hpp"
+
+using namespace knacki::cd;
+
+void Usage(std::ostream &out, const std::string &progName)
+{
+    // --history
+    // --histDir
+    // --histFile
+    out << progName << std::endl;
+}
+
+AppContext *GetAppContext(char **av, char **en)
+{
+    AppContext *app;
+
+    try
+    {
+        app = new AppContext(av, en);
+    }
+    catch (ArgumentException &e)
+    {
+        std::cerr << av[0] << ": Invalid argument " << e.GetWrongArg() << std::endl;
+        Usage(std::cerr, av[0]);
+        return nullptr;
+    }
+    return app;
+}
+
+void PrintHistory(std::ostream &out, const std::string &progName, const History &hist)
+{
+    if (hist.GetEntries().empty())
+    {
+        out << progName << ": No entry" << std::endl;
+    }
+    else
+    {
+        const unsigned int maxSize = (unsigned int) std::log10(hist.GetEntries().size()) +1;
+
+        for (HistoryEntry i : hist.GetEntries())
+        {
+            if (hist.IsPrevious(i))
+                out << "-";
+            else if (hist.IsCurrent(i))
+                out << ">";
+            else
+                out << " ";
+            out << std::setfill(' ') << std::setw(maxSize) << ((size_t)(i.GetIndex())) << ": " << i.GetPath() << std::endl;
+        }
+    }
+}
+
+int main(int ac, char **av, char **ev)
+{
+    AppContext *app = GetAppContext(av, ev);
+    bool written = false;
+
+    if (!app)
+        return 0;
+    if (app->GetArgs().usageOnly)
+    {
+        Usage(std::cout, av[0]);
+        return 0;
+    }
+
+    History *hist;
+    try
+    {
+        if (app->IsReadOnly())
+        {
+            hist = new History(app->GetArgs().histAbsolutePath);
+        }
+        else
+        {
+            hist = new HistoryRW(app->GetArgs().histAbsolutePath);
+        }
+    }
+    catch (IOError &e)
+    {
+        std::cerr << av[0] << ": " << app->GetArgs().histAbsolutePath << ": " << e.what << std::endl;
+        return 0;
+    }
+
+    if (app->GetArgs().historyOnly)
+        PrintHistory(std::cerr, av[0], *hist);
+    if (app->GetArgs().getHistoryFile)
+        std::cout << app->GetArgs().histAbsolutePath;
+    if (app->GetArgs().flush)
+        written |= ((HistoryRW*)hist)->Flush();
+
+    const HistoryEntry *nextEnt = app->IsReadOnly() ? nullptr : hist->GetNextWorkingDirectory(*app);
+    if (nextEnt)
+    {
+        written |= ((HistoryRW*)hist)->Append(*nextEnt);
+        std::cerr << "exec {" << nextEnt->GetPath() << "}" << std::endl;
+        std::cout << nextEnt->GetPath() << std::endl;
+        delete nextEnt;
+    }
+
+    if (written)
+        ((HistoryRW*)hist)->Write();
+
+    delete hist;
+    delete app;
+}
+