const args);
diff --git a/src_bac/gd-handwritten.sh b/src_bac/gd-handwritten.sh
deleted file mode 100755
index 56c767c..0000000
--- a/src_bac/gd-handwritten.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-gd-echo --font-family "ArmedLemon" "$@"
diff --git a/src_bac/gd-mandarin.sh b/src_bac/gd-mandarin.sh
deleted file mode 100644
index daec765..0000000
--- a/src_bac/gd-mandarin.sh
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/bash
-#
-# gd-tools - a set of programs to enhance goldendict for immersion learning.
-# Copyright (C) 2023 Ajatt-Tools
-#
-# 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 3 of the License, or
-# (at your option) 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, see .
-
-set -euo pipefail
-
-# Requires installing mecab
-# Open GoldenDict, press "Edit" > "Dictionaries" > "Programs" and add this script as type html.
-
-TEMP_FILE=/tmp/gd-mandarin_cache
-
-# Default path to user_dic.dic, assuming that the user ran `xmake install`.
-USER_DICT=~/.local/gd-mandarin/user.dic
-
-FONT_SIZE=2rem
-GDWORD=""
-GDSEARCH=""
-
-sanitize_input() {
- sed -E \
- -e 's, +,,g' \
- -e 's,[a-z]+, ,Ig' \
- -e 's,\n, ,g'
-}
-
-usage() {
- local -r bn=$(basename -- "$0")
- cat <<-EOF
- usage: $bn [OPTIONS] --word %GDWORD% --sentence %GDSEARCH%
-
- echo input back to GoldenDict as HTML with sentence split into parts
-
- OPTIONS
- EOF
- column -t -s'|' <<-EOF
- --user-dict PATH|path to the user dictionary.
- --font-size SIZE|font size. The default value is 30px
- EOF
-}
-
-find_dicdir() {
- dirname -- "$(
- find \
- ~/.local/gd-mandarin \
- /usr/lib/mecab/dic \
- ~/.local/share/Anki2/addons21 \
- -type f -name dicrc -print -quit
- )"
-}
-
-mecab_split() {
- if ! command -v mecab &>/dev/null; then
- echo "Error: MeCab is not installed. Please install MeCab and try again."
- exit 1
- fi
-
- mecab -d . -Ogdmandarin --dicdir="$(find_dicdir)" --userdic="${USER_DICT}"
-
-}
-
-print_css() {
- cat <<-EOF
-
- EOF
-}
-
-highlight_word() {
- local target_word=$* search=""
- search=$(cat -- -)
- search=${search//">$target_word<"/">${target_word}<"}
- echo "$search"
-}
-
-die() {
- echo "$*" >&2
- notify-send "$(basename -- "$0")" "$*" &
- exit 1
-}
-
-main() {
- if (($# == 0)) || [[ $* == --help ]] || [[ $* == -h ]]; then
- usage
- exit
- fi
- while (($# > 0)); do
- case $1 in
- --font-size)
- shift
- FONT_SIZE=$1
- ;;
- --user-dict)
- shift
- USER_DICT=$1
- ;;
- --word)
- shift
- GDWORD=$1
- ;;
- --sentence)
- shift
- GDSEARCH=$1
- ;;
- *)
- die "Invalid argument passed."
- ;;
- esac
- shift
- done
-
- if ! [[ -f $USER_DICT ]]; then
- die "Provided user dictionary doesn't exist or isn't a file."
- fi
-
- if [[ -z $GDSEARCH ]] || [[ -z $GDWORD ]]; then
- die "Not enough parameters."
- fi
-
- local -r input=$(echo "$GDSEARCH" | sanitize_input)
- local -r output=$(grep -Fxs -A1 "$input" -- "$TEMP_FILE")
-
- echo ''
- if [[ -n $output ]]; then
- echo "$output" |
- tail -1 |
- highlight_word "$GDWORD"
- else
- echo "$input" |
- tee -a "$TEMP_FILE" |
- mecab_split |
- tee -a "$TEMP_FILE" |
- highlight_word "$GDWORD"
- printf -- '\n\n' >>"$TEMP_FILE"
- fi
- echo '
'
-
- print_css
-}
-
-main "$@"
diff --git a/src_bac/gd-mecab.sh b/src_bac/gd-mecab.sh
deleted file mode 100755
index 3ae3e78..0000000
--- a/src_bac/gd-mecab.sh
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/bin/bash
-#
-# gd-tools - a set of programs to enhance goldendict for immersion learning.
-# Copyright (C) 2023 Ajatt-Tools
-#
-# 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 3 of the License, or
-# (at your option) 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, see .
-
-set -euo pipefail
-
-# Requires installing mecab
-# Open GoldenDict, press "Edit" > "Dictionaries" > "Programs" and add this script as type html.
-
-TEMP_FILE=/tmp/mecab-cache
-
-# Default path to user_dic.dic, assuming that the user ran `xmake install`.
-USER_DICT=""
-
-FONT_SIZE=2rem
-GDWORD=""
-GDSEARCH=""
-
-sanitize_input() {
- sed -E \
- -e 's, +,,g' \
- -e 's,[a-z]+, ,Ig' \
- -e 's,\n, ,g'
-}
-
-usage() {
- local -r bn=$(basename -- "$0")
- cat <<-EOF
- usage: $bn [OPTIONS] --word %GDWORD% --sentence %GDSEARCH%
-
- echo input back to GoldenDict as HTML with sentence split into parts
-
- OPTIONS
- EOF
- column -t -s'|' <<-EOF
- --user-dict PATH|path to the user dictionary.
- --font-size SIZE|font size. The default value is 30px
- EOF
-}
-
-find_user_dict() {
- find \
- "$USER_DICT" \
- /usr/share/gd-tools/user_dic.dic \
- ~/.local/share/gd-tools/user_dic.dic \
- -type f -print -quit 2>/dev/null
-}
-
-find_dicdir() {
- dirname -- "$(
- find \
- /usr/lib/mecab/dic/mecab-ipadic-neologd \
- /usr/lib/mecab/dic \
- ~/.local/share/Anki2/addons21 \
- -type f -name dicrc -print -quit 2>/dev/null
- )"
-}
-
-mecab_split() {
- if ! command -v mecab &>/dev/null; then
- echo "Error: MeCab is not installed. Please install MeCab and try again."
- exit 1
- fi
-
- local -r user_dict=$(find_user_dict)
- local -a args=(
- mecab
- --node-format='%m'
- --unk-format='%m'
- --eos-format='
'
- --dicdir="$(find_dicdir)"
- --userdic="${USER_DICT}"
- )
- if [[ -n $user_dict ]]; then
- args+=("$user_dict")
- fi
- "${args[@]}"
-}
-
-print_css() {
- cat <<-EOF
-
- EOF
-}
-
-highlight_word() {
- local target_word=$* search=""
- search=$(cat -- -)
- search=${search//">$target_word<"/">${target_word}<"}
- echo "$search"
-}
-
-die() {
- echo "$*" >&2
- notify-send "$(basename -- "$0")" "$*" &
- exit 1
-}
-
-main() {
- if (($# == 0)) || [[ $* == --help ]] || [[ $* == -h ]]; then
- usage
- exit
- fi
- while (($# > 0)); do
- case $1 in
- --font-size)
- shift
- FONT_SIZE=$1
- ;;
- --user-dict)
- shift
- USER_DICT=$1
- ;;
- --word)
- shift
- GDWORD=$1
- ;;
- --sentence)
- shift
- GDSEARCH=$1
- ;;
- *)
- die "Invalid argument passed."
- ;;
- esac
- shift
- done
-
- if [[ -n $USER_DICT ]] && ! [[ -f $USER_DICT ]]; then
- die "Provided user dictionary doesn't exist or isn't a file."
- fi
-
- if [[ -z $GDSEARCH ]] || [[ -z $GDWORD ]]; then
- die "Not enough parameters."
- fi
-
- local -r input=$(echo "$GDSEARCH" | sanitize_input)
- local -r output=$(grep -Fxs -A1 "$input" -- "$TEMP_FILE")
-
- echo ''
- if [[ -n $output ]]; then
- echo "$output" |
- tail -1 |
- highlight_word "$GDWORD"
- else
- echo "$input" |
- tee -a "$TEMP_FILE" |
- mecab_split |
- tee -a "$TEMP_FILE" |
- highlight_word "$GDWORD"
- printf -- '\n\n' >>"$TEMP_FILE"
- fi
- echo '
'
-
- print_css
-}
-
-main "$@"
diff --git a/src_bac/gd-strokeorder.sh b/src_bac/gd-strokeorder.sh
deleted file mode 100755
index 3590d45..0000000
--- a/src_bac/gd-strokeorder.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-gd-echo --font-family "KanjiStrokeOrders" "$@"
diff --git a/src_bac/images.cpp b/src_bac/images.cpp
deleted file mode 100644
index 869c9f5..0000000
--- a/src_bac/images.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "precompiled.h"
-#include "util.h"
-
-using namespace std::literals;
-
-static constexpr std::size_t default_max_time_s{ 6 };
-static constexpr std::string_view help_text = R"EOF(usage: gd-images [OPTIONS]
-
-Get images from Bing.
-
-OPTIONS
- --max-time SECONDS maximum time in seconds to wait for response.
- --word WORD search term.
-
-EXAMPLES
- gd-images --max-time 6 --word "犬"
- gd-images --word 猫
-)EOF";
-static constexpr std::string_view css_style = R"EOF()EOF";
-
-struct images_params
-{
- std::chrono::seconds max_time{ default_max_time_s };
- std::string gd_word{};
-
- void assign(std::string_view const key, std::string_view const value)
- {
- if (key == "--max-time") {
- max_time = std::chrono::seconds{ parse_number(value).value_or(default_max_time_s) };
- } else if (key == "--word") {
- gd_word = value;
- }
- }
-};
-
-void fetch_images(images_params const& params)
-{
- cpr::Response const r = cpr::Get(
- cpr::Url{ "https://www.bing.com/images/search"sv },
- cpr::Parameters{ { "q", params.gd_word }, { "mkt", "ja-JP" } },
- cpr::Header{ { "User-Agent", "Mozilla/5.0" } },
- cpr::VerifySsl{ false },
- cpr::Timeout{ params.max_time }
- );
- raise_if(r.status_code != 200, "Couldn't connect to Bing.");
- static std::regex const img_re("]*class=\"mimg[^<>]*>");
- auto images_begin = std::sregex_iterator(std::begin(r.text), std::end(r.text), img_re);
- auto images_end = std::sregex_iterator();
- fmt::print("\n");
- for (auto const& match: std::ranges::subrange(images_begin, images_end) | std::views::take(5)) {
- fmt::print("{}\n", match.str());
- }
- fmt::print("
\n");
- fmt::print("{}\n", css_style);
-}
-
-void images(std::span const args)
-{
- try {
- fetch_images(fill_args(args));
- } catch (gd::help_requested const& ex) {
- fmt::print(help_text);
- } catch (gd::runtime_error const& ex) {
- fmt::print("{}\n", ex.what());
- }
-}
diff --git a/src_bac/images.h b/src_bac/images.h
deleted file mode 100644
index 62104a7..0000000
--- a/src_bac/images.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-#include "precompiled.h"
-
-void images(std::span const args);
diff --git a/src_bac/kana_conv.cpp b/src_bac/kana_conv.cpp
deleted file mode 100644
index f35b329..0000000
--- a/src_bac/kana_conv.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "kana_conv.h"
-#include "precompiled.h"
-#include "util.h"
-
-auto unicode_char_byte_len(char const& ch) -> CharByteLen
-{
- // See `man charsets` -> Unicode for explanation of below
- if ((ch & 0xC0) == 0x80) {
- // intermediate unicode char
- return CharByteLen::SKIP;
- } else if ((ch & 0x80) == 0x00) {
- // Start of ASCII character
- return CharByteLen::ONE;
- } else if ((ch & 0xE0) == 0xC0) {
- // Cyrillic, etc.
- return CharByteLen::TWO;
- } else if ((ch & 0xF0) == 0xE0) {
- // Start of 3 byte sequence (CJK)
- return CharByteLen::THREE;
- } else if ((ch & 0xF8) == 0xF0) {
- // Other Unicode
- return CharByteLen::FOUR;
- }
- throw gd::runtime_error{ fmt::format("Can't recognize byte: '{:x}'.", ch) };
-}
-
-auto create_map(std::string_view from, std::string_view to) -> KanaConvMap
-{
- KanaConvMap result{};
- for (auto const [idx, uni_char]: enum_unicode_chars(from)) {
- result.emplace(uni_char, to.substr(idx, uni_char.length()));
- }
- return result;
-}
-
-auto half_to_full(std::string& str) -> std::string&
-{
- static KanaConvMap const conv_map = {
- { "ア", "ア" }, { "イ", "イ" }, { "ウ", "ウ" }, { "エ", "エ" }, { "オ", "オ" }, { "カ", "カ" }, { "キ", "キ" },
- { "ク", "ク" }, { "ケ", "ケ" }, { "コ", "コ" }, { "サ", "サ" }, { "シ", "シ" }, { "ス", "ス" }, { "セ", "セ" },
- { "ソ", "ソ" }, { "タ", "タ" }, { "チ", "チ" }, { "ツ", "ツ" }, { "テ", "テ" }, { "ト", "ト" }, { "ナ", "ナ" },
- { "ニ", "ニ" }, { "ヌ", "ヌ" }, { "ネ", "ネ" }, { "ノ", "ノ" }, { "ハ", "ハ" }, { "ヒ", "ヒ" }, { "フ", "フ" },
- { "ヘ", "ヘ" }, { "ホ", "ホ" }, { "マ", "マ" }, { "ミ", "ミ" }, { "ム", "ム" }, { "メ", "メ" }, { "モ", "モ" },
- { "ヤ", "ヤ" }, { "ユ", "ユ" }, { "ヨ", "ヨ" }, { "ラ", "ラ" }, { "リ", "リ" }, { "ル", "ル" }, { "レ", "レ" },
- { "ロ", "ロ" }, { "ワ", "ワ" }, { "ヲ", "ヲ" }, { "ン", "ン" }, { "ァ", "ァ" }, { "ィ", "ィ" }, { "ゥ", "ゥ" },
- { "ェ", "ェ" }, { "ォ", "ォ" }, { "ッ", "ッ" }, { "ャ", "ャ" }, { "ュ", "ュ" }, { "ョ", "ョ" }, { "。", "。" },
- { "、", "、" }, { "・", "・" }, { "゛", "゙" }, { "゜", "゚" }, { "「", "「" }, { "」", "」" }, { "ー", "ー" }
- };
- for (auto const [idx, uni_char]: enum_unicode_chars(str)) {
- if (conv_map.contains(uni_char)) {
- str.replace(idx, uni_char.length(), conv_map.at(uni_char));
- }
- }
- return str;
-}
diff --git a/src_bac/kana_conv.h b/src_bac/kana_conv.h
deleted file mode 100644
index 231c710..0000000
--- a/src_bac/kana_conv.h
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-
-#include "precompiled.h"
-
-using KanaConvMap = std::unordered_map;
-
-enum CharByteLen : std::size_t { SKIP = 0, ONE = 1, TWO = 2, THREE = 3, FOUR = 4 };
-enum class Direction { kata_to_hira, hira_to_kata };
-
-auto create_map(std::string_view from, std::string_view to) -> KanaConvMap;
-auto unicode_char_byte_len(char const& ch) -> CharByteLen;
-
-inline constexpr std::string_view hiragana_chars =
- "ぁあぃいぅうぇえぉおかがか゚きぎき゚くぐく゚けげけ゚こごこ゚さざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふ"
- "ぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖゝゞ";
-inline constexpr std::string_view katakana_chars =
- "ァアィイゥウェエォオカガカ゚キギキ゚クグク゚ケゲケ゚コゴコ゚サザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフ"
- "ブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヽヾ";
-
-struct Utf8CharView
-{
- std::size_t idx; // starting position of a char in string, counting by bytes
- std::string_view ch; // the whole character, e.g. "あ" (len=3)
-};
-
-struct Utf8IdxLen
-{
- std::size_t idx; // starting position of a char in string, counting by bytes
- std::size_t len; // length of the utf8 character in bytes.
-
- static Utf8IdxLen from_tuple(std::tuple enumerated)
- {
- return Utf8IdxLen(
- std::get<0>(enumerated), //
- unicode_char_byte_len(std::get<1>(enumerated))
- );
- }
-};
-
-constexpr auto enum_unicode_chars(std::string_view str)
-{
- // return a sequence of Utf8CharView.
- // e.g. [ (0, "あ"), (3, "い") ]
- return std::views::enumerate(str) //
- | std::views::transform(Utf8IdxLen::from_tuple)
- | std::views::filter([](Utf8IdxLen const ch) { return (ch.len != CharByteLen::SKIP); })
- | std::views::transform([str](Utf8IdxLen const ch) {
- assert(ch.len > 0);
- return Utf8CharView(ch.idx, str.substr(ch.idx, ch.len));
- });
-}
-
-constexpr auto iter_unicode_chars(std::string_view str)
-{
- return enum_unicode_chars(str) | std::views::transform([](Utf8CharView const v) { return v.ch; });
-}
-
-template
-auto convert_kana(std::string_view str) -> std::string
-{
- static auto const conv_map{ [] {
- if constexpr (D == Direction::kata_to_hira) {
- return create_map(katakana_chars, hiragana_chars);
- } else {
- return create_map(hiragana_chars, katakana_chars);
- }
- }() };
-
- std::string result{};
- result.reserve(str.length());
- for (auto const [idx, uni_char]: enum_unicode_chars(str)) {
- result.append(conv_map.contains(uni_char) ? conv_map.at(uni_char) : uni_char);
- }
- return result;
-}
-
-inline auto katakana_to_hiragana(std::string_view str) -> std::string
-{
- return convert_kana(str);
-}
-
-inline auto hiragana_to_katakana(std::string_view str) -> std::string
-{
- return convert_kana(str);
-}
-
-auto half_to_full(std::string& str) -> std::string&;
-
-struct KanaInsensitiveMore
-{
- bool operator()(std::string const& lhs, std::string const& rhs) const
- {
- if (lhs.length() != rhs.length()) {
- return lhs.length() > rhs.length();
- } else {
- return hiragana_to_katakana(lhs) > hiragana_to_katakana(rhs);
- }
- }
-};
-
-using JpSet = std::set;
diff --git a/src_bac/main.cpp b/src_bac/main.cpp
deleted file mode 100644
index 019fe4e..0000000
--- a/src_bac/main.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "anki_search.h"
-#include "echo.h"
-#include "images.h"
-#include "marisa.h"
-#include "massif.h"
-#include "precompiled.h"
-
-static constexpr std::string_view help_text = R"EOF(usage: {} ACTION [OPTIONS]
-A set of helpful programs to enhance GoldenDict for immersion learning.
-
-ACTIONS
- ankisearch Search word in Anki.
- massif Search word on Massif.
- images Search images on Bing.
- marisa Split search string using MARISA.
- strokeorder Show stroke order of a word.
- handwritten Display the handwritten form of a word.
-
-OPTIONS
- -h,--help Print this help screen.
-
-EXAMPLES
-gd-tools ankisearch --field-name VocabKanji %GDWORD%
-gd-ankisearch --field-name VocabKanji %GDWORD%
-gd-ankisearch --deck-name Mining %GDWORD%
-)EOF";
-
-auto get_help_str(std::string_view program_name) -> std::string
-{
- return fmt::format(help_text, program_name);
-}
-
-auto print_help(std::string_view const program_name) -> void
-{
- fmt::print("{}", get_help_str(program_name));
-}
-
-auto base_name(auto file_path) -> std::string
-{
- return std::filesystem::path(file_path).filename();
-}
-
-template
-constexpr auto djbx33a(std::string_view const s) -> Ret
-{
- Ret acc = 5381;
- for (auto const ch: s) { acc = (acc * 33) + static_cast(ch); }
- return acc;
-}
-
-constexpr auto operator""_h(char const* s, [[maybe_unused]] size_t const size)
-{
- return djbx33a(std::string_view(s, size));
-}
-
-auto take_action(std::span const args) -> void
-{
- auto const program_name = base_name(args.front());
-
- // Command passed as program name (first arg).
- std::span rest = args.subspan(1);
- switch (djbx33a(program_name)) {
- case "gd-ankisearch"_h:
- return search_anki_cards(rest);
- case "gd-echo"_h:
- return stroke_order(rest);
- case "gd-massif"_h:
- return massif(rest);
- case "gd-images"_h:
- return images(rest);
- case "gd-marisa"_h:
- return marisa_split(rest);
- }
-
- // Help requested explicitly.
- if (std::size(args) < 2 or args[1] == "-h" or args[1] == "--help") {
- return print_help(program_name);
- }
-
- // Command passed as second arg (first is "gd-tools").
- rest = rest.subspan(1);
- switch (djbx33a(args[1])) {
- case "ankisearch"_h:
- return search_anki_cards(rest);
- case "echo"_h:
- return stroke_order(rest);
- case "massif"_h:
- return massif(rest);
- case "images"_h:
- return images(rest);
- case "marisa"_h:
- return marisa_split(rest);
- }
-
- // Couldn't determine command.
- return print_help(program_name);
-}
-
-auto main(int const argc, char const* const* const argv) -> int
-{
- take_action(std::vector{ argv, std::next(argv, argc) });
- return 0;
-}
diff --git a/src_bac/marisa.cpp b/src_bac/marisa.cpp
deleted file mode 100644
index 99d688f..0000000
--- a/src_bac/marisa.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "kana_conv.h"
-#include "precompiled.h"
-#include "util.h"
-
-using namespace std::string_literals;
-using namespace ajt::rdricpp;
-static constexpr std::string_view help_text = R"EOF(usage: gd-marisa [OPTIONS]
-
-Split sentence using MARISA and print links to each word.
-
-OPTIONS
- --word WORD required word
- --sentence SENTENCE required sentence
- --path-to-dic optional path to words.dic
-
-EXAMPLES
-gd-marisa --word %GDWORD% --sentence %GDSEARCH%
-)EOF";
-static constexpr std::string_view css_style = R"EOF(
-
-)EOF";
-static constexpr std::size_t max_forward_search_len_bytes{ CharByteLen::THREE * 20UL };
-
-auto find_dic_file() -> std::filesystem::path
-{
- static auto const locations = {
- // possible .dic locations
- std::filesystem::path("/usr/share/gd-tools/marisa_words.dic"),
- std::filesystem::path(std::getenv("HOME")) / ".local/share/gd-tools/marisa_words.dic"
- };
- for (auto const& location: locations) {
- if (std::filesystem::exists(location) and std::filesystem::is_regular_file(location)) {
- return location;
- }
- }
- throw gd::runtime_error("Couldn't find the word list.");
-}
-
-struct marisa_params
-{
- std::string gd_word{};
- std::string gd_sentence{};
- std::string path_to_dic{ find_dic_file() };
-
- auto assign(std::string_view const key, std::string_view const value) -> void
- {
- if (key == "--word") {
- gd_word = value;
- } else if (key == "--sentence") {
- gd_sentence = value;
- } else if (key == "--path-to-dic") {
- path_to_dic = value;
- }
- }
-};
-
-auto cmp_len(std::string_view a, std::string_view b) -> bool
-{
- return a.length() < b.length();
-}
-
-struct Deinflected
-{
- std::string_view from;
- std::vector to;
-};
-
-auto find_deinflections_starting_with(std::string_view const search_str) -> std::vector
-{
- std::vector hits;
- // loop from larger towards shorter substrings
- for (std::string_view substr: enum_unicode_chars(search_str) //
- | std::views::reverse
- | std::views::transform( //
- [&search_str](Utf8CharView const ch) { //
- return search_str.substr(0UL, ch.idx + ch.ch.size());
- }
- )) {
- hits.emplace_back(substr, deinflect(substr));
- }
- return hits;
-}
-
-auto find_keywords_starting_with(marisa::Agent& agent, marisa::Trie const& trie, std::string const& search_str) -> JpSet
-{
- JpSet results{};
- auto const variants = { search_str, hiragana_to_katakana(search_str), katakana_to_hiragana(search_str) };
-
- auto deinflections = std::views::all(variants) //
- | std::views::transform(find_deinflections_starting_with) //
- | std::views::join //
- | std::views::transform([](Deinflected const& group) { return group.to; }) //
- | std::views::join;
-
- for (auto const& deinflection: deinflections) {
- agent.set_query(deinflection.term.c_str());
- while (trie.common_prefix_search(agent)) { //
- results.emplace(agent.key().ptr(), agent.key().length());
- }
- }
-
- return results;
-}
-
-void lookup_words(marisa_params params)
-{
- half_to_full(params.gd_word);
- std::erase_if(params.gd_word, is_space);
- std::erase_if(params.gd_sentence, is_space);
-
- if (params.gd_sentence.empty()) {
- params.gd_sentence = params.gd_word;
- } else {
- half_to_full(params.gd_sentence);
- }
-
- marisa::Trie trie;
- marisa::Agent agent;
-
- std::ifstream file{ params.path_to_dic };
- raise_if(not file.good(), fmt::format(R"(Error. The dictionary file "{}" does not exist.)", params.path_to_dic));
- trie.load(params.path_to_dic.c_str());
-
- fmt::println(R"()");
- std::ptrdiff_t pos_in_gd_word{ 0 };
- std::vector
alternatives{};
- alternatives.reserve(20);
-
- // Link longest words starting with each position in sentence.
- for (auto const [idx, uni_char]: enum_unicode_chars(params.gd_sentence)) {
- auto const headwords{ find_keywords_starting_with(
- agent,
- trie, //
- params.gd_sentence.substr(idx, max_forward_search_len_bytes)
- ) };
-
- // set bword to the longest found key in the trie.
- std::string const bword{ headwords.empty() ? std::string{ uni_char } : std::ranges::max(headwords, cmp_len) };
- if (params.gd_word == bword) {
- pos_in_gd_word = static_cast(bword.length());
- } else {
- pos_in_gd_word -= static_cast(uni_char.length());
- }
-
- fmt::print(
- R"({})",
- (pos_in_gd_word > 0 ? "gd-headword" : "gd-word"),
- bword,
- uni_char
- );
- alternatives.push_back(headwords);
- }
-
- // Show available entries for other substrings.
- fmt::println(R"()");
- for (auto const& group: alternatives | std::views::filter(&JpSet::size)) {
- fmt::println("
");
- for (auto const& word: group) {
- fmt::println(
- R"(- {}
)",
- (word == params.gd_word ? "gd-headword" : ""),
- word,
- word
- );
- }
- fmt::println("
"); // close ul
- }
- fmt::println("
"); // close div.alternatives
-
- fmt::println(" "); // close div.gd-marisa
- fmt::println("{}", css_style);
-}
-
-void marisa_split(std::span const args)
-{
- try {
- lookup_words(fill_args(args));
- } catch (gd::help_requested const& ex) {
- fmt::println(help_text);
- } catch (gd::runtime_error const& ex) {
- fmt::println("{}", ex.what());
- }
-}
diff --git a/src_bac/marisa.h b/src_bac/marisa.h
deleted file mode 100644
index 3c3d283..0000000
--- a/src_bac/marisa.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-#include "precompiled.h"
-
-auto marisa_split(std::span const args) -> void;
diff --git a/src_bac/massif.cpp b/src_bac/massif.cpp
deleted file mode 100644
index 5e98cba..0000000
--- a/src_bac/massif.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "precompiled.h"
-#include "util.h"
-
-using namespace std::literals;
-
-static constexpr std::size_t default_max_time_s{ 6 };
-static constexpr std::string_view help_text = R"EOF(usage: gd-massif [OPTIONS]
-
-Get example sentences from Massif.
-
-OPTIONS
- --max-time SECONDS maximum time in seconds to wait for response.
- --word WORD search term.
-
-EXAMPLES
- gd-massif --max-time 6 --word "貴様"
- gd-massif --word 貴様
-)EOF";
-static constexpr std::string_view css_style = R"EOF()EOF";
-
-struct massif_params
-{
- std::chrono::seconds max_time{ default_max_time_s };
- std::string_view gd_word{};
-
- void assign(std::string_view const key, std::string_view const value)
- {
- if (key == "--max-time") {
- max_time = std::chrono::seconds{ parse_number(value).value_or(default_max_time_s) };
- } else if (key == "--word") {
- gd_word = value;
- }
- }
-};
-
-void fetch_massif_examples(massif_params const& params)
-{
- cpr::Response const r = cpr::Get(
- cpr::Url{ fmt::format("https://massif.la/ja/search?q={}", params.gd_word) },
- cpr::Timeout{ params.max_time },
- cpr::VerifySsl{ false }
- );
- raise_if(r.status_code != 200, "Couldn't connect to Massif.");
- fmt::print("\n");
- for (auto const& line:
- r.text //
- | std::views::split('\n') //
- | std::views::transform([](auto const& sub_view) { return std::string_view{ sub_view }; })
- | std::views::drop_while([](auto const str_view) {
- return not str_view.contains("- ");
- })
- | std::views::take_while([](auto const str_view) { return not str_view.contains("
"); })) {
- fmt::print("{}\n", line);
- }
- fmt::print("\n");
- fmt::print("{}\n", css_style);
-}
-
-void massif(std::span const args)
-{
- try {
- fetch_massif_examples(fill_args(args));
- } catch (gd::help_requested const& ex) {
- fmt::print(help_text);
- } catch (gd::runtime_error const& ex) {
- fmt::print("{}\n", ex.what());
- }
-}
diff --git a/src_bac/massif.h b/src_bac/massif.h
deleted file mode 100644
index 269ed0d..0000000
--- a/src_bac/massif.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-#include "precompiled.h"
-
-void massif(std::span const args);
diff --git a/src_bac/precompiled.h b/src_bac/precompiled.h
deleted file mode 100644
index 9b6ca89..0000000
--- a/src_bac/precompiled.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// STL
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-// Glibc
-#include
-
-// Other
-#include
-#include
-#include
-#include
-#include
-#include
diff --git a/src_bac/util.cpp b/src_bac/util.cpp
deleted file mode 100644
index 31f78d3..0000000
--- a/src_bac/util.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * gd-tools - a set of programs to enhance goldendict for immersion learning.
- * Copyright (C) 2023 Ajatt-Tools
- *
- * 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 3 of the License, or
- * (at your option) 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, see .
- */
-
-#include "util.h"
-#include "precompiled.h"
-
-auto determine_card_class(int64_t const card_queue, int64_t const card_type) noexcept -> std::string_view
-{
- // determine card class to be used in CSS when printing
- // https://github.com/ankidroid/Anki-Android/wiki/Database-Structure
- switch (card_queue) {
- case -3:
- case -2:
- return "buried";
- case -1:
- return "suspended";
- case 0:
- return "new";
- case 1:
- case 3:
- return "learning";
- case 2:
- return "review";
- default:
- break;
- }
- switch (card_type) {
- case 0:
- return "new";
- case 1:
- case 3:
- return "learning";
- case 2:
- return "review";
- default:
- break;
- }
- return "unknown";
-}
-
-auto is_space(char const ch) noexcept -> bool
-{
- return static_cast(std::isspace(static_cast(ch)));
-}
-
-auto strtrim(std::string_view str) noexcept -> std::string
-{
- auto v = std::views::all(str) //
- | std::views::reverse //
- | std::views::drop_while(is_space) //
- | std::views::reverse //
- | std::views::drop_while(is_space);
- return std::string{ v.begin(), v.end() };
-}
-
-void str_replace(std::string& str, std::string_view from, std::string_view to) noexcept
-{
- if (auto idx = str.find(from); idx != std::string::npos) {
- str.replace(idx, from.length(), to);
- }
-}
diff --git a/src_bac/util.h b/src_bac/util.h
deleted file mode 100644
index 2f82e5e..0000000
--- a/src_bac/util.h
+++ /dev/null
@@ -1,77 +0,0 @@
-#pragma once
-
-#include "precompiled.h"
-
-namespace gd {
-class help_requested : public std::invalid_argument
-{
-public:
- help_requested() : std::invalid_argument("Help requested.") {}
-};
-
-class runtime_error : public std::runtime_error
-{
-public:
- runtime_error(std::string_view const what) : std::runtime_error(std::string{ what }) {}
-};
-} // namespace gd
-
-inline void raise_if(bool expr, std::string_view const message = "Invalid argument.")
-{
- if (expr) {
- throw gd::runtime_error(message);
- }
-}
-
-template
-concept PassedParamsStruct = requires(T params) {
- {
- params.gd_word
- } -> std::convertible_to;
- params.assign("a", "b");
-};
-
-template
-auto fill_args(std::span const args) -> T
-{
- auto params = T{};
- for (auto it = std::begin(args); it != std::end(args);) {
- if (*it == "--help" or *it == "-h") {
- throw gd::help_requested();
- }
- std::ptrdiff_t advance = 1;
- std::string_view value = (std::next(it) != std::end(args)) ? *std::next(it) : "";
-
- if (it->starts_with("-")) {
- // Expect next arg to be the value.
- raise_if(value.empty() or value.starts_with("-"));
- advance = 2;
- }
-
- params.assign(*it, value);
- it += advance;
- }
- if (params.gd_word.empty()) {
- throw gd::help_requested();
- }
- return params;
-}
-
-auto determine_card_class(int64_t const card_queue, int64_t const card_type) noexcept -> std::string_view;
-
-template
-auto parse_number(std::string_view const s) -> std::optional
-{
- if (Integral value{}; std::from_chars(s.begin(), s.end(), value).ec == std::errc{})
- return value;
- else
- return std::nullopt;
-};
-
-auto is_space(char const ch) noexcept -> bool;
-
-auto strtrim(std::string_view str) noexcept -> std::string;
-
-void str_replace(std::string& str, std::string_view from, std::string_view to) noexcept;
-
-inline std::string const this_pid{ std::to_string(getpid()) };