|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// 設定
//
// 設定ファイルは、テキスト形式、行志向で順序を持たない key=value ペアの羅列、
// アプリケーションが書き戻しを行わないためユーザが任意にコメント行を入れる
// ことが出来る、タイプのもの。
//
// 設定(Config)は次の4段階によって確定する。
// c0. 初期値
// 項目はそれぞれ固有の初期値を持っている。ここでいう初期値は単なるゼロ
// クリアではなく、その項目に最も自然な、あるいは極力問題なく動作するよう
// 選択した値のこと。
// c1. ~/.nono.cfg
// c2. VM ディレクトリの設定ファイル
// 設定ファイルで指定された値はそれを上書きする。
// c3. コマンドライン引数
// コマンドライン引数で指定された値はそれを上書きする。
//
// おそらくたぶんすべての項目が例外なくこのように動作するようにしたい。
#pragma once
#include "mainapp.h"
#include <map>
#include <vector>
// 設定の1項目
class ConfigItem
{
friend class Config;
public:
enum from_t {
FromInitial = 0, // 初期値
FromHome, // 共通設定ファイル(~/.nono.cfg)で更新
FromConfig, // VM 設定ファイルで更新
FromOption, // コマンドライン引数で更新
FromPerf, // --perf オプション(を展開したもの)で更新
FromRunning, // 実行中に更新
};
public:
ConfigItem() { }
ConfigItem(const std::string& key_, const std::string& val_, VMCap vmcap_,
from_t from_, const std::string& where_) {
key = key_;
value = val_;
vmcap = vmcap_;
from = from_;
where = where_;
}
// 値を文字列のまま取得する。
const std::string& AsString() const { return value; }
// 値をブール値として取得する。
// この関数はよく似たことをたびたびやるのを避けるために提供してある
// ただのユーティリティ関数。変換できない場合や空文字列の場合 false に
// なるので、それでもよければ使ってよい。
bool AsBool() const { return (bool)AsInt(); }
// 値を整数値として取得する。
// この関数はよく似たことをたびたびやるのを避けるために提供してある
// ただのユーティリティ関数。変換できない場合や空文字列の場合 0 に
// なるので、それでもよければ使ってよい。
int AsInt() const { return atoi(value.c_str()); }
// 値を整数値として取得する。
// 変換できたら true を返す。
// 数値として評価出来ないときは false を返す。
bool TryInt(int *val) const;
// 値を double 値として取得する。
// 変換できたら true を返す。
// double 値として評価出来ないときは false を返す。
bool TryDouble(double *val) const;
// 値を固定小数点数として取得する。
// 変換できたら true を返す。
// 数値として評価出来ないときは false を返す。
bool TryFixedDecimal(int *val, int digit) const;
// デフォルト値を差し替える。
// この変数が FromInitial なら値(初期値)を val に差し替える。この時
// from は FromInitial のまま。
// FromInitial でなければ (ユーザが値を上書きしていれば) 何もしない。
// 本来は初期値設定 -> 設定ファイルやオプションで上書きの順に動作する
// のが理想だが、設定ファイルを読まないと VM 種別が確定しないにわたま
// 問題のため、VM 種別によって初期値が決定する場合などはデバイスの
// コンストラクタでこれを呼び出すこと。例えば mpu-clock など。
void SetDefault(const std::string& val);
// vmcap を取得する
VMCap GetVMCap() const { return vmcap; }
// from を取得する
from_t GetFrom() const { return from; }
// where を取得する
const std::string& GetWhere() const { return where; }
// この項目で "Invalid argument" のエラーメッセージ文字列を返す。
std::string ErrMsg() const;
// この項目で指定のメッセージのエラーメッセージ文字列を返す。
std::string ErrMsg(const char *fmt, ...) const __printflike(2, 3);
std::string ErrMsg(const std::string& msg) const;
// この項目で "invalid argument" を warnx() で出力する
void Err() const;
// この項目での指定のエラーメッセージを warnx() で出力する
void Err(const char *fmt, ...) const __printflike(2, 3);
void Err(const std::string& msg) const;
private:
static std::string ErrFrom(ConfigItem::from_t from,
const std::string& where,
const std::string& key, const std::string& value);
// key が変数名、value が値。
// value はここでは文字列でありそれ以上の構造は持たないが、
// 整数値として解釈する或いはカンマ区切りにするなどは使う側の裁量。
// value からは前後の空白は削除する。
// またエスケープの類も今の所ない。
std::string key {};
std::string value {};
// ケーパビリティ。
// 機種確定後に、この機種に不要な変数は削除する。
VMCap vmcap {};
// from (と where) はこの変数の由来を示す。
// o FromOption なら値はコマンドラインオプションで指定されたことを示す。
// この場合 where に "-V" とかが入っている。
// o FromConfig, FromHome なら値は設定ファイルで指定されたことを示す。
// この場合 where に "ファイルパス:行番号" が入っている。
// o FromInitial なら値は初期値のままであることを示す。引数や
// 設定ファイルで値を指定した場合は例え初期値と同じ値を指定したと
// しても初期値扱いとはしない。
from_t from {};
std::string where {};
};
// 設定ファイル
class ConfigFile
{
public:
ConfigFile(const std::string& filepath_, ConfigItem::from_t from_);
~ConfigFile();
// ファイルの読み込み。
// 成功すれば true を返す。失敗すればエラーを表示して false を返す。
bool Load();
// パス
std::string filepath {};
// 設定元
ConfigItem::from_t from {};
// 読み込んだ行一覧
// 行(改行と前後の空白を除く) と行番号(1から始まる)の pair。
// コメント行や空行は取り除いてあるが、文法チェックはしていないので
// 何か書いてある行の集合。
std::vector<std::pair<std::string, int>> lines {};
};
// 設定
class Config
{
public:
Config();
~Config();
// 設定ファイルの内容で上書きする
bool Update(const ConfigFile& file);
// コマンドライン引数の内容で上書きする
bool Update(const std::string& line, const std::string& optname);
// 実行中に動的に変更する
bool UpdateRunning(const std::string& line);
// 内容を表示する
void Show(bool all) const;
// key から ConfigItem を取得する。key は必ず存在するので参照を返す。
// 見付からなければ assert する。それはプログラムミスなので。
const ConfigItem& Find(const std::string& key) const;
// デフォルト値を差し替える。
// key が見付からなければ assert する。それはプログラムミスなので。
void SetDefault(const std::string& key, int value);
void SetDefault(const std::string& key, const std::string& value);
// 項目を追加
bool Add(const std::string& key, VMCap vmcap = VMCap::ALL);
bool Add(const std::string& key, int value, VMCap vmcap = VMCap::ALL);
bool Add(const std::string& key, const std::string& value,
VMCap vmcap = VMCap::ALL);
// エイリアスを追加
bool AddAlias(const std::string& alias, const std::string& key);
// 廃止項目を追加
void AddObsolete(const std::string& old, const std::string& alter);
// 項目を削除
void Delete(const std::string& key);
// 以降書き込み禁止にする (プログラムミスを避けるため)
bool Fix();
private:
// key から ConfigItem を返す。見付からなければ NULL を返す。
// (こっちは内部で変更するために使う)
ConfigItem *FindItem(const std::string& key);
// 更新1行分の共通処理
bool UpdateLine(const std::string& line, ConfigItem::from_t from,
const std::string& where);
// キーの正規名を返す
const std::string& GetCanonKey(const std::string& key) const;
// "key=val" 形式の行から key, val を取り出す。
static bool SplitKeyVal(std::string *keyp, std::string *valp,
const std::string& line);
// key が obsolete[] に含まれていれば true を返す。
bool IsObsolete(const std::string& key) const {
return (obsolete.find(key) != obsolete.end());
}
// 設定項目の集合
std::vector<ConfigItem> list {};
// エイリアス(別名)の集合
std::vector<std::pair<std::string, std::string>> aliases {};
// 廃止項目名の集合
std::map<std::string, std::string> obsolete {};
// 設定はある時点までは追加更新可能だが、途中からは読み込み専用としたい。
// プログラムミスを避けるため。
bool fixed {};
};
extern Config *gConfig;
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.