/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 Jon Evans * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include ///! The following environment variables will never be migrated from a previous version const std::set envVarBlacklist = { wxT( "KICAD7_SYMBOL_DIR" ), wxT( "KICAD7_FOOTPRINT_DIR" ), wxT( "KICAD7_TEMPLATES_DIR" ), wxT( "KICAD7_3DMODEL_DIR" ) }; ///! Update the schema version whenever a migration is required const int commonSchemaVersion = 3; COMMON_SETTINGS::COMMON_SETTINGS() : JSON_SETTINGS( "kicad_common", SETTINGS_LOC::USER, commonSchemaVersion ), m_Appearance(), m_Backup(), m_Env(), m_Input(), m_Graphics(), m_Session(), m_System(), m_DoNotShowAgain(), m_NetclassPanel(), m_PackageManager() { /* * Automatic dark mode detection works fine on Mac. */ #if defined( __WXGTK__ ) || defined( __WXMSW__ ) m_params.emplace_back( new PARAM_ENUM( "appearance.icon_theme", &m_Appearance.icon_theme, ICON_THEME::AUTO, ICON_THEME::LIGHT, ICON_THEME::AUTO ) ); #else m_Appearance.icon_theme = ICON_THEME::AUTO; #endif /* * Automatic icon scaling works fine on Mac. It works mostly fine on MSW, but perhaps not * uniformly enough to exclude the explicit controls there. */ #if defined( __WXGTK__ ) || defined( __WXMSW__ ) m_params.emplace_back( new PARAM( "appearance.icon_scale", &m_Appearance.icon_scale, 0 ) ); #else m_Appearance.icon_scale = 0.0; #endif /* * Automatic canvas scaling works fine on all supported platforms, so it's no longer exposed as * a configuration option. */ m_Appearance.canvas_scale = 0.0; /* * Menu icons are off by default on OSX and on for all other platforms. */ #ifdef __WXMAC__ m_params.emplace_back( new PARAM( "appearance.use_icons_in_menus", &m_Appearance.use_icons_in_menus, false ) ); #else m_params.emplace_back( new PARAM( "appearance.use_icons_in_menus", &m_Appearance.use_icons_in_menus, true ) ); #endif /* * Font scaling hacks are only needed on GTK under wxWidgets 3.0. */ #if defined( __WXGTK__ ) && !wxCHECK_VERSION( 3, 1, 0 ) m_params.emplace_back( new PARAM( "appearance.apply_icon_scale_to_fonts", &m_Appearance.apply_icon_scale_to_fonts, false ) ); #else m_Appearance.apply_icon_scale_to_fonts = false; #endif m_params.emplace_back( new PARAM( "appearance.show_scrollbars", &m_Appearance.show_scrollbars, false ) ); m_params.emplace_back( new PARAM( "appearance.hicontrast_dimming_factor", &m_Appearance.hicontrast_dimming_factor, 0.8f ) ); m_params.emplace_back( new PARAM( "appearance.text_editor_zoom", &m_Appearance.text_editor_zoom, 0 ) ); m_params.emplace_back( new PARAM( "auto_backup.enabled", &m_Backup.enabled, true ) ); m_params.emplace_back( new PARAM( "auto_backup.backup_on_autosave", &m_Backup.backup_on_autosave, false ) ); m_params.emplace_back( new PARAM( "auto_backup.limit_total_files", &m_Backup.limit_total_files, 25 ) ); m_params.emplace_back( new PARAM( "auto_backup.limit_total_size", &m_Backup.limit_total_size, 104857600 ) ); m_params.emplace_back( new PARAM( "auto_backup.limit_daily_files", &m_Backup.limit_daily_files, 5 ) ); m_params.emplace_back( new PARAM( "auto_backup.min_interval", &m_Backup.min_interval, 300 ) ); m_params.emplace_back( new PARAM_LAMBDA( "environment.vars", [&]() -> nlohmann::json { nlohmann::json ret = {}; for( const std::pair entry : m_Env.vars ) { const ENV_VAR_ITEM& var = entry.second; wxASSERT( entry.first == var.GetKey() ); // Default values are never persisted if( var.IsDefault() ) { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Env var %s skipping save (default)" ), var.GetKey() ); continue; } wxString value = var.GetValue(); // Vars that existed in JSON are persisted, but if they were overridden // externally, we persist the old value (i.e. the one that was loaded from JSON) if( var.GetDefinedExternally() ) { if( var.GetDefinedInSettings() ) { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Env var %s was overridden externally, " ) "saving previously-loaded value %s", var.GetKey(), var.GetSettingsValue() ); value = var.GetSettingsValue(); } else { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Env var %s skipping save (external)" ), var.GetKey() ); continue; } } wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Saving env var %s = %s" ), var.GetKey(), value); std::string key( var.GetKey().ToUTF8() ); ret[key] = value; } return ret; }, [&]( const nlohmann::json& aJson ) { if( !aJson.is_object() ) return; for( const auto& entry : aJson.items() ) { wxString key = wxString( entry.key().c_str(), wxConvUTF8 ); wxString val = entry.value().get(); if( m_Env.vars.count( key ) ) { if( m_Env.vars[key].GetDefinedExternally() ) { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: %s is defined externally" ), key ); m_Env.vars[key].SetDefinedInSettings(); m_Env.vars[key].SetSettingsValue( val ); continue; } else { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Updating %s: %s -> %s"), key, m_Env.vars[key].GetValue(), val ); m_Env.vars[key].SetValue( val ); } } else { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Loaded new var: %s = %s" ), key, val ); m_Env.vars[key] = ENV_VAR_ITEM( key, val ); } m_Env.vars[key].SetDefinedInSettings(); m_Env.vars[key].SetSettingsValue( val ); } }, {} ) ); m_params.emplace_back( new PARAM( "input.focus_follow_sch_pcb", &m_Input.focus_follow_sch_pcb, false ) ); m_params.emplace_back( new PARAM( "input.auto_pan", &m_Input.auto_pan, false ) ); m_params.emplace_back( new PARAM( "input.auto_pan_acceleration", &m_Input.auto_pan_acceleration, 5 ) ); m_params.emplace_back( new PARAM( "input.center_on_zoom", &m_Input.center_on_zoom, true ) ); m_params.emplace_back( new PARAM( "input.immediate_actions", &m_Input.immediate_actions, true ) ); m_params.emplace_back( new PARAM( "input.warp_mouse_on_move", &m_Input.warp_mouse_on_move, true ) ); m_params.emplace_back( new PARAM( "input.horizontal_pan", &m_Input.horizontal_pan, false ) ); m_params.emplace_back( new PARAM( "input.hotkey_feedback", &m_Input.hotkey_feedback, true ) ); m_params.emplace_back( new PARAM( "input.zoom_acceleration", &m_Input.zoom_acceleration, false ) ); #ifdef __WXMAC__ int default_zoom_speed = 5; #else int default_zoom_speed = 1; #endif m_params.emplace_back( new PARAM( "input.zoom_speed", &m_Input.zoom_speed, default_zoom_speed ) ); m_params.emplace_back( new PARAM( "input.zoom_speed_auto", &m_Input.zoom_speed_auto, true ) ); m_params.emplace_back( new PARAM( "input.scroll_modifier_zoom", &m_Input.scroll_modifier_zoom, 0 ) ); m_params.emplace_back( new PARAM( "input.scroll_modifier_pan_h", &m_Input.scroll_modifier_pan_h, WXK_CONTROL ) ); m_params.emplace_back( new PARAM( "input.scroll_modifier_pan_v", &m_Input.scroll_modifier_pan_v, WXK_SHIFT ) ); m_params.emplace_back( new PARAM_ENUM( "input.mouse_left", &m_Input.drag_left, MOUSE_DRAG_ACTION::DRAG_SELECTED, MOUSE_DRAG_ACTION::DRAG_ANY, MOUSE_DRAG_ACTION::SELECT ) ); m_params.emplace_back( new PARAM_ENUM( "input.mouse_middle", &m_Input.drag_middle, MOUSE_DRAG_ACTION::PAN, MOUSE_DRAG_ACTION::SELECT, MOUSE_DRAG_ACTION::NONE ) ); m_params.emplace_back( new PARAM_ENUM( "input.mouse_right", &m_Input.drag_right, MOUSE_DRAG_ACTION::PAN, MOUSE_DRAG_ACTION::SELECT, MOUSE_DRAG_ACTION::NONE ) ); m_params.emplace_back( new PARAM( "graphics.opengl_antialiasing_mode", &m_Graphics.opengl_aa_mode, 1, 0, 2 ) ); m_params.emplace_back( new PARAM( "graphics.cairo_antialiasing_mode", &m_Graphics.cairo_aa_mode, 0, 0, 2 ) ); m_params.emplace_back( new PARAM( "system.autosave_interval", &m_System.autosave_interval, 600 ) ); #ifdef __WXMAC__ m_params.emplace_back( new PARAM( "system.text_editor", &m_System.text_editor, wxS( "/usr/bin/open -e" ) ) ); #else m_params.emplace_back( new PARAM( "system.text_editor", &m_System.text_editor, wxS( "" ) ) ); #endif m_params.emplace_back( new PARAM( "system.file_history_size", &m_System.file_history_size, 9 ) ); m_params.emplace_back( new PARAM( "system.language", &m_System.language, wxS( "Default" ) ) ); m_params.emplace_back( new PARAM( "system.pdf_viewer_name", &m_System.pdf_viewer_name, wxS( "" ) ) ); m_params.emplace_back( new PARAM( "system.use_system_pdf_viewer", &m_System.use_system_pdf_viewer, true ) ); m_params.emplace_back( new PARAM( "system.working_dir", &m_System.working_dir, wxS( "" ) ) ); m_params.emplace_back( new PARAM( "system.clear_3d_cache_interval", &m_System.clear_3d_cache_interval, 30 ) ); m_params.emplace_back( new PARAM( "do_not_show_again.zone_fill_warning", &m_DoNotShowAgain.zone_fill_warning, false ) ); m_params.emplace_back( new PARAM( "do_not_show_again.env_var_overwrite_warning", &m_DoNotShowAgain.env_var_overwrite_warning, false ) ); m_params.emplace_back( new PARAM( "do_not_show_again.scaled_3d_models_warning", &m_DoNotShowAgain.scaled_3d_models_warning, false ) ); m_params.emplace_back( new PARAM( "do_not_show_again.data_collection_prompt", &m_DoNotShowAgain.data_collection_prompt, false ) ); m_params.emplace_back( new PARAM( "session.remember_open_files", &m_Session.remember_open_files, false ) ); m_params.emplace_back( new PARAM_LIST( "session.pinned_symbol_libs", &m_Session.pinned_symbol_libs, {} ) ); m_params.emplace_back( new PARAM_LIST( "session.pinned_fp_libs", &m_Session.pinned_fp_libs, {} ) ); m_params.emplace_back( new PARAM( "netclass_panel.sash_pos", &m_NetclassPanel.sash_pos, 160 ) ); m_params.emplace_back( new PARAM( "package_manager.sash_pos", &m_PackageManager.sash_pos, 380 ) ); registerMigration( 0, 1, std::bind( &COMMON_SETTINGS::migrateSchema0to1, this ) ); registerMigration( 1, 2, std::bind( &COMMON_SETTINGS::migrateSchema1to2, this ) ); registerMigration( 2, 3, std::bind( &COMMON_SETTINGS::migrateSchema2to3, this ) ); } bool COMMON_SETTINGS::migrateSchema0to1() { /** * Schema version 0 to 1: * * mousewheel_pan is replaced by explicit settings for scroll wheel behavior */ nlohmann::json::json_pointer mwp_pointer( "/input/mousewheel_pan"_json_pointer ); bool mwp = false; try { mwp = m_internals->at( mwp_pointer ); m_internals->At( "input" ).erase( "mousewheel_pan" ); } catch( ... ) { wxLogTrace( traceSettings, wxT( "COMMON_SETTINGS::Migrate 0->1: mousewheel_pan not found" ) ); } if( mwp ) { ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL; } else { ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT; ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0; } return true; } bool COMMON_SETTINGS::migrateSchema1to2() { nlohmann::json::json_pointer v1_pointer( "/input/prefer_select_to_drag"_json_pointer ); bool prefer_selection = false; try { prefer_selection = m_internals->at( v1_pointer ); m_internals->at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "prefer_select_to_drag" ); } catch( ... ) { wxLogTrace( traceSettings, wxT( "COMMON_SETTINGS::Migrate 1->2: prefer_select_to_drag not found" ) ); } if( prefer_selection ) ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT; else ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY; return true; } bool COMMON_SETTINGS::migrateSchema2to3() { wxFileName cfgpath; cfgpath.AssignDir( SETTINGS_MANAGER::GetUserSettingsPath() ); cfgpath.AppendDir( wxT( "3d" ) ); cfgpath.SetFullName( wxS( "3Dresolver.cfg" ) ); cfgpath.MakeAbsolute(); std::vector legacyPaths; readLegacy3DResolverCfg( cfgpath.GetFullPath(), legacyPaths ); // env variables have a limited allowed character set for names wxRegEx nonValidCharsRegex( wxS( "[^A-Z0-9_]+" ), wxRE_ADVANCED ); for( const LEGACY_3D_SEARCH_PATH& path : legacyPaths ) { wxString key = path.m_Alias; const wxString& val = path.m_Pathvar; // The 3d alias config didnt use the same naming restrictions as real env variables // We need to sanitize them // upper case only key.MakeUpper(); // logically swap - with _ key.Replace( wxS( "-" ), wxS( "_" ) ); // remove any other chars nonValidCharsRegex.Replace( &key, wxEmptyString ); if( !m_Env.vars.count( key ) ) { wxLogTrace( traceEnvVars, wxS( "COMMON_SETTINGS: Loaded new var: %s = %s" ), key, val ); m_Env.vars[key] = ENV_VAR_ITEM( key, val ); } } if( cfgpath.FileExists() ) { wxRemoveFile( cfgpath.GetFullPath() ); } return true; } bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) { bool ret = true; ret &= fromLegacy( aCfg, "CanvasScale", "appearance.canvas_scale" ); ret &= fromLegacy( aCfg, "IconScale", "appearance.icon_scale" ); ret &= fromLegacy( aCfg, "UseIconsInMenus", "appearance.use_icons_in_menus" ); ret &= fromLegacy( aCfg, "ShowEnvVarWarningDialog", "environment.show_warning_dialog" ); auto load_env_vars = [&]() { wxString key, value; long index = 0; nlohmann::json::json_pointer ptr = m_internals->PointerFromString( "environment.vars" ); aCfg->SetPath( "EnvironmentVariables" ); ( *m_internals )[ptr] = nlohmann::json( {} ); while( aCfg->GetNextEntry( key, index ) ) { if( envVarBlacklist.count( key ) ) { wxLogTrace( traceSettings, wxT( "Migrate Env: %s is blacklisted; skipping." ), key ); continue; } value = aCfg->Read( key, wxEmptyString ); if( !value.IsEmpty() ) { ptr.push_back( key.ToStdString() ); wxLogTrace( traceSettings, wxT( "Migrate Env: %s=%s" ), ptr.to_string(), value ); ( *m_internals )[ptr] = value.ToUTF8(); ptr.pop_back(); } } aCfg->SetPath( ".." ); }; load_env_vars(); bool mousewheel_pan = false; if( aCfg->Read( "MousewheelPAN", &mousewheel_pan ) && mousewheel_pan ) { Set( "input.horizontal_pan", true ); Set( "input.scroll_modifier_pan_h", static_cast( WXK_SHIFT ) ); Set( "input.scroll_modifier_pan_v", 0 ); Set( "input.scroll_modifier_zoom", static_cast( WXK_CONTROL ) ); } ret &= fromLegacy( aCfg, "AutoPAN", "input.auto_pan" ); ret &= fromLegacy( aCfg, "ImmediateActions", "input.immediate_actions" ); ret &= fromLegacy( aCfg, "PreferSelectionToDragging", "input.prefer_select_to_drag" ); ret &= fromLegacy( aCfg, "MoveWarpsCursor", "input.warp_mouse_on_move" ); ret &= fromLegacy( aCfg, "ZoomNoCenter", "input.center_on_zoom" ); // This was stored inverted in legacy config if( std::optional value = Get( "input.center_on_zoom" ) ) Set( "input.center_on_zoom", !( *value ) ); ret &= fromLegacy( aCfg, "OpenGLAntialiasingMode", "graphics.opengl_antialiasing_mode" ); ret &= fromLegacy( aCfg, "CairoAntialiasingMode", "graphics.cairo_antialiasing_mode" ); ret &= fromLegacy( aCfg, "AutoSaveInterval", "system.autosave_interval" ); ret &= fromLegacyString( aCfg, "Editor", "system.editor_name" ); ret &= fromLegacy( aCfg, "FileHistorySize", "system.file_history_size" ); ret &= fromLegacyString( aCfg, "LanguageID", "system.language" ); ret &= fromLegacyString( aCfg, "PdfBrowserName", "system.pdf_viewer_name" ); ret &= fromLegacy( aCfg, "UseSystemBrowser", "system.use_system_pdf_viewer" ); ret &= fromLegacyString( aCfg, "WorkingDir", "system.working_dir" ); return ret; } void COMMON_SETTINGS::InitializeEnvironment() { auto addVar = [&]( const wxString& aKey, const wxString& aDefault ) { m_Env.vars[aKey] = ENV_VAR_ITEM( aKey, aDefault, aDefault ); wxString envValue; if( wxGetEnv( aKey, &envValue ) == true && !envValue.IsEmpty() ) { m_Env.vars[aKey].SetValue( envValue ); m_Env.vars[aKey].SetDefinedExternally(); wxLogTrace( traceEnvVars, wxS( "InitializeEnvironment: Entry %s defined externally as %s" ), aKey, envValue ); } else { wxLogTrace( traceEnvVars, wxS( "InitializeEnvironment: Setting entry %s to default %s" ), aKey, aDefault ); } }; wxFileName basePath( PATHS::GetStockEDALibraryPath(), wxEmptyString ); wxFileName path( basePath ); path.AppendDir( wxT( "footprints" ) ); addVar( wxT( "KICAD7_FOOTPRINT_DIR" ), path.GetFullPath() ); path = basePath; path.AppendDir( wxT( "3dmodels" ) ); addVar( wxT( "KICAD7_3DMODEL_DIR" ), path.GetFullPath() ); addVar( wxT( "KICAD7_TEMPLATE_DIR" ), PATHS::GetStockTemplatesPath() ); addVar( wxT( "KICAD_USER_TEMPLATE_DIR" ), PATHS::GetUserTemplatesPath() ); addVar( wxT( "KICAD7_3RD_PARTY" ), PATHS::GetDefault3rdPartyPath() ); path = basePath; path.AppendDir( wxT( "symbols" ) ); addVar( wxT( "KICAD7_SYMBOL_DIR" ), path.GetFullPath() ); } bool COMMON_SETTINGS::readLegacy3DResolverCfg( const wxString& path, std::vector& aSearchPaths ) { wxFileName cfgpath( path ); // This should be the same as wxWidgets 3.0 wxPATH_NORM_ALL which is deprecated in 3.1. // There are known issues with environment variable expansion so maybe we should be using // our own ExpandEnvVarSubstitutions() here instead. cfgpath.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); wxString cfgname = cfgpath.GetFullPath(); std::ifstream cfgFile; std::string cfgLine; if( !wxFileName::Exists( cfgname ) ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = "no 3D configuration file"; ostr << " * " << errmsg.ToUTF8() << " '"; ostr << cfgname.ToUTF8() << "'"; wxLogTrace( traceSettings, "%s\n", ostr.str().c_str() ); return false; } cfgFile.open( cfgname.ToUTF8() ); if( !cfgFile.is_open() ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "Could not open configuration file" ); ostr << " * " << errmsg.ToUTF8() << " '" << cfgname.ToUTF8() << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } int lineno = 0; LEGACY_3D_SEARCH_PATH al; size_t idx; int vnum = 0; // version number while( cfgFile.good() ) { cfgLine.clear(); std::getline( cfgFile, cfgLine ); ++lineno; if( cfgLine.empty() ) { if( cfgFile.eof() ) break; continue; } if( 1 == lineno && cfgLine.compare( 0, 2, "#V" ) == 0 ) { // extract the version number and parse accordingly if( cfgLine.size() > 2 ) { std::istringstream istr; istr.str( cfgLine.substr( 2 ) ); istr >> vnum; } continue; } idx = 0; if( !getLegacy3DHollerith( cfgLine, idx, al.m_Alias ) ) continue; // Don't add KICAD7_3DMODEL_DIR, one of its legacy equivalents, or KIPRJMOD from a // config file. They're system variables are are defined at runtime. if( al.m_Alias == wxS( "${KICAD7_3DMODEL_DIR}" ) || al.m_Alias == wxS( "${KIPRJMOD}" ) || al.m_Alias == wxS( "$(KIPRJMOD)" ) || al.m_Alias == wxS( "${KISYS3DMOD}" ) || al.m_Alias == wxS( "$(KISYS3DMOD)" ) ) { continue; } if( !getLegacy3DHollerith( cfgLine, idx, al.m_Pathvar ) ) continue; if( !getLegacy3DHollerith( cfgLine, idx, al.m_Description ) ) continue; aSearchPaths.push_back( al ); } cfgFile.close(); return true; } bool COMMON_SETTINGS::getLegacy3DHollerith( const std::string& aString, size_t& aIndex, wxString& aResult ) { aResult.clear(); if( aIndex >= aString.size() ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "bad Hollerith string on line" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } size_t i2 = aString.find( '"', aIndex ); if( std::string::npos == i2 ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "missing opening quote mark in config file" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } ++i2; if( i2 >= aString.size() ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "invalid entry (unexpected end of line)" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } std::string tnum; while( aString[i2] >= '0' && aString[i2] <= '9' ) tnum.append( 1, aString[i2++] ); if( tnum.empty() || aString[i2++] != ':' ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "bad Hollerith string on line" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } std::istringstream istr; istr.str( tnum ); size_t nchars; istr >> nchars; if( ( i2 + nchars ) >= aString.size() ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "invalid entry (unexpected end of line)" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } if( nchars > 0 ) { aResult = wxString::FromUTF8( aString.substr( i2, nchars ).c_str() ); i2 += nchars; } if( i2 >= aString.size() || aString[i2] != '"' ) { std::ostringstream ostr; ostr << __FILE__ << ": " << __FUNCTION__ << ": " << __LINE__ << "\n"; wxString errmsg = wxS( "missing closing quote mark in config file" ); ostr << " * " << errmsg.ToUTF8() << "\n'" << aString << "'"; wxLogTrace( traceSettings, wxS( "%s\n" ), ostr.str().c_str() ); return false; } aIndex = i2 + 1; return true; }