132 lines
3.9 KiB
C++

//===--- History.cpp - Previously Entered Lines -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interface for setting and retrieving previously
// entered input, with a persistent backend (i.e. a history file).
//
// Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//
#include "textinput/History.h"
#include <iostream>
#include <fstream>
#ifdef WIN32
# include <stdio.h>
#else
# include <unistd.h>
#endif
namespace textinput {
History::History(const char* filename):
fHistFileName(filename ? filename : ""), fMaxDepth((size_t) -1),
fPruneLength(0), fNumHistFileLines(0) {
// Create a history object, initialize from filename if the file
// exists. Append new lines to filename taking into account the
// maximal number of lines allowed by SetMaxDepth().
if (filename) ReadFile(filename);
}
History::~History() {}
void
History::AddLine(const std::string& line) {
// Add a line to entries and file.
if (line.empty()) return;
fEntries.push_back(line);
AppendToFile();
}
void
History::ReadFile(const char* FileName) {
// Inject all lines of FileName.
// Intentionally ignore fMaxDepth
std::ifstream InHistFile(FileName);
if (!InHistFile) return;
std::string line;
while (std::getline(InHistFile, line)) {
while (!line.empty()) {
size_t len = line.length();
char c = line[len - 1];
if (c != '\n' && c != '\r') break;
line.erase(len - 1);
}
if (!line.empty()) {
fEntries.push_back(line);
}
}
fNumHistFileLines = fEntries.size();
}
void
History::AppendToFile() {
// Write last entry to hist file.
// Prune if needed.
if (fHistFileName.empty() || !fMaxDepth) return;
// Calculate prune length to use
size_t nPrune = fPruneLength;
if (nPrune == (size_t)kPruneLengthDefault) {
nPrune = (size_t)(fMaxDepth * 0.8);
} else if (nPrune > fMaxDepth) {
nPrune = fMaxDepth - 1; // fMaxDepth is guaranteed to be > 0.
}
// Don't check for the actual line count of the history file after every
// single line. Once every 50% on the way between nPrune and fMaxDepth is
// enough.
if (fNumHistFileLines < fMaxDepth
&& (fNumHistFileLines % (fMaxDepth - nPrune)) == 0) {
fNumHistFileLines = 0;
std::string line;
std::ifstream in(fHistFileName.c_str());
while (std::getline(in, line))
++fNumHistFileLines;
}
size_t numLines = fNumHistFileLines;
if (numLines >= fMaxDepth) {
// Prune! But don't simply write our lines - other processes might have
// added their own.
std::string line;
std::ifstream in(fHistFileName.c_str());
std::string pruneFileName = fHistFileName + "_prune";
std::ofstream out(pruneFileName.c_str());
if (out) {
if (in) {
while (numLines >= nPrune && std::getline(in, line)) {
// skip
--numLines;
}
while (std::getline(in, line)) {
out << line << '\n';
}
}
out << fEntries.back() << '\n';
in.close();
out.close();
#ifdef WIN32
::_unlink(fHistFileName.c_str());
#else
::unlink(fHistFileName.c_str());
#endif
if (::rename(pruneFileName.c_str(), fHistFileName.c_str()) == -1) {
std::cerr << "ERROR in textinput::History::AppendToFile(): "
"cannot rename " << pruneFileName << " to " << fHistFileName;
}
fNumHistFileLines = nPrune;
}
} else {
std::ofstream out(fHistFileName.c_str(), std::ios_base::app);
out << fEntries.back() << '\n';
++fNumHistFileLines;
}
}
}