// restless.cc // (C) 2008 Philip Endecott // See http://chezphil.org/restless/ // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "restless.hh" #include #include #include #include #include #include #include #include #include "code_or_text.hh" using namespace std; // Find URLs and turn them into HTML links. static string make_hyperlinks(string s) { static const string url_end_chars = "-a-zA-Z0-9$%&_=+@~#?\\/"; static const string url_chars = url_end_chars + ":;,.!"; static const boost::regex urlpat("(http|https|ftp):\\/\\/["+url_chars+"]+["+url_end_chars+"]"); static const string replacement = "$&"; return regex_replace(s, urlpat, replacement, boost::match_default|boost::format_default); } // Escape any special characters and format any URLs as links. static string format_html(string s) { string r; for (string::const_iterator i = s.begin(); i != s.end(); ++i) { char c = *i; switch (c) { case '<': r += "<"; break; case '>': r += ">"; break; case '&': r += "&"; break; default: r += c; break; } } return make_hyperlinks(r); } // We process the input paragraph at a time. typedef vector para_t; // Newlines are not included in the elements of a para_t. Format and print a // paragraph, putting newlines between the lines. static void print_para(const para_t& para) { for (para_t::const_iterator i = para.begin(); i != para.end(); ++i) { if (i != para.begin()) { cout << "\n"; } cout << format_html(*i); } } static bool is_heading(const para_t& para) { return para.size()==2 && para[0].size()==para[1].size() && para[1]==string(para[1].length(),para[1][0]); } // We print the header as soon as we've seen a heading to use as the // document title. // We're formatting hyperlinks in the title, which is not ideal. // Maybe some way to influence the stylesheet from the command line would be useful. static void print_header(string title) { cout << "\n" << "\n" << "\n" << "" << format_html(title) << "\n" << "\n" << "\n" << "\n" << "\n"; } static void print_footer() { cout << "\n" << "\n" << "\n"; } static void restless(istream& strm) { bool ul = false; bool code = false; bool header_done = false; while (!strm.fail()) { para_t para; while (1) { string l; getline(strm,l); if (strm.fail()) { break; } if (boost::trim_copy(l).empty()) { if (para.empty()) { continue; } else { break; } } para.push_back(l); } if (para.empty()) { break; } if (para[0].substr(0,2)=="* ") { if (!header_done) { cerr << "Error: file must start with a heading\n"; exit(1); } if (code) { cout << "\n\n"; code = false; } if (!ul) { cout << "
    \n"; ul = true; } para[0] = para[0].substr(2); cout << "
  • "; print_para(para); cout << "
  • \n\n"; } else { if (ul) { cout << "
\n\n"; ul = false; } if (is_heading(para)) { int level; switch (para[1][0]) { case '#': level=1; break; case '=': level=2; break; case '-': level=3; break; case '~': level=4; break; default: level=5; break; } if (!header_done) { print_header(para[0]); header_done = true; } if (code) { cout << "\n\n"; code = false; } cout << "\n" << format_html(para[0]) << "\n\n"; } else { if (!header_done) { cerr << "Error: file must start with a heading\n"; exit(1); } if (is_code(para)) { if (!code) { cout << "
";
            code = true;
          } else {
            cout << "\n\n";
          }
          print_para(para);

        } else {
          if (code) {
            cout << "
\n\n"; code = false; } cout << "

"; print_para(para); cout << "

\n\n"; } } } } print_footer(); } int main(int argc, char* argv[]) { // If a filename is given on the command line, process it. // Otherwise, read the standard input. // Output always goes to the standard output. if (argc==1) { restless(cin); exit(0); } else if (argc==2) { ifstream f(argv[1]); if (f.fail()) { cerr << "Failed to open " << argv[1] << "\n"; exit(1); } restless(f); exit(0); } else { cout << "Usage: restless [filename]\n"; exit(1); } }