/* * 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 using namespace std; struct Substring { const char * start, * end; Substring(const char * start, const char * end) : start(start), end(end) {} Substring(const char * str): start(str), end(str + strlen(str)) {} Substring(): start(nullptr), end(nullptr) {} std::string toString() const { std::string temp; temp.reserve(size()); const char * it = start; while(it != end) { temp += *it; it++; } return temp; } operator std::string() { return toString(); } std::string::size_type size() const { return (std::string::size_type)(end - start); } friend std::ostream &operator<<(std::ostream &out, const Substring &str); template int __compare(IterA a, IterA aend, IterB b, IterB bend) const { while(a != aend && b != bend) { if (*a < * b) { return -1; } if (*a > *b) { return 1; } a++; b++; } if (a == aend && b == bend) { return 0; } if (a != aend) { return 1; } return -1; } int compare(const Substring &other) const { return __compare(start, end, other.start, other.end); } int compare(const std::string &other) const { return __compare(start, end, other.begin(), other.end()); } template bool operator<(const T &other) const { return compare(other) < 0; } template bool operator>(const T &other) const { return compare(other) > 0; } template bool operator==(const T &other) const { return compare(other) == 0; } template bool operator!=(const T &other) const { return !(*this == other); } }; std::ostream &operator<<(std::ostream &out, const Substring &str) { auto temp = str.start; while(temp != str.end) { out << *temp; temp++; } return out; } 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 Substring &input); std::string macroExpand(Substring input); protected: Substring file; std::map macros; }; Substring readBrackets(Substring &input, const char * brackets) { if (input.size() <= 0 || *input.start != brackets[0]) { cout << "expected " << brackets[0] << ", got: '" << *input.start << "'"; return Substring(); } int depth = 1; Substring result(input.start, input.start); while(depth > 0 && input.size() > 0) { result.end = ++input.start; if (*input.start == brackets[0]) { depth++; } if (*input.start == brackets[1]) { depth--; } } // advance beyond last bracket if (input.size()) input.start++; return result; } std::string InputExtractor::macroExpand(Substring input) { std::string result; Substring name; std::map::iterator lookup; //cout << "expanding: " << input << endl; while(input.size() > 0) { if (*input.start == '\\') { input.start++; name.start = name.end = input.start; while(input.size() > 0) { name.end = ++input.start; if ((lookup = macros.find(name.toString())) != macros.end()) { break; } } if (lookup == macros.end()) { throw Exception("unknown macro in macro expansion: " + name.toString()); } result += lookup->second.toString(); } else { result += *input.start; } input.start++; } return result; } #include typedef std::map> CommandList; std::string Extension(std::string str) { auto it = str.end(); while(it != str.begin() && *it != '.' && *it != '/' && *it != '\\') { it--; } return std::string(it, str.end()); } std::string Basedir(std::string path) { auto it = path.end(); while(it != path.begin() && *it != '/' && *it != '\\') { it--; } return std::string(path.begin(), it); } std::string Name(std::string path) { auto it = path.end(); while(it != path.begin() && *it != '/' && *it != '\\') { it--; } return std::string(it, path.end()); } std::string Basename(std::string path) { std::string temp = Name(path); auto it = temp.end(); while(it != temp.begin() && *it != '.') { it--; } if (it == temp.begin()) { return path; } return std::string(temp.begin(), it); } inline bool PathRelative(std::string path) { return path.size() && path[0] != '/'; } #include std::string cwd() { ssize_t size = 100, nsize; while(1) { char buffer[size]; if ((nsize = readlink("/proc/self/cwd", buffer, size)) < size) { buffer[size+1] = 0; return std::string(buffer); } size = nsize + 100; } // // buffer[rsize+1] = 0; // // std::string result; // result.assign(buffer); // return result; } #include bool Exists(std::string path) { std::ifstream file(path); if (!file) { return false; } file.close(); return true; } InputExtractor::List Include(std::string path) { InputExtractor::List list; 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); std::string basedir = Basedir(path); list = InputExtractor()(str); // add basedir to list for all relative paths for (auto it = list.begin(); it != list.end(); it++) { if (PathRelative(*it)) *it = basedir + '/' + *it; } // cleanup munmap(memptr, fileinfo.st_size); close(fd); return list; } InputExtractor::List InputExtractor::operator()(const Substring &input){ file = input; List result; CommandList IncludeCommands; IncludeCommands["input"] = [](List &l, std::string a) { if (a.empty()) return; if (Extension(a) != ".tex") a += ".tex"; l.push_back(a); // try to extract all inputs of that file auto sub = Include(a); std::copy(sub.begin(), sub.end(), std::inserter(l, l.end())); }; IncludeCommands["include"] = IncludeCommands["input"]; IncludeCommands["lstinputlisting"] = [](List &l, std::string a){ l.push_back(a); }; // skip normie text while(file.size()) { if (*file.start == '%') { // line commment while(file.size() > 0 && *file.start != '\n') file.start++; continue; } if (*file.start != '\\') { file.start++; continue; } // read macro name file.start++; // TODO: throw exception if (!file.size()) throw Exception("unexpected EOF"); Substring name; name.start = name.end = file.start; for(name.end = ++file.start; file.size() > 0 && !isspace(*file.start) && *file.start != '{' && *file.start != '\\'; name.end = (++file.start+1)) { auto searchHit = IncludeCommands.find(name); if ((searchHit = IncludeCommands.find(name)) != IncludeCommands.end()) { file.start++; cout << searchHit->first; cout << "[" << readBrackets(file, "[]") << "]"; auto args = readBrackets(file, "{}"); cout << ":" << args << endl; searchHit->second(result, macroExpand(args)); break; } else if (name == std::string("def")) { file.start++; if (file.size() <= 0 || *file.start != '\\') { continue; } file.start++; Substring name(file.start, file.end); while (file.size() > 0 && *file.start != '{' && !isspace(*file.start)) { name.end = ++file.start; } cout << "new macro definition: " << name << endl; macros.insert(std::pair(name.toString(), readBrackets(file, "{}"))); break; } } } return result; } #include int main(int argc, char ** args) { // find all the files the given tex files depend on cout << cwd() << std::endl; int fd = 0; struct stat filestat; for(;argc > 1; --argc) { char * filename = args[argc-1]; cout << "looking at " << filename << std::endl; // 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 << "mmap success, parse file" << endl; Substring file((const char*)memory_area, (const char*)memory_area + filestat.st_size); try { auto list = InputExtractor()(file); // write in makefile style std::ofstream output(Basename(filename) + ".d"); if (!output) { std::cout << "could not create output file" << std::endl; } else { output << filename << ": "; for (auto it = list.begin(); it != list.end(); it++) { output << '\t'; if (PathRelative(*it)) { if (PathRelative(filename)) { output << cwd(); } else { output << Basename(filename); } output << "/" << *it; } else { output << *it; } output << "\t\\\n"; } output << endl; } output.close(); } catch(InputExtractor::Exception &e) { cout << e.what() << endl; } // cleanup munmap(memory_area, filestat.st_size); close(fd); } printf("done\n"); }