// Author: Maurizio Loreti, aka MLO or (HAM) I3NOO
// Work: University of Padova - Department of Physics
// Via F. Marzolo, 8 - 35131 PADOVA - Italy
// Phone: +39 (049) 827-7216 FAX: +39 (049) 827-7102
// EMail:
[email protected]
// WWW:
http://www.pd.infn.it/~loreti/mlo.html
//
// -------------------------------------------------------------------
//
// $Id: cleandir.cxx,v 1.11 2002/09/25 12:19:41 loreti Exp $
//
// -------------------------------------------------------------------
#include <algorithm>
#include <iterator>
#include <vector>
#include <cstring>
#include "ltx.hh" // Includes: functional, iostream, string
#include "cleandir.hh" // Includes: string
#include "cleanup.hh" // Includes: string
#include "file.hh" // Includes: list, map, string, utility, ctime
extern "C" {
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
}
using std::cerr;
using std::cout;
using std::strcmp;
using std::string;
// Local variables
namespace {
// In "re" are defined the extensions of the files relevant for
// LaTeX; ".tex" is missing, being handled separately. THIS ARRAY
// MUST BE SORTED IN ASCENDING ORDER.
const char * re[] = {
".aux", ".dvi", ".idx", ".ilg", ".ind",
".lof", ".log", ".lot", ".pdf", ".ps",
".toc"
};
const size_t nRE = sizeof(re) / sizeof(re[0]);
std::vector<string> texExts(re, re + nRE);
const string tex(".tex");
const string dot(".");
const size_t filename_max = 256;
}
// Local functions (declarations)
namespace {
void check_file(const string &, const time_t, currDir &);
}
// Code
void scan_dir(
const string & name
) {
// Scans the directory "name", building the related instantiation of
// the class "currDir" containing all the informations for the
// relevant files; then calls "clean_dir" to perform the actual
// cleanup. If the "-r" options has been specified, recurses over
// all the directories under the current one.
#if defined(DEBUG)
static bool firstTime(true);
if (firstTime) {
cout << "--------------------Relevant extensions ("
<< nRE << ")\n";
copy(texExts.begin(), texExts.end(),
std::ostream_iterator<string>(cout, " "));
cout << std::endl;
firstTime = false;
}
cout << "--------------------scan_dir called for \""
<< name << "\"\n";
#endif // DEBUG
DIR * pDir;
if ((pDir = opendir( name.c_str() )) != 0) {
string fullName(name);
if (*(fullName.rbegin()) != '/') fullName.append("/");
currDir thisDir(fullName);
std::list<string> subDirs;
struct dirent * pDe;
// Reads every file: skips null inodes (already deleted
// files), and the two special files "." and ".." .
while ((pDe = readdir(pDir)) != 0) {
if (pDe->d_ino == 0) continue;
#if defined(DEBUG)
cout << "Next file: " << pDe->d_name << " - ";
#endif // DEBUG
if (strcmp(pDe->d_name, ".") == 0) {
#if defined(DEBUG)
cout << "skipped\n";
#endif // DEBUG
continue;
}
if (strcmp(pDe->d_name, "..") == 0) {
#if defined(DEBUG)
cout << "skipped\n";
#endif // DEBUG
continue;
}
// Gets the file related informations with stat(2) (we need file
// type and modification time). If the call to "stat" fails,
// the file is not considered.
char tName[filename_max];
std::strcpy(tName, fullName.c_str());
std::strcat(tName, pDe->d_name);
struct stat sStat;
if (stat(tName, &sStat) != 0) {
#if defined(DEBUG)
cout << "got error from stat()\n";
#else
cerr << ltx::progname << ": error calling stat("
<< tName << ")\n";
#endif // DEBUG
} else {
if (S_ISDIR(sStat.st_mode) != 0) {
#if defined(DEBUG)
cout << "is a directory\n";
#endif // DEBUG
// If needed, push the subdirectory names in the dedicated
// list, for future recursion; plain files are handled by
// the local procedure check_file().
if (ltx::recurse) subDirs.push_back(tName);
} else {
check_file(pDe->d_name, sStat.st_mtime, thisDir);
}
}
}
// Looks if some cleanup has to be performed
clean_files(thisDir);
if (ltx::recurse) {
for_each(subDirs.begin(), subDirs.end(), std::ptr_fun(scan_dir));
}
closedir(pDir);
} else {
cerr << ltx::progname << ": \"" << name
<< "\" could not be opened (or is not a directory)\n";
}
}
namespace {
void check_file(
const string & name,
const time_t mTime,
currDir & CDir
) {
// - If the file "name" matches the trailing string identifying
// backup editor files, is removed;
// - if it matches a relevant extension, is inserted in the
// "currDir" instance.
string::size_type where;
if (ltx::lTrailEd > 0) {
if ((where = name.rfind(ltx::trailEd)) != string::npos) {
if ((where + ltx::lTrailEd) == name.size()) {
#if defined(DEBUG)
cout << "matches the default editor extension\n";
#endif // DEBUG
nuke(CDir.getName(), name);
return;
}
}
}
// Breaks the file name in "basename" and "extension"; and checks
// the latter against the known ones (".tex" files are handled
// separately).
if ((where = name.find_last_of(dot)) != string::npos) {
string extension = name.substr(where);
string basename = name.substr(0, where);
fileFamily & fF = CDir.getFileFamily(basename);
if (extension == tex) {
fF.addExtension(mTime, 0);
#if defined(DEBUG)
cout << "inserted\n";
#endif // DEBUG
} else if (binary_search(texExts.begin(), texExts.end(), extension)) {
fF.addExtension(mTime, &extension);
#if defined(DEBUG)
cout << "extension " << extension << " - inserted\n";
} else {
cout << "extension not relevant\n";
#endif // DEBUG
}
#if defined(DEBUG)
} else {
cout << "no extension\n";
#endif // DEBUG
}
}
}