/* * main.cpp * * Created on: 07.10.2017 * Author: julian */ #include // for mmap() #include // for fstat() #include // for open() #include // for close() #include // for perror() #include #include #include #include #include #include #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 List; List operator()(const Path::Path &file, const MemoryString &str); std::string macroExpand(const std::string &input); List Include(Path::Path); protected: std::map macros; std::set includes; }; #include template std::string readTill(iterator &start, const iterator &end, std::function limiter) { iterator current = start; while(current != end && !limiter(current)) { ++current; } return std::string(start, current); } template 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::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 typedef std::map> CommandList; #include #include 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 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(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); */ } }