#include "gb_json.h" #include #include #include "../../sdk/include/huagao/brand.h" #if defined(WIN32) || defined(_WIN64) #define bzero(b, s) memset(b, 0, s) #endif namespace gb { static std::vector split_with(const char* str, const char* splitor = "/") { std::vector ret; std::string src(str); size_t pos = src.find(splitor); while(pos != std::string::npos) { if(pos++) ret.push_back(src.substr(0, pos - 1)); src.erase(0, pos + strlen(splitor)); pos = src.find(splitor); } if(src.length()) ret.push_back(src); return ret; } static int load_mini_file(const char* file, std::string& cont) { FILE* src = fopen(file, "rb"); if (src) { size_t size = 0; char *buf = NULL; fseek(src, 0, SEEK_END); size = ftell(src); fseek(src, 0, SEEK_SET); buf = new char[size + 4]; memset(buf, 0, size + 4); fread(buf, 1, size, src); fclose(src); cont = std::string(buf, size); delete[] buf; return 0; } else return errno; } refer::refer() : ref_(1) {} refer::~refer() {} long refer::add_ref(void) { #if defined(WIN32) || defined(_WIN64) return InterlockedIncrement(&ref_); #else return ++ref_; #endif } long refer::release(void) { #if defined(WIN32) || defined(_WIN64) long ref = InterlockedDecrement(&ref_); #else long ref = --ref_; #endif if (ref == 0) delete this; return ref; } json::json(char* json_txt) : obj_(0), cur_child_(0), is_array_(false) { memset(&walk_head_, 0, sizeof(walk_head_)); attach_text(json_txt); } json::~json() { memset(&walk_head_, 0, sizeof(walk_head_)); clear(); } std::string json::to_string(cJSON* root, bool formatted) { char* txt = formatted ? cJSON_Print(root) : cJSON_PrintUnformatted(root); std::string ret(txt ? txt : ""); if (txt) free(txt); return ret; } std::string json::get_value_as_string(cJSON* root, bool integer) { std::string ret(""); switch (root->type) { case cJSON_False: ret = "false"; break; case cJSON_True: ret = "true"; break; case cJSON_NULL: ret = "null"; break; case cJSON_Number: { char buf[40]; if (integer) sprintf(buf, "%d", root->valueint); else sprintf(buf, "%f", root->valuedouble); ret = buf; } break; case cJSON_String: if (root->valuestring) ret = root->valuestring; break; default: ret = json::to_string(root, false); break; } return ret; } void json::free_node_data(cJSON* node) { if (node->type == cJSON_String && node->valuestring) free(node->valuestring); else if((node->type == cJSON_Object || node->type == cJSON_Array) && node->child) cJSON_Delete(node->child); node->type = cJSON_NULL; node->valuestring = NULL; node->child = NULL; } cJSON* json::create_element(bool is_array) { cJSON* obj = is_array ? cJSON_CreateArray() : cJSON_CreateObject(); // bzero(obj, sizeof(*obj)); // cleared in constructor already ! return obj; } cJSON* json::create_element_with_name(const char* name) { cJSON* obj = json::create_element(); if(name) { obj->string = (char*)malloc(strlen(name) + 4); bzero(obj->string, strlen(name) + 4); strcpy(obj->string, name); } return obj; } cJSON* json::find_sibling(cJSON* first, const char* name, cJSON*** prev) { cJSON* now = first, **prv = NULL; while(now) { if(now->string && strcmp(now->string, name) == 0) { break; } prv = &now->next; now = now->next; } if(prev) *prev = prv; return now; } cJSON* json::find_child(cJSON *parent, std::vector& path, bool create, cJSON*** addr) { if(!parent->child) { if(!create) return NULL; parent->child = json::create_element_with_name(path[0].c_str()); if(path.size() == 1) { if(addr) *addr = &parent->child; return parent->child; } } cJSON** prev = NULL, *now = find_sibling(parent->child, path[0].c_str(), &prev); if(!now) { if(!create) return now; now = json::create_element_with_name(path[0].c_str()); if (prev) { cJSON* pr = (cJSON*)((DWORD_PTR)prev - (DWORD_PTR)&((cJSON*)0)->next); pr->next = now; now->prev = pr; // *prev = now; } else { obj_->child = now; prev = &obj_->child; } } path.erase(path.begin()); if(path.empty()) { if(addr) *addr = prev ? prev : &parent->child; return now; } return find_child(now, path, create, addr); } cJSON* json::find(const char* path, bool create, cJSON*** addr) { std::vector tree(split_with(path)); if(tree.empty()) return NULL; if(!obj_) { if(!create) return NULL; obj_ = json::create_element(); obj_->child = json::create_element_with_name(tree[0].c_str()); } return find_child(obj_, tree, create, addr); } bool json::attach_text(char* json_txt) { clear(); obj_ = cJSON_Parse(json_txt); if(obj_) is_array_ = obj_->type == cJSON_Array; return obj_ != 0; } bool json::attach_cjson(cJSON* cjson) { clear(); if (cjson) { std::string txt(json::to_string(cjson, false)); if (txt.length()) obj_ = cJSON_Parse(txt.c_str()); } if(obj_) is_array_ = obj_->type == cJSON_Array; return obj_ != 0; } bool json::create_empty(bool array) { clear(); obj_ = json::create_element(array); is_array_ = array; return true; } void json::clear(void) { if (obj_) { cJSON_Delete(obj_); obj_ = 0; } } std::string json::to_string(bool formatted) { if (obj_) return json::to_string(obj_, formatted); else return ""; } bool json::get_value(const char* key, bool& val) { cJSON* obj = find(key); if (!obj) return false; if (obj->type == cJSON_True) val = true; else if (obj->type == cJSON_False) val = false; else return false; return true; } bool json::get_value(const char* key, int& val) { cJSON* obj = find(key); if (!obj) return false; if (obj->type != cJSON_Number) return false; val = obj->valueint; return true; } bool json::get_value(const char* key, double& val) { cJSON *obj = find(key); if (!obj) return false; if (obj->type != cJSON_Number) return false; val = obj->valuedouble; return true; } bool json::get_value(const char* key, std::string& val) { cJSON *obj = find(key); if (!obj) return false; if (obj->type != cJSON_String) return false; val = obj->valuestring ? obj->valuestring : ""; return true; } bool json::get_value(const char* key, json*& val) { cJSON *obj = find(key); if (!obj) return false; val = new json(); if (!val->attach_cjson(obj)) { val->release(); return false; } return true; } bool json::get_value_as_string(const char* key, std::string& val, bool integer) { cJSON* obj = find(key); if (!obj) return false; val = json::get_value_as_string(obj, integer); return true; } bool json::get_as_array(const char* key, std::vector& val) { cJSON *obj = find(key); val.clear(); if (obj && obj->type == cJSON_Array) { cJSON *child = obj->child; while (child) { if (child->type == cJSON_Number) { char buf[40]; sprintf(buf, "%d", child->valueint); val.push_back(buf); } else if (child->type == cJSON_String) val.push_back(child->valuestring ? child->valuestring : ""); else { char *text = cJSON_Print(child); val.push_back(text); free(text); } child = child->next; } return true; } return false; } bool json::first_child(std::string& val, std::string* name) { cur_child_ = obj_->child; val = ""; if (cur_child_) { val = json::get_value_as_string(cur_child_); if (name && cur_child_->string) *name = cur_child_->string; return true; } else { return false; } } bool json::next_child(std::string& val, std::string* name) { if (cur_child_) cur_child_ = cur_child_->next; val = ""; if (cur_child_) { val = json::get_value_as_string(cur_child_); if (name && cur_child_->string) *name = cur_child_->string; return true; } else { return false; } } bool json::set_value(const char* key, bool val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, val ? cJSON_CreateTrue() : cJSON_CreateFalse()); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); if (val) ele->type = cJSON_True; else ele->type = cJSON_False; return true; } bool json::set_value(const char* key, int val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_Number; ele->valuedouble = ele->valueint = val; return true; } bool json::set_value(const char* key, double val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_Number; ele->valuedouble = val; return true; } bool json::set_value(const char* key, std::string val) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); cJSON_AddItemToArray(obj_, cJSON_CreateString(val.c_str())); } return is_array_; } cJSON* ele = this->find(key, true); if (!ele) return false; json::free_node_data(ele); ele->type = cJSON_String; ele->valuestring = (char*)malloc(val.length() + 4); bzero(ele->valuestring, val.length() + 4); strcpy(ele->valuestring, val.c_str()); return true; } bool json::set_value(const char* key, const char* val) { return set_value(key, std::string(val)); } bool json::set_value(const char* key, json* obj) { if(!key) { if(is_array_) { if(!obj_) obj_ = json::create_element(true); if(obj && obj->obj_) { cJSON_AddItemToArray(obj_, obj->obj_); obj->obj_ = NULL; } } return is_array_; } cJSON** addr = NULL; cJSON* ele = this->find(key, true, &addr); if (!ele) return false; // json::free_node_data(ele); cJSON_Delete(ele); *addr = obj->obj_; ele = obj->obj_; if(ele->string) free(ele->string); ele->string = (char*)malloc(strlen(key) + 4); bzero(ele->string, strlen(key) + 4); strcpy(ele->string, key); obj->obj_ = NULL; return true; } bool json::change_key(const char* old_key, const char* new_key) { if (!obj_ || !new_key || *new_key == 0 || !old_key || *old_key == 0) return false; cJSON** addr = NULL, *ele = find(old_key, false, &addr); if (!ele) return false; if (strlen(ele->string) < strlen(new_key)) { int l = strlen(new_key) + 4; free(ele->string); ele->string = (char*)malloc(l); memset(ele->string, 0, l); } strcpy(ele->string, new_key); return true; } bool json::remove(const char* key) { if(!obj_) return false; cJSON **addr = NULL, *ele = find(key, false, &addr); if(ele) { bool cur_child = cur_child_ == obj_->child; if(addr) *addr = ele->next; if (cur_child_ == ele) { if (cur_child) { walk_head_.next = obj_->child; cur_child_ = &walk_head_; } else cur_child_ = ele->prev; } if (ele->prev) ele->prev->next = ele->next; if (ele->next) ele->next->prev = ele->prev; ele->prev = NULL; ele->next = NULL; cJSON_Delete(ele); return true; } else return false; } } namespace gb { static char base64_default_table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" }; base64::base64() : padding_char_('=') { base64_ind_[0] = base64_char_[0] = 0; initialize_base64_table(base64_default_table); } base64::~base64() {} bool base64::is_valid_base64_table(const char* table) { bool valid = false; if (table && strlen(table) >= 64) { char repeat[4] = { 0 }; valid = true; for (int i = 0; i < 63; ++i) { repeat[0] = table[i]; if (strstr(table + i + 1, repeat)) { valid = false; break; } } } return valid; } bool base64::initialize_base64_table(const char* table) { if (!table || strlen(table) < 64) { if (memcmp(base64_default_table, base64_char_, 64) == 0) { return !table; } memcpy(base64_char_, base64_default_table, 64); } else { if (memcmp(base64_char_, table, 64) == 0) { return true; } else if (!is_valid_base64_table(table)) return false; memcpy(base64_char_, table, 64); } base64_char_[64] = base64_char_[65] = 0; // initialize base64_index memset(base64_ind_, 0, sizeof(base64_ind_)); for (int i = 0; i < 64; ++i) { base64_ind_[base64_char_[i]] = i; } // padding char padding_char_ = '='; if (base64_ind_[padding_char_]) { for (padding_char_ = 0x21; padding_char_ < 0x7e && base64_ind_[padding_char_] && padding_char_ != base64_char_[0]; ++padding_char_); } return padding_char_ < 0x7e; } bool base64::set_base64_table(const char* table) { return initialize_base64_table(table ? table : base64_default_table); } std::string base64::encode(const char* data, size_t bytes, unsigned int line_bytes, bool need_padding) { char* str = (char*)malloc(bytes * 2 + 3); unsigned char c1 = 0, c2 = 0, c3 = 0; unsigned long line_len = 0; unsigned long words = bytes / 3; int rest = bytes % 3, pos = 0; std::string ret(""); for (unsigned long i = 0; i < words; ++i) { // fetch 3 letters c1 = *data++; c2 = *data++; c3 = *data++; // encoding into 4-bytes str[pos++] = base64_char_[c1 >> 2]; str[pos++] = base64_char_[((c1 << 4) | (c2 >> 4)) & 0x3f]; str[pos++] = base64_char_[((c2 << 2) | (c3 >> 6)) & 0x3f]; str[pos++] = base64_char_[c3 & 0x3f]; line_len += 4; // new line ... if ((unsigned int)line_len > line_bytes - 4) { str[pos++] = '\r'; str[pos++] = '\n'; line_len = 0; } } // rest ... if (rest == 1) { c1 = *data++; str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; str[pos++] = base64_char_[((c1 & 0x03) << 4)]; if (need_padding) { str[pos++] = padding_char_; str[pos++] = padding_char_; } } else if (rest == 2) { c1 = *data++; c2 = *data++; str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; str[pos++] = base64_char_[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; str[pos++] = base64_char_[((c2 & 0x0f) << 2)]; if (need_padding) { str[pos++] = padding_char_; } } if (pos > 0) { str[pos++] = 0; ret = std::string(str, pos - 1); } free(str); return ret; } std::string base64::decode(const char* data, size_t bytes) { char* str = (char*)malloc(bytes + 1); int pos = 0, shifts = 18, value = 0; std::string ret(""); for (int i = 0; i < (int)bytes; ++i) { if (*data != '\r' && *data != '\n') { if (*data == padding_char_) { break; } value += base64_ind_[*data] << shifts; if (shifts == 0) { shifts = 18; str[pos++] = (value >> 16) & 0x0ff; str[pos++] = (value >> 8) & 0x0ff; str[pos++] = (value >> 0) & 0x0ff; value = 0; } else { shifts -= 6; } } data++; } if (shifts == 12 || shifts == 6) { str[pos++] = (value >> 16) & 0x0ff; } else if (shifts == 0) { str[pos++] = (value >> 16) & 0x0ff; str[pos++] = (value >> 8) & 0x0ff; } if (pos > 0) { str[pos++] = 0; ret = std::string(str, pos - 1); } free(str); return ret; } } namespace updater { static std::string hg_model_from_pid(const char* pid) { if (strcmp(pid, "7823") == 0) return "G200"; char m[] = { 'G', pid[0], '0', '0', 0}; return std::string(m) + " - " + pid; } static std::string hv_model_from_pid(const char* pid) { std::string m(""); if (strcmp(pid, "1000") == 0) m = "HW-1000NS"; else if (strcmp(pid, "1002") == 0) m = "HW-1000"; else if (strcmp(pid, "7000") == 0) m = "HW-7000NS"; else if (strcmp(pid, "7002") == 0) m = "HW-7000"; else if (strcmp(pid, "7039") == 0) m = "HW-7000NS"; else m = std::string("HW-") + pid; return m + " - " + pid; } static std::string lsc_model_from_pid(const char* pid) { if (strcmp(pid, "8200") == 0) return "G42S - 8200"; else { char m[] = {'G', pid[1], pid[2], 'S', 0}; return std::string(m) + " - " + pid; } } static std::string scanner_chinese_name_2_model(const char* cn) { static const char* hg = "\345\215\216\351\253\230", * hw = "\346\261\211\347\216\213", * lsc = "\347\253\213\346\200\235\350\276\260", * smy = "\346\211\253\346\217\217\344\273\252\342\200\224G", * f = strstr(cn, hg); std::string model(""); std::string(* model_from_pid)(const char* pid) = nullptr; if (f == cn) { model = "HUAGOSCAN "; model_from_pid = hg_model_from_pid;; } else if (strstr(cn, hw) == cn) { model = "Hanvon "; model_from_pid = hv_model_from_pid;; } else if (strstr(cn, lsc) == cn) { model = "LANXUMSCAN "; model_from_pid = lsc_model_from_pid;; } else return ""; f = strstr(cn, smy); if (!f) return ""; f += strlen(smy); model += model_from_pid(f); return model; } static int update_app_config(const char* scanner_name, const char* jsn_txt, const char* path, gb::scanner_cfg::LPUDF lpfunc) { std::string scanner(""), jsn_str(jsn_txt); std::vector efiles; if ((unsigned char)scanner_name[0] > 0x7f) scanner = scanner_chinese_name_2_model(scanner_name); else scanner = scanner_name; gb::json* jsn = new gb::json(); int cur_sel = -1, ret = 0; gb::scanner_cfg* cfg = nullptr; if (!jsn->attach_text(&jsn_str[0])) { jsn->release(); return EINVAL; } if (jsn->first_child(jsn_str)) { gb::json* child = new gb::json(); if (child->attach_text(&jsn_str[0])) { if (!child->get_value("cur_sel", cur_sel)) ret = EINVAL; } if (ret == 0) { cfg = new gb::scanner_cfg(); int ind = 0; while (jsn->next_child(jsn_str)) { if (!child->attach_text(&jsn_str[0])) { ret = EINVAL; break; } std::string schm_name(""); if (!child->get_value("scheme", schm_name)) { ret = EINVAL; break; } gb::json* items = nullptr; if (!child->get_value("opts", items) || !items) { ret = EINVAL; break; } gb::sane_config_schm* schm = new gb::sane_config_schm(); if (items->first_child(jsn_str)) { do { std::string name(""), val(""); gb::json* item = new gb::json(); if (item->attach_text(&jsn_str[0])) { if (item->get_value("name", name) && item->get_value("value", val)) { name = lpfunc->title2name(name.c_str(), lpfunc->func_param); lpfunc->trans_number(name.c_str(), val, lpfunc->func_param); schm->set_value(name.c_str(), val.c_str(), val.length()); val = ""; item->get_value("extra", val); if (val.length() && gb::load_mini_file(val.c_str(), val) == 0) { schm->set_value(name.c_str(), val.c_str(), val.length(), true); item->get_value("extra", val); efiles.push_back(val); } } } item->release(); } while (items->next_child(jsn_str)); } items->release(); cfg->add_scheme(schm, schm_name.c_str()); schm->release(); if (ind++ == cur_sel) cfg->select_scheme(schm_name.c_str()); } } child->release(); } jsn->release(); if (cfg) { if (ret == 0) { cfg->save((path + scanner + ".cfg").c_str()); for (auto& v : efiles) rename(v.c_str(), (v + "_bk").c_str()); } cfg->release(); } return ret; } } namespace gb { std::string sane_config_schm::opt_data_appendix_("_data"); sane_config_schm::sane_config_schm(scanner_cfg* scanner) : jsn_(NULL), bkp_(NULL), in_setting_(false), scheme_name_("") , scanner_(scanner) { char empty[8] = { "{}" }; jsn_ = new gb::json(); jsn_->attach_text(empty); def_val_ = new gb::json(); if (scanner_) scanner_->add_ref(); } sane_config_schm::~sane_config_schm() { clear(); def_val_->release(); if (scanner_) scanner_->release(); } bool sane_config_schm::hex(unsigned char ch, unsigned char* val) { bool ret = true; if (ch >= '0' && ch <= '9') ch -= '0'; else if (ch >= 'a' && ch <= 'f') ch = ch - 'a' + 10; else if (ch >= 'A' && ch <= 'F') ch = ch - 'A' + 10; else ret = false; if (ret && val) { *val &= 0x0f0; *val |= ch; } return ret; } bool sane_config_schm::hex_char(const char* data, unsigned char* val) { unsigned char v = 0; bool ret = false; if (sane_config_schm::hex(*data++, &v)) { v <<= 4; if (sane_config_schm::hex(*data++, &v)) { if (val) *val = v; ret = true; } } return ret; } bool sane_config_schm::is_option_data(std::string& name) { size_t pos = name.find(sane_config_schm::opt_data_appendix_); if (pos != std::string::npos) { if (pos + sane_config_schm::opt_data_appendix_.length() == name.length()) { name.erase(pos); return true; } } return false; } void sane_config_schm::clear() { if (jsn_) jsn_->release(); jsn_ = NULL; if (bkp_) bkp_->release(); bkp_ = NULL; // file_ = ""; } std::string sane_config_schm::to_hex_letter(const char* data, size_t bytes) { std::string hex(""); const unsigned char* ptr = (const unsigned char*)data; char buf[8] = { 0 }; for (size_t i = 0; i < bytes; ++i) { sprintf(buf, "%02X", ptr[i]); hex += buf; } return hex; } std::string sane_config_schm::from_hex_letter(const char* data, size_t bytes) { std::string stream(""); bytes /= 2; for (size_t i = 0; i < bytes; ++i) { unsigned char ch = 0; if (!sane_config_schm::hex_char(data, &ch)) break; stream.append(1, ch); data += 2; } return stream; } std::string sane_config_schm::default_value(const char* hex_title) { std::string val(""); def_val_->get_value(hex_title, val); return val; } sane_config_schm* sane_config_schm::copy(void) { sane_config_schm *cp = new sane_config_schm(scanner_); std::string val(jsn_->to_string(false)); cp->scheme_name_ = scheme_name_; cp->file_ = file_; cp->jsn_->attach_text(&val[0]); cp->id_name_ = id_name_; val = def_val_->to_string(false); cp->def_val_->attach_text(&val[0]); return cp; } bool sane_config_schm::load_from_file(const char* file) { clear(); std::string cont(""); file_ = file; if (gb::load_mini_file(file, cont)) return false; return load_from_mem(cont.c_str()); } bool sane_config_schm::load_from_mem(const char* mem, bool in_b64) { gb::base64 b64; std::string stream(in_b64 ? b64.decode(mem, strlen(mem)) : mem); clear(); jsn_ = new gb::json(); if (!jsn_->attach_text(&stream[0])) { jsn_->release(); jsn_ = NULL; return false; } return true; } bool sane_config_schm::save_to(const char* file) { bool ret = false; std::string encode(to_text_stream()); if (!file || *file == 0) file = file_.c_str(); if (encode.length()) { FILE* dst = fopen(file, "wb"); if (dst) { fwrite(encode.c_str(), 1, encode.length(), dst); fclose(dst); ret = true; } } return ret; } void sane_config_schm::set_default_value(int sn, const char* name, const char* val, size_t bytes) { id_name_[sn] = name; def_val_->set_value(name, to_hex_letter(val, bytes).c_str()); } void sane_config_schm::copy_default_value(sane_config_schm* from) { if(from) { std::string t(from->def_val_->to_string(false)); id_name_ = from->id_name_; def_val_->attach_text(&t[0]); } } bool sane_config_schm::first_config(std::string& name, std::string& val) { bool ret = false; std::string raw_v(""); if (jsn_ && jsn_->first_child(raw_v, &name)) { val = sane_config_schm::from_hex_letter(raw_v.c_str(), raw_v.length()); ret = true; } return ret; } bool sane_config_schm::next_config(std::string& name, std::string& val) { bool ret = false; std::string raw_v(""); if (jsn_ && jsn_->next_child(raw_v, &name)) { val = sane_config_schm::from_hex_letter(raw_v.c_str(), raw_v.length()); ret = true; } return ret; } bool sane_config_schm::get_config(const char* name, std::string& val) { bool ret = jsn_ ? jsn_->get_value(name, val) : false; if(!ret && def_val_) ret = def_val_->get_value(name, val); if(ret) val = sane_config_schm::from_hex_letter(val.c_str(), val.length()); return ret; } void sane_config_schm::begin_setting(bool restore) { if (bkp_) bkp_->release(); bkp_ = jsn_; in_setting_ = true; jsn_ = new gb::json(); if (!restore && bkp_) { std::string stream(bkp_->to_string(false)); if(stream.length()) jsn_->attach_text(&stream[0]); } } void sane_config_schm::config_changed(const char* name, const char* val, size_t bytes, bool extra) { std::string hex_v(to_hex_letter(val, bytes)); if (extra) { jsn_->set_value((name + sane_config_schm::opt_data_appendix_).c_str(), hex_v.c_str()); } else { std::string def = default_value(name); if (hex_v == def) { jsn_->remove(name); jsn_->remove((name + sane_config_schm::opt_data_appendix_).c_str()); } else jsn_->set_value(name, hex_v.c_str()); } } void sane_config_schm::config_changed(int sn, const char* val, size_t bytes, bool extra) { std::string name(""); if (id_name_.count(sn)) { name = id_name_[sn]; config_changed(name.c_str(), val, bytes, extra); } } void sane_config_schm::remove_config(const char* name) { if (jsn_) jsn_->remove(name); } void sane_config_schm::set_value(const char* name, const char* val, size_t bytes, bool extra) { std::string hex_v(to_hex_letter(val, bytes)); if (extra) jsn_->set_value((name + sane_config_schm::opt_data_appendix_).c_str(), hex_v.c_str()); else jsn_->set_value(name, hex_v.c_str()); } bool sane_config_schm::has_changed(void) { if(!bkp_) return false; std::map old; std::string n(""), v(""); if(bkp_->first_child(v, &n)) { do { old[n] = v; }while(bkp_->next_child(v, &n)); } if(jsn_->first_child(v, &n)) { do { if(old.count(n) == 0) return true; if(old[n]!=v) return true; old.erase(n); }while(jsn_->next_child(v, &n)); } return old.size() > 0; } void sane_config_schm::end_setting(bool cancel) { if (in_setting_) { if (cancel) { jsn_->release(); jsn_ = bkp_; bkp_ = NULL; } else if (bkp_) { bkp_->release(); bkp_ = NULL; } } in_setting_ = false; } int sane_config_schm::id_from_name(const char* name) { for (const auto& v : id_name_) { if (v.second == name) return v.first; } return -1; } std::string sane_config_schm::to_text_stream(bool b64, bool with_ver) { if (jsn_) { if(with_ver) { char ver[40] = { 0 }; sprintf(ver, "%u.%u", VERSION_MAIN, VERSION_SUB); jsn_->set_value("ver", ver); } std::string cont(jsn_->to_string(false)); if (b64) { gb::base64 b64; cont = b64.encode(cont.c_str(), cont.length()); } return cont; } else return ""; } std::string sane_config_schm::get_version(void) { std::string ver(""); if (jsn_) jsn_->get_value("ver", ver); return ver; } std::string sane_config_schm::get_scheme_name(void) { return scheme_name_; } void sane_config_schm::set_scheme_name(const char* name) { scheme_name_ = name ? name : ""; } void sane_config_schm::update(bool(* is_float)(int, void*), void* param, const char* (* t2n)(const char*), std::string* discard) { if (!jsn_) return; std::string ver(get_version()), name(""), val(""); int mv = atoi(ver.c_str()), sv = ver.find("."); bool changed = false; char vs[40] = { 0 }; if (sv++ != -1) sv = atoi(ver.c_str() + sv); // change title to name ... if (mv <= 4 && sv < 30) { if (jsn_->first_child(val, &name)) { changed = true; do { jsn_->change_key(name.c_str(), t2n(sane_config_schm::from_hex_letter(name.c_str(), name.length()).c_str())); } while (jsn_->next_child(val, &name)); } } // fix float does not convert to SANE_Fixed bug in eldest version .... (discard them) if (ver.empty()) { if (jsn_->first_child(val, &name)) { do { int id = id_from_name(name.c_str()); if (id == -1 || is_float(id, param)) { jsn_->remove(name.c_str()); if (discard) *discard += name + "\r\n"; changed = true; } } while (jsn_->next_child(val, &name)); } } sprintf(vs, "%u.%u", VERSION_MAIN, VERSION_SUB); jsn_->set_value("ver", vs); if (changed) save_to(NULL); } /////////////////////////////////////////////////////////////////////////////////// // scanner_cfg std::string scanner_cfg::global_name_ = "global"; std::string scanner_cfg::cur_sel_ = "cur"; std::string scanner_cfg::default_setting_name_ = "\351\273\230\350\256\244\350\256\276\347\275\256"; // utf-8: 默认设置 scanner_cfg::scanner_cfg() : path_(""), scanner_name_(""), global_(new json()) { init_version(); init_select(); } scanner_cfg::~scanner_cfg() { clear(); global_->release(); } bool scanner_cfg::update(const char* file, LPUDF func) { std::string cont(""), name(""), path(file); int ret = gb::load_mini_file(file, cont); base64 b64; json *jsn = nullptr; bool ok = true; if (ret) return false; else if (cont.empty()) return true; cont = b64.decode(cont.c_str(), cont.length()); jsn = new json(); if (!jsn->attach_text(&cont[0])) { jsn->release(); return false; } cont = ""; ret = path.rfind(PATH_SYMBOL[0]); if (ret++ != std::string::npos) path.erase(ret); if (jsn->first_child(cont, &name)) { do { ok &= updater::update_app_config(name.c_str(), cont.c_str(), path.c_str(), func) == 0; } while (jsn->next_child(cont, &name)); } jsn->release(); if (ok) rename(file, (std::string(file) + "_bk").c_str()); return true; } void scanner_cfg::clear(void) { global_->set_value("ver", ""); global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); for (size_t i = 0; i < schemes_.size(); ++i) schemes_[i].schm->release(); schemes_.clear(); scanner_name_ = ""; } void scanner_cfg::init_version(void) { char vstr[40] = { 0 }; sprintf(vstr, "%u.%u", VERSION_MAIN, VERSION_SUB); global_->set_value("ver", vstr); } void scanner_cfg::init_select(void) { global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); } void scanner_cfg::walk_sibling_schemes(cJSON* first) { if (!first) return; cJSON* next = first->next; std::string name(first->string ? first->string : ""), cont(""); CFGSCHM sch; first->next = nullptr; cont = json::to_string(first, false); if (name == scanner_cfg::global_name_) { global_->attach_text(&cont[0]); } else { sch.schm = new sane_config_schm(); if (sch.schm->load_from_mem(cont.c_str(), false)) { sch.name = sane_config_schm::from_hex_letter(name.c_str(), name.length()); sch.schm->set_scheme_name(sch.name.c_str()); schemes_.push_back(sch); } else sch.schm->release(); } first->next = next; walk_sibling_schemes(next); } int scanner_cfg::load_file(const char* file) { std::string cont(""); int ret = gb::load_mini_file(file, cont); if (ret == 0) ret = load_mem(cont.c_str()); // if (ret == 0 && scanner_name_.empty()) { const char* name = strrchr(file, PATH_SYMBOL[0]); if (name++ == nullptr) name = file; else path_ = std::string(file, name - file); scanner_name_ = name; ret = scanner_name_.rfind('.'); if (ret != std::string::npos) scanner_name_.erase(ret); ret = 0; } return ret; } int scanner_cfg::load_mem(const char* mem) { base64 b64; std::string text(b64.decode(mem, strlen(mem))); cJSON* root = cJSON_Parse(text.c_str()); if (!root) { FILE* dst = fopen((path_ + "err_cfg.txt").c_str(), "wb"); fwrite(text.c_str(), 1, text.length(), dst); fclose(dst); return EINVAL; } clear(); walk_sibling_schemes(root->child); cJSON_Delete(root); return 0; } int scanner_cfg::save(const char* file) { if (!file && path_.empty() && scanner_name_.empty()) return EINVAL; std::string cont("{\"" + scanner_cfg::global_name_ + "\":"), f(file ? file : path_ + scanner_name_ + ".cfg"), v(""); int sel = -1; if (!global_->get_value("ver", v) || v.empty()) init_version(); if (!global_->get_value(scanner_cfg::cur_sel_.c_str(), sel) || sel >= schemes_.size()) init_select(); cont += global_->to_string(false); for (auto& v: schemes_) { cont += ",\"" + sane_config_schm::to_hex_letter(v.name.c_str(), v.name.length()) + "\":"; cont += v.schm->to_text_stream(false, false); } cont += "}"; base64 b64; FILE* dst = fopen(f.c_str(), "wb"); if (!dst) return errno; f = b64.encode(cont.c_str(), cont.length()); fwrite(f.c_str(), 1, f.length(), dst); fclose(dst); return 0; } void scanner_cfg::get_all_schemes(std::vector& schemes) { schemes.push_back(scanner_cfg::default_setting_name_); for (auto& v : schemes_) schemes.push_back(v.name); } sane_config_schm* scanner_cfg::get_scheme(const char* scheme_name) { sane_config_schm* found = nullptr; if (scheme_name && *scheme_name) { std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), scheme_name); if (it != schemes_.end()) found = it->schm; } else { int ind = -1; global_->get_value(scanner_cfg::cur_sel_.c_str(), ind); if (ind >= 0 && ind < schemes_.size()) found = schemes_[ind].schm; } if (found) found->add_ref(); return found; } std::string scanner_cfg::get_current_scheme_name(void) { int ind = -1; global_->get_value(scanner_cfg::cur_sel_.c_str(), ind); if (ind >= 0 && ind < schemes_.size()) return schemes_[ind].name; else return scanner_cfg::default_setting_name_; } bool scanner_cfg::remove_scheme(const char* scheme_name) { std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), scheme_name); if (it != schemes_.end()) { int id = it - schemes_.begin(), ind = -1; it->schm->release(); schemes_.erase(it); global_->get_value(scanner_cfg::cur_sel_.c_str(), ind); if (ind == id) global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); else if (ind > id) global_->set_value(scanner_cfg::cur_sel_.c_str(), ind - 1); return true; } return false; } void scanner_cfg::remove_all_schemes(void) { for(auto& v: schemes_) v.schm->release(); schemes_.clear(); } bool scanner_cfg::select_scheme(const char* scheme_name) { std::vector::iterator it = scheme_name ? std::find(schemes_.begin(), schemes_.end(), scheme_name) : schemes_.end(); if (it == schemes_.end()) global_->set_value(scanner_cfg::cur_sel_.c_str(), -1); else global_->set_value(scanner_cfg::cur_sel_.c_str(), (int)(it - schemes_.begin())); return true; } sane_config_schm* scanner_cfg::copy_scheme(const char* cp_from_name) // for UI setting, call release() if not use anymore { if (!cp_from_name) return nullptr; else if (scanner_cfg::default_setting_name_ == cp_from_name) return new sane_config_schm(); else { std::vector::iterator it = std::find(schemes_.begin(), schemes_.end(), cp_from_name); if (it == schemes_.end()) return nullptr; std::string cont(it->schm->to_text_stream()); sane_config_schm* schm = new sane_config_schm(); schm->load_from_mem(cont.c_str()); return schm; } } bool scanner_cfg::add_scheme(sane_config_schm* schm, const char* name) { CFGSCHM cs; cs.name = name ? name : schm->get_scheme_name(); if(cs.name.empty() || cs.name == scanner_cfg::global_name_) return false; if (std::find(schemes_.begin(), schemes_.end(), cs.name.c_str()) != schemes_.end()) return false; cs.schm = schm; schemes_.push_back(cs); schm->set_scheme_name(cs.name.c_str()); schm->add_ref(); return true; } bool scanner_cfg::rename_scheme(const char* from, const char* to) { if (to && std::find(schemes_.begin(), schemes_.end(), to) != schemes_.end()) return false; for(auto& v: schemes_) { if(v.name == from) { v.name = to; v.schm->set_scheme_name(to); return true; } } return false; } }