|
|
@@ -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 *)(¤t), 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*)(¤t), 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);
|
|
|
+}
|
|
|
+
|