#include #include #include #include #include using namespace std; #define FOOTNOTE_HEADER "@footnote:" #define FOOTNOTE_START '[' #define FOOTNOTE_END ']' #define FOOTNOTE_ALLOWEDCHARS "0123456789" // not very clean, but fast... #define to_string(from, to, stringstream) \ stringstream.clear(); \ stringstream.str(""); \ stringstream << from; \ to = stringstream.str(); #define from_string(from, to, stringstream) \ stringstream.clear(); \ stringstream.str(from); \ stringstream >> to; int main(int argc, char ** argv) { int FOOTNOTE_HEADER_LEN = strlen(FOOTNOTE_HEADER); int FOOTNOTE_ALLOWEDCHARS_LEN = strlen(FOOTNOTE_ALLOWEDCHARS); ifstream input(argv[1]); string line; // ... for reading from file string strold; // old footnote as string string strnew; // new footnote as string string::size_type pos1 = 0; string::size_type pos2 = 0; bool found = false; // indicates whether we've found the footnote header size_t last = 0; // last used footnote size_t old = 0; // old footnote as number std::vector mapping(1024, 0); // mapping between old and new footnote numbers stringstream ss; // for conversions (string <-> size_t) // process input until footnote header has been found while (!found && getline(input, line)) { // check for footnote header if (line.compare(0, FOOTNOTE_HEADER_LEN, FOOTNOTE_HEADER) == 0) { found = true; cout << line << endl; continue; } // search footnotes pos1 = 0; while ( (pos1 = line.find_first_of(FOOTNOTE_START, pos1)) != string::npos) { pos2 = line.find_first_not_of(FOOTNOTE_ALLOWEDCHARS, pos1 + 1, FOOTNOTE_ALLOWEDCHARS_LEN); // skip invalid footnote references if (pos2 == string::npos || pos2 < pos1 + 2 || line[pos2] != FOOTNOTE_END) { pos1 = pos2; continue; } // we found a valid footnote reference strold = line.substr(pos1 + 1, pos2 - pos1 - 1); from_string(strold, old, ss); // resize vector if it is to small if (old + 1 > mapping.size()) mapping.resize(old + 1, 0); if (mapping[old] != 0) { // known footnote -> use stored number to_string(mapping[old], strnew, ss); } else { // unknown footnote -> new number last++; mapping[old] = last; to_string(last, strnew, ss); } // replace footnote number line.replace(pos1 + 1, pos2 - pos1 - 1, strnew); pos1 = pos2 - strold.size() + strnew.size(); } cout << line << endl; } // process foot notes std::vector storage(last, string()); while (getline(input, line)) { // print empty lines directly if (line.empty()) { cout << endl; continue; } // skip lines without a footnote if (line[0] != FOOTNOTE_START) continue; pos1 = line.find_first_not_of(FOOTNOTE_ALLOWEDCHARS, 1, FOOTNOTE_ALLOWEDCHARS_LEN); if (pos1 == string::npos || line[pos1] != FOOTNOTE_END) continue; // store footnote strold = line.substr(1, pos1 - 1); from_string(strold, old, ss); if (old + 1> mapping.size() || mapping[old] == 0) { // skip unknown (i.e. unreferenced) footnotes cerr << "unreferenced footnote " << old << endl; continue; } storage[mapping[old] - 1] = line.substr(pos1 + 1); } // print sorted list of footnotes for (size_t i = 0; i < storage.size(); i++) { if (storage[i].empty()) cerr << "missing footnote " << i + 1 << endl; cout << FOOTNOTE_START << i + 1 << FOOTNOTE_END << storage[i] << endl; } }