LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-commodity.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 77.0 % 946 728
Test Date: 2026-11-02 18:19:43 Functions: 86.6 % 134 116
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************
       2              :  * gnc-commodity.c -- api for tradable commodities (incl. currency) *
       3              :  * Copyright (C) 2000 Bill Gribble                                  *
       4              :  * Copyright (C) 2001,2003 Linas Vepstas <linas@linas.org>          *
       5              :  * Copyright (c) 2006 David Hampton <hampton@employees.org>         *
       6              :  *                                                                  *
       7              :  * This program is free software; you can redistribute it and/or    *
       8              :  * modify it under the terms of the GNU General Public License as   *
       9              :  * published by the Free Software Foundation; either version 2 of   *
      10              :  * the License, or (at your option) any later version.              *
      11              :  *                                                                  *
      12              :  * This program is distributed in the hope that it will be useful,  *
      13              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      14              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      15              :  * GNU General Public License for more details.                     *
      16              :  *                                                                  *
      17              :  * You should have received a copy of the GNU General Public License*
      18              :  * along with this program; if not, contact:                        *
      19              :  *                                                                  *
      20              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      21              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      22              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      23              :  *                                                                  *
      24              :  *******************************************************************/
      25              : 
      26              : #include <config.h>
      27              : 
      28              : #include <glib.h>
      29              : #include <glib/gi18n.h>
      30              : #include <ctype.h>
      31              : #include <limits.h>
      32              : #include <string.h>
      33              : #include <stdio.h>
      34              : #include <stdlib.h>
      35              : #include <regex.h>
      36              : #include <qofinstance-p.h>
      37              : 
      38              : #include "gnc-commodity.hpp"
      39              : #include "gnc-commodity.h"
      40              : #include "gnc-locale-utils.h"
      41              : #include "gnc-prefs.h"
      42              : #include "guid.h"
      43              : #include "qofinstance.h"
      44              : 
      45              : #include <list>
      46              : #include <unordered_map>
      47              : 
      48              : static QofLogModule log_module = GNC_MOD_COMMODITY;
      49              : 
      50              : /* Parts per unit is nominal, i.e. number of 'partname' units in
      51              :  * a 'unitname' unit.  fraction is transactional, i.e. how many
      52              :  * of the smallest-transactional-units of the currency are there
      53              :  * in a 'unitname' unit. */
      54              : 
      55              : enum
      56              : {
      57              :     PROP_0,
      58              :     PROP_NAMESPACE,     /* Table */
      59              :     PROP_FULL_NAME,     /* Table */
      60              :     PROP_MNEMONIC,      /* Table */
      61              :     PROP_PRINTNAME,     /* Constructed */
      62              :     PROP_CUSIP,         /* Table */
      63              :     PROP_FRACTION,      /* Table */
      64              :     PROP_UNIQUE_NAME,   /* Constructed */
      65              :     PROP_QUOTE_FLAG,    /* Table */
      66              :     PROP_QUOTE_SOURCE,  /* Table */
      67              :     PROP_QUOTE_TZ,      /* Table */
      68              : };
      69              : 
      70              : struct gnc_commodity_s
      71              : {
      72              :     QofInstance inst;
      73              : };
      74              : 
      75              : typedef struct gnc_commodityPrivate
      76              : {
      77              :     gnc_commodity_namespace *name_space;
      78              : 
      79              :     const char *fullname;
      80              :     const char *mnemonic;
      81              :     char       *printname;
      82              :     const char *cusip;                /* CUSIP or other identifying code */
      83              :     int         fraction;
      84              :     char       *unique_name;
      85              :     char       *user_symbol;
      86              : 
      87              :     gboolean    quote_flag;           /* user wants price quotes */
      88              :     gnc_quote_source *quote_source;   /* current/old source of quotes */
      89              :     const char *quote_tz;
      90              : 
      91              :     /* the number of accounts using this commodity - this field is not
      92              :      * persisted */
      93              :     int         usage_count;
      94              : 
      95              :     /* the default display_symbol, set in iso-4217-currencies at start-up */
      96              :     const char *default_symbol;
      97              : } gnc_commodityPrivate;
      98              : 
      99              : #define GET_PRIVATE(o) \
     100              :     ((gnc_commodityPrivate*)gnc_commodity_get_instance_private((gnc_commodity*)o))
     101              : 
     102              : struct _GncCommodityClass
     103              : {
     104              :     QofInstanceClass parent_class;
     105              : };
     106              : 
     107              : static void commodity_free(gnc_commodity * cm);
     108              : static void gnc_commodity_set_default_symbol(gnc_commodity *, const char *);
     109              : 
     110              : struct gnc_commodity_namespace_s
     111              : {
     112              :     QofInstance inst;
     113              : 
     114              :     const gchar *name;
     115              :     gboolean     iso4217;
     116              :     GHashTable * cm_table;
     117              :     GList      * cm_list;
     118              : };
     119              : 
     120              : struct _GncCommodityNamespaceClass
     121              : {
     122              :     QofInstanceClass parent_class;
     123              : };
     124              : 
     125              : struct gnc_commodity_table_s
     126              : {
     127              :     GHashTable * ns_table;
     128              :     GList      * ns_list;
     129              : };
     130              : 
     131              : static const std::unordered_map<std::string,std::string> gnc_new_iso_codes =
     132              : {
     133              :     {"RUR", "RUB"}, /* Russian Ruble: RUR through 1997-12, RUB from 1998-01 onwards; see bug #393185 */
     134              :     {"PLZ", "PLN"}, /* Polish Zloty */
     135              :     {"UAG", "UAH"}, /* Ukraine Hryvnia */
     136              :     {"NIS", "ILS"}, /* New Israeli Shekel: The informal abbreviation may be "NIS", but
     137              :                      its iso-4217 is clearly ILS and only this! Incorrectly changed
     138              :                      due to bug#152755 (Nov 2004) and changed back again by bug#492417
     139              :                      (Oct 2008). */
     140              :     {"MXP", "MXN"}, /* Mexican (Nuevo) Peso */
     141              :     {"TRL", "TRY"}, /* New Turkish Lira: changed 2005 */
     142              : 
     143              :     /* Only add currencies to this table when the old currency no longer
     144              :      * exists in the file iso-4217-currencies.xml */
     145              : };
     146              : 
     147              : static std::string fq_version;
     148              : 
     149              : struct gnc_quote_source_s
     150              : {
     151              : private:
     152              :     gboolean m_supported;
     153              :     QuoteSourceType m_type;
     154              :     std::string m_user_name;            /* User friendly name incl. region code*/
     155              :     std::string m_internal_name;        /* Name used internally and by finance::quote. */
     156              : public:
     157           23 :     bool get_supported () const { return m_supported; }
     158           15 :     void set_supported (bool supported) { m_supported = supported; }
     159            0 :     QuoteSourceType get_type () const { return m_type; }
     160            2 :     const char* get_user_name () const { return m_user_name.c_str(); }
     161       144072 :     const char* get_internal_name () const { return m_internal_name.c_str(); }
     162         6210 :     gnc_quote_source_s (gboolean supported, QuoteSourceType type,
     163              :                         const char* username, const char* int_name)
     164         6210 :         : m_supported{supported}
     165         6210 :         , m_type{type}
     166        12420 :         , m_user_name{username ? username : ""}
     167        18630 :         , m_internal_name{int_name ? int_name: ""} { };
     168              : };
     169              : 
     170              : using QuoteSourceList = std::list<gnc_quote_source>;
     171              : 
     172              : /* To update the following lists scan
     173              :  * from github.com/finance-quote/finance-quote
     174              :  * in lib/Finance/Quote/ all *.pm for "methods"
     175              :  * because many of them have more than one -
     176              :  * ideally after each release of them.
     177              :  *
     178              :  * Apply changes here also to the FQ appendix of help.
     179              :  */
     180              : static QuoteSourceList currency_quote_sources =
     181              : {
     182              :     { true, SOURCE_CURRENCY, "Currency", "currency" }
     183              : };
     184              : 
     185              : /* The single quote method is usually the module name, but
     186              :  * sometimes it gets the suffix "_direct"
     187              :  * and the failover method is without suffix.
     188              :  */
     189              : static QuoteSourceList single_quote_sources =
     190              : {
     191              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Alphavantage"), "alphavantage" },
     192              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Association of Mutual Funds in India"), "amfiindia" },
     193              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Athens Exchange Group, GR"), "asegr" },
     194              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Australian Stock Exchange, AU"), "asx" },
     195              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Italian Stock Exchange, IT"), "borsa_italiana" },
     196              :     { false, SOURCE_SINGLE, NC_("FQ Source", "BSE India, IN"), "bseindia" },
     197              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Bucharest Stock Exchange, RO"), "bvb" },
     198              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Colombo Stock Exchange, LK"), "cse" },
     199              :     { false, SOURCE_SINGLE, NC_("FQ Source", "comdirect, DE"), "comdirect" },
     200              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Consors Bank, DE"), "consorsbank" },
     201              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Deka Investments, DE"), "deka" },
     202              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Financial Times Funds service, GB"), "ftfunds" },
     203              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Finanzpartner, DE"), "finanzpartner" },
     204              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Finnhub, US"), "finnhub" },
     205              :     { false, SOURCE_SINGLE, NC_("FQ Source", "FondsWeb, DE"), "fondsweb" },
     206              :     { false, SOURCE_SINGLE, NC_("FQ Source", "GoldMoney precious metals"), "goldmoney" },
     207              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Google Web, US Stocks"), "googleweb" },
     208              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Market Watch"), "marketwatch" },
     209              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, JP"), "morningstarjp" },
     210              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Motley Fool"), "fool" },
     211              :     { false, SOURCE_SINGLE, NC_("FQ Source", "National Pension Scheme, IN"), "npsnav" },
     212              :     { false, SOURCE_SINGLE, NC_("FQ Source", "New Zealand stock eXchange, NZ"), "nzx" },
     213              :     { false, SOURCE_SINGLE, NC_("FQ Source", "NSE (National Stock Exchange), IN"), "nseindia" },
     214              :     { false, SOURCE_SINGLE, NC_("FQ Source", "OnVista, DE"), "onvista"},
     215              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Paris Stock Exchange/Boursorama, FR"), "bourso" },
     216              :     { false, SOURCE_SINGLE, NC_("FQ Source", "S-Investor, DE"), "sinvestor"},
     217              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Sharenet, ZA"), "za" },
     218              :     { false, SOURCE_SINGLE, NC_("FQ Source", "SIX Swiss Exchange shares, CH"), "six" },
     219              :     { false, SOURCE_SINGLE, NC_("FQ Source", "StockData"), "stockdata" },
     220              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Stooq, PL"), "stooq" },
     221              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Swiss Fund Data AG, CH"), "swissfunddata" },
     222              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Tesouro Direto bonds, BR"), "tesouro_direto" },
     223              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Toronto Stock eXchange, CA"), "tsx" },
     224              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Tradegate, DE"), "tradegate" },
     225              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Treasury Direct bonds, US"), "treasurydirect" },
     226              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Twelve Data"), "twelvedata" },
     227              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Union Investment, DE"), "unionfunds" },
     228              :     { false, SOURCE_SINGLE, NC_("FQ Source", "US Savings Bonds, US"), "usbonds" },
     229              :     { false, SOURCE_SINGLE, NC_("FQ Source", "US Govt. Thrift Savings Plan"), "tsp" },
     230              :     { false, SOURCE_SINGLE, NC_("FQ Source", "XETRA, DE"), "xetra" },
     231              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo as JSON"), "yahoo_json" },
     232              :     { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo Web"), "yahooweb" },
     233              :     { false, SOURCE_SINGLE, NC_("FQ Source", "YH Finance (FinanceAPI)"), "financeapi" },
     234              : };
     235              : 
     236              : // Finance::Quote defines these as failover methods
     237              : static QuoteSourceList multiple_quote_sources =
     238              : {
     239              :     { false, SOURCE_MULTI, NC_("FQ Source", "Canada (Alphavantage, TMX)"), "canada" },
     240              :     { false, SOURCE_MULTI, NC_("FQ Source", "Europe (ASEGR, Bourso, …)"), "europe" },
     241              :     { false, SOURCE_MULTI, NC_("FQ Source", "Germany (Comdirect, Consorsbank, …)"), "germany" },
     242              :     { false, SOURCE_MULTI, NC_("FQ Source", "India (BSEIndia, NSEIndia)"), "india"},
     243              :     { false, SOURCE_MULTI, NC_("FQ Source", "Nasdaq (Alphavantage, FinanceAPI, …)"), "nasdaq" },
     244              :     { false, SOURCE_MULTI, NC_("FQ Source", "NYSE (Alphavantage, FinanceAPI, …)"), "nyse" },
     245              :     { false, SOURCE_MULTI, NC_("FQ Source", "U.K. Funds (FTfunds)"), "ukfunds" },
     246              :     { false, SOURCE_MULTI, NC_("FQ Source", "USA (Alphavantage, FinanceAPI, …)"), "usa" },
     247              : };
     248              : 
     249              : static QuoteSourceList new_quote_sources;
     250              : 
     251              : // cannot use map or unordered_map because order must be preserved
     252              : static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
     253              :     {
     254              :         { SOURCE_CURRENCY, currency_quote_sources },
     255              :         { SOURCE_SINGLE, single_quote_sources },
     256              :         { SOURCE_MULTI, multiple_quote_sources },
     257              :         { SOURCE_UNKNOWN, new_quote_sources }
     258              :     };
     259              : 
     260              : /********************************************************************
     261              :  * gnc_quote_source_fq_installed
     262              :  *
     263              :  * This function indicates whether or not the Finance::Quote module
     264              :  * is installed on a users computer.
     265              :  ********************************************************************/
     266              : gboolean
     267            0 : gnc_quote_source_fq_installed (void)
     268              : {
     269            0 :     return (!fq_version.empty());
     270              : }
     271              : 
     272              : 
     273              : /********************************************************************
     274              :  * gnc_quote_source_fq_version
     275              :  *
     276              :  * This function the version of the Finance::Quote module installed
     277              :  * on a user's computer or nullptr if no installation is found.
     278              :  ********************************************************************/
     279              : const char*
     280            0 : gnc_quote_source_fq_version (void)
     281              : {
     282            0 :     return fq_version.c_str();
     283              : }
     284              : 
     285              : static QuoteSourceList&
     286           11 : get_quote_source_from_type (QuoteSourceType type)
     287              : {
     288           11 :     auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
     289           38 :                                           [type] (const auto& qs) { return type == qs.first; });
     290              : 
     291           22 :     if (quote_sources_it != quote_sources_map.end())
     292            7 :         return quote_sources_it->second;
     293              : 
     294            4 :     PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
     295            4 :     return new_quote_sources;
     296              : }
     297              : 
     298              : /********************************************************************
     299              :  * gnc_quote_source_num_entries
     300              :  *
     301              :  * Return the number of entries for a given type of price source.
     302              :  ********************************************************************/
     303            5 : gint gnc_quote_source_num_entries(QuoteSourceType type)
     304              : {
     305            5 :     return get_quote_source_from_type(type).size();
     306              : }
     307              : 
     308              : 
     309              : 
     310              : /********************************************************************
     311              :  * gnc_quote_source_add_new
     312              :  *
     313              :  * Add a new price source. Called when unknown source names are found
     314              :  * either in the F::Q installation (a newly available source) or in
     315              :  * the user's data file (a source that has vanished but needs to be
     316              :  * tracked.)
     317              :  ********************************************************************/
     318              : gnc_quote_source *
     319           22 : gnc_quote_source_add_new (const char *source_name, gboolean supported)
     320              : {
     321           22 :     DEBUG("Creating new source %s", (!source_name ? "(null)" : source_name));
     322              :     /* This name can be changed if/when support for this price source is
     323              :      * integrated into gnucash. */
     324              :     /* This name is permanent and must be kept the same if/when support
     325              :      * for this price source is integrated into gnucash (i.e. for a
     326              :      * nice user name). */
     327           22 :     return &new_quote_sources.emplace_back (supported, SOURCE_UNKNOWN, source_name, source_name);
     328              : }
     329              : 
     330              : /********************************************************************
     331              :  * gnc_quote_source_lookup_by_xxx
     332              :  *
     333              :  * Lookup a price source data structure based upon various criteria.
     334              :  ********************************************************************/
     335              : gnc_quote_source *
     336            6 : gnc_quote_source_lookup_by_ti (QuoteSourceType type, gint index)
     337              : {
     338            6 :     ENTER("type/index is %d/%d", type, index);
     339            6 :     auto& sources = get_quote_source_from_type (type);
     340            6 :     if ((size_t) index < sources.size())
     341              :     {
     342            4 :         auto it = std::next(sources.begin(), index);
     343            4 :         LEAVE("found %s", it->get_user_name());
     344            4 :         return &*it;
     345              :     }
     346              : 
     347            2 :     LEAVE("not found");
     348            2 :     return nullptr;
     349              : }
     350              : 
     351              : gnc_quote_source *
     352       143409 : gnc_quote_source_lookup_by_internal(const char * name)
     353              : {
     354       143409 :     if (!name || !*name)
     355            1 :         return nullptr;
     356              : 
     357       430299 :     for (const auto& [_, sources] : quote_sources_map)
     358              :     {
     359       143445 :         auto source_it = std::find_if (sources.begin(), sources.end(),
     360       143534 :                                        [name] (const auto& qs)
     361       143534 :                                        { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
     362       143445 :         if (source_it != sources.end())
     363       143407 :             return &(*source_it);
     364              :     }
     365              : 
     366            1 :     DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name);
     367            1 :     return nullptr;
     368              : }
     369              : 
     370              : /********************************************************************
     371              :  * gnc_quote_source_get_xxx
     372              :  *
     373              :  * Accessor functions - get functions only. There are no set functions.
     374              :  ********************************************************************/
     375              : QuoteSourceType
     376            0 : gnc_quote_source_get_type (const gnc_quote_source *source)
     377              : {
     378            0 :     ENTER("%p", source);
     379            0 :     if (!source)
     380              :     {
     381            0 :         LEAVE("bad source");
     382            0 :         return SOURCE_SINGLE;
     383              :     }
     384              : 
     385            0 :     LEAVE("type is %d", source->get_type());
     386            0 :     return source->get_type();
     387              : }
     388              : 
     389              : gint
     390            0 : gnc_quote_source_get_index (const gnc_quote_source *source)
     391              : {
     392            0 :     if (!source)
     393              :     {
     394            0 :         PWARN ("bad source");
     395            0 :         return 0;
     396              :     }
     397              : 
     398            0 :     auto& sources = get_quote_source_from_type (source->get_type());
     399            0 :     auto is_source = [&source](const auto& findif_source)
     400            0 :     { return &findif_source == source; };
     401              : 
     402            0 :     auto iter = std::find_if (sources.begin(), sources.end(), is_source);
     403            0 :     if (iter != sources.end())
     404            0 :         return std::distance (sources.begin(), iter);
     405              : 
     406            0 :     PWARN ("couldn't locate source");
     407            0 :     return 0;
     408              : }
     409              : 
     410              : gboolean
     411           23 : gnc_quote_source_get_supported (const gnc_quote_source *source)
     412              : {
     413           23 :     ENTER("%p", source);
     414           23 :     if (!source)
     415              :     {
     416            0 :         LEAVE("bad source");
     417            0 :         return FALSE;
     418              :     }
     419              : 
     420           23 :     LEAVE("%s supported", source && source->get_supported() ? "" : "not ");
     421           23 :     return source->get_supported();
     422              : }
     423              : 
     424              : const char *
     425            2 : gnc_quote_source_get_user_name (const gnc_quote_source *source)
     426              : {
     427            2 :     ENTER("%p", source);
     428            2 :     if (!source)
     429              :     {
     430            0 :         LEAVE("bad source");
     431            0 :         return nullptr;
     432              :     }
     433            2 :     LEAVE("user name %s", source->get_user_name());
     434            2 :     return source->get_user_name();
     435              : }
     436              : 
     437              : const char *
     438           89 : gnc_quote_source_get_internal_name (const gnc_quote_source *source)
     439              : {
     440           89 :     ENTER("%p", source);
     441           89 :     if (!source)
     442              :     {
     443            6 :         LEAVE("bad source");
     444            6 :         return nullptr;
     445              :     }
     446           83 :     LEAVE("internal name %s", source->get_internal_name());
     447           83 :     return source->get_internal_name();
     448              : }
     449              : 
     450              : 
     451              : /********************************************************************
     452              :  * gnc_quote_source_set_fq_installed
     453              :  *
     454              :  * Update gnucash internal tables on what Finance::Quote sources are
     455              :  * installed.
     456              :  ********************************************************************/
     457              : void
     458           15 : gnc_quote_source_set_fq_installed (const char* version_string,
     459              :                                    const std::vector<std::string>& sources_list)
     460              : {
     461           15 :     ENTER(" ");
     462              : 
     463           15 :     if (sources_list.empty())
     464            0 :         return;
     465              : 
     466           15 :     if (version_string)
     467           15 :         fq_version = version_string;
     468              :     else
     469            0 :         fq_version.clear();
     470              : 
     471           45 :     for (const auto& source_name_str : sources_list)
     472              :     {
     473           15 :         auto source_name = source_name_str.c_str();
     474           15 :         auto source = gnc_quote_source_lookup_by_internal(source_name);
     475              : 
     476           15 :         if (source)
     477              :         {
     478           15 :             DEBUG("Found source %s: %s", source_name, source->get_user_name());
     479           15 :             source->set_supported (true);
     480           15 :             continue;
     481              :         }
     482              : 
     483            0 :         gnc_quote_source_add_new(source_name, TRUE);
     484              :     }
     485           15 :     LEAVE(" ");
     486              : }
     487              : 
     488              : /********************************************************************
     489              :  * QoF Helpers
     490              :  ********************************************************************/
     491              : 
     492              : void
     493       561404 : gnc_commodity_begin_edit (gnc_commodity *cm)
     494              : {
     495       561404 :     qof_begin_edit(&cm->inst);
     496       561404 : }
     497              : 
     498            0 : static void commit_err (QofInstance *inst, QofBackendError errcode)
     499              : {
     500            0 :     PERR ("Failed to commit: %d", errcode);
     501            0 :     gnc_engine_signal_commit_error( errcode );
     502            0 : }
     503              : 
     504        75201 : static void noop (QofInstance *inst) {}
     505              : 
     506              : static void
     507        44057 : comm_free(QofInstance* inst)
     508              : {
     509        44057 :     commodity_free( GNC_COMMODITY(inst) );
     510        44057 : }
     511              : 
     512              : void
     513       561404 : gnc_commodity_commit_edit (gnc_commodity *cm)
     514              : {
     515       561404 :     if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
     516       119258 :     qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
     517              : }
     518              : 
     519              : /********************************************************************
     520              :  * gnc_commodity_new
     521              :  ********************************************************************/
     522              : 
     523              : static void
     524       517112 : mark_commodity_dirty (gnc_commodity *cm)
     525              : {
     526       517112 :     qof_instance_set_dirty(&cm->inst);
     527       517112 :     qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
     528       517112 : }
     529              : 
     530              : static void
     531       296181 : reset_printname(gnc_commodityPrivate *priv)
     532              : {
     533       296181 :     g_free(priv->printname);
     534       592362 :     priv->printname = g_strdup_printf("%s (%s)",
     535       296181 :                                       priv->mnemonic ? priv->mnemonic : "",
     536       296181 :                                       priv->fullname ? priv->fullname : "");
     537       296181 : }
     538              : 
     539              : static void
     540       221984 : reset_unique_name(gnc_commodityPrivate *priv)
     541              : {
     542              :     gnc_commodity_namespace *ns;
     543              : 
     544       221984 :     g_free(priv->unique_name);
     545       221984 :     ns = priv->name_space;
     546       221984 :     priv->unique_name = g_strdup_printf("%s::%s",
     547              :                                         ns ? ns->name : "",
     548       221984 :                                         priv->mnemonic ? priv->mnemonic : "");
     549       221984 : }
     550              : 
     551              : /* GObject Initialization */
     552      1229020 : G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
     553              : 
     554              : static void
     555        74064 : gnc_commodity_init(gnc_commodity* com)
     556              : {
     557              :     gnc_commodityPrivate* priv;
     558              : 
     559        74064 :     priv = GET_PRIVATE(com);
     560              : 
     561        74064 :     priv->name_space = nullptr;
     562        74064 :     priv->fullname = CACHE_INSERT("");
     563        74064 :     priv->mnemonic = CACHE_INSERT("");
     564        74064 :     priv->cusip = CACHE_INSERT("");
     565        74064 :     priv->fraction = 10000;
     566        74064 :     priv->quote_flag = 0;
     567        74064 :     priv->quote_source = nullptr;
     568        74064 :     priv->quote_tz = CACHE_INSERT("");
     569              : 
     570        74064 :     reset_printname(priv);
     571        74064 :     reset_unique_name(priv);
     572        74064 : }
     573              : 
     574              : static void
     575        44215 : gnc_commodity_dispose(GObject *comp)
     576              : {
     577        44215 :     G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
     578        44215 : }
     579              : 
     580              : static void
     581        44215 : gnc_commodity_finalize(GObject* comp)
     582              : {
     583        44215 :     G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
     584        44215 : }
     585              : /* Note that g_value_set_object() refs the object, as does
     586              :  * g_object_get(). But g_object_get() only unrefs once when it disgorges
     587              :  * the object, leaving an unbalanced ref, which leaks. So instead of
     588              :  * using g_value_set_object(), use g_value_take_object() which doesn't
     589              :  * ref the object when used in get_property().
     590              :  */
     591              : static void
     592          354 : gnc_commodity_get_property (GObject         *object,
     593              :                             guint            prop_id,
     594              :                             GValue          *value,
     595              :                             GParamSpec      *pspec)
     596              : {
     597              :     gnc_commodity *commodity;
     598              :     gnc_commodityPrivate* priv;
     599              : 
     600          354 :     g_return_if_fail(GNC_IS_COMMODITY(object));
     601              : 
     602          354 :     commodity = GNC_COMMODITY(object);
     603          354 :     priv = GET_PRIVATE(commodity);
     604          354 :     switch (prop_id)
     605              :     {
     606            0 :     case PROP_NAMESPACE:
     607            0 :         g_value_take_object(value, priv->name_space);
     608            0 :         break;
     609           59 :     case PROP_FULL_NAME:
     610           59 :         g_value_set_string(value, priv->fullname);
     611           59 :         break;
     612           59 :     case PROP_MNEMONIC:
     613           59 :         g_value_set_string(value, priv->mnemonic);
     614           59 :         break;
     615            0 :     case PROP_PRINTNAME:
     616            0 :         g_value_set_string(value, priv->printname);
     617            0 :         break;
     618           59 :     case PROP_CUSIP:
     619           59 :         g_value_set_string(value, priv->cusip);
     620           59 :         break;
     621           59 :     case PROP_FRACTION:
     622           59 :         g_value_set_int(value, priv->fraction);
     623           59 :         break;
     624            0 :     case PROP_UNIQUE_NAME:
     625            0 :         g_value_set_string(value, priv->unique_name);
     626            0 :         break;
     627           59 :     case PROP_QUOTE_FLAG:
     628           59 :         g_value_set_boolean(value, priv->quote_flag);
     629           59 :         break;
     630            0 :     case PROP_QUOTE_SOURCE:
     631            0 :         g_value_set_pointer(value, priv->quote_source);
     632            0 :         break;
     633           59 :     case PROP_QUOTE_TZ:
     634           59 :         g_value_set_string(value, priv->quote_tz);
     635           59 :         break;
     636            0 :     default:
     637            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     638            0 :         break;
     639              :     }
     640              : }
     641              : 
     642              : static void
     643           60 : gnc_commodity_set_property (GObject         *object,
     644              :                             guint            prop_id,
     645              :                             const GValue    *value,
     646              :                             GParamSpec      *pspec)
     647              : {
     648              :     gnc_commodity *commodity;
     649              : 
     650           60 :     g_return_if_fail(GNC_IS_COMMODITY(object));
     651              : 
     652           60 :     commodity = GNC_COMMODITY(object);
     653           60 :     g_assert (qof_instance_get_editlevel(commodity));
     654              : 
     655           60 :     switch (prop_id)
     656              :     {
     657            0 :     case PROP_NAMESPACE:
     658            0 :         gnc_commodity_set_namespace(commodity, static_cast<const char*>(g_value_get_object(value)));
     659            0 :         break;
     660           10 :     case PROP_FULL_NAME:
     661           10 :         gnc_commodity_set_fullname(commodity, g_value_get_string(value));
     662           10 :         break;
     663           10 :     case PROP_MNEMONIC:
     664           10 :         gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
     665           10 :         break;
     666           10 :     case PROP_CUSIP:
     667           10 :         gnc_commodity_set_cusip(commodity, g_value_get_string(value));
     668           10 :         break;
     669           10 :     case PROP_FRACTION:
     670           10 :         gnc_commodity_set_fraction(commodity, g_value_get_int(value));
     671           10 :         break;
     672           10 :     case PROP_QUOTE_FLAG:
     673           10 :         gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
     674           10 :         break;
     675            0 :     case PROP_QUOTE_SOURCE:
     676            0 :         gnc_commodity_set_quote_source(commodity, static_cast<gnc_quote_source*>(g_value_get_pointer(value)));
     677            0 :         break;
     678           10 :     case PROP_QUOTE_TZ:
     679           10 :         gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
     680           10 :         break;
     681            0 :     default:
     682            0 :         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
     683            0 :         break;
     684              :     }
     685              : }
     686              : static void
     687           65 : gnc_commodity_class_init(struct _GncCommodityClass* klass)
     688              : {
     689           65 :     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     690              : 
     691           65 :     gobject_class->dispose = gnc_commodity_dispose;
     692           65 :     gobject_class->finalize = gnc_commodity_finalize;
     693           65 :     gobject_class->set_property = gnc_commodity_set_property;
     694           65 :     gobject_class->get_property = gnc_commodity_get_property;
     695              : 
     696           65 :     g_object_class_install_property(gobject_class,
     697              :                                     PROP_NAMESPACE,
     698              :                                     g_param_spec_object ("namespace",
     699              :                                             "Namespace",
     700              :                                             "The namespace field denotes the "
     701              :                                             "namespace for this commodity, either "
     702              :                                             "a currency or symbol from a quote source.",
     703              :                                             GNC_TYPE_COMMODITY_NAMESPACE,
     704              :                                             G_PARAM_READWRITE));
     705           65 :     g_object_class_install_property(gobject_class,
     706              :                                     PROP_FULL_NAME,
     707              :                                     g_param_spec_string ("fullname",
     708              :                                             "Full Commodity Name",
     709              :                                             "The fullname is the official full name of"
     710              :                                             "the currency.",
     711              :                                             nullptr,
     712              :                                             G_PARAM_READWRITE));
     713           65 :     g_object_class_install_property(gobject_class,
     714              :                                     PROP_MNEMONIC,
     715              :                                     g_param_spec_string ("mnemonic",
     716              :                                             "Commodity Mnemonic",
     717              :                                             "The mnemonic is the official abbreviated"
     718              :                                             "designation for the currency.",
     719              :                                             nullptr,
     720              :                                             G_PARAM_READWRITE));
     721           65 :     g_object_class_install_property(gobject_class,
     722              :                                     PROP_PRINTNAME,
     723              :                                     g_param_spec_string ("printname",
     724              :                                             "Commodity Print Name",
     725              :                                             "Printable form of the commodity name.",
     726              :                                             nullptr,
     727              :                                             G_PARAM_READABLE));
     728           65 :     g_object_class_install_property(gobject_class,
     729              :                                     PROP_CUSIP,
     730              :                                     g_param_spec_string ("cusip",
     731              :                                             "Commodity CUSIP Code",
     732              :                                             "?????",
     733              :                                             nullptr,
     734              :                                             G_PARAM_READWRITE));
     735           65 :     g_object_class_install_property(gobject_class,
     736              :                                     PROP_FRACTION,
     737              :                                     g_param_spec_int ("fraction",
     738              :                                             "Fraction",
     739              :                                             "The fraction is the number of sub-units that "
     740              :                                             "the basic commodity can be divided into.",
     741              :                                             1,
     742              :                                             GNC_COMMODITY_MAX_FRACTION,
     743              :                                             1,
     744              :                                             G_PARAM_READWRITE));
     745           65 :     g_object_class_install_property(gobject_class,
     746              :                                     PROP_UNIQUE_NAME,
     747              :                                     g_param_spec_string ("unique-name",
     748              :                                             "Commodity Unique Name",
     749              :                                             "Unique form of the commodity name which combines "
     750              :                                             "the namespace name and the commodity name.",
     751              :                                             nullptr,
     752              :                                             G_PARAM_READABLE));
     753           65 :     g_object_class_install_property(gobject_class,
     754              :                                     PROP_QUOTE_FLAG,
     755              :                                     g_param_spec_boolean ("quote_flag",
     756              :                                             "Quote Flag",
     757              :                                             "TRUE if prices are to be downloaded for this "
     758              :                                             "commodity from a quote source.",
     759              :                                             FALSE,
     760              :                                             G_PARAM_READWRITE));
     761           65 :     g_object_class_install_property(gobject_class,
     762              :                                     PROP_QUOTE_SOURCE,
     763              :                                     g_param_spec_pointer("quote-source",
     764              :                                             "Quote Source",
     765              :                                             "The quote source from which prices are downloaded.",
     766              :                                             G_PARAM_READWRITE));
     767           65 :     g_object_class_install_property(gobject_class,
     768              :                                     PROP_QUOTE_TZ,
     769              :                                     g_param_spec_string ("quote-tz",
     770              :                                             "Commodity Quote Timezone",
     771              :                                             "?????",
     772              :                                             nullptr,
     773              :                                             G_PARAM_READWRITE));
     774           65 : }
     775              : 
     776              : gnc_commodity *
     777        74064 : gnc_commodity_new(QofBook *book, const char * fullname,
     778              :                   const char * name_space, const char * mnemonic,
     779              :                   const char * cusip, int fraction)
     780              : {
     781        74064 :     auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
     782              : 
     783        74064 :     qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
     784        74064 :     gnc_commodity_begin_edit(retval);
     785              : 
     786        74064 :     if ( name_space != nullptr )
     787              :     {
     788              :         /* Prevent setting anything except template in namespace template. */
     789        74248 :         if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
     790          318 :             g_strcmp0 (mnemonic, "template") != 0)
     791              :         {
     792            0 :             PWARN("Converting commodity %s from namespace template to "
     793              :                   "namespace User", mnemonic);
     794            0 :             name_space = "User";
     795              :         }
     796        73930 :         gnc_commodity_set_namespace(retval, name_space);
     797        73930 :         if (gnc_commodity_namespace_is_iso(name_space))
     798              :         {
     799        71770 :             gnc_commodity_set_quote_source(retval,
     800              :                                            gnc_quote_source_lookup_by_internal("currency") );
     801              :         }
     802              :     }
     803        74064 :     gnc_commodity_set_fullname(retval, fullname);
     804        74064 :     gnc_commodity_set_mnemonic(retval, mnemonic);
     805        74064 :     gnc_commodity_set_cusip(retval, cusip);
     806        74064 :     gnc_commodity_set_fraction(retval, fraction);
     807        74064 :     mark_commodity_dirty (retval);
     808        74064 :     gnc_commodity_commit_edit(retval);
     809              : 
     810        74064 :     qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
     811              : 
     812        74064 :     return retval;
     813              : }
     814              : 
     815              : 
     816              : /********************************************************************
     817              :  * gnc_commodity_destroy
     818              :  ********************************************************************/
     819              : 
     820              : static void
     821        44057 : commodity_free(gnc_commodity * cm)
     822              : {
     823              :     QofBook *book;
     824              :     gnc_commodity_table *table;
     825              :     gnc_commodityPrivate* priv;
     826              : 
     827        44057 :     if (!cm) return;
     828              : 
     829        44057 :     book = qof_instance_get_book(&cm->inst);
     830        44057 :     table = gnc_commodity_table_get_table(book);
     831        44057 :     gnc_commodity_table_remove(table, cm);
     832        44057 :     priv = GET_PRIVATE(cm);
     833              : 
     834        44057 :     qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
     835              : 
     836              :     /* Set at creation */
     837        44057 :     CACHE_REMOVE (priv->fullname);
     838        44057 :     CACHE_REMOVE (priv->cusip);
     839        44057 :     CACHE_REMOVE (priv->mnemonic);
     840        44057 :     CACHE_REMOVE (priv->quote_tz);
     841        44057 :     priv->name_space = nullptr;
     842              : 
     843              :     /* Set through accessor functions */
     844        44057 :     priv->quote_source = nullptr;
     845              : 
     846              :     /* Automatically generated */
     847        44057 :     g_free(priv->printname);
     848        44057 :     priv->printname = nullptr;
     849              : 
     850        44057 :     g_free(priv->unique_name);
     851        44057 :     priv->unique_name = nullptr;
     852              : 
     853              : #ifdef ACCOUNTS_CLEANED_UP
     854              :     /* Account objects are not actually cleaned up when a book is closed (in fact
     855              :      * a memory leak), but commodities are, so in currently this warning gets hit
     856              :      * quite frequently.  Disable the check until cleaning up of accounts objects
     857              :      * on close is implemented.  */
     858              :     if (priv->usage_count != 0)
     859              :     {
     860              :         PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm,
     861              :               priv->usage_count);
     862              :     }
     863              : #endif
     864              : 
     865              :     /* qof_instance_release (&cm->inst); */
     866        44057 :     g_object_unref(cm);
     867              : }
     868              : 
     869              : void
     870        44057 : gnc_commodity_destroy(gnc_commodity * cm)
     871              : {
     872        44057 :     gnc_commodity_begin_edit(cm);
     873        44057 :     qof_instance_set_destroying(cm, TRUE);
     874        44057 :     gnc_commodity_commit_edit(cm);
     875        44057 : }
     876              : 
     877              : void
     878           66 : gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
     879              : {
     880           66 :     gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
     881           66 :     gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
     882              : 
     883           66 :     gnc_commodity_set_fullname (dest, src_priv->fullname);
     884           66 :     gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
     885           66 :     dest_priv->name_space = src_priv->name_space;
     886           66 :     gnc_commodity_set_fraction (dest, src_priv->fraction);
     887           66 :     gnc_commodity_set_cusip (dest, src_priv->cusip);
     888           66 :     gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
     889           66 :     gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src));
     890           66 :     gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
     891           66 :     qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
     892           66 : }
     893              : 
     894              : gnc_commodity *
     895            0 : gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
     896              : {
     897              :     gnc_commodityPrivate* src_priv;
     898              :     gnc_commodityPrivate* dest_priv;
     899              : 
     900            0 :     auto dest = GNC_COMMODITY (g_object_new(GNC_TYPE_COMMODITY, nullptr));
     901            0 :     qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book);
     902            0 :     src_priv = GET_PRIVATE(src);
     903            0 :     dest_priv = GET_PRIVATE(dest);
     904              : 
     905            0 :     dest_priv->fullname = CACHE_INSERT(src_priv->fullname);
     906            0 :     dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic);
     907            0 :     dest_priv->cusip = CACHE_INSERT(src_priv->cusip);
     908            0 :     dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz);
     909              : 
     910            0 :     dest_priv->name_space = src_priv->name_space;
     911              : 
     912            0 :     dest_priv->fraction = src_priv->fraction;
     913            0 :     dest_priv->quote_flag = src_priv->quote_flag;
     914              : 
     915            0 :     gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src));
     916              : 
     917            0 :     qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
     918              : 
     919            0 :     reset_printname(dest_priv);
     920            0 :     reset_unique_name(dest_priv);
     921              : 
     922            0 :     return dest;
     923              : }
     924              : 
     925              : /********************************************************************
     926              :  * gnc_commodity_get_mnemonic
     927              :  ********************************************************************/
     928              : 
     929              : const char *
     930        35054 : gnc_commodity_get_mnemonic(const gnc_commodity * cm)
     931              : {
     932        35054 :     if (!cm) return nullptr;
     933        35054 :     return GET_PRIVATE(cm)->mnemonic;
     934              : }
     935              : 
     936              : /********************************************************************
     937              :  * gnc_commodity_get_printname
     938              :  ********************************************************************/
     939              : 
     940              : const char *
     941         1465 : gnc_commodity_get_printname(const gnc_commodity * cm)
     942              : {
     943         1465 :     if (!cm) return nullptr;
     944         1465 :     return GET_PRIVATE(cm)->printname;
     945              : }
     946              : 
     947              : 
     948              : /********************************************************************
     949              :  * gnc_commodity_get_namespace
     950              :  ********************************************************************/
     951              : 
     952              : const char *
     953         9134 : gnc_commodity_get_namespace(const gnc_commodity * cm)
     954              : {
     955         9134 :     if (!cm) return nullptr;
     956         9134 :     return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
     957              : }
     958              : 
     959              : gnc_commodity_namespace *
     960            1 : gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
     961              : {
     962            1 :     if (!cm) return nullptr;
     963            1 :     return GET_PRIVATE(cm)->name_space;
     964              : }
     965              : 
     966              : /********************************************************************
     967              :  * gnc_commodity_get_fullname
     968              :  ********************************************************************/
     969              : 
     970              : const char *
     971           70 : gnc_commodity_get_fullname(const gnc_commodity * cm)
     972              : {
     973           70 :     if (!cm) return nullptr;
     974           70 :     return GET_PRIVATE(cm)->fullname;
     975              : }
     976              : 
     977              : 
     978              : /********************************************************************
     979              :  * gnc_commodity_get_unique_name
     980              :  ********************************************************************/
     981              : 
     982              : const char *
     983         3449 : gnc_commodity_get_unique_name(const gnc_commodity * cm)
     984              : {
     985         3449 :     if (!cm) return nullptr;
     986         3443 :     return GET_PRIVATE(cm)->unique_name;
     987              : }
     988              : 
     989              : 
     990              : /********************************************************************
     991              :  * gnc_commodity_get_cusip
     992              :  ********************************************************************/
     993              : 
     994              : const char *
     995           46 : gnc_commodity_get_cusip(const gnc_commodity * cm)
     996              : {
     997           46 :     if (!cm) return nullptr;
     998           46 :     return GET_PRIVATE(cm)->cusip;
     999              : }
    1000              : 
    1001              : /********************************************************************
    1002              :  * gnc_commodity_get_fraction
    1003              :  ********************************************************************/
    1004              : 
    1005              : int
    1006        65931 : gnc_commodity_get_fraction(const gnc_commodity * cm)
    1007              : {
    1008        65931 :     if (!cm) return 0;
    1009        65784 :     return GET_PRIVATE(cm)->fraction;
    1010              : }
    1011              : 
    1012              : /********************************************************************
    1013              :  * gnc_commodity_get_auto_quote_control_flag
    1014              :  ********************************************************************/
    1015              : 
    1016              : gboolean
    1017          925 : gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
    1018              : {
    1019          925 :     if (!cm) return FALSE;
    1020         1850 :     auto str{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE (cm), {"auto_quote_control"})};
    1021          925 :     return !str || g_strcmp0 (*str, "false");
    1022              : }
    1023              : 
    1024              : /********************************************************************
    1025              :  * gnc_commodity_get_quote_flag
    1026              :  ********************************************************************/
    1027              : 
    1028              : gboolean
    1029         1042 : gnc_commodity_get_quote_flag(const gnc_commodity *cm)
    1030              : {
    1031         1042 :     if (!cm) return FALSE;
    1032         1042 :     return (GET_PRIVATE(cm)->quote_flag);
    1033              : }
    1034              : 
    1035              : /********************************************************************
    1036              :  * gnc_commodity_get_quote_source
    1037              :  ********************************************************************/
    1038              : 
    1039              : gnc_quote_source*
    1040          175 : gnc_commodity_get_quote_source(const gnc_commodity *cm)
    1041              : {
    1042              :     gnc_commodityPrivate* priv;
    1043              : 
    1044          175 :     if (!cm) return nullptr;
    1045          175 :     priv = GET_PRIVATE(cm);
    1046          175 :     if (!priv->quote_source && gnc_commodity_is_iso(cm))
    1047            0 :         return &currency_quote_sources.front();
    1048          175 :     return priv->quote_source;
    1049              : }
    1050              : 
    1051              : gnc_quote_source*
    1052          165 : gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
    1053              : {
    1054          165 :     if (cm && gnc_commodity_is_iso(cm))
    1055          165 :         return &currency_quote_sources.front();
    1056              :     /* Should make this a user option at some point. */
    1057            0 :     return gnc_quote_source_lookup_by_internal("alphavantage");
    1058              : }
    1059              : 
    1060              : /********************************************************************
    1061              :  * gnc_commodity_get_quote_tz
    1062              :  ********************************************************************/
    1063              : 
    1064              : const char*
    1065            4 : gnc_commodity_get_quote_tz(const gnc_commodity *cm)
    1066              : {
    1067            4 :     if (!cm) return nullptr;
    1068            4 :     return GET_PRIVATE(cm)->quote_tz;
    1069              : }
    1070              : 
    1071              : /********************************************************************
    1072              :  * gnc_commodity_get_user_symbol
    1073              :  ********************************************************************/
    1074              : const char*
    1075         8786 : gnc_commodity_get_user_symbol(const gnc_commodity *cm)
    1076              : {
    1077         8786 :     g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
    1078              : 
    1079        17572 :     auto sym{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(cm), {"user_symbol"})};
    1080         8786 :     return sym ? *sym : nullptr;
    1081              : }
    1082              : 
    1083              : /********************************************************************
    1084              :  * gnc_commodity_get_default_symbol
    1085              :  *******************************************************************/
    1086              : const char*
    1087         8278 : gnc_commodity_get_default_symbol(const gnc_commodity *cm)
    1088              : {
    1089         8278 :     if (!cm) return nullptr;
    1090         8278 :     return GET_PRIVATE(cm)->default_symbol;
    1091              : }
    1092              : 
    1093              : /********************************************************************
    1094              :  * gnc_commodity_get_nice_symbol
    1095              :  *******************************************************************/
    1096              : const char*
    1097         8784 : gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
    1098              : {
    1099              :     const char *nice_symbol;
    1100              :     struct lconv *lc;
    1101         8784 :     if (!cm) return nullptr;
    1102              : 
    1103         8784 :     nice_symbol = gnc_commodity_get_user_symbol(cm);
    1104         8784 :     if (nice_symbol && *nice_symbol)
    1105          513 :         return nice_symbol;
    1106              : 
    1107         8271 :     lc = gnc_localeconv();
    1108         8271 :     nice_symbol = lc->currency_symbol;
    1109         8271 :     if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
    1110            0 :         return nice_symbol;
    1111              : 
    1112         8271 :     nice_symbol = gnc_commodity_get_default_symbol(cm);
    1113         8271 :     if (nice_symbol && *nice_symbol)
    1114         7955 :         return nice_symbol;
    1115              : 
    1116          316 :     return gnc_commodity_get_mnemonic(cm);
    1117              : }
    1118              : 
    1119              : /********************************************************************
    1120              :  * gnc_commodity_set_mnemonic
    1121              :  ********************************************************************/
    1122              : 
    1123              : void
    1124        74263 : gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
    1125              : {
    1126              :     gnc_commodityPrivate* priv;
    1127              : 
    1128        74263 :     if (!cm) return;
    1129        74263 :     priv = GET_PRIVATE(cm);
    1130        74263 :     if (priv->mnemonic == mnemonic) return;
    1131              : 
    1132        74216 :     gnc_commodity_begin_edit(cm);
    1133        74216 :     CACHE_REMOVE (priv->mnemonic);
    1134        74216 :     priv->mnemonic = CACHE_INSERT(mnemonic);
    1135              : 
    1136        74216 :     mark_commodity_dirty (cm);
    1137        74216 :     reset_printname(priv);
    1138        74216 :     reset_unique_name(priv);
    1139        74216 :     gnc_commodity_commit_edit(cm);
    1140              : }
    1141              : 
    1142              : /********************************************************************
    1143              :  * gnc_commodity_set_namespace
    1144              :  ********************************************************************/
    1145              : 
    1146              : void
    1147        74063 : gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
    1148              : {
    1149              :     QofBook *book;
    1150              :     gnc_commodity_table *table;
    1151              :     gnc_commodity_namespace *nsp;
    1152              :     gnc_commodityPrivate* priv;
    1153              : 
    1154        74063 :     if (!cm) return;
    1155        74063 :     priv = GET_PRIVATE(cm);
    1156        74063 :     book = qof_instance_get_book (&cm->inst);
    1157        74063 :     table = gnc_commodity_table_get_table(book);
    1158        74063 :     nsp = gnc_commodity_table_add_namespace(table, name_space, book);
    1159        74063 :     if (priv->name_space == nsp)
    1160          359 :         return;
    1161              : 
    1162        73704 :     gnc_commodity_begin_edit(cm);
    1163        73704 :     priv->name_space = nsp;
    1164        73704 :     if (nsp->iso4217)
    1165        71559 :         priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
    1166        73704 :     mark_commodity_dirty(cm);
    1167        73704 :     reset_printname(priv);
    1168        73704 :     reset_unique_name(priv);
    1169        73704 :     gnc_commodity_commit_edit(cm);
    1170              : }
    1171              : 
    1172              : /********************************************************************
    1173              :  * gnc_commodity_set_fullname
    1174              :  ********************************************************************/
    1175              : 
    1176              : void
    1177        74244 : gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
    1178              : {
    1179              :     gnc_commodityPrivate* priv;
    1180              : 
    1181        74244 :     if (!cm) return;
    1182        74244 :     priv = GET_PRIVATE(cm);
    1183        74244 :     if (priv->fullname == fullname) return;
    1184              : 
    1185        74197 :     CACHE_REMOVE (priv->fullname);
    1186        74197 :     priv->fullname = CACHE_INSERT (fullname);
    1187              : 
    1188        74197 :     gnc_commodity_begin_edit(cm);
    1189        74197 :     mark_commodity_dirty(cm);
    1190        74197 :     reset_printname(priv);
    1191        74197 :     gnc_commodity_commit_edit(cm);
    1192              : }
    1193              : 
    1194              : /********************************************************************
    1195              :  * gnc_commodity_set_cusip
    1196              :  ********************************************************************/
    1197              : 
    1198              : void
    1199        74187 : gnc_commodity_set_cusip(gnc_commodity * cm,
    1200              :                         const char * cusip)
    1201              : {
    1202              :     gnc_commodityPrivate* priv;
    1203              : 
    1204        74187 :     if (!cm) return;
    1205              : 
    1206        74187 :     priv = GET_PRIVATE(cm);
    1207        74187 :     if (priv->cusip == cusip) return;
    1208              : 
    1209        74140 :     gnc_commodity_begin_edit(cm);
    1210        74140 :     CACHE_REMOVE (priv->cusip);
    1211        74140 :     priv->cusip = CACHE_INSERT (cusip);
    1212        74140 :     mark_commodity_dirty(cm);
    1213        74140 :     gnc_commodity_commit_edit(cm);
    1214              : }
    1215              : 
    1216              : /********************************************************************
    1217              :  * gnc_commodity_set_fraction
    1218              :  ********************************************************************/
    1219              : 
    1220              : void
    1221        74244 : gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
    1222              : {
    1223        74244 :     if (!cm) return;
    1224        74244 :     gnc_commodity_begin_edit(cm);
    1225        74244 :     GET_PRIVATE(cm)->fraction = fraction;
    1226        74244 :     mark_commodity_dirty(cm);
    1227        74244 :     gnc_commodity_commit_edit(cm);
    1228              : }
    1229              : 
    1230              : /********************************************************************
    1231              :  * gnc_commodity_set_auto_quote_control_flag
    1232              :  ********************************************************************/
    1233              : 
    1234              : void
    1235            2 : gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
    1236              :         const gboolean flag)
    1237              : {
    1238            2 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1239              : 
    1240            2 :     if (!cm)
    1241              :     {
    1242            0 :         LEAVE("");
    1243            0 :         return;
    1244              :     }
    1245            2 :     gnc_commodity_begin_edit(cm);
    1246            3 :     auto val = flag ? std::nullopt : std::make_optional<const char*>(g_strdup("false"));
    1247            4 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE (cm), val, {"auto_quote_control"});
    1248            2 :     mark_commodity_dirty(cm);
    1249            2 :     gnc_commodity_commit_edit(cm);
    1250            2 :     LEAVE("");
    1251              : }
    1252              : 
    1253              : /********************************************************************
    1254              :  * gnc_commodity_user_set_quote_flag
    1255              :  ********************************************************************/
    1256              : 
    1257              : void
    1258            0 : gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
    1259              : {
    1260              :     gnc_commodityPrivate* priv;
    1261              : 
    1262            0 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1263              : 
    1264            0 :     if (!cm)
    1265              :     {
    1266            0 :         LEAVE("");
    1267            0 :         return;
    1268              :     }
    1269              : 
    1270            0 :     priv = GET_PRIVATE(cm);
    1271            0 :     gnc_commodity_begin_edit(cm);
    1272            0 :     gnc_commodity_set_quote_flag(cm, flag);
    1273            0 :     if (gnc_commodity_is_iso(cm))
    1274              :     {
    1275              :         /* For currencies, disable auto quote control if the quote flag is being
    1276              :          * changed from its default value and enable it if the quote flag is being
    1277              :          * reset to its default value.  The defaults for the quote flag are
    1278              :          * disabled if no accounts are using the currency, and true otherwise.
    1279              :          * Thus enable auto quote control if flag is FALSE and there are not any
    1280              :          * accounts using this currency OR flag is TRUE and there are accounts
    1281              :          * using this currency; otherwise disable auto quote control */
    1282            0 :         gnc_commodity_set_auto_quote_control_flag(cm,
    1283            0 :                 (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
    1284              :     }
    1285            0 :     gnc_commodity_commit_edit(cm);
    1286            0 :     LEAVE("");
    1287              : }
    1288              : 
    1289              : /********************************************************************
    1290              :  * gnc_commodity_set_quote_flag
    1291              :  ********************************************************************/
    1292              : 
    1293              : void
    1294          418 : gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
    1295              : {
    1296          418 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1297              : 
    1298          418 :     if (!cm) return;
    1299          418 :     gnc_commodity_begin_edit(cm);
    1300          418 :     GET_PRIVATE(cm)->quote_flag = flag;
    1301          418 :     mark_commodity_dirty(cm);
    1302          418 :     gnc_commodity_commit_edit(cm);
    1303          418 :     LEAVE(" ");
    1304              : }
    1305              : 
    1306              : /********************************************************************
    1307              :  * gnc_commodity_set_quote_source
    1308              :  ********************************************************************/
    1309              : 
    1310              : void
    1311        72091 : gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
    1312              : {
    1313        72091 :     ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
    1314              : 
    1315        72091 :     if (!cm) return;
    1316        72091 :     gnc_commodity_begin_edit(cm);
    1317        72091 :     GET_PRIVATE(cm)->quote_source = src;
    1318        72091 :     mark_commodity_dirty(cm);
    1319        72091 :     gnc_commodity_commit_edit(cm);
    1320        72091 :     LEAVE(" ");
    1321              : }
    1322              : 
    1323              : /********************************************************************
    1324              :  * gnc_commodity_set_quote_tz
    1325              :  ********************************************************************/
    1326              : 
    1327              : void
    1328           95 : gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
    1329              : {
    1330              :     gnc_commodityPrivate* priv;
    1331              : 
    1332           95 :     if (!cm) return;
    1333              : 
    1334           95 :     ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
    1335              : 
    1336           95 :     priv = GET_PRIVATE(cm);
    1337              : 
    1338           95 :     if (tz == priv->quote_tz)
    1339              :     {
    1340           66 :         LEAVE("Already correct TZ");
    1341           66 :         return;
    1342              :     }
    1343              : 
    1344           29 :     gnc_commodity_begin_edit(cm);
    1345           29 :     CACHE_REMOVE (priv->quote_tz);
    1346           29 :     priv->quote_tz = CACHE_INSERT (tz);
    1347           29 :     mark_commodity_dirty(cm);
    1348           29 :     gnc_commodity_commit_edit(cm);
    1349           29 :     LEAVE(" ");
    1350              : }
    1351              : 
    1352              : /********************************************************************
    1353              :  * gnc_commodity_set_user_symbol
    1354              :  ********************************************************************/
    1355              : 
    1356              : void
    1357            7 : gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
    1358              : {
    1359              :     struct lconv *lc;
    1360              : 
    1361            7 :     if (!cm) return;
    1362              : 
    1363            7 :     ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
    1364              : 
    1365            7 :     lc = gnc_localeconv();
    1366            7 :     if (!user_symbol || !*user_symbol)
    1367            0 :         user_symbol = nullptr;
    1368            7 :     else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
    1369            0 :              !g_strcmp0(lc->currency_symbol, user_symbol))
    1370              :         /* if the user gives the ISO symbol for the locale currency or the
    1371              :          * default symbol, actually remove the user symbol */
    1372            0 :         user_symbol = nullptr;
    1373            7 :     else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
    1374            0 :         user_symbol = nullptr;
    1375              : 
    1376            7 :     gnc_commodity_begin_edit (cm);
    1377              : 
    1378           14 :     auto val = user_symbol ? std::make_optional<const char*>(g_strdup(user_symbol)) : std::nullopt;
    1379           14 :     qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(cm), val, {"user_symbol"});
    1380              : 
    1381            7 :     mark_commodity_dirty(cm);
    1382            7 :     gnc_commodity_commit_edit(cm);
    1383              : 
    1384            7 :     LEAVE(" ");
    1385              : }
    1386              : 
    1387              : /********************************************************************
    1388              :  * gnc_commodity_set_default_symbol
    1389              :  * Not made visible in gnc-commodity.h, it is only called from
    1390              :  * iso-4217-currencies.c at startup.
    1391              :  ********************************************************************/
    1392              : void
    1393        71505 : gnc_commodity_set_default_symbol(gnc_commodity * cm,
    1394              :                                  const char * default_symbol)
    1395              : {
    1396        71505 :     GET_PRIVATE(cm)->default_symbol = default_symbol;
    1397        71505 : }
    1398              : 
    1399              : /********************************************************************
    1400              :  * gnc_commodity_increment_usage_count
    1401              :  ********************************************************************/
    1402              : 
    1403              : void
    1404         3168 : gnc_commodity_increment_usage_count(gnc_commodity *cm)
    1405              : {
    1406              :     gnc_commodityPrivate* priv;
    1407              : 
    1408         3168 :     ENTER("(cm=%p)", cm);
    1409              : 
    1410         3168 :     if (!cm)
    1411              :     {
    1412            1 :         LEAVE("");
    1413            1 :         return;
    1414              :     }
    1415              : 
    1416         3167 :     priv = GET_PRIVATE(cm);
    1417              : 
    1418          851 :     if ((priv->usage_count == 0) && !priv->quote_flag
    1419          823 :             && gnc_commodity_get_auto_quote_control_flag(cm)
    1420         4018 :             && gnc_commodity_is_iso(cm))
    1421              :     {
    1422              :         /* compatibility hack - Gnucash 1.8 gets currency quotes when a
    1423              :            non-default currency is assigned to an account.  */
    1424          165 :         gnc_commodity_begin_edit(cm);
    1425          165 :         gnc_commodity_set_quote_flag(cm, TRUE);
    1426          165 :         gnc_commodity_set_quote_source(cm,
    1427              :                                        gnc_commodity_get_default_quote_source(cm));
    1428          165 :         gnc_commodity_commit_edit(cm);
    1429              :     }
    1430         3167 :     priv->usage_count++;
    1431         3167 :     LEAVE("(usage_count=%d)", priv->usage_count);
    1432              : }
    1433              : 
    1434              : /********************************************************************
    1435              :  * gnc_commodity_decrement_usage_count
    1436              :  ********************************************************************/
    1437              : 
    1438              : void
    1439         5395 : gnc_commodity_decrement_usage_count(gnc_commodity *cm)
    1440              : {
    1441              :     gnc_commodityPrivate* priv;
    1442              : 
    1443         5395 :     ENTER("(cm=%p)", cm);
    1444              : 
    1445         5395 :     if (!cm)
    1446              :     {
    1447         3914 :         LEAVE("");
    1448         3914 :         return;
    1449              :     }
    1450              : 
    1451         1481 :     priv = GET_PRIVATE(cm);
    1452              : 
    1453         1481 :     if (priv->usage_count == 0)
    1454              :     {
    1455            0 :         PWARN("usage_count already zero");
    1456            0 :         LEAVE("");
    1457            0 :         return;
    1458              :     }
    1459              : 
    1460         1481 :     priv->usage_count--;
    1461          323 :     if ((priv->usage_count == 0) && priv->quote_flag
    1462           99 :             && gnc_commodity_get_auto_quote_control_flag(cm)
    1463         1804 :             && gnc_commodity_is_iso(cm))
    1464              :     {
    1465              :         /* if this is a currency with auto quote control enabled and no more
    1466              :          * accounts reference this currency, disable quote retrieval */
    1467           97 :         gnc_commodity_set_quote_flag(cm, FALSE);
    1468              :     }
    1469         1481 :     LEAVE("(usage_count=%d)", priv->usage_count);
    1470              : }
    1471              : 
    1472              : /********************************************************************\
    1473              : \********************************************************************/
    1474              : 
    1475              : 
    1476              : /********************************************************************
    1477              :  * gnc_commodity_equiv
    1478              :  * are two commodities the same?
    1479              :  ********************************************************************/
    1480              : 
    1481              : gboolean
    1482        70620 : gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
    1483              : {
    1484              :     gnc_commodityPrivate* priv_a;
    1485              :     gnc_commodityPrivate* priv_b;
    1486              : 
    1487        70620 :     if (a == b) return TRUE;
    1488        34023 :     if (!a || !b) return FALSE;
    1489              : 
    1490         7009 :     priv_a = GET_PRIVATE(a);
    1491         7009 :     priv_b = GET_PRIVATE(b);
    1492         7009 :     if (priv_a->name_space != priv_b->name_space) return FALSE;
    1493         2995 :     if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
    1494              : 
    1495           69 :     return TRUE;
    1496              : }
    1497              : 
    1498              : gboolean
    1499         5831 : gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
    1500              : {
    1501         5831 :     return gnc_commodity_compare(a, b) == 0;
    1502              : }
    1503              : 
    1504         5860 : int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
    1505              : {
    1506         5860 :     if (a == b) return 0;
    1507         1752 :     if (a && !b) return 1;
    1508         1744 :     if (b && !a) return -1;
    1509         1716 :     if (auto rv = g_strcmp0 (gnc_commodity_get_unique_name (a), gnc_commodity_get_unique_name (b)))
    1510         1628 :         return rv;
    1511           88 :     return qof_instance_guid_compare(a, b);
    1512              : }
    1513              : 
    1514              : // Used as a callback to g_list_find_custom, it should return 0
    1515              : // when the commodities match.
    1516            0 : int gnc_commodity_compare_void(const void * a, const void * b)
    1517              : {
    1518            0 :     return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
    1519              : }
    1520              : 
    1521              : /************************************************************
    1522              :  *                   Namespace functions                    *
    1523              :  ************************************************************/
    1524              : const char *
    1525        87126 : gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
    1526              : {
    1527        87126 :     if (ns == nullptr)
    1528           18 :         return nullptr;
    1529        87108 :     return ns->name;
    1530              : }
    1531              : 
    1532              : const char *
    1533            0 : gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
    1534              : {
    1535            0 :     if (ns == nullptr)
    1536            0 :         return nullptr;
    1537            0 :     if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
    1538            0 :         return GNC_COMMODITY_NS_ISO_GUI;
    1539            0 :     return ns->name;
    1540              : }
    1541              : 
    1542              : GList *
    1543            0 : gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
    1544              : {
    1545            0 :     if (!name_space)
    1546            0 :         return nullptr;
    1547              : 
    1548            0 :     return g_list_copy (name_space->cm_list);
    1549              : }
    1550              : 
    1551              : gboolean
    1552        75088 : gnc_commodity_namespace_is_iso(const char *name_space)
    1553              : {
    1554        78515 :     return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
    1555        78515 :             (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
    1556              : }
    1557              : 
    1558              : static const gchar *
    1559       380741 : gnc_commodity_table_map_namespace(const char * name_space)
    1560              : {
    1561       380741 :     if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
    1562        73338 :         return GNC_COMMODITY_NS_CURRENCY;
    1563       307403 :     return name_space;
    1564              : }
    1565              : 
    1566              : /********************************************************************
    1567              :  * gnc_commodity_table_new
    1568              :  * make a new commodity table
    1569              :  ********************************************************************/
    1570              : 
    1571              : gnc_commodity_table *
    1572          339 : gnc_commodity_table_new(void)
    1573              : {
    1574          339 :     gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
    1575          339 :     retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
    1576          339 :     retval->ns_list = nullptr;
    1577          339 :     return retval;
    1578              : }
    1579              : 
    1580              : /********************************************************************
    1581              :  * book anchor functions
    1582              :  ********************************************************************/
    1583              : 
    1584              : gnc_commodity_table *
    1585       128240 : gnc_commodity_table_get_table(QofBook *book)
    1586              : {
    1587       128240 :     if (!book) return nullptr;
    1588       128240 :     return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
    1589              : }
    1590              : 
    1591              : gnc_commodity *
    1592            2 : gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
    1593              : {
    1594              :     gnc_commodity *twin;
    1595              :     const char * ucom;
    1596              :     gnc_commodity_table * comtbl;
    1597              : 
    1598            2 :     if (!from) return nullptr;
    1599            1 :     comtbl = gnc_commodity_table_get_table (book);
    1600            1 :     if (!comtbl) return nullptr;
    1601              : 
    1602            1 :     ucom = gnc_commodity_get_unique_name (from);
    1603            1 :     twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
    1604            1 :     if (!twin)
    1605              :     {
    1606            0 :         twin = gnc_commodity_clone (from, book);
    1607            0 :         twin = gnc_commodity_table_insert (comtbl, twin);
    1608              :     }
    1609            1 :     return twin;
    1610              : }
    1611              : 
    1612              : /********************************************************************
    1613              :  * gnc_commodity_table_get_size
    1614              :  * get the size of the commodity table
    1615              :  ********************************************************************/
    1616              : 
    1617              : static void
    1618          172 : count_coms(gpointer key, gpointer value, gpointer user_data)
    1619              : {
    1620          172 :     GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
    1621          172 :     guint *count = (guint*)user_data;
    1622              : 
    1623          172 :     if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
    1624              :     {
    1625              :         /* don't count default commodities */
    1626            8 :         return;
    1627              :     }
    1628              : 
    1629          164 :     if (!value) return;
    1630              : 
    1631          164 :     *count += g_hash_table_size(tbl);
    1632              : }
    1633              : 
    1634              : guint
    1635           49 : gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
    1636              : {
    1637           49 :     guint count = 0;
    1638           49 :     g_return_val_if_fail(tbl, 0);
    1639           49 :     g_return_val_if_fail(tbl->ns_table, 0);
    1640              : 
    1641           49 :     g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
    1642              : 
    1643           49 :     return count;
    1644              : }
    1645              : 
    1646              : /********************************************************************
    1647              :  * gnc_commodity_table_lookup
    1648              :  * locate a commodity by namespace and mnemonic.
    1649              :  ********************************************************************/
    1650              : 
    1651              : gnc_commodity *
    1652        84545 : gnc_commodity_table_lookup(const gnc_commodity_table * table,
    1653              :                            const char * name_space, const char * mnemonic)
    1654              : {
    1655        84545 :     gnc_commodity_namespace * nsp = nullptr;
    1656              : 
    1657        84545 :     if (!table || !name_space || !mnemonic) return nullptr;
    1658              : 
    1659        84503 :     nsp = gnc_commodity_table_find_namespace(table, name_space);
    1660              : 
    1661        84503 :     if (nsp)
    1662              :     {
    1663              :         /*
    1664              :          * Backward compatibility support for currencies that have
    1665              :          * recently changed.
    1666              :          */
    1667        83362 :         if (nsp->iso4217)
    1668              :         {
    1669        77295 :             auto it = gnc_new_iso_codes.find (mnemonic);
    1670        77295 :             if (it != gnc_new_iso_codes.end())
    1671            0 :                 mnemonic = it->second.c_str();
    1672              :         }
    1673        83362 :         return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
    1674              :     }
    1675              :     else
    1676              :     {
    1677         1141 :         return nullptr;
    1678              :     }
    1679              : }
    1680              : 
    1681              : /********************************************************************
    1682              :  * gnc_commodity_table_lookup
    1683              :  * locate a commodity by unique name.
    1684              :  ********************************************************************/
    1685              : 
    1686              : gnc_commodity *
    1687            1 : gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
    1688              :                                   const char * unique_name)
    1689              : {
    1690              :     char *name_space;
    1691              :     char *mnemonic;
    1692              :     gnc_commodity *commodity;
    1693              : 
    1694            1 :     if (!table || !unique_name) return nullptr;
    1695              : 
    1696            1 :     name_space = g_strdup (unique_name);
    1697            1 :     mnemonic = strstr (name_space, "::");
    1698            1 :     if (!mnemonic)
    1699              :     {
    1700            0 :         g_free (name_space);
    1701            0 :         return nullptr;
    1702              :     }
    1703              : 
    1704            1 :     *mnemonic = '\0';
    1705            1 :     mnemonic += 2;
    1706              : 
    1707            1 :     commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
    1708              : 
    1709            1 :     g_free (name_space);
    1710              : 
    1711            1 :     return commodity;
    1712              : }
    1713              : 
    1714              : /********************************************************************
    1715              :  * gnc_commodity_table_find_full
    1716              :  * locate a commodity by namespace and printable name
    1717              :  ********************************************************************/
    1718              : 
    1719              : gnc_commodity *
    1720            0 : gnc_commodity_table_find_full(const gnc_commodity_table * table,
    1721              :                               const char * name_space,
    1722              :                               const char * fullname)
    1723              : {
    1724            0 :     gnc_commodity * retval = nullptr;
    1725              :     GList         * all;
    1726              :     GList         * iterator;
    1727              : 
    1728            0 :     if (!fullname || (fullname[0] == '\0'))
    1729            0 :         return nullptr;
    1730              : 
    1731            0 :     all = gnc_commodity_table_get_commodities(table, name_space);
    1732              : 
    1733            0 :     for (iterator = all; iterator; iterator = iterator->next)
    1734              :     {
    1735            0 :         auto commodity = GNC_COMMODITY (iterator->data);
    1736            0 :         if (!strcmp(fullname,
    1737              :                     gnc_commodity_get_printname(commodity)))
    1738              :         {
    1739            0 :             retval = commodity;
    1740            0 :             break;
    1741              :         }
    1742              :     }
    1743              : 
    1744            0 :     g_list_free (all);
    1745              : 
    1746            0 :     return retval;
    1747              : }
    1748              : 
    1749              : 
    1750              : /********************************************************************
    1751              :  * gnc_commodity_table_insert
    1752              :  * add a commodity to the table.
    1753              :  ********************************************************************/
    1754              : 
    1755              : gnc_commodity *
    1756        73656 : gnc_commodity_table_insert(gnc_commodity_table * table,
    1757              :                            gnc_commodity * comm)
    1758              : {
    1759        73656 :     gnc_commodity_namespace * nsp = nullptr;
    1760              :     gnc_commodity *c;
    1761              :     const char *ns_name;
    1762              :     gnc_commodityPrivate* priv;
    1763              :     QofBook *book;
    1764              : 
    1765        73656 :     if (!table) return nullptr;
    1766        73656 :     if (!comm) return nullptr;
    1767              : 
    1768        73656 :     priv = GET_PRIVATE(comm);
    1769              : 
    1770        73656 :     ENTER ("(table=%p, comm=%p) %s %s", table, comm,
    1771              :            (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
    1772              :            (priv->fullname == nullptr ? "(null)" : priv->fullname));
    1773        73656 :     ns_name = gnc_commodity_namespace_get_name(priv->name_space);
    1774        73656 :     c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
    1775              : 
    1776        73656 :     if (c)
    1777              :     {
    1778           50 :         if (c == comm)
    1779              :         {
    1780            3 :             LEAVE("already in table");
    1781            3 :             return c;
    1782              :         }
    1783              : 
    1784              :         /* Backward compatibility support for currencies that have
    1785              :          * recently changed. */
    1786           47 :         if (priv->name_space->iso4217)
    1787              :         {
    1788           27 :             auto it = gnc_new_iso_codes.find (priv->mnemonic);
    1789           27 :             if (it != gnc_new_iso_codes.end())
    1790            0 :                 gnc_commodity_set_mnemonic(comm, it->second.c_str());
    1791              :         }
    1792           47 :         gnc_commodity_copy (c, comm);
    1793           47 :         gnc_commodity_destroy (comm);
    1794           47 :         LEAVE("found at %p", c);
    1795           47 :         return c;
    1796              :     }
    1797              : 
    1798              :     /* Prevent setting anything except template in namespace template. */
    1799        73921 :     if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
    1800          315 :         g_strcmp0 (priv->mnemonic, "template") != 0)
    1801              :     {
    1802            0 :         PWARN("Converting commodity %s from namespace template to "
    1803              :               "namespace User", priv->mnemonic);
    1804            0 :         gnc_commodity_set_namespace (comm, "User");
    1805            0 :         ns_name = "User";
    1806            0 :         mark_commodity_dirty (comm);
    1807              :     }
    1808              : 
    1809        73606 :     book = qof_instance_get_book (&comm->inst);
    1810        73606 :     nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
    1811              : 
    1812        73606 :     PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
    1813              :            nsp->cm_table, nsp->name);
    1814        73606 :     g_hash_table_insert(nsp->cm_table,
    1815        73606 :                         (gpointer)CACHE_INSERT(priv->mnemonic),
    1816              :                         (gpointer)comm);
    1817        73606 :     nsp->cm_list = g_list_append(nsp->cm_list, comm);
    1818              : 
    1819        73606 :     qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
    1820        73606 :     LEAVE ("(table=%p, comm=%p)", table, comm);
    1821        73606 :     return comm;
    1822              : }
    1823              : 
    1824              : /********************************************************************
    1825              :  * gnc_commodity_table_remove
    1826              :  * remove a commodity from the table.
    1827              :  ********************************************************************/
    1828              : 
    1829              : void
    1830        44057 : gnc_commodity_table_remove(gnc_commodity_table * table,
    1831              :                            gnc_commodity * comm)
    1832              : {
    1833              :     gnc_commodity_namespace * nsp;
    1834              :     gnc_commodity *c;
    1835              :     gnc_commodityPrivate* priv;
    1836              :     const char *ns_name;
    1837              : 
    1838        44057 :     if (!table) return;
    1839         1111 :     if (!comm) return;
    1840              : 
    1841         1111 :     priv = GET_PRIVATE(comm);
    1842         1111 :     ns_name = gnc_commodity_namespace_get_name(priv->name_space);
    1843         1111 :     c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
    1844         1111 :     if (c != comm) return;
    1845              : 
    1846           65 :     qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
    1847              : 
    1848           65 :     nsp = gnc_commodity_table_find_namespace(table, ns_name);
    1849           65 :     if (!nsp) return;
    1850              : 
    1851           65 :     nsp->cm_list = g_list_remove(nsp->cm_list, comm);
    1852           65 :     g_hash_table_remove (nsp->cm_table, priv->mnemonic);
    1853              :     /* XXX minor mem leak, should remove the key as well */
    1854              : }
    1855              : 
    1856              : /********************************************************************
    1857              :  * gnc_commodity_table_has_namespace
    1858              :  * see if the commodities namespace exists. May have zero commodities.
    1859              :  ********************************************************************/
    1860              : 
    1861              : int
    1862           20 : gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
    1863              :                                   const char * name_space)
    1864              : {
    1865           20 :     gnc_commodity_namespace * nsp = nullptr;
    1866              : 
    1867           20 :     if (!table || !name_space)
    1868              :     {
    1869            0 :         return 0;
    1870              :     }
    1871              : 
    1872           20 :     nsp = gnc_commodity_table_find_namespace(table, name_space);
    1873           20 :     if (nsp)
    1874              :     {
    1875           20 :         return 1;
    1876              :     }
    1877              :     else
    1878              :     {
    1879            0 :         return 0;
    1880              :     }
    1881              : }
    1882              : 
    1883              : static void
    1884         1636 : hash_keys_helper(gpointer key, gpointer value, gpointer data)
    1885              : {
    1886         1636 :     auto l = (GList**)data;
    1887         1636 :     *l = g_list_prepend(*l, key);
    1888         1636 : }
    1889              : 
    1890              : static GList *
    1891          238 : g_hash_table_keys(GHashTable * table)
    1892              : {
    1893          238 :     GList * l = nullptr;
    1894          238 :     g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
    1895          238 :     return l;
    1896              : }
    1897              : 
    1898              : static void
    1899        10164 : hash_values_helper(gpointer key, gpointer value, gpointer data)
    1900              : {
    1901        10164 :     auto l = (GList**)data;
    1902        10164 :     *l = g_list_prepend(*l, value);
    1903        10164 : }
    1904              : 
    1905              : static GList *
    1906          240 : g_hash_table_values(GHashTable * table)
    1907              : {
    1908          240 :     GList * l = nullptr;
    1909          240 :     g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
    1910          240 :     return l;
    1911              : }
    1912              : 
    1913              : /********************************************************************
    1914              :  * gnc_commodity_table_get_namespaces
    1915              :  * see if any commodities in the namespace exist
    1916              :  ********************************************************************/
    1917              : 
    1918              : GList *
    1919          238 : gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
    1920              : {
    1921          238 :     if (!table)
    1922            0 :         return nullptr;
    1923              : 
    1924          238 :     return g_hash_table_keys(table->ns_table);
    1925              : }
    1926              : 
    1927              : GList *
    1928            2 : gnc_commodity_table_get_namespaces_list(const gnc_commodity_table * table)
    1929              : {
    1930            2 :     if (!table)
    1931            0 :         return nullptr;
    1932              : 
    1933            2 :     return g_list_copy (table->ns_list);
    1934              : }
    1935              : 
    1936              : /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
    1937              :    GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
    1938              :    either of these, the net result is that the iso4217 bit is set only
    1939              :    for GNC_COMMODITY_NS_CURRENCY.  This means that gnc_commodity_is_iso is
    1940              :    a subset of gnc_commodity_is_currency.  Most callers seem to use
    1941              :    gnc_commodity_is_iso. */
    1942              : gboolean
    1943        31491 : gnc_commodity_is_iso(const gnc_commodity * cm)
    1944              : {
    1945              :     gnc_commodityPrivate* priv;
    1946              : 
    1947        31491 :     if (!cm) return FALSE;
    1948              : 
    1949        31491 :     priv = GET_PRIVATE(cm);
    1950        31491 :     if ( !priv->name_space) return FALSE;
    1951        31293 :     return priv->name_space->iso4217;
    1952              : }
    1953              : 
    1954              : gboolean
    1955         3224 : gnc_commodity_is_currency(const gnc_commodity *cm)
    1956              : {
    1957              :     const char *ns_name;
    1958         3224 :     if (!cm) return FALSE;
    1959              : 
    1960         3221 :     ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
    1961         6442 :     return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
    1962         6442 :             !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
    1963              : }
    1964              : 
    1965              : /********************************************************************
    1966              :  * gnc_commodity_table_get_commodities
    1967              :  * list commodities in a given namespace
    1968              :  ********************************************************************/
    1969              : 
    1970              : static CommodityList*
    1971            0 : commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
    1972              : {
    1973            0 :     GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
    1974            0 :     CommodityList *retval = nullptr;
    1975            0 :     for (node = nslist; node; node=g_list_next(node))
    1976              :     {
    1977            0 :         gnc_commodity_namespace *ns = nullptr;
    1978            0 :         if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
    1979            0 :             || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
    1980            0 :             continue;
    1981            0 :         ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
    1982            0 :         if (!ns)
    1983            0 :             continue;
    1984            0 :         retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
    1985              :     }
    1986            0 :     g_list_free(nslist);
    1987            0 :     return retval;
    1988              : }
    1989              : 
    1990              : CommodityList *
    1991          240 : gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
    1992              :                                     const char * name_space)
    1993              : {
    1994          240 :     gnc_commodity_namespace * ns = nullptr;
    1995              : 
    1996          240 :     if (!table)
    1997            0 :         return nullptr;
    1998          240 :     if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
    1999            0 :         return commodity_table_get_all_noncurrency_commodities(table);
    2000          240 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2001          240 :     if (!ns)
    2002            0 :         return nullptr;
    2003              : 
    2004          240 :     return g_hash_table_values(ns->cm_table);
    2005              : }
    2006              : 
    2007              : /********************************************************************
    2008              :  * gnc_commodity_table_get_quotable_commodities
    2009              :  * list commodities in a given namespace that get price quotes
    2010              :  ********************************************************************/
    2011              : 
    2012              : static void
    2013            0 : get_quotables_helper1(gpointer key, gpointer value, gpointer data)
    2014              : {
    2015            0 :     auto comm = GNC_COMMODITY(value);
    2016            0 :     gnc_commodityPrivate* priv = GET_PRIVATE(comm);
    2017            0 :     auto l = static_cast<GList**>(data);
    2018              : 
    2019            0 :     if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
    2020            0 :         return;
    2021            0 :     *l = g_list_prepend(*l, value);
    2022              : }
    2023              : 
    2024              : static gboolean
    2025            0 : get_quotables_helper2 (gnc_commodity *comm, gpointer data)
    2026              : {
    2027            0 :     auto l = static_cast<GList**>(data);
    2028            0 :     gnc_commodityPrivate* priv = GET_PRIVATE(comm);
    2029              : 
    2030            0 :     if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
    2031            0 :         return TRUE;
    2032            0 :     *l = g_list_prepend(*l, comm);
    2033            0 :     return TRUE;
    2034              : }
    2035              : 
    2036              : CommodityList *
    2037            0 : gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table)
    2038              : {
    2039            0 :     gnc_commodity_namespace * ns = nullptr;
    2040              :     const char *name_space;
    2041              :     GList * nslist, * tmp;
    2042            0 :     GList * l = nullptr;
    2043              :     regex_t pattern;
    2044            0 :     const char *expression = gnc_prefs_get_namespace_regexp();
    2045              : 
    2046            0 :     ENTER("table=%p, expression=%s", table, expression);
    2047            0 :     if (!table)
    2048            0 :         return nullptr;
    2049              : 
    2050            0 :     if (expression && *expression)
    2051              :     {
    2052            0 :         if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
    2053              :         {
    2054            0 :             LEAVE("Cannot compile regex");
    2055            0 :             return nullptr;
    2056              :         }
    2057              : 
    2058            0 :         nslist = gnc_commodity_table_get_namespaces(table);
    2059            0 :         for (tmp = nslist; tmp; tmp = tmp->next)
    2060              :         {
    2061            0 :             name_space = static_cast<const char*>(tmp->data);
    2062            0 :             if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
    2063              :             {
    2064            0 :                 DEBUG("Running list of %s commodities", name_space);
    2065            0 :                 ns = gnc_commodity_table_find_namespace(table, name_space);
    2066            0 :                 if (ns)
    2067              :                 {
    2068            0 :                     g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
    2069              :                 }
    2070              :             }
    2071              :         }
    2072            0 :         g_list_free(nslist);
    2073            0 :         regfree(&pattern);
    2074              :     }
    2075              :     else
    2076              :     {
    2077            0 :         gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
    2078              :                                               (gpointer) &l);
    2079              :     }
    2080            0 :     LEAVE("list head %p", l);
    2081            0 :     return l;
    2082              : }
    2083              : 
    2084              : /********************************************************************
    2085              :  * gnc_commodity_table_add_namespace
    2086              :  * add an empty namespace if it does not exist
    2087              :  ********************************************************************/
    2088              : 
    2089              : /* GObject Initialization */
    2090         2552 : QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
    2091              : 
    2092              : static void
    2093         1036 : gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
    2094              : {
    2095         1036 : }
    2096              : 
    2097              : static void
    2098          625 : gnc_commodity_namespace_dispose_real (GObject *nsp)
    2099              : {
    2100          625 : }
    2101              : 
    2102              : static void
    2103          625 : gnc_commodity_namespace_finalize_real(GObject* nsp)
    2104              : {
    2105          625 : }
    2106              : 
    2107              : gnc_commodity_namespace *
    2108       147984 : gnc_commodity_table_add_namespace(gnc_commodity_table * table,
    2109              :                                   const char * name_space,
    2110              :                                   QofBook *book)
    2111              : {
    2112       147984 :     gnc_commodity_namespace * ns = nullptr;
    2113              : 
    2114       147984 :     if (!table) return nullptr;
    2115              : 
    2116       147644 :     name_space = gnc_commodity_table_map_namespace(name_space);
    2117       147644 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2118       147644 :     if (!ns)
    2119              :     {
    2120         1036 :         ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
    2121         1036 :         ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
    2122         1036 :         ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
    2123         1036 :         ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
    2124         1036 :         qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
    2125         1036 :         qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
    2126              : 
    2127         1036 :         g_hash_table_insert(table->ns_table,
    2128         1036 :                             (gpointer) ns->name,
    2129              :                             (gpointer) ns);
    2130         1036 :         table->ns_list = g_list_append(table->ns_list, ns);
    2131         1036 :         qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
    2132              :     }
    2133       147644 :     return ns;
    2134              : }
    2135              : 
    2136              : bool
    2137            0 : gnc_commodity_table_rename_namespace(const gnc_commodity_table * table,
    2138              :                                      const char * namespace_name,
    2139              :                                      const char * new_namespace_name)
    2140              : {
    2141            0 :     if (!table || !namespace_name || !new_namespace_name ||
    2142            0 :         (g_strcmp0 (namespace_name, new_namespace_name) == 0) ||
    2143            0 :         (g_strcmp0 (new_namespace_name, GNC_COMMODITY_NS_ISO_GUI) == 0) ||
    2144            0 :         (g_strcmp0 (new_namespace_name, _(GNC_COMMODITY_NS_ISO_GUI)) == 0) ||
    2145            0 :         gnc_commodity_table_find_namespace (table, new_namespace_name))
    2146            0 :         return false;
    2147              : 
    2148            0 :     auto ns = gnc_commodity_table_find_namespace (table, namespace_name);
    2149            0 :     if (!ns)
    2150            0 :         return false;
    2151              : 
    2152            0 :     ns->name = CACHE_INSERT(static_cast<const char*>(new_namespace_name));
    2153              : 
    2154            0 :     g_hash_table_insert (table->ns_table,
    2155            0 :                          (gpointer) ns->name,
    2156              :                          (gpointer) ns);
    2157              : 
    2158            0 :     g_hash_table_remove (table->ns_table,
    2159              :                          (gpointer) namespace_name);
    2160              : 
    2161            0 :     CACHE_REMOVE(namespace_name);
    2162              : 
    2163            0 :     qof_instance_set_dirty (&ns->inst);
    2164            0 :     qof_event_gen (&ns->inst, QOF_EVENT_MODIFY, nullptr);
    2165            0 :     return true;
    2166              : }
    2167              : 
    2168              : gnc_commodity_namespace *
    2169       233097 : gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
    2170              :                                    const char * name_space)
    2171              : {
    2172       233097 :     if (!table || !name_space)
    2173            0 :         return nullptr;
    2174              : 
    2175       233097 :     name_space = gnc_commodity_table_map_namespace(name_space);
    2176       233097 :     return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
    2177              : }
    2178              : 
    2179              : 
    2180              : gnc_commodity *
    2181           45 : gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
    2182              : {
    2183              :     QofCollection *col;
    2184           45 :     if (!guid || !book) return nullptr;
    2185           45 :     col = qof_book_get_collection (book, GNC_ID_COMMODITY);
    2186           45 :     return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
    2187              : }
    2188              : 
    2189              : /********************************************************************
    2190              :  * gnc_commodity_table_delete_namespace
    2191              :  * delete a namespace
    2192              :  ********************************************************************/
    2193              : 
    2194              : static int
    2195        43852 : ns_helper(gpointer key, gpointer value, gpointer user_data)
    2196              : {
    2197        43852 :     auto c = GNC_COMMODITY(value);
    2198        43852 :     gnc_commodity_destroy(c);
    2199        43852 :     CACHE_REMOVE(static_cast<char*>(key));  /* key is commodity mnemonic */
    2200        43852 :     return TRUE;
    2201              : }
    2202              : 
    2203              : void
    2204          625 : gnc_commodity_table_delete_namespace(gnc_commodity_table * table,
    2205              :                                      const char * name_space)
    2206              : {
    2207              :     gnc_commodity_namespace * ns;
    2208              : 
    2209          625 :     if (!table) return;
    2210              : 
    2211          625 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2212          625 :     if (!ns)
    2213            0 :         return;
    2214              : 
    2215          625 :     qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
    2216          625 :     g_hash_table_remove(table->ns_table, name_space);
    2217          625 :     table->ns_list = g_list_remove(table->ns_list, ns);
    2218              : 
    2219          625 :     g_list_free(ns->cm_list);
    2220          625 :     ns->cm_list = nullptr;
    2221              : 
    2222          625 :     g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
    2223          625 :     g_hash_table_destroy(ns->cm_table);
    2224          625 :     CACHE_REMOVE(ns->name);
    2225              : 
    2226          625 :     qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
    2227              :     /* qof_instance_release(&ns->inst); */
    2228          625 :     g_object_unref(ns);
    2229              : }
    2230              : 
    2231              : /********************************************************************
    2232              :  * gnc_commodity_table_foreach_commodity
    2233              :  * call user-defined function once for every commodity in every
    2234              :  * namespace
    2235              :  ********************************************************************/
    2236              : 
    2237              : typedef struct
    2238              : {
    2239              :     gboolean ok;
    2240              :     gboolean (*func)(gnc_commodity *, gpointer);
    2241              :     gpointer user_data;
    2242              : } IterData;
    2243              : 
    2244              : static void
    2245         5100 : iter_commodity (gpointer key, gpointer value, gpointer user_data)
    2246              : {
    2247         5100 :     IterData *iter_data = (IterData *) user_data;
    2248         5100 :     gnc_commodity *cm = (gnc_commodity *) value;
    2249              : 
    2250         5100 :     if (iter_data->ok)
    2251              :     {
    2252         5100 :         iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
    2253              :     }
    2254         5100 : }
    2255              : 
    2256              : static void
    2257           67 : iter_namespace (gpointer key, gpointer value, gpointer user_data)
    2258              : {
    2259           67 :     GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
    2260           67 :     g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
    2261           67 : }
    2262              : 
    2263              : gboolean
    2264           26 : gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
    2265              :                                        gboolean (*f)(gnc_commodity *, gpointer),
    2266              :                                        gpointer user_data)
    2267              : {
    2268              :     IterData iter_data;
    2269              : 
    2270           26 :     if (!tbl || !f) return FALSE;
    2271              : 
    2272           26 :     iter_data.ok = TRUE;
    2273           26 :     iter_data.func = f;
    2274           26 :     iter_data.user_data = user_data;
    2275              : 
    2276           26 :     g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
    2277              : 
    2278           26 :     return iter_data.ok;
    2279              : }
    2280              : 
    2281              : /********************************************************************
    2282              :  * gnc_commodity_table_destroy
    2283              :  * cleanup and free.
    2284              :  ********************************************************************/
    2285              : 
    2286              : void
    2287          213 : gnc_commodity_table_destroy(gnc_commodity_table * t)
    2288              : {
    2289              :     gnc_commodity_namespace * ns;
    2290              :     GList *item, *next;
    2291              : 
    2292          213 :     if (!t) return;
    2293          213 :     ENTER ("table=%p", t);
    2294              : 
    2295          838 :     for (item = t->ns_list; item; item = next)
    2296              :     {
    2297          625 :         next = g_list_next(item);
    2298          625 :         ns = static_cast<gnc_commodity_namespace*>(item->data);
    2299          625 :         gnc_commodity_table_delete_namespace(t, ns->name);
    2300              :     }
    2301              : 
    2302          213 :     g_list_free(t->ns_list);
    2303          213 :     t->ns_list = nullptr;
    2304          213 :     g_hash_table_destroy(t->ns_table);
    2305          213 :     t->ns_table = nullptr;
    2306          213 :     LEAVE ("table=%p", t);
    2307          213 :     g_free(t);
    2308              : }
    2309              : 
    2310              : /* =========================================================== */
    2311              : 
    2312              : /********************************************************************
    2313              :  * gnc_commodity_table_add_default_data
    2314              :  ********************************************************************/
    2315              : 
    2316              : #define CUR_I18N(String) dgettext ("iso_4217", String)
    2317              : 
    2318              : gboolean
    2319          315 : gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
    2320              : {
    2321              :     QofCollection *col;
    2322              :     gnc_commodity* c;
    2323              : 
    2324          315 :     ENTER ("table=%p", table);
    2325          315 :     gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
    2326          315 :     c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
    2327          315 :     gnc_commodity_table_insert(table, c);
    2328              : 
    2329              : #include "iso-4217-currencies.c"
    2330              : 
    2331              :     /* We've just created the default namespaces and currencies.  Mark
    2332              :      * these collections as clean because there is no USER entered data
    2333              :      * in these collections as of yet. */
    2334          315 :     col = qof_book_get_collection(book, GNC_ID_COMMODITY);
    2335          315 :     qof_collection_mark_clean(col);
    2336          315 :     col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
    2337          315 :     qof_collection_mark_clean(col);
    2338              : 
    2339          315 :     LEAVE ("table=%p", table);
    2340          315 :     return TRUE;
    2341              : }
    2342              : 
    2343              : /********************************************************************
    2344              :  ********************************************************************/
    2345              : /* QofObject function implementation and registration */
    2346              : 
    2347              : #ifdef _MSC_VER
    2348              : /* MSVC compiler doesn't have C99 "designated initializers"
    2349              :  * so we wrap them in a macro that is empty on MSVC. */
    2350              : # define DI(x) /* */
    2351              : #else
    2352              : # define DI(x) x
    2353              : #endif
    2354              : static QofObject commodity_object_def =
    2355              : {
    2356              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2357              :     DI(.e_type            = ) GNC_ID_COMMODITY,
    2358              :     DI(.type_label        = ) "Commodity",
    2359              :     DI(.create            = ) nullptr,
    2360              :     DI(.book_begin        = ) nullptr,
    2361              :     DI(.book_end          = ) nullptr,
    2362              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2363              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2364              :     DI(.foreach           = ) qof_collection_foreach,
    2365              :     DI(.printable         = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
    2366              : };
    2367              : 
    2368              : static QofObject namespace_object_def =
    2369              : {
    2370              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2371              :     DI(.e_type            = ) GNC_ID_COMMODITY_NAMESPACE,
    2372              :     DI(.type_label        = ) "Namespace",
    2373              :     DI(.create            = ) nullptr,
    2374              :     DI(.book_begin        = ) nullptr,
    2375              :     DI(.book_end          = ) nullptr,
    2376              :     DI(.is_dirty          = ) nullptr,
    2377              :     DI(.mark_clean        = ) nullptr,
    2378              :     DI(.foreach           = ) nullptr,
    2379              :     DI(.printable         = ) nullptr,
    2380              : };
    2381              : 
    2382              : static void
    2383          326 : commodity_table_book_begin (QofBook *book)
    2384              : {
    2385              :     gnc_commodity_table *ct;
    2386          326 :     ENTER ("book=%p", book);
    2387              : 
    2388          326 :     if (gnc_commodity_table_get_table(book))
    2389           15 :         return;
    2390              : 
    2391          311 :     ct = gnc_commodity_table_new ();
    2392          311 :     qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
    2393              : 
    2394          311 :     if (!gnc_commodity_table_add_default_data(ct, book))
    2395              :     {
    2396            0 :         PWARN("unable to initialize book's commodity_table");
    2397              :     }
    2398              : 
    2399          311 :     LEAVE ("book=%p", book);
    2400              : }
    2401              : 
    2402              : static void
    2403          200 : commodity_table_book_end (QofBook *book)
    2404              : {
    2405              :     gnc_commodity_table *ct;
    2406              : 
    2407          200 :     ct = gnc_commodity_table_get_table (book);
    2408          200 :     qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
    2409          200 :     gnc_commodity_table_destroy (ct);
    2410          200 : }
    2411              : 
    2412              : static QofObject commodity_table_object_def =
    2413              : {
    2414              :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2415              :     DI(.e_type            = ) GNC_ID_COMMODITY_TABLE,
    2416              :     DI(.type_label        = ) "CommodityTable",
    2417              :     DI(.create            = ) nullptr,
    2418              :     DI(.book_begin        = ) commodity_table_book_begin,
    2419              :     DI(.book_end          = ) commodity_table_book_end,
    2420              :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2421              :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2422              :     DI(.foreach           = ) nullptr,
    2423              :     DI(.printable         = ) nullptr,
    2424              :     DI(.version_cmp       = ) nullptr,
    2425              : };
    2426              : 
    2427              : gboolean
    2428          101 : gnc_commodity_table_register (void)
    2429              : {
    2430          101 :     if (!qof_object_register (&commodity_object_def))
    2431            1 :         return FALSE;
    2432          100 :     if (!qof_object_register (&namespace_object_def))
    2433            0 :         return FALSE;
    2434          100 :     return qof_object_register (&commodity_table_object_def);
    2435              : }
    2436              : 
    2437              : /* *******************************************************************
    2438              : *  gnc_monetary methods
    2439              : ********************************************************************/
    2440              : 
    2441              : /** Add a gnc_monetary to the list */
    2442              : MonetaryList *
    2443           44 : gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
    2444              : {
    2445           44 :     MonetaryList *l = list, *tmp;
    2446           70 :     for (tmp = list; tmp; tmp = tmp->next)
    2447              :     {
    2448           49 :         auto list_mon = static_cast<gnc_monetary*>(tmp->data);
    2449           49 :         if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
    2450              :         {
    2451           23 :             list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
    2452              :                                               GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    2453           23 :             break;
    2454              :         }
    2455              :     }
    2456              : 
    2457              :     /* See if we found an entry, and add one if not */
    2458           44 :     if (tmp == nullptr)
    2459              :     {
    2460           21 :         auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
    2461           21 :         *new_mon = add_mon;
    2462           21 :         l = g_list_prepend(l, new_mon);
    2463              :     }
    2464              : 
    2465           44 :     return l;
    2466              : }
    2467              : 
    2468              : /** Delete all entries in the list that have zero value.  Return list
    2469              :     pointer will be a null pointer if there are no non-zero entries **/
    2470              : MonetaryList *
    2471           12 : gnc_monetary_list_delete_zeros(MonetaryList *list)
    2472              : {
    2473              :     MonetaryList *node, *next;
    2474           33 :     for (node = list; node; node = next)
    2475              :     {
    2476           21 :         auto mon = static_cast<gnc_monetary*>(node->data);
    2477           21 :         next = node->next;
    2478           21 :         if (gnc_numeric_zero_p(mon->value))
    2479              :         {
    2480           10 :             g_free(mon);
    2481           10 :             list = g_list_delete_link(list, node);
    2482              :         }
    2483              :     }
    2484           12 :     return list;
    2485              : }
    2486              : 
    2487              : /** Free a MonetaryList and all the monetaries it points to */
    2488              : void
    2489           11 : gnc_monetary_list_free(MonetaryList *list)
    2490              : {
    2491              :     MonetaryList *tmp;
    2492           22 :     for (tmp = list; tmp; tmp = tmp->next)
    2493              :     {
    2494           11 :         g_free(tmp->data);
    2495              :     }
    2496              : 
    2497           11 :     g_list_free(list);
    2498           11 : }
    2499              : 
    2500              : /* ========================= END OF FILE ============================== */
        

Generated by: LCOV version 2.0-1