とにかく書く

日々の雑感や知り得たことを、とにかく書いています

CDEmu のデスクトップエントリ作成

自作のファイラにて iso ファイルを開こうとしたとき、MIMEタイプが application/x-gamecube-rom と認識される。1 さらに、CDEmu のデスクトップエントリがないため、自動的にマウントしてくれない。

CDEmuの GitHub には、cdemu-client.desktop.in としてデスクトップエントリが準備されていたので、ホームディレクトリに cdemu-client.desktop として保存した。

追加で以下を行った。

  • _Nameと_Commentは、行頭のアンダーラインを削除した。
  • application/x-gamecube-rom; を application/x-gamecube-iso-image; の後ろに追加した。

確認しOK。

$ desktop-file-validate cdemu-client.desktop

インストール

$ desktop-file-install --dir=$HOME/.local/share/applications ~/cdemu-client.desktop

デスクトップエントリデータベースの更新

$ update-desktop-database ~/.local/share/applications

一連の処理手順については Arch Wikiデスクトップエントリ を参考にした。

iso ファイルをダブルクリックして、cdemu status コマンドでマウントload できていることを確認した。


  1. MIMEタイプは wxWidgetswxMimeTypesManager クラスにある GetFileTypeFromExtension メソッドによって取得している。

libucl を自前の C++ プログラムで使用する方法

この記事では、libucl を自前の C++ プログラムで使用する方法を記述します。

はじめに:libucl とは

libucl は、UCL: Universal Configuration Language (和訳すれば汎用設定言語) で書かれた設定ファイルを読み込むライブラリです。 GitHub の Readme.md にある Conclusion を一部引用します。

UCL は非常に読み書きしやすく設計されている。 また、同時に JSON を包括しており(上位互換であり)、libucl は JSON パーサーとしても使える。

さらに、以下の追加機能を持ちます。

  • コメントの追加
  • nginx 風の記述(key = value)の解釈
  • マクロの使用

BSD 系で、既存の設定ファイルを置き換えることを目指している模様です。1

インストール

Arch Linux の場合

Arch Linux の場合、AUR からインストールできます。

 $ git clone https://aur.archlinux.org/libucl.git
 $ cd libucl
 $ makepkg -si

ヘッダファイル (ucl++.h など) は /usr/include に、ライブラリファイル (libucl.so) は /usr/lib にインストールされます。

そのほかのディストリビューションや、Unix系OSもパッケージがある可能性があります。

本家 GitHub から取得する場合

本家の GitHub から取得します。

$ git clone https://github.com/vstakhov/libucl

CMakeLists.txt が用意されているため、CMake でコンパイルします。

$ mkdir build
$ cd build
$ cmake -G "Unix Makefiles" ..
$ cmake --build .

libucl.a が build ディレクトリに生成されます。

プログラムへの取り込み

CMake を使っている場合

CMakefile.txt に以下のように記述します。私が作成しているプログラムでは、wxWidgets も使っているため、そのライブラリも指定しています。 ここでは、.h ファイル(ヘッダファイル)は /usr/include に、.so ファイル(ライブラリファイル)は /usr/lib にインストールされた状態の例となります。 それぞれのディレクトリはデフォルトで探索パスに含まれるので、ucl (libucl.so のlibと.soを除いた部分) だけを追加しています。2

target_link_libraries(${PROJ_NAME} ${wxWidgets_LIBRARIES} ucl)

g++ を使用してコンパイルする場合

公式の GitHub をチェックアウトした場合、次節で挙げる example/ucl_cpp.cc は、以下のコマンドでコンパイルできます。3

$ cd libucl
$ g++ -lucl example/ucl_cpp.cc

プログラム内での使用方法

公式の例

公式の GitHub の example ディレクトリに、ucl_cpp.cc が例として掲載されています。 説明を1行コメントとして追記した上で転記します。

#include <iostream>
#include <string>
#include "ucl++.h" // include path を通していれば、<ucl++.h> と書けます

int main(int argc, char **argv)
{
    std::string input, err;

    // 読み込む文字列を標準入力から取得します。
    input.assign((std::istreambuf_iterator<char>(std::cin)),
        std::istreambuf_iterator<char>());

    // 読み込んだ文字列を解析します。
    // 解析に失敗すると nullptr が返り、理由が err にセットされます。
    auto obj = ucl::Ucl::parse(input, err);

    if (obj) {
        // 解析したデータを一括で出力します。
        std::cout << obj.dump(UCL_EMIT_CONFIG) << std::endl;

        // 解析したデータを1つずつ出力します。
        for (const auto &o : obj) {
            std::cout << o.dump(UCL_EMIT_CONFIG) << std::endl;
        }
    }
    else {
        // 失敗した場合、その行と内容等が表示されます。
        std::cerr << "Error: " << err << std::endl;

        return 1;
    }
    return 0;
}

ファイルからの読み込み

auto obj = ucl::Ucl::parse(input, err); の代わりに、auto obj = ucl::Ucl::parse_from_file(ファイル名, err) で、指定したファイル名から直接 UCL データを読み込んで解析します。auto で受け取っていますが、ucl::Ucl 型が返ります。

値の参照

取得する値の型によって、メソッドを変えます。

"a": 10.0;
"b": 20;
"c": true;
"d": "eee";

とあるとき、以下のメソッド呼び出しでそれぞれの値を取得できます。

obj["a"].number_value(); // double      => 10.0
obj["b"].int_value();    // int64_t     => 20
obj["c"].bool_value();   // bool        => true
obj["d"].string_value(); // std::string => "eee"

同一キーは配列として扱う

同一のキーで複数の設定を記載した場合、自動的に配列として解析されます。例えば、

"foo": "value1"
"foo": "value2"

"foo": ["value1", "value2"] として扱われます。 配列の場合、添字でのアクセスが可能です。

obj["foo"][0].string_value() => "value1"

また、配列の場合のみ size メソッドが使用可能です。4

obj["foo"].size() => 2

入れ子構造

データは、入れ子構造で定義することができます。

{
    "foo" {
        "bar" {
            "key": "value"
        }
    }
}

このとき、obj["foo"]["bar"]["key"].string_value() の形で "value" を取得することができます。

なお、obj["foo"] は、以下の ucl::Ucl 型の値となります。

{
    "bar" {
        "key": "value"
    }
}

出力

公式の例にある、dump メソッドを使用します。

"key": "value" の追加

キー "foo" がない状態で obj["foo"] = "something" などとコード中に記述しても、"foo": "something" は追加されません。

おそらく全体を一旦 dump し、"foo": "something" を追加したうえで再解釈するしかないように思われます。私の場合、以下の Add メソッドを自作しました。

#include <ucl++.h>

ucl::Ucl Add(ucl::Ucl obj, const std::string key, const std::string value)
{
    // \n# は、obj.dump() の出力にある最初の開きカッコ「{」をコメントアウトするために追加しています。
    std::string in = "{" + key + ": " + value + "\n#" + obj.dump();
    std::string err;

    obj = ucl::Ucl::parse(in, err);
    if (! obj) {
        std::cerr << "Error: " << err << std::endl;
        return ucl::Ucl(nullptr); // 空の ucl::Ucl を返す
    }
    return obj;
}

その他

各関数は ucl++.h を参照することで詳細がわかります。 at, begin, end, 演算子 (= (既存の"key"に"value"を設定する), ==, <, !=, <=, >, >=) 等も定義されています。


  1. BSDCan 2015 UCL Working Group
  2. target_link_libraries(${PROJ_NAME} ${wxWidgets_LIBRARIES} /usr/lib/libucl.so)絶対パスで指定してもコンパイラはリンクすることができます。自前でコンパイルした場合は、こちらになります。なお、includeパスを指定する場合は target_include_directories(${PROJ_NAME} PRIVATE /usr/include) と書きます。
  3. /usr/include を指定する場合は、-I /usr/include を含めます。このディレクトリはデフォルトで見に行くため本文では記載していません。libucl を自前でコンパイルした場合は、-I や -l オプションによって各ファイルを絶対パスで指定できます。
  4. 言い換えれば、key: value; を列挙している場合、その要素数イテレータで数え上げるしか取得方法がありません。

Firefox の日付表示が UTC になる(9時間前にずれる)

FirefoxGmail などを開くと9時間前の時刻で表示されていた。
conky などで表示されるシステム時刻は正しい。
結局、about:config にある privacy.resistFingerprinting が true だったためだった。
この値を false (初期値) に戻せば正しく表示された。

その他に試したことも記載しておく。

システムクロックの確認

$ ls -l /etc/localtime 
lrwxrwxrwx 1 root root 30  54  2020 /etc/localtime -> /usr/share/zoneinfo/Asia/Tokyo

arch wiki にある環境変数TZは、設定されていなかったため、.xinitrc に追加した。

export TZ=JST-9

以下のコマンドで確認できる。

$ echo $TZ
JST-9

ArchLinuxの署名検証エラーを解決する方法

ArchLinux で Wayland コンポジタの hikari を試してみたくて、AUR の wlroots0.15 をインストールしようとしたが、wlroots0.15-0.15.1.tar.gz ... %s is unable to verify the signature. と出てインストールできなかった。
その対処法をメモする。

$ makepkg -si
...中略...
==> gpg でソースファイルの署名を検証...
wlroots0.15-0.15.1.tar.gz ... %s is unable to verify the signature.
gpg
==> エラー: PGP 鍵を検証できませんでした!

sigファイルからRSA鍵を取得する。

$ gpg --verify wlroots0.15-0.15.1.tar.gz.sig
gpg: 署名されたデータが'wlroots0.15-0.15.1.tar.gz'にあると想定します
gpg: 2022年02月04日 06時21分37秒 JSTに施された署名
gpg: RSA鍵34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48を使用
gpg: 署名を検査できません: 公開鍵がありません

RSA鍵を(信頼して)インポートする。

$ gpg --recv-keys 34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48
gpg: 鍵 0FDE7BE0E88F5E48: 1個の重複した署名が除去されました
gpg: 鍵0FDE7BE0E88F5E48: 公開鍵"Simon Ser "をインポートしました
gpg: 処理数の合計: 1
gpg: インポート: 1

Arch Wiki の GnuPG にあるとおり、gpg --recv-keys は自己責任で実施してください。

UTF8 の日本語を表示させる

wxWidegets で、日本語を含む文字列 str を表示させたかったのに、wxString で変換されると表示できなかった。std::string 型なら std::cout << str << std::endl でも表示される。
Linux 環境だけど、マクロの wxUSE_UNICODE_WCHAR が 1 で、wxUSE_UNICODE_UTF8 が 0 であることが原因だと思う。
だけど、#undef と #def で再定義しても治らなかった。
std::string 型の文字列 str を wxString::FromUTF8(str.c_char()) で無事に日本語も表示できた。