// (c) 2008 Sebastian Redl // Released under the GPLv2. #define BOOST_DISABLE_THREADS #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; namespace ip = boost::interprocess; namespace fs = boost::filesystem; // The possible modes of sorting footnotes. enum sort_mode { sort_by_text, sort_by_footnote }; // Input operator for the enum. std::istream &operator >>(std::istream &is, sort_mode &sm) { std::string s; is >> s; if(s == "text") { sm = sort_by_text; } else if(s == "footnote") { sm = sort_by_footnote; } else { is.setstate(std::ios_base::failbit); } return is; } // Output operator for the enum. std::ostream &operator <<(std::ostream &os, sort_mode sm) { return os << (sm == sort_by_text ? "text" : "footnote"); } bool parse_command_line(int argc, char **argv, sort_mode &sm, std::string &input_file); std::tr1::unordered_map< int, int> g_footnote_number_translations(10000); int g_footnote_count = 0; inline int lookup_footnote_translation(int number) { int &r = g_footnote_number_translations[number]; if(r == 0) { r = ++g_footnote_count; } return r; } inline bool isdig(char c) { return c >= '0' && c <= '9'; } void load_footnote_order(const char *first, const char *last); void copy_text(const char *first, const char *last, std::ostream &os); void sort_footnotes(const char *first, const char *last, std::ostream &os); int main(int argc, char **argv) { std::ios::sync_with_stdio(false); sort_mode sm = sort_by_text; std::string input_file; if(!parse_command_line(argc, argv, sm, input_file)) { return 1; } // Memory-map the input file. ip::file_mapping input_map(input_file.c_str(), ip::read_only); std::size_t input_size = fs::file_size(fs::path(input_file)); ip::mapped_region mapping(input_map, ip::read_only, 0, input_size); const char *text_start = static_cast(mapping.get_address()); const char *footnote_end = text_start + input_size; // Locate the footnote marker. Search in reverse, since it's probably close // to the end. const char * const MARKER_START = "\n@footnote:\n"; const char * const MARKER_END = MARKER_START + 12; const char* loc = std::find_end(text_start, footnote_end, MARKER_START, MARKER_END); if(loc == footnote_end) { throw std::runtime_error("No footnote section."); } // Include the last newline. const char *text_end = loc + 1; const char *footnote_start = loc + 12; if(sm == sort_by_footnote) { load_footnote_order(footnote_start, footnote_end); } copy_text(text_start, text_end, std::cout); std::cout << "@footnote:\n"; sort_footnotes(footnote_start, footnote_end, std::cout); } bool parse_command_line(int argc, char **argv, sort_mode &sm, std::string &input_file) { po::options_description visible_desc("Allowed options"); visible_desc.add_options() ("help,h", "produce help message") ("sort-by,s", po::value(&sm)->default_value(sort_by_text), "sort by order of in-text appearance ('text') or in-footnotes " "appearance ('footnote')") ; po::options_description hidden_desc("positional options"); hidden_desc.add_options()("input", po::value(&input_file), "input file (must be real file)"); po::positional_options_description positional; positional.add("input", 1); po::options_description merged("all options"); merged.add(visible_desc).add(hidden_desc); po::variables_map vm; try { po::store( po::command_line_parser(argc, argv) .options(merged).positional(positional).run(), vm); po::notify(vm); } catch(po::error &e) { std::cerr << "Command line error: " << e.what() << "\n\n" << "Usage: babylon [] \n" << visible_desc << '\n'; return false; } if(vm.count("help")) { std::cerr << "Babylon Challenge in C++, Sebastian Redl " "\n" << "Usage: babylon [] \n" << visible_desc << '\n'; return false; } return true; } void load_footnote_order(const char *first, const char *last) { for( ; first != last; ++first) { if(*first == '[') { int num = 0; for(++first; first != last && isdig(*first); ++first) { num *= 10; num += *first - '0'; } if(first == last) { break; } if(*first == ']' && num != 0) { lookup_footnote_translation(num); } } } } void copy_text(const char *first, const char *last, std::ostream &os) { const char *last_end = first; for( ; first != last; ++first) { if(*first == '[') { int num = 0; const char *f = first + 1; for( ; f != last && isdig(*f); ++f) { num *= 10; num += *f - '0'; } if(f == last) { break; } if(*f == ']' && num != 0) { os.write(last_end, first - last_end + 1); os << lookup_footnote_translation(num); last_end = first = f; } } } os.write(last_end, last - last_end); } void sort_footnotes(const char *first, const char *last, std::ostream &os) { typedef std::pair txtrange; typedef std::vector txtmap; txtmap order(g_footnote_count, txtrange(0, 0)); for( ; first != last ; ++first) { if(*first == '[') { int num = 0; ++first; for( ; first != last && isdig(*first); ++first) { num *= 10; num += *first - '0'; } if(first == last) { break; } const char *fnbegin = first; for( ; first != last && *first != '\n'; ++first) { } int idx = lookup_footnote_translation(num); order[idx-1] = txtrange(fnbegin, first); if(first == last) { std::cerr << "Warning: File doesn't end with a newline.\n"; break; } } else { //std::cerr << "Warning: Bad '" << *first << "' in footnotes.\n"; } } for(std::size_t i = 0, len = order.size(); i < len; ++i) { const txtrange &r = order[i]; if(r.first == 0) { os << '[' << (i+1) << "] Error: No footnote text found.\n"; std::cerr << "Warning: No footnote for reference " << (i+1) << '\n'; continue; } os << '[' << (i+1); os.write(r.first, r.second - r.first); os << '\n'; } }