#include "device_opt.h" #include #include "base_opt.h" #include #include //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // static const char* empty_from_default_language(const char* deflang) { return deflang; } static const char*(*from_def_lang)(const char*) = empty_from_default_language; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // image-processing jsons ... static bool refine_string_data(gb_json* jsn, void* value) { bool refined = false; gb_json* range = nullptr; std::string v((char*)value); jsn->get_value("range", range); if (range) { std::string vl(""), vu(""), s(""); if (range->get_value("min", vl)) { if (v < vl) { strcpy((char*)value, vl.c_str()); refined = true; } else if (range->get_value("max", vu)) { if (v > vu) { strcpy((char*)value, vu.c_str()); refined = true; } //else if (range->get_value("step", s)) //{ // T cur(*(TC*)value); // // vl = cur - vl; // vl /= s; // if (!IS_DOUBLE_EQUAL(vl, (int)vl)) // { // vl += .5f; // vl = (int)vl * s; // if (vl > vu) // vl = vu; // cp((TC*)value, vl); // refined = true; // } //} } } else { gb_json* val = range->first_child(); bool found = false; while (val) { if (val->value(vl)) { if (v == vl) { found = true; val->release(); break; } } val->release(); val = range->next_child(); } if (!found) { if (jsn->get_value("default", vl)) { refined = true; strcpy((char*)value, vl.c_str()); } } } range->release(); } return refined; } template static std::string get_real_value(gb_json* jsn, bool def_val) { T v; jsn->get_value(def_val ? "default" : "cur", v); return std::move(std::string((char*)&v, sizeof(v))); } template static std::string get_real_string(gb_json* jsn, bool def_val) { T v; jsn->get_value(def_val ? "default" : "cur", v); return std::move(v); } static std::string get_real_value(gb_json* jsn, const char* type) { if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { bool v = false; jsn->value(v); return std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { int v = 0; jsn->value(v); return std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { double v = .0f; jsn->value(v); return std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { std::string v(""); jsn->value(v); return std::move(v); } return ""; } static struct { std::string name; std::string title; // in UTF8 }g_known_group_with_sn[] = { {"base", "\xE5\x9F\xBA\xE6\x9C\xAC\xE8\xAE\xBE\xE7\xBD\xAE"} , {"bright", "\xE4\xBA\xAE\xE5\xBA\xA6"} , {"imgp", "\xE5\x9B\xBE\xE5\x83\x8F\xE5\xA4\x84\xE7\x90\x86"} , {"feeder", "\xE9\x80\x81\xE7\xBA\xB8\xE6\x96\xB9\xE5\xBC\x8F\xE8\xAE\xBE\xE7\xBD\xAE"} // ���ã�\xE8\xAE\xBE\xE7\xBD\xAE , {"advance", "\xE9\xAB\x98\xE7\xBA\xA7\xE8\xAE\xBE\xE7\xBD\xAE"} // ���ã�\xE8\xAE\xBE\xE7\xBD\xAE , {"user", "\xE7\x94\xA8\xE6\x88\xB7"} // ���ã�\xE8\xAE\xBE\xE7\xBD\xAE , {"about", "\xE5\x85\xB3\xE4\xBA\x8E"} // ���ã�\xE8\xAE\xBE\xE7\xBD\xAE }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // class device_opt ... bool device_option::condition_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains only ONE value or its object { bool ret = false; clear(); if (!jsn) { // consistant value, and type is name ... CONDVAL cv; cv.logic = nullptr; cv.value = type; vals_.push_back(cv); parent_ = parent; return true; } if (jsn->is_leaf_node()) { CONDVAL cv; cv.logic = nullptr; cv.value = get_real_value(jsn, type); vals_.push_back(cv); ret = true; } else { gb_json* item = jsn->first_child(); CONDVAL val0; // make the default value at last ret = true; val0.logic = nullptr; val0.value = ""; while (item) { // "condition": value std::string cond(item->key()), val(""); CONDVAL cv; if (cond == "default") { val0.logic = nullptr; val0.value = get_real_value(item, type); } else { int end = 0; cv.logic = new simple_logic(); if (cv.logic->parse(cond.c_str(), &end, &device_option::init_condition, parent)) { cv.value = get_real_value(item, type); vals_.push_back(cv); } else { delete cv.logic; item->release(); ret = false; break; } } item->release(); item = jsn->next_child(); } if (!val0.value.empty()) vals_.push_back(val0); } return ret; } std::string device_option::condition_value::value(bool(*compare)(const char*, void*), void* param) { if (parent_) return parent_->get_option_value(vals_[0].value.c_str(), SANE_ACTION_GET_VALUE); for (auto& v : vals_) { if (!v.logic) return v.value; else if (v.logic->value(compare, param)) return v.value; } return ""; } bool device_option::range_value::set_value(gb_json* jsn, const char* type, device_option* parent) // jsn contains all range object { bool ret = true; parent->depend_opts_[jsn->key()]; clear(); if (jsn->is_array()) { gb_json* item = jsn->first_child(); while (item) { device_option::condition_value* v = new device_option::condition_value(); if (v->set_value(item, type, parent)) { vals_.push_back(v); } else { delete v; item->release(); ret = false; break; } item->release(); item = jsn->next_child(); } } else { condition_value* cv = nullptr; if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { cv = device_option::to_condition_value(jsn, "min", type, parent); ret = false; if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "max", type, parent); if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "step", type, parent); if (cv) { vals_.push_back(cv); ret = true; } } } } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { cv = device_option::to_condition_value(jsn, "min", type, parent); ret = false; if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "max", type, parent); if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "step", type, parent); if (cv) { vals_.push_back(cv); ret = true; } } } } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { cv = device_option::to_condition_value(jsn, "min", type, parent); ret = false; if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "max", type, parent); if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "step", type, parent); if (cv) { vals_.push_back(cv); ret = true; } } } } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { cv = device_option::to_condition_value(jsn, "min", type, parent); ret = false; if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "max", type, parent); if (cv) { vals_.push_back(cv); cv = device_option::to_condition_value(jsn, "step", type, parent); if (cv) { vals_.push_back(cv); ret = true; } } } } } return ret; } device_option::device_option(bool no_group, std::function user_priv , std::function log) : no_grp_(no_group), origin_(nullptr), now_(nullptr) , user_(user_priv), log_(log) {} device_option::~device_option() { clear(); } bool device_option::is_equal_b(gb_json* opt, void* val, void* v1, void* v2) { return *(bool*)val == *(bool*)v1; } bool device_option::is_equal_i(gb_json* opt, void* val, void* v1, void* v2) { return *(int*)val == *(int*)v1; } bool device_option::is_equal_f(gb_json* opt, void* val, void* v1, void* v2) { return IS_DOUBLE_EQUAL(*(double*)val, *(double*)v1); } bool device_option::is_equal_s(gb_json* opt, void* val, void* v1, void* v2) { return strcmp((char*)val, from_def_lang((char*)v1)) == 0; } bool device_option::is_less_b(gb_json* opt, void* val, void* v1, void* v2) { return *(bool*)val < *(bool*)v1; } bool device_option::is_less_i(gb_json* opt, void* val, void* v1, void* v2) { return *(int*)val < *(int*)v1; } bool device_option::is_less_f(gb_json* opt, void* val, void* v1, void* v2) { return *(double*)val < *(double*)v1; } bool device_option::is_less_s(gb_json* opt, void* val, void* v1, void* v2) { return strcmp((char*)val, from_def_lang((char*)v1)) < 0; } bool device_option::is_great_b(gb_json* opt, void* val, void* v1, void* v2) { return *(bool*)val > *(bool*)v1; } bool device_option::is_great_i(gb_json* opt, void* val, void* v1, void* v2) { return *(int*)val > *(int*)v1; } bool device_option::is_great_f(gb_json* opt, void* val, void* v1, void* v2) { return *(double*)val > *(double*)v1; } bool device_option::is_great_s(gb_json* opt, void* val, void* v1, void* v2) { return strcmp((char*)val, from_def_lang((char*)v1)) > 0; } bool device_option::is_between_b(gb_json* opt, void* val, void* v1, void* v2) { return *(bool*)val == *(bool*)v1 || *(bool*)val <= *(bool*)v2; } bool device_option::is_between_i(gb_json* opt, void* val, void* v1, void* v2) { return *(int*)val >= *(int*)v1 && *(int*)val <= *(int*)v2; } bool device_option::is_between_f(gb_json* opt, void* val, void* v1, void* v2) { return *(double*)val >= *(double*)v1 && *(double*)val <= *(double*)v2; } bool device_option::is_between_s(gb_json* opt, void* val, void* v1, void* v2) { return strcmp((char*)val, from_def_lang((char*)v1)) >= 0 && strcmp((char*)val, from_def_lang((char*)v2)) <= 0; } bool device_option::is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2) { bool en = true; if (!opt->get_value("enabled", en)) en = true; return en; } bool device_option::get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*)) { bool ret = true; if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { *f = &device_option::is_equal_b; } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { *f = &device_option::is_equal_i; } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { *f = &device_option::is_equal_f; } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { *f = &device_option::is_equal_s; } else { ret = false; } return ret; } bool device_option::get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*)) { bool ret = true; if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { *f = &device_option::is_less_b; } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { *f = &device_option::is_less_i; } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { *f = &device_option::is_less_f; } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { *f = &device_option::is_less_s; } else { ret = false; } return ret; } bool device_option::get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*)) { bool ret = true; if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { *f = &device_option::is_great_b; } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { *f = &device_option::is_great_i; } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { *f = &device_option::is_great_f; } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { *f = &device_option::is_great_s; } else { ret = false; } return ret; } bool device_option::get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*)) { bool ret = true; if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { *f = &device_option::is_between_b; } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { *f = &device_option::is_between_i; } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { *f = &device_option::is_between_f; } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { *f = &device_option::is_between_s; } else { ret = false; } return ret; } bool device_option::is_string_function(const char* expr, std::string& name, int& start, int& cnt) { bool ret = false; std::string exp(expr); size_t pos = exp.find("."); cnt = 0; if (pos != std::string::npos) { name = exp.substr(0, pos++); exp.erase(0, pos); if (exp.find("left(") == 0) { exp.erase(0, 5); start = 0; cnt = atoi(exp.c_str()); pos = 0; } else if (exp.find("right(") == 0) { exp.erase(0, 6); start = -1 * atoi(exp.c_str()); cnt = -1; pos = 0; } else if (exp.find("mid(") == 0) { exp.erase(0, 4); start = atoi(exp.c_str()); pos = exp.find(","); if (pos != std::string::npos) { exp.erase(0, pos + 1); cnt = atoi(exp.c_str()); pos = 0; } } if (pos == 0) { pos = exp.find(")"); ret = pos != std::string::npos && (cnt > 0 || cnt == -1); } } return ret; } std::string device_option::from_text_value(const char* type, const char* text_val) { std::string real_v(""); if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0) { bool v = STRICMP(text_val, "true") == 0; real_v = std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_INT) == 0) { int v = atoi(text_val); real_v = std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0) { double v = atof(text_val); real_v = std::string((char*)&v, sizeof(v)); } else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0) { real_v = text_val; } return std::move(real_v); } bool device_option::parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc) { const char* opers[] = { ">", "<", "!", "=", "." }, * tag = nullptr, * dot = strstr(expr, "."); for (auto& v : opers) { tag = strstr(expr, v); if (tag) break; } if (!tag) return false; if (dot && dot < tag) // is mode.!enable ? { tag = dot; } std::string n(expr, tag - expr); bool ret = true; gb_json *child = nullptr; if (name) *name = n; calc.name = n; root->get_value(n.c_str(), child); if (!child) return false; if (!child->get_value("type", n)) { child->release(); return false; } child->release(); if (*tag == '>') { if (tag[1] == '=') { if (!device_option::get_less(n.c_str(), &calc.compare)) return false; calc.not_op = true; tag++; } else { if (!device_option::get_great(n.c_str(), &calc.compare)) return false; calc.not_op = false; } tag++; } else if (*tag == '<') { if (tag[1] == '=') { if (!device_option::get_great(n.c_str(), &calc.compare)) return false; calc.not_op = true; tag++; } else { if (!device_option::get_less(n.c_str(), &calc.compare)) return false; calc.not_op = false; } tag++; } else if (*tag == '!') { if (tag[1] == '=') { if (!device_option::get_equal(n.c_str(), &calc.compare)) return false; calc.not_op = true; tag++; } else { return false; } tag++; } else if (*tag == '=') { if (tag[1] == '=') { if (!device_option::get_equal(n.c_str(), &calc.compare)) return false; calc.not_op = false; tag++; } else { return false; } tag++; } else if (*tag++ == '.') { calc.compare = &device_option::is_opt_enabled; if (strcmp(tag, "enabled") == 0) { calc.not_op = false; } else if (strcmp(tag, "!enabled") == 0) { calc.not_op = true; } else { // substr function ... if (strstr(tag, "left(") || strstr(tag, "right(") || strstr(tag, "mid(")) { const char* end = nullptr, *start = strstr(tag, "("); int l = string_util::find_end_of_pair(start + 1, '(', ')'); bool ret = start[++l] == ')'; if (ret) { std::string exp(calc.name + (start + l + 1)), name1(tag, start + l - tag + 1); name1.insert(0, calc.name + "."); ret = device_option::parse_simple_logic_expression(root, exp.c_str(), name, calc); if (ret) calc.name = std::move(name1); } return ret; } return false; } tag = ""; } else { return false; } // value ... if (*tag) { if (*tag == '[') { const char* next = strstr(tag++, ","); if (next++ == nullptr) { ret = false; } else { std::string v(tag, next - tag - 1); calc.val1 = device_option::from_text_value(n.c_str(), v.c_str()); tag = strstr(next, "]"); if (!tag) ret = false; else { v = std::string(next, tag - next); calc.val2 = device_option::from_text_value(n.c_str(), v.c_str()); ret = device_option::get_between(n.c_str(), &calc.compare); } } } else { calc.val1 = device_option::from_text_value(n.c_str(), tag); } } return ret; } void device_option::init_condition(const char* expr, void* param) { if (((device_option*)param)->compare_.count(expr)) return; std::string name(""); EXPRCALC calc; if (device_option::parse_simple_logic_expression(((device_option*)param)->origin_, expr, &name, calc)) { ((device_option*)param)->compare_[expr] = calc; if (std::find(((device_option*)param)->master_opts_.begin(), ((device_option*)param)->master_opts_.end(), name) == ((device_option*)param)->master_opts_.end()) ((device_option*)param)->master_opts_.push_back(name); } } bool device_option::calc_simple_logic_expression(const char* expr, void* param) { device_option* obj = (device_option*)param; bool ret = true; if (obj->compare_.count(expr)) { gb_json* child = nullptr; bool substr = false; int start = 0, cnt = 0; if(obj->now_) obj->now_->get_value(obj->compare_[expr].name.c_str(), child); else obj->origin_->get_value(obj->compare_[expr].name.c_str(), child); if (!child) { // check string function ... std::string name(""); substr = device_option::is_string_function(expr, name, start, cnt); if (substr) { if (obj->now_) obj->now_->get_value(name.c_str(), child); else obj->origin_->get_value(name.c_str(), child); } } if (child) { bool bv = false; int nv = 0; double dv = .0f; std::string sv(""); void* val = nullptr; child->get_value("type", sv); if (strcmp(sv.c_str(), JSON_SANE_TYPE_BOOL) == 0) { sv = get_real_value(child, false); } else if (strcmp(sv.c_str(), JSON_SANE_TYPE_INT) == 0) { sv = get_real_value(child, false); } else if (strcmp(sv.c_str(), JSON_SANE_TYPE_FIXED) == 0) { sv = get_real_value(child, false); } else if (strcmp(sv.c_str(), JSON_SANE_TYPE_STRING) == 0) { child->get_value("cur", sv); if (substr) { if (start < 0) start = sv.length() + start; if (start > 0 && start < sv.length()) sv.erase(0, start); if (cnt > 0 && cnt < sv.length()) sv.erase(cnt); } } val = &sv[0]; ret = obj->compare_[expr].compare(child, val, &obj->compare_[expr].val1[0], &obj->compare_[expr].val2[0]) ^ obj->compare_[expr].not_op; child->release(); } } return ret; } gb_json* device_option::meaningless_option(const char* group) { gb_json* ml = new gb_json(); ml->set_value("type", JSON_SANE_TYPE_BUTTON); ml->set_value("group", group); ml->set_value("enabled", false); return ml; } void device_option::clear_for_reconstruct(void) { if (now_) now_->release(); now_ = nullptr; master_opts_.clear(); compare_.clear(); for (auto& v : slaver_) delete v.second; slaver_.clear(); for (auto& v : range_value_) delete v.second; range_value_.clear(); for (auto& v : init_value_) delete v.second; init_value_.clear(); for (auto& v : support_value_) delete v.second; support_value_.clear(); depend_opts_.clear(); } gb_json* device_option::group_opt(const char* title) { gb_json* jsn = new gb_json(); jsn->set_value("title", title); jsn->set_value("type", "group"); return jsn; } int device_option::next_group(gb_json* root, int start) { for (; start < root->children(); ++start) { gb_json* child = root->child(start); std::string str(""); child->get_value("type", str); child->release(); if (str == JSON_SANE_TYPE_GROUP) break; } return start; } int device_option::insert_group(const char* title) { gb_json* group = nullptr; int ind = origin_->children(), grpind = next_group(origin_, 0); std::string name("grp-" + std::to_string(++grp_ind_)), t(""); while(grpind < origin_->children()) { group = origin_->child(grpind); group->get_value("title", t); if(t == title || group->key() == title) break; group->release(); group = nullptr; grpind = next_group(origin_, grpind + 1); } if (group) { ind = grpind; group->release(); } else { for (auto& v : g_known_group_with_sn) { if (v.title == title) { name = v.name; break; } else if (v.name == title) { name = v.name; title = v.title.c_str(); break; } gb_json* child = nullptr; origin_->get_value(v.name.c_str(), child); if (child) { if (group) group->release(); group = child; } } if (group) { ind = origin_->index(group) + 1; group->release(); } else ind = 0; ind = next_group(origin_, ind); group = group_opt(title); origin_->insert(ind, name.c_str(), group); group->release(); } return ind; } void device_option::insert_option(gb_json* opt, sane_opt_provider* from, const char* group) { // first compare version, reserve higher version // second compare position, replace with last insertion // last, sort by position gb_json* existing = nullptr; bool added = true; origin_->get_value(opt->key().c_str(), existing); if (existing) { int vo = 0, vn = 0; opt->get_value("ver", vn); existing->get_value("ver", vo); if (vn > vo) { // replace and discard all following options ... write_log("SANE-OPT: use %s::%s(ver: %d) replaced with %s::%s(ver: %d)\n", src_[existing->key()]->from(), existing->key().c_str(), vo , from->from(), opt->key().c_str(), vn); vo = origin_->index(existing); origin_->remove(vo); if (src_.count(existing->key())) { // disable discarded options ... sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str()); src_[existing->key()]->enable(existing->key().c_str(), false); while (next) { next->enable(existing->key().c_str(), false); sane_opt_provider* n = next->get_following(existing->key().c_str()); next->release(); next = n; } src_[existing->key()]->release(); src_.erase(existing->key()); } origin_->insert(vo, opt->key().c_str(), opt); opt->add_ref(); src_[opt->key()] = from; from->add_ref(); } else if (vn == vo) { // insert into following queue ... write_log("SANE-OPT: inserting %s::%s to provider queue ...\n", from->from(), opt->key().c_str()); opt->get_value("pos", vn); existing->get_value("pos", vo); if (vo < vn) { sane_opt_provider* prev = src_[existing->key()], *que = src_[existing->key()]->get_following(existing->key().c_str()); prev->add_ref(); while (que) { std::string text(que->get_opt_json()); gb_json* jsn = new gb_json(), *child = nullptr; jsn->attach_text(&text[0]); jsn->get_value(existing->key().c_str(), child); if (child) { child->get_value("ver", vo); child->release(); } jsn->release(); if (vo >= vn) break; prev->release(); prev = que; que = que->get_following(existing->key().c_str()); } prev->set_following_provider(opt->key().c_str(), from); if (vo == vn) { // replace ... if(que) { sane_opt_provider* next = que->get_following(opt->key().c_str()); que->set_following_provider(opt->key().c_str(), nullptr); if (next) { from->set_following_provider(opt->key().c_str(), next); next->release(); } que->release(); // release get_following reference } } else { // insert ... from->set_following_provider(opt->key().c_str(), que); if (que) que->release(); } prev->release(); } else if (vo > vn) { // re-init existing ... std::string text(opt->to_string()), fo(src_[existing->key()]->from()); existing->attach_text(&text[0]); existing->key() = opt->key(); write_log("Re-Initialize option '%s' for new provider(%s - %d) is ahead of existing(%s - %d) ...\n", opt->key().c_str() , from->from(), vn, fo.c_str(), vo); from->set_following_provider(opt->key().c_str(), src_[existing->key()]); src_[existing->key()]->release(); src_[opt->key()] = from; from->add_ref(); } else { // replace ... vo = origin_->index(existing); origin_->remove(vo); if (src_.count(existing->key())) { sane_opt_provider* next = src_[existing->key()]->get_following(existing->key().c_str()); src_[existing->key()]->set_following_provider(existing->key().c_str(), nullptr); src_[existing->key()]->release(); src_.erase(existing->key()); while (next) { sane_opt_provider* next1 = next->get_following(existing->key().c_str()); next->set_following_provider(existing->key().c_str(), nullptr); next->release(); next = next1; } } origin_->insert(vo, opt->key().c_str(), opt); opt->add_ref(); src_[opt->key()] = from; from->add_ref(); } { std::string logi("SANE-OPT: option '"); logi += opt->key() + "' queue: " + src_[opt->key().c_str()]->from(); sane_opt_provider* next = src_[opt->key().c_str()]->get_following(opt->key().c_str()); while (next) { logi += std::string(" -> ") + next->from(); sane_opt_provider* next1 = next; next = next->get_following(opt->key().c_str()); next1->release(); } write_log("%s\n", logi.c_str()); } } else { // discard new option ... write_log("SANE-OPT: discard %s::%s(ver: %d) for %s::%s(ver: %d) existed!\n", from->from(), opt->key().c_str(), vn , src_[existing->key()]->from(), existing->key().c_str(), vo); // disable discarded option from->enable(existing->key().c_str(), false); added = false; } existing->release(); } else { int index = -1, pos = -1; if (group) index = insert_group(group); // insert poisition according to 'ui-pos' if (!opt->get_value("ui-pos", pos) || pos == -1) { index = next_group(origin_, index + 1); } else { for (index++; index < origin_->children(); ++index) { gb_json* sib = origin_->child(index); std::string t(""); int sit = -1; if (!sib->get_value("type", t) || t == JSON_SANE_TYPE_GROUP) { sib->release(); break; } if (!sib->get_value("ui-pos", sit) || sit == -1) { sib->release(); break; } sib->release(); if (pos < sit) break; } } origin_->insert(index, opt->key().c_str(), opt); src_[opt->key()] = from; from->add_ref(); } //if (added) // error occurs when condition default value, move to after to_now ... //{ // // restore to default value ... // int size = 0; // bool can_auto = false; // std::string val(get_option_value(opt->key().c_str(), SANE_ACTION_GET_DEFAULT_VALUE, &size)); // // if (!opt->get_value("auto", can_auto) || can_auto) // { // std::string type(""); // opt->get_value("type", type); // val.resize(size); // type = sane_opt_provider::sane_value_2_readable_text(type.c_str(), &val[0]); // write_log("Set option '%s' to default value: '%s'\n", opt->key().c_str(), type.c_str()); // from->set_value(opt->key().c_str(), &val[0]); // sane_opt_provider::set_opt_value(opt, &val[0]); // } //} } bool device_option::arrange_raw_json(sane_opt_provider* sop) { std::vector ungroup; std::map> ingroup; gb_json* jsn = new gb_json(), *child = nullptr; std::string text(sop->get_opt_json()), str(""); bool ret = jsn->attach_text(&text[0]); if (ret) { if (!origin_) origin_ = new gb_json(); text.clear(); child = jsn->first_child(); while (child) { child->get_value("type", str); if (str != JSON_SANE_TYPE_GROUP) // omit group { if (str == JSON_SANE_TYPE_FIXED) { // revise cur and default device_option::revise_number_type(child, true); } if(no_grp_) { insert_option(child, sop); } else { child->get_value("group", str); insert_option(child, sop, str.empty() ? nullptr : str.c_str()); } } child->release(); child = jsn->next_child(); } } jsn->release(); return ret; } void device_option::init_depends(gb_json* opt) { gb_json* range = nullptr; std::string dpnd(""); if (opt->get_value("depend", dpnd) && !dpnd.empty()) { simple_logic* logic = new simple_logic(); int pos = 0; if (logic->parse(dpnd.c_str(), &pos, &device_option::init_condition, this)) slaver_[opt->key().c_str()] = logic; else delete logic; } // values ... opt->get_value("range", range); if (range) { range_value* val = new range_value(); opt->get_value("type", dpnd); if (val->set_value(range, dpnd.c_str(), this)) range_value_[opt->key()] = val; else delete val; range->release(); } // default: range = nullptr; opt->get_value("default", range); opt->get_value("type", dpnd); if (range) { condition_value* rv = new condition_value(); if (rv->set_value(range, dpnd.c_str(), this)) init_value_[opt->key()] = rv; else delete rv; range->release(); } else if (opt->get_value("default", dpnd)) { if (dpnd.find("=") == 0) { // consitant with another option ... dpnd.erase(0, 1); condition_value* rv = new condition_value(); rv->set_value(nullptr, dpnd.c_str(), this); init_value_[opt->key()] = rv; if (std::find(master_opts_.begin(), master_opts_.end(), dpnd) == master_opts_.end()) master_opts_.push_back(dpnd); } } // visible range = nullptr; opt->get_value("visible", range); if(range) { condition_value* v = new condition_value(); if (v->set_value(range, JSON_SANE_TYPE_INT, this)) support_value_[opt->key()] = v; else delete v; } } gb_json* device_option::copy_opt(gb_json* from, bool* changed_cur) { std::string text(from->to_string()); gb_json* to = new gb_json(); if (!to->attach_text(&text[0])) { to->release(); to = nullptr; } else { std::string val(""); to->key() = from->key(); // 1: language changed ... (title, description, string-list) if (to->get_value("title", val)) to->set_value("title", from_def_lang(val.c_str())); if (to->get_value("desc", val)) to->set_value("desc", from_def_lang(val.c_str())); // 2: enabled ... if (slaver_.count(to->key())) { bool enable = slaver_[to->key()]->value(&device_option::calc_simple_logic_expression, this); to->set_value("enabled", enable); if (src_.count(to->key())) { src_[to->key()]->enable(to->key().c_str(), enable); sane_opt_provider* next = src_[to->key()]->get_following(to->key().c_str()); while (next) { next->enable(to->key().c_str(), enable); sane_opt_provider* next1 = next->get_following(to->key().c_str()); next->release(); next = next1; } } } // 3: default value ... if (init_value_.count(to->key())) { std::string val(init_value_[to->key()]->value(&device_option::calc_simple_logic_expression, this)); std::string type(""); bool apply_cur = false, rdo = false; // should we change current value ??? - answer: add boolean field "bind" to specify if (!from->get_value("bind", apply_cur)) apply_cur = false; if (!from->get_value("readonly", rdo)) rdo = false; if (changed_cur) *changed_cur = apply_cur; sane_opt_provider::set_opt_value(to, &val[0], "default"); if (apply_cur) { if (sane_opt_provider::set_opt_value(to, &val[0], "cur")) update_provider_value(to->key().c_str(), &val[0], rdo); else if (changed_cur) *changed_cur = false; } } // 4: range value ... if (range_value_.count(to->key())) { gb_json* src = nullptr, * dst = nullptr; from->get_value("range", src); to->get_value("range", dst); if (src && dst) { dst->clear(src->is_array()); dst->key() = src->key(); to->get_value("type", val); if (val == JSON_SANE_TYPE_BOOL) { if (dst->is_array()) { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); for (int i = 0; i < range_value_[to->key()]->count(); ++i) { if (!val.empty()) *dst += *(bool*)&val[0]; val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); } } else { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); dst->set_value("min", *(bool*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("max", *(bool*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("step", *(bool*)&val[0]); } } else if (val == JSON_SANE_TYPE_INT) { if (dst->is_array()) { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); for (int i = 0; i < range_value_[to->key()]->count(); ++i) { if (!val.empty()) *dst += *(int*)&val[0]; val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); } } else { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); dst->set_value("min", *(int*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("max", *(int*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("step", *(int*)&val[0]); } } else if (val == JSON_SANE_TYPE_FIXED) { if (dst->is_array()) { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); for (int i = 0; i < range_value_[to->key()]->count(); ++i) { if (!val.empty()) *dst += *(double*)&val[0]; val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); } } else { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); dst->set_value("min", *(double*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("max", *(double*)&val[0]); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("step", *(double*)&val[0]); } } else if (val == JSON_SANE_TYPE_STRING) { if (dst->is_array()) { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); for(int i = 0; i < range_value_[to->key()]->count(); ++i) { if(!val.empty()) *dst += from_def_lang(val.c_str()); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); } } else { val = range_value_[to->key()]->first_value(&device_option::calc_simple_logic_expression, this); dst->set_value("min", from_def_lang(val.c_str())); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("max", from_def_lang(val.c_str())); val = range_value_[to->key()]->next_value(&device_option::calc_simple_logic_expression, this); dst->set_value("step", from_def_lang(val.c_str())); } } } if (src) src->release(); if (dst) dst->release(); } } return to; } int device_option::visibility(gb_json* jsn) { int visb = OPT_VISIBLE_ALL; if (!jsn->get_value("visible", visb)) { if (support_value_.count(jsn->key())) { std::string val(support_value_[jsn->key()]->value(&device_option::calc_simple_logic_expression, this)); visb = *(int*)&val[0]; } } return visb; } bool device_option::to_now(bool init, bool* changed) { if (!origin_) return false; gb_json* from = nullptr, * to = nullptr, * tmp = new gb_json(); from = origin_->first_child(); while (from) { std::string name(from->key()); bool cur_chged = false; if (init) init_depends(from); // check hidden option is moved to do when get all options json, here should add into, or else the option maybe in wrong status !!! to = copy_opt(from, &cur_chged); from->release(); if (to) { // copy cur value ... if (now_ && !cur_chged) { gb_json* now = nullptr; now_->get_value(to->key().c_str(), now); if (now) { std::string type(""); now->get_value("type", type); if (type == JSON_SANE_TYPE_BOOL) { bool v = false; now->get_value("cur", v); refine_data_to_range(to, &v); to->set_value("cur", v); } else if (type == JSON_SANE_TYPE_INT) { int v = 0; now->get_value("cur", v); refine_data_to_range(to, &v); to->set_value("cur", v); } else if (type == JSON_SANE_TYPE_FIXED) { double v = .0f; now->get_value("cur", v); refine_data_to_range(to, &v); to->set_value("cur", v); } else if (type == JSON_SANE_TYPE_STRING) { std::string v(""); int size = 0, s1 = 0; char *vv = nullptr; to->get_value("size", size); now->get_value("size", s1); if (size < s1) size = s1; vv = new char[size + 4]; memset(vv, 0, size + 4); now->get_value("cur", v); strcpy(vv, v.c_str()); refine_string_data(to, vv); to->set_value("cur", vv); delete[] vv; } now->release(); } } tmp->set_value(name.c_str(), to); to->release(); from = origin_->next_child(); } else { tmp->release(); tmp = nullptr; break; } } if (changed) { if (!now_ || !tmp) *changed = true; else *changed = *now_ != *tmp; } if (now_) now_->release(); now_ = tmp; return now_ != nullptr; } int device_option::update_provider_value(const char* name, void* value, bool skip_first) { int ret = SCANNER_ERR_OK; if (src_.count(name)) { sane_opt_provider* opt = nullptr; if (skip_first) { opt = src_[name]->get_following(name); } else { opt = src_[name]; opt->add_ref(); } while (opt) { int err = opt->set_value(name, value); if (err == SCANNER_ERR_RELOAD_OPT_PARAM || err == SCANNER_ERR_CONFIGURATION_CHANGED) { ret = err; } sane_opt_provider* next = opt->get_following(name); opt->release(); opt = next; } } return ret; } void device_option::remove_provider(gb_json* root, sane_opt_provider* sop) { if (root) { gb_json* child = root->first_child(); while (child) { bool chk_child = src_.count(child->key()) == 0; if (!chk_child) { sane_opt_provider* sp = src_[child->key()], * next = sp->get_following(child->key().c_str()); sp->add_ref(); while (sp) { if (sp == sop) { if (next) src_[child->key()] = next; else src_.erase(child->key()); sp->release(); // release from map: src_ chk_child = true; break; } sp->release(); // release from get_following sp = next; next = sp->get_following(child->key().c_str()); } } if (chk_child && src_.count(child->key()) == 0) { utils::to_log(LOG_LEVEL_DEBUG, "remove_provider: Remove option '%s' from '%s'\n", child->key().c_str(), sop->from()); root->remove(child); child->release(); child = root->first_child(); continue; } child->release(); child = root->next_child(); } } } void device_option::remove_empty_group(gb_json* root) { int prev = -2, index = next_group(root, 0); std::vector grp0; while(index < root->children()) { if(index - prev == 1) grp0.push_back(prev); else if(prev >= 0) { // contains only-accessible option int cnt = 0; for (int i = prev + 1; i < index; ++i) { gb_json* child = root->child(i); int visible = 0; child->get_value("visible", visible); child->release(); if (visible != OPT_VISIBLE_ACCESS) { cnt++; break; } } if(cnt == 0) grp0.push_back(prev); } prev = index; index = next_group(root, index + 1); } if (prev >= 0) { // contains only-accessible option int cnt = 0; for (int i = prev + 1; i < index; ++i) { gb_json* child = root->child(i); int visible = 0; child->get_value("visible", visible); child->release(); if (visible != OPT_VISIBLE_ACCESS) { cnt++; break; } } if (cnt == 0) grp0.push_back(prev); } for(int i = grp0.size() - 1; i >= 0; --i) { std::string title(""); gb_json *grp = root->child(grp0[i]); grp->get_value("title", title); grp->release(); utils::to_log(LOG_LEVEL_ALL, "remove empty group '%s'.\n", title.c_str()); root->remove(grp0[i]); } } bool device_option::is_hidden_option(gb_json* opt) { bool hidden = false; int auth = 0; opt->get_value("auth", auth); if (!user_ || !user_(auth)) { hidden = true; } return hidden; } void device_option::revise_number_type(gb_json* opt, bool to_double) { gb_json* child = nullptr; opt->get_value("cur", child); if (child) { if (child->is_leaf_node()) child->revise_number_type(to_double); //else // no object now, fixed me if be object child->release(); } opt->get_value("default", child); if (child) { if (child->is_leaf_node()) child->revise_number_type(to_double); else { gb_json* v = child->first_child(); while (v) { if (v->is_leaf_node()) v->revise_number_type(to_double); v->release(); v = child->next_child(); } } child->release(); } opt->get_value("range", child); if (child) { gb_json* v = child->first_child(); while (v) { if (v->is_leaf_node()) v->revise_number_type(to_double); v->release(); v = child->next_child(); } child->release(); } } std::string device_option::trans_group(const char* utf8, bool to_title) { if (to_title) { for (auto& v : g_known_group_with_sn) { if (v.name == utf8) return v.title; } } else { for (auto& v : g_known_group_with_sn) { if (v.title == utf8) return v.name; } } return utf8; } std::string device_option::get_group(int ind, bool title) { if (ind >= 0 && ind < _countof(g_known_group_with_sn)) return title ? g_known_group_with_sn[ind].title : g_known_group_with_sn[ind].name; else return ""; } void device_option::set_from_default_language_api(const char*(*fdl)(const char*)) { from_def_lang = fdl ? fdl : empty_from_default_language; } void device_option::clear(void) { clear_for_reconstruct(); if (origin_) origin_->release(); origin_ = nullptr; for (auto& v : src_) v.second->release(); src_.clear(); grp_ind_ = 0; } bool device_option::add(sane_opt_provider* sop, bool apply_default_val) { bool ret = false; clear_for_reconstruct(); if (arrange_raw_json(sop)) { ret = to_now(true, nullptr); if (ret) { if(apply_default_val) restore(nullptr); } else clear(); } else { clear(); } return ret; } bool device_option::remove(sane_opt_provider* sop) { remove_provider(origin_, sop); remove_provider(now_, sop); return true; } bool device_option::refine_data(const char* name, void* value) { bool refined = false; gb_json* child = nullptr; now_->get_value(name, child); if (child) { std::string type(""), org(""), result(""); child->get_value("type", type); org = sane_opt_provider::sane_value_2_readable_text(type.c_str(), value); if (type == JSON_SANE_TYPE_BOOL) { refined = refine_data_to_range(child, value); } else if (type == JSON_SANE_TYPE_INT) { refined = refine_data_to_range(child, value); } else if (type == JSON_SANE_TYPE_FIXED) { refined = refine_data_to_range(child, value); } else if (type == JSON_SANE_TYPE_STRING) { refined = refine_string_data(child, value); } if(refined) { result = sane_opt_provider::sane_value_2_readable_text(type.c_str(), value); write_log("Refine value of '%s' from '%s' to '%s'.\n", name, org.c_str(), result.c_str()); } child->release(); } return refined; } int device_option::update_data(const char* name, void* value, bool reorder_if_need, bool from_user) { int err = SCANNER_ERR_NO_DATA; if (!name) { to_now(false, nullptr); err = SCANNER_ERR_RELOAD_OPT_PARAM; } else if(now_) { std::string type(""); gb_json* child = nullptr; err = SCANNER_ERR_DEVICE_NOT_SUPPORT; now_->get_value(name, child); if (child) { bool ro = false; if (from_user && ((child->get_value("readonly", ro) && ro) || (child->get_value("auth", err) && user_ && !user_(err)))) { err = SCANNER_ERR_ACCESS_DENIED; // following ... update_provider_value(name, value, true); } else { std::string pre(sane_opt_provider::option_value(child, false)); bool changed = false; // pass to sane_opt_provider ... err = update_provider_value(name, value); child->get_value("type", type); write_log("set option '%s' value to '%s' = %d.\n", name, sane_opt_provider::sane_value_2_readable_text(type.c_str(), value).c_str(), err); if (err == SCANNER_ERR_OK || err == SCANNER_ERR_NOT_EXACT || err == SCANNER_ERR_RELOAD_IMAGE_PARAM || err == SCANNER_ERR_RELOAD_OPT_PARAM // || err == SCANNER_ERR_CONFIGURATION_CHANGED ) { gb_json* org = nullptr; sane_opt_provider::set_opt_value(child, value); origin_->get_value(name, org); if (org) { sane_opt_provider::set_opt_value(org, value); org->release(); } changed = !sane_opt_provider::is_opt_value_equal(child, &pre[0], value); // set paper-w and paper-h if (strcmp(name, SANE_STD_OPT_NAME_PAPER) == 0 || strcmp(name, SANE_STD_OPT_NAME_LATERAL) == 0) { if (src_.count(SANE_STD_OPT_NAME_PAPER_W)) { size_t wl = 0, hl = 0; char *ws = src_[SANE_STD_OPT_NAME_PAPER_W]->get_value(SANE_STD_OPT_NAME_PAPER_W, nullptr, &wl), *hs = src_[SANE_STD_OPT_NAME_PAPER_H]->get_value(SANE_STD_OPT_NAME_PAPER_H, nullptr, &hl); std::string w(ws ? std::string(ws, wl) : ""), h(hs ? std::string(hs, hl) : ""), lateral(get_option_value(SANE_STD_OPT_NAME_LATERAL, SANE_ACTION_GET_VALUE)); gb_json *jsnl = nullptr; if (ws) free(ws); if (hs) free(hs); now_->get_value(SANE_STD_OPT_NAME_LATERAL, jsnl); if (jsnl) { if (device_option::is_opt_enabled(jsnl, nullptr, nullptr, nullptr)) { refine_data(SANE_STD_OPT_NAME_LATERAL, &lateral[0]); if (*(bool*)&lateral[0]) w.swap(h); } jsnl->release(); } gb_json* pw = nullptr, * ph = nullptr; now_->get_value(SANE_STD_OPT_NAME_PAPER_W, pw); now_->get_value(SANE_STD_OPT_NAME_PAPER_H, ph); if (pw) { pw->set_value("cur", *(double*)&w[0]); pw->release(); } if (ph) { ph->set_value("cur", *(double*)&h[0]); ph->release(); } name = SANE_STD_OPT_NAME_PAPER; // ensure invoked refresh ... } } if (reorder_if_need && changed && // value has changed std::find(master_opts_.begin(), master_opts_.end(), name) != master_opts_.end()) // can affect others { changed = false; do { changed = false; to_now(false, &changed); } while (changed); err = SCANNER_ERR_RELOAD_OPT_PARAM; } } // provider has processed right } // not read-only option child->release(); } // has option named 'name' } // has initialized return err; } int device_option::restore(sane_opt_provider* holder) { gb_json* child = nullptr, * cur = now_; if (cur) { cur->add_ref(); child = cur->first_child(); while (child) { std::string type(""); child->get_value("type", type); if (type != JSON_SANE_TYPE_GROUP && type != JSON_SANE_TYPE_BUTTON && (!holder || src_.count(child->key()) && src_[child->key()] == holder) // && is_auto_restore_default(child) ) { std::string val(sane_opt_provider::option_value(child, true)); update_data(child->key().c_str(), &val[0], false); } child->release(); child = cur->next_child(); } cur->release(); bool changed = false; do { changed = false; to_now(false, &changed); } while (changed); } return SCANNER_ERR_RELOAD_OPT_PARAM; } int device_option::count(void) { gb_json* jsn = now_ ? now_ : origin_; if (jsn) return jsn->children(); else return 0; } bool device_option::is_auto_restore_default(gb_json* jsn) { bool support = true; std::string type(""); jsn->get_value("type", type); if(type == JSON_SANE_TYPE_BUTTON || type == JSON_SANE_TYPE_GROUP) return false; if (!jsn->get_value("auto", support)) support = true; return support; } int device_option::sane_id_from_name(const char* name) { gb_json* jsn = now_ ? now_ : origin_; int sn = -1; if(jsn) { gb_json* child = nullptr; jsn->get_value(name, child); if(child) { sn = jsn->index(child) + 1; child->release(); } } return sn; } std::string device_option::get_name_by_sane_id(int sane_ind) { std::string value(""); gb_json* jsn = now_ ? now_ : origin_; if (sane_ind > 0 && sane_ind - 1 < jsn->children()) { gb_json* child = now_->child(sane_ind - 1); //child->get_value("name", value); value = child->key(); child->release(); } return std::move(value); } std::string device_option::get_option_value_type(const char* name, size_t* size) { std::string value(""); gb_json* jsn = now_ ? now_ : origin_; if (jsn) { gb_json* child = nullptr; jsn->get_value(name, child); if (child) { child->get_value("type", value); if (size) { int v = 0; child->get_value("size", v); *size = v; } child->release(); } } return std::move(value); } std::string device_option::get_option_value(const char* name, int type, int* size, void* in_data, SANE_Constraint_Type *constraint) { std::string value(""); gb_json* jsn = now_ ? now_ : origin_; if (jsn) { if (!name) { gb_json *filter = new gb_json(), *child = jsn->first_child(); int hidden = 0; while(child) { if (is_hidden_option(child)) // The option count must stay the same in SANE protocol, replace hidden option with meaninless option here { char key[40] = { 0 }; child->get_value("group", value); child->release(); child = device_option::meaningless_option(value.c_str()); sprintf(key, "%p-%d", this, ++hidden); child->key() = key; // "hidden-" + std::to_string(++hidden); // the same hidden id ... } filter->set_value(child->key().c_str(), child); child->release(); child = jsn->next_child(); } if(!no_grp_) remove_empty_group(filter); value = filter->to_string(); filter->release(); // if (no_grp_ && origin_) // value = origin_->to_string(); // else // value = jsn->to_string(); } else { gb_json* child = nullptr; bool own_read = false; jsn->get_value(name, child); if (child) { if (type == SANE_ACTION_GET_ENTIRE_JSON) value = child->to_string(); else if(type == SANE_ACTION_GET_ALL_VALUES) { value = sane_opt_provider::get_all_values(child, constraint, nullptr); } else if (child->get_value("ownread", own_read) && own_read) { if (src_.count(name)) { size_t len = 0; char* v = src_[name]->get_value(name, in_data, &len); if (v) { value = std::string(v, len); free(v); } } } else value = sane_opt_provider::option_value(child, type == SANE_ACTION_GET_DEFAULT_VALUE); if (size) { int n = 0; child->get_value("size", n); *size = n; } child->release(); } } } return std::move(value); } std::string device_option::get_option_field_string(const char* name, const char* key) { std::string value(""); gb_json* jsn = now_ ? now_ : origin_; if (jsn) { gb_json* child = nullptr; jsn->get_value(name, child); if (child) { child->get_value(key, value); child->release(); } } return std::move(value); } std::string device_option::get_option_value_type(int sane_ind, size_t* size) { std::string value(""); gb_json* jsn = now_ ? now_ : origin_; if (sane_ind > 0 && sane_ind - 1 < jsn->children()) { gb_json* child = now_->child(sane_ind - 1); child->get_value("type", value); if (size) { int v = 0; child->get_value("size", v); *size = v; } child->release(); } return std::move(value); } std::string device_option::get_option_value_by_id(int sane_ind, int type, int* size, void* in_data) { std::string value(""); if (now_) { if (sane_ind <= 0) { value = std::move(now_->to_string()); } else if(sane_ind - 1 < now_->children()) { gb_json* child = now_->child(sane_ind - 1); std::string name(child->key()); child->release(); value = std::move(get_option_value(name.c_str(), type, size, in_data)); } } return std::move(value); }