From a3259626ff790107eb5de8f0ff443c1bdaf70596 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 16 May 2019 08:37:50 +0200 Subject: [PATCH] Added AsciiDoc support. --- CMakeLists.txt | 2 +- README.adoc | 3 +- remwharead.1.adoc | 5 ++ src/adoc.cpp | 135 ++++++++++++++++++++++++++++++++++++ src/csv.cpp | 3 +- src/{csv.hpp => export.hpp} | 11 ++- src/main.cpp | 34 ++++++++- src/parse_options.cpp | 2 +- 8 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 src/adoc.cpp rename src/{csv.hpp => export.hpp} (77%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fae1b3..13355b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.2) project(remwharead - VERSION 0.0.1 + VERSION 0.0.2 LANGUAGES CXX ) diff --git a/README.adoc b/README.adoc index f16aaf3..95c2fd1 100644 --- a/README.adoc +++ b/README.adoc @@ -1,8 +1,7 @@ = remwharead [NOTE] -This program is not usable at the moment. Most of the described functions are -not yet available. +Some of the described functions are not yet available. *remwharead* saves URLs of things you read in a database along with an URL to the archived version, the current date and time, title, description, the full diff --git a/remwharead.1.adoc b/remwharead.1.adoc index a1f7a93..48c3d9f 100644 --- a/remwharead.1.adoc +++ b/remwharead.1.adoc @@ -66,6 +66,11 @@ commas. Line breaks in the full text are converted to "\n". Our CSV implementation follows RFC 4180 and the full MIME media type is `text/csv;charset=utf-8;header=present`. +=== AsciiDoc + +AsciiDoc is a markup language that can be read as plain text or converted to +HTML, PDF and many other formats. + == FILES * *Database*: `${XDG_DATA_HOME}/remwharead/database.sqlite` diff --git a/src/adoc.cpp b/src/adoc.cpp new file mode 100644 index 0000000..d6fb51c --- /dev/null +++ b/src/adoc.cpp @@ -0,0 +1,135 @@ +/* This file is part of remwharead. + * Copyright © 2019 tastytea + * + * 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, version 3. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include "version.hpp" +#include "time.hpp" +#include "export.hpp" + +using std::cerr; +using std::endl; +using std::uint64_t; +using std::regex; +using std::regex_replace; +using std::pair; + +void export_adoc(const vector &entries, ostream &out) +{ + try + { + out << "= Visited things\n" + << ":Author: remwharead " << global::version << endl + << ":Date: " << timepoint_to_string(system_clock::now()) << endl + << ":toc: right" << endl + // << ":toc-title:" << endl + << endl; + + std::map alltags; + string day; + for (const Database::entry &entry : entries) + { + const string datetime = timepoint_to_string(entry.datetime); + const string newday = datetime.substr(0, datetime.find('T')); + const string time = datetime.substr(datetime.find('T') + 1); + if (newday != day) + { + day = newday; + out << "== " << day << endl << endl; + } + out << ".link:" << entry.uri; + if (!entry.title.empty()) + { + out << '[' << entry.title << ']'; + } + else + { + out << "[]"; + } + out << endl; + out << '_' << time << '_'; + for (const string &tag : entry.tags) + { + if (tag.empty()) + { + continue; + } + + auto globaltag = alltags.find(tag); + if (globaltag != alltags.end()) + { + ++(globaltag->second); + } + else + { + alltags.insert({ tag, 1 }); + } + + out << " xref:" << replace_spaces(tag) << '[' << tag << ']'; + if (tag != *(entry.tags.rbegin())) + { + out << ','; + } + } + out << endl; + if (!entry.archive_uri.empty()) + { + out << " (" << entry.archive_uri << "[archived version])\n"; + } + out << endl; + if (!entry.description.empty()) + { + out << entry.description << endl << endl; + } + } + + if (!alltags.empty()) + { + out << "== Tags\n\n"; + vector> sortedtags(alltags.size()); + std::move(alltags.begin(), alltags.end(), sortedtags.begin()); + std::sort(sortedtags.begin(), sortedtags.end(), + [](const pair &a, pair &b) + { + return a.second > b.second; + }); + + for (const auto &tag : sortedtags) + { + out << "[[" << replace_spaces(tag.first) << "]]" << tag.first + << "(" << tag.second << ")"; + if (tag != *(sortedtags.rbegin())) + { + out << ", "; + } + } + out << endl; + } + } + catch (std::exception &e) + { + cerr << "Error in " << __func__ << ": " << e.what() << endl; + } +} + +const string replace_spaces(const string &text) +{ + return regex_replace(text, regex(" "), "-"); +} diff --git a/src/csv.cpp b/src/csv.cpp index 723f24b..91b3bf1 100644 --- a/src/csv.cpp +++ b/src/csv.cpp @@ -14,10 +14,9 @@ * along with this program. If not, see . */ -#include #include #include "time.hpp" -#include "csv.hpp" +#include "export.hpp" using std::cerr; using std::endl; diff --git a/src/csv.hpp b/src/export.hpp similarity index 77% rename from src/csv.hpp rename to src/export.hpp index 169dc3b..3e4e253 100644 --- a/src/csv.hpp +++ b/src/export.hpp @@ -14,8 +14,8 @@ * along with this program. If not, see . */ -#ifndef REMWHAREAD_CSV_HPP -#define REMWHAREAD_CSV_HPP +#ifndef REMWHAREAD_EXPORT_HPP +#define REMWHAREAD_EXPORT_HPP #include #include @@ -26,6 +26,11 @@ using std::ostream; using std::cout; void export_csv(const vector &entries, ostream &out = cout); +//! replaces " with "". const string quote_csv(const string &field); -#endif // REMWHAREAD_CSV_HPP +void export_adoc(const vector &entries, ostream &out = cout); +//! Replaces spaces with -. +const string replace_spaces(const string &text); + +#endif // REMWHAREAD_EXPORT_HPP diff --git a/src/main.cpp b/src/main.cpp index 8300c39..44d09c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,10 +17,12 @@ #include #include #include +#include +#include #include "sqlite.hpp" #include "parse_options.hpp" -#include "csv.hpp" #include "uri.hpp" +#include "export.hpp" using std::cout; using std::cerr; @@ -52,16 +54,42 @@ int main(const int argc, const char *argv[]) page.title, page.description, page.fulltext}); } + std::ofstream file; + if (!opts.file.empty()) + { + file.open(opts.file); + if (!file.good()) + { + cerr << "Error: Could not open file: " << opts.file << endl; + return 3; + } + } switch (opts.format) { case export_format::csv: { - export_csv(db.retrieve(opts.span[0], opts.span[1])); + if (file.is_open()) + { + export_csv(db.retrieve(opts.span[0], opts.span[1]), file); + file.close(); + } + else + { + export_csv(db.retrieve(opts.span[0], opts.span[1])); + } break; } case export_format::asciidoc: { - cerr << "Error: AsciiDoc is not yet supported.\n"; + if (file.is_open()) + { + export_adoc(db.retrieve(opts.span[0], opts.span[1]), file); + file.close(); + } + else + { + export_adoc(db.retrieve(opts.span[0], opts.span[1])); + } break; } default: diff --git a/src/parse_options.cpp b/src/parse_options.cpp index 0bee8e5..6b59076 100644 --- a/src/parse_options.cpp +++ b/src/parse_options.cpp @@ -99,7 +99,7 @@ const options parse_options(const int argc, const char *argv[]) { opts.format = export_format::csv; } - else if (format == "asciidoc") + else if (format == "asciidoc" || format == "adoc") { opts.format = export_format::asciidoc; }