395 lines
8.7 KiB
C++
395 lines
8.7 KiB
C++
/*
|
|
* main.cpp
|
|
*
|
|
* Created on: 07.10.2017
|
|
* Author: julian
|
|
*/
|
|
|
|
#include <sys/mman.h> // for mmap()
|
|
#include <sys/stat.h> // for fstat()
|
|
#include <fcntl.h> // for open()
|
|
#include <unistd.h> // for close()
|
|
#include <errno.h> // for perror()
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#include "fs.hpp"
|
|
#include "memory_string.hpp"
|
|
|
|
using namespace std;
|
|
|
|
class InputExtractor
|
|
{
|
|
public:
|
|
class Exception : public std::runtime_error {
|
|
public:
|
|
Exception(const std::string &str) : std::runtime_error(str) {}
|
|
};
|
|
|
|
typedef std::vector<Path::Path> List;
|
|
|
|
List operator()(const Path::Path &file, const MemoryString &str);
|
|
|
|
std::string macroExpand(const std::string &input);
|
|
List Include(Path::Path);
|
|
|
|
protected:
|
|
std::map<std::string, std::string> macros;
|
|
std::set<Path::Path> includes;
|
|
};
|
|
|
|
#include <functional>
|
|
|
|
|
|
template <typename iterator>
|
|
std::string readTill(iterator &start, const iterator &end, std::function<bool(const iterator&)> limiter) {
|
|
iterator current = start;
|
|
|
|
while(current != end && !limiter(current)) {
|
|
++current;
|
|
}
|
|
|
|
return std::string(start, current);
|
|
}
|
|
|
|
|
|
template <typename iterator>
|
|
std::string readBrackets(iterator &begin, const iterator &end, const char * brackets) {
|
|
auto current = begin;
|
|
|
|
if (begin == end || *current != brackets[0]) {
|
|
return std::string();
|
|
}
|
|
|
|
// skip first opening bracket
|
|
current++;
|
|
|
|
int depth = 1;
|
|
auto bbegin = current, bend = current;
|
|
|
|
while(depth > 0 && current != end) {
|
|
if (*current== brackets[0]) {
|
|
depth++;
|
|
} else if (*current == brackets[1]) {
|
|
depth--;
|
|
} else {
|
|
bend = ++current;
|
|
}
|
|
}
|
|
|
|
// advance beyond last bracket
|
|
if (current != end)
|
|
current++;
|
|
|
|
|
|
return std::string(bbegin, bend);
|
|
}
|
|
|
|
std::string InputExtractor::macroExpand(const std::string &input) {
|
|
std::string result;
|
|
std::map<std::string, std::string>::iterator lookup;
|
|
//cout << "expanding: " << input << endl;
|
|
|
|
std::string::const_iterator current = input.begin();
|
|
while(current != input.end()) {
|
|
if (*current == '\\') {
|
|
current++;
|
|
std::string::const_iterator start = current;
|
|
|
|
while(current != input.end() && *current != '\\') {
|
|
if ((lookup = macros.find(std::string(start, current))) != macros.end()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lookup == macros.end()) {
|
|
throw Exception("unknown macro in macro expansion: " + std::string(start, current));
|
|
}
|
|
|
|
result += lookup->second;
|
|
} else {
|
|
result += *current;
|
|
}
|
|
++current;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#include <functional>
|
|
typedef std::map<std::string, std::function<void(InputExtractor::List&, std::string)>> CommandList;
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <fstream>
|
|
|
|
bool Exists(std::string path) {
|
|
std::ifstream file(path);
|
|
if (!file) {
|
|
return false;
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
InputExtractor::List InputExtractor::Include(Path::Path path) {
|
|
path = Path::Clean(path);
|
|
List list;
|
|
|
|
std::cout << "including file " << path << "...";
|
|
|
|
// look for include
|
|
if (includes.find(path) != includes.end()) {
|
|
cout << "SKIP" << endl;
|
|
return list; // already been there
|
|
}
|
|
|
|
cout << endl;
|
|
|
|
// add to include list
|
|
includes.insert(path);
|
|
|
|
int fd = open(path.c_str(), O_RDONLY);
|
|
if (fd == -1) {
|
|
cerr << "cannot open " << path << endl;
|
|
return list;
|
|
}
|
|
|
|
struct stat fileinfo;
|
|
if (fstat(fd, &fileinfo) == -1) {
|
|
perror("stat");
|
|
close(fd);
|
|
return list;
|
|
}
|
|
|
|
void * memptr = mmap(NULL, fileinfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
|
|
if (memptr == NULL) {
|
|
perror("mmap");
|
|
close(fd);
|
|
return list;
|
|
}
|
|
|
|
// Substring str((const char *)memptr, (const char*)memptr + fileinfo.st_size);
|
|
MemoryString file((char*)memptr, fileinfo.st_size);
|
|
|
|
std::string basedir = Path::Dir(path);
|
|
list = (*this)(basedir, file); // follow include
|
|
|
|
// cleanup
|
|
munmap(memptr, fileinfo.st_size);
|
|
close(fd);
|
|
return list;
|
|
|
|
cout << path << "done" << endl;
|
|
}
|
|
|
|
Path::Path fixFilename(const Path::Path &file, const Path::Path &root) {
|
|
Path::Path temp(file);
|
|
|
|
// try to make path absolute
|
|
if (Path::isRelative(temp)) {
|
|
// add current file's path
|
|
temp = Path::Join(root, temp);
|
|
}
|
|
if (Path::isRelative(temp)) {
|
|
// add process working directory
|
|
temp = Path::Join(fs::cwd(), temp);
|
|
}
|
|
|
|
return Path::Clean(temp);
|
|
}
|
|
|
|
|
|
InputExtractor::List InputExtractor::operator()(const Path::Path &file, const MemoryString &str){
|
|
List result;
|
|
CommandList IncludeCommands;
|
|
|
|
|
|
IncludeCommands["input"] = [&file, this](List &l, std::string a) {
|
|
if (a.empty()) return;
|
|
if (Path::Extension(a) != ".tex") a += ".tex";
|
|
a = fixFilename(a, file);
|
|
|
|
l.push_back(a);
|
|
// try to extract all inputs of that file
|
|
auto sub = Include(a);
|
|
if (!sub.empty())
|
|
std::copy(sub.begin(), sub.end(), std::inserter(l, l.end()));
|
|
};
|
|
|
|
IncludeCommands["include"] = IncludeCommands["input"];
|
|
IncludeCommands["lstinputlisting"] = [file](List &l, std::string a){ if (!a.empty()) { l.push_back(fixFilename(a, file)); } };
|
|
|
|
MemoryString::const_iterator current = str.begin();
|
|
|
|
while(current != str.end()) {
|
|
if (*current == '%') {
|
|
// line commment
|
|
while(current != str.end() && *current != '\n')
|
|
current++;
|
|
|
|
continue;
|
|
}
|
|
if (*current != '\\') {
|
|
// skip non macros
|
|
current++;
|
|
continue;
|
|
}
|
|
|
|
// read macro name
|
|
current++;
|
|
|
|
if (current == str.end())
|
|
throw Exception("unexpected EOF");
|
|
|
|
auto start = current, end = current;
|
|
|
|
auto limiter = [](char c) -> bool { return isspace(c) || c == '{' || c == '\\'; };
|
|
|
|
for(; current != str.end() && !limiter(*current); end = (++current+1))
|
|
{
|
|
auto searchHit = IncludeCommands.find(std::string(start, end));
|
|
if (searchHit != IncludeCommands.end()) {
|
|
// handle crosslink
|
|
current++;
|
|
if (current == str.end()) continue;
|
|
|
|
cout << searchHit->first << "[" << readBrackets(current, str.end(), "[]") << "]";
|
|
auto inner = readBrackets(current, str.end(), "{}");
|
|
cout << ":" << inner << endl;
|
|
|
|
// add to results
|
|
searchHit->second(result, macroExpand(inner));
|
|
break;
|
|
} else if (std::string(start, end) == std::string("def")) {
|
|
// define new macro
|
|
current++;
|
|
if (current == str.end() || *current != '\\') {
|
|
continue;
|
|
}
|
|
current++;
|
|
|
|
std::function<bool(const MemoryString::const_iterator&)> limiter = [](const MemoryString::const_iterator &it) -> bool { return std::string("{ \t\n").find(*it) != std::string::npos; };
|
|
|
|
std::string name = readTill(current, str.end(), limiter);
|
|
|
|
cout << "new macro definition: " << name << endl;
|
|
macros.insert(std::pair<std::string, std::string>(name, readBrackets(current, str.end(), "{}")));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, char ** args) {
|
|
// find all the files the given tex files depend on
|
|
int fd = 0;
|
|
struct stat filestat;
|
|
|
|
|
|
for(;argc > 1; --argc) {
|
|
|
|
Path::Path filename = args[argc-1];
|
|
InputExtractor parser;
|
|
|
|
InputExtractor::List list = parser.Include(filename);
|
|
|
|
// output results in makefile rule style
|
|
|
|
Path::Path outfile_name = Path::Basename(filename) + ".d";
|
|
cout << "writing dependecy rules to " << outfile_name << "...";
|
|
|
|
std::ofstream outfile(outfile_name);
|
|
|
|
if (!outfile) {
|
|
cout << "could not create file!" << endl;
|
|
continue;
|
|
}
|
|
|
|
outfile << filename << ":";
|
|
|
|
for (auto it = list.begin(); it != list.end(); it++) {
|
|
outfile << *it << "\t\\\n";
|
|
}
|
|
|
|
cout << "done" << endl;
|
|
|
|
/*
|
|
char * filename = args[argc-1];
|
|
cout << "opening " << filename << "...";
|
|
|
|
// try to open file
|
|
fd = open(filename, O_RDONLY);
|
|
if (fd == -1) {
|
|
perror("could not open input file");
|
|
continue;
|
|
}
|
|
|
|
fstat(fd, &filestat);
|
|
//cout << "file size: " << filestat.st_size << endl;
|
|
|
|
// try to mmap file
|
|
void * memory_area = mmap(NULL, filestat.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
if (memory_area == nullptr) {
|
|
perror("could not mmap the input");
|
|
continue;
|
|
}
|
|
|
|
cout << "start parsing" << endl;
|
|
|
|
MemoryString file((char*)memory_area, filestat.st_size);
|
|
|
|
try {
|
|
InputExtractor::List list = InputExtractor()(file);
|
|
|
|
Path::Path outfilename = Path::Basename(Path::Path(filename)) + ".d";
|
|
|
|
cout << "writing makedeps file to " << outfilename << "..." << endl;
|
|
|
|
// write in makefile style
|
|
std::ofstream output(outfilename);
|
|
if (!output) {
|
|
std::cout << "could not create output file" << std::endl;
|
|
} else {
|
|
output << filename << ": ";
|
|
|
|
for (auto it = list.begin(); it != list.end(); it++) {
|
|
if (Path::isRelative(*it)) {
|
|
if (Path::isRelative(filename)) {
|
|
*it = Path::Join(fs::cwd(), filename, *it);
|
|
} else {
|
|
*it = Path::Join(Path::Path(filename), *it);
|
|
}
|
|
}
|
|
|
|
cout << "depends: " << *it << endl;
|
|
output << '\t' << *it << "\t\\\n";
|
|
}
|
|
output << endl;
|
|
}
|
|
|
|
cout << filename << done;
|
|
|
|
} catch(InputExtractor::Exception &e) {
|
|
cout << e.what() << endl;
|
|
}
|
|
|
|
// cleanup
|
|
munmap(memory_area, filestat.st_size);
|
|
close(fd);
|
|
|
|
*/
|
|
}
|
|
|
|
}
|