475 lines
9.5 KiB
C++
475 lines
9.5 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>
|
|
|
|
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 <class IterA, class IterB>
|
|
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 <typename T>
|
|
bool operator<(const T &other) const {
|
|
return compare(other) < 0;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator>(const T &other) const {
|
|
return compare(other) > 0;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator==(const T &other) const {
|
|
return compare(other) == 0;
|
|
}
|
|
|
|
template <typename T>
|
|
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<std::string> List;
|
|
List operator()(const Substring &input);
|
|
|
|
std::string macroExpand(Substring input);
|
|
|
|
protected:
|
|
Substring file;
|
|
std::map<std::string, Substring> 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();
|
|
}
|
|
|
|
input.start++;
|
|
|
|
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<std::string, Substring>::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 <functional>
|
|
typedef std::map<std::string, std::function<void(InputExtractor::List&, std::string)>> 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 <unistd.h>
|
|
|
|
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 <fstream>
|
|
|
|
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<std::string, Substring>(name.toString(), readBrackets(file, "{}")));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#include <experimental/filesystem>
|
|
|
|
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");
|
|
}
|