LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-commodity.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 75.1 % 947 711
Test Date: 2025-06-27 06:18:36 Functions: 85.0 % 133 113
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     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                 :      125826 :     const char* get_internal_name () const { return m_internal_name.c_str(); }
     162                 :        5886 :     gnc_quote_source_s (gboolean supported, QuoteSourceType type,
     163                 :             :                         const char* username, const char* int_name)
     164                 :        5886 :         : m_supported{supported}
     165                 :        5886 :         , m_type{type}
     166                 :       11772 :         , m_user_name{username ? username : ""}
     167                 :       17658 :         , 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", "Amsterdam Euronext eXchange, NL"), "aex" },
     193                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Association of Mutual Funds in India"), "amfiindia" },
     194                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Athens Exchange Group, GR"), "asegr" },
     195                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Australian Stock Exchange, AU"), "asx" },
     196                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Italian Stock Exchange, IT"), "borsa_italiana" },
     197                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "BSE India, IN"), "bseindia" },
     198                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Bucharest Stock Exchange, RO"), "bvb" },
     199                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "China Merchants Bank, CN"), "cmbchina" },
     200                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Colombo Stock Exchange, LK"), "cse" },
     201                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "comdirect, DE"), "comdirect" },
     202                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Consors Bank, DE"), "consorsbank" },
     203                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Deka Investments, DE"), "deka" },
     204                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Financial Times Funds service, GB"), "ftfunds" },
     205                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Finanzpartner, DE"), "finanzpartner" },
     206                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "FondsWeb, DE"), "fondsweb" },
     207                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "GoldMoney precious metals"), "goldmoney" },
     208                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Google Web, US Stocks"), "googleweb" },
     209                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Market Watch"), "marketwatch" },
     210                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, CH"), "morningstarch" },
     211                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, GB"), "morningstaruk" },
     212                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Morningstar, JP"), "morningstarjp" },
     213                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Motley Fool"), "fool" },
     214                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "New Zealand stock eXchange, NZ"), "nzx" },
     215                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "NSE (National Stock Exchange), IN"), "nseindia" },
     216                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "OnVista, DE"), "onvista"},
     217                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Paris Stock Exchange/Boursorama, FR"), "bourso" },
     218                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "S-Investor, DE"), "sinvestor"},
     219                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Sharenet, ZA"), "za" },
     220                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "SIX Swiss Exchange shares, CH"), "six" },
     221                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "StockData"), "stockdata" },
     222                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Stooq, PL"), "stooq" },
     223                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Tesouro Direto bonds, BR"), "tesouro_direto" },
     224                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Toronto Stock eXchange, CA"), "tsx" },
     225                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Tradegate, DE"), "tradegate" },
     226                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Treasury Direct bonds, US"), "treasurydirect" },
     227                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Twelve Data"), "twelvedata" },
     228                 :             :     { false, SOURCE_SINGLE, NC_("FQ Source", "Union Investment, DE"), "unionfunds" },
     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", "India (BSEIndia, NSEIndia)"), "india"},
     242                 :             :     { false, SOURCE_MULTI, NC_("FQ Source", "Nasdaq (Alphavantage, FinanceAPI, …)"), "nasdaq" },
     243                 :             :     { false, SOURCE_MULTI, NC_("FQ Source", "NYSE (Alphavantage, FinanceAPI, …)"), "nyse" },
     244                 :             :     { false, SOURCE_MULTI, NC_("FQ Source", "U.K. Funds (FTfunds, MorningstarUK)"), "ukfunds" },
     245                 :             :     { false, SOURCE_MULTI, NC_("FQ Source", "USA (Alphavantage, FinanceAPI, …)"), "usa" },
     246                 :             : };
     247                 :             : 
     248                 :             : static QuoteSourceList new_quote_sources;
     249                 :             : 
     250                 :             : // cannot use map or unordered_map because order must be preserved
     251                 :             : static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
     252                 :             :     {
     253                 :             :         { SOURCE_CURRENCY, currency_quote_sources },
     254                 :             :         { SOURCE_SINGLE, single_quote_sources },
     255                 :             :         { SOURCE_MULTI, multiple_quote_sources },
     256                 :             :         { SOURCE_UNKNOWN, new_quote_sources }
     257                 :             :     };
     258                 :             : 
     259                 :             : /********************************************************************
     260                 :             :  * gnc_quote_source_fq_installed
     261                 :             :  *
     262                 :             :  * This function indicates whether or not the Finance::Quote module
     263                 :             :  * is installed on a users computer.
     264                 :             :  ********************************************************************/
     265                 :             : gboolean
     266                 :           0 : gnc_quote_source_fq_installed (void)
     267                 :             : {
     268                 :           0 :     return (!fq_version.empty());
     269                 :             : }
     270                 :             : 
     271                 :             : 
     272                 :             : /********************************************************************
     273                 :             :  * gnc_quote_source_fq_version
     274                 :             :  *
     275                 :             :  * This function the version of the Finance::Quote module installed
     276                 :             :  * on a user's computer or nullptr if no installation is found.
     277                 :             :  ********************************************************************/
     278                 :             : const char*
     279                 :           0 : gnc_quote_source_fq_version (void)
     280                 :             : {
     281                 :           0 :     return fq_version.c_str();
     282                 :             : }
     283                 :             : 
     284                 :             : static QuoteSourceList&
     285                 :          11 : get_quote_source_from_type (QuoteSourceType type)
     286                 :             : {
     287                 :          11 :     auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
     288                 :          38 :                                           [type] (const auto& qs) { return type == qs.first; });
     289                 :             : 
     290                 :          11 :     if (quote_sources_it != quote_sources_map.end())
     291                 :           7 :         return quote_sources_it->second;
     292                 :             : 
     293                 :           4 :     PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
     294                 :           4 :     return new_quote_sources;
     295                 :             : }
     296                 :             : 
     297                 :             : /********************************************************************
     298                 :             :  * gnc_quote_source_num_entries
     299                 :             :  *
     300                 :             :  * Return the number of entries for a given type of price source.
     301                 :             :  ********************************************************************/
     302                 :           5 : gint gnc_quote_source_num_entries(QuoteSourceType type)
     303                 :             : {
     304                 :           5 :     auto source{get_quote_source_from_type(type)};
     305                 :          15 :     return std::distance(source.begin(), source.end());
     306                 :           5 : }
     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                 :          21 : gnc_quote_source_add_new (const char *source_name, gboolean supported)
     320                 :             : {
     321                 :          21 :     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                 :          21 :     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                 :      125169 : gnc_quote_source_lookup_by_internal(const char * name)
     353                 :             : {
     354                 :      125169 :     if (!name || !*name)
     355                 :           1 :         return nullptr;
     356                 :             : 
     357                 :      125206 :     for (const auto& [_, sources] : quote_sources_map)
     358                 :             :     {
     359                 :      125205 :         auto source_it = std::find_if (sources.begin(), sources.end(),
     360                 :      125293 :                                        [name] (const auto& qs)
     361                 :      125293 :                                        { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
     362                 :      125205 :         if (source_it != sources.end())
     363                 :      125167 :             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                 :          83 : gnc_quote_source_get_internal_name (const gnc_quote_source *source)
     439                 :             : {
     440                 :          83 :     ENTER("%p", source);
     441                 :          83 :     if (!source)
     442                 :             :     {
     443                 :           6 :         LEAVE("bad source");
     444                 :           6 :         return nullptr;
     445                 :             :     }
     446                 :          77 :     LEAVE("internal name %s", source->get_internal_name());
     447                 :          77 :     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                 :          30 :     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                 :      503012 : gnc_commodity_begin_edit (gnc_commodity *cm)
     494                 :             : {
     495                 :      503012 :     qof_begin_edit(&cm->inst);
     496                 :      503012 : }
     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                 :       67391 : static void noop (QofInstance *inst) {}
     505                 :             : 
     506                 :             : static void
     507                 :       41354 : comm_free(QofInstance* inst)
     508                 :             : {
     509                 :       41354 :     commodity_free( GNC_COMMODITY(inst) );
     510                 :       41354 : }
     511                 :             : 
     512                 :             : void
     513                 :      503012 : gnc_commodity_commit_edit (gnc_commodity *cm)
     514                 :             : {
     515                 :      503012 :     if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
     516                 :      108745 :     qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
     517                 :             : }
     518                 :             : 
     519                 :             : /********************************************************************
     520                 :             :  * gnc_commodity_new
     521                 :             :  ********************************************************************/
     522                 :             : 
     523                 :             : static void
     524                 :      461444 : mark_commodity_dirty (gnc_commodity *cm)
     525                 :             : {
     526                 :      461444 :     qof_instance_set_dirty(&cm->inst);
     527                 :      461444 :     qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
     528                 :      461444 : }
     529                 :             : 
     530                 :             : static void
     531                 :      265208 : reset_printname(gnc_commodityPrivate *priv)
     532                 :             : {
     533                 :      265208 :     g_free(priv->printname);
     534                 :      530416 :     priv->printname = g_strdup_printf("%s (%s)",
     535                 :      265208 :                                       priv->mnemonic ? priv->mnemonic : "",
     536                 :      265208 :                                       priv->fullname ? priv->fullname : "");
     537                 :      265208 : }
     538                 :             : 
     539                 :             : static void
     540                 :      198759 : reset_unique_name(gnc_commodityPrivate *priv)
     541                 :             : {
     542                 :             :     gnc_commodity_namespace *ns;
     543                 :             : 
     544                 :      198759 :     g_free(priv->unique_name);
     545                 :      198759 :     ns = priv->name_space;
     546                 :      198759 :     priv->unique_name = g_strdup_printf("%s::%s",
     547                 :             :                                         ns ? ns->name : "",
     548                 :      198759 :                                         priv->mnemonic ? priv->mnemonic : "");
     549                 :      198759 : }
     550                 :             : 
     551                 :             : /* GObject Initialization */
     552                 :     1692747 : G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
     553                 :             : 
     554                 :             : static void
     555                 :       66320 : gnc_commodity_init(gnc_commodity* com)
     556                 :             : {
     557                 :             :     gnc_commodityPrivate* priv;
     558                 :             : 
     559                 :       66320 :     priv = GET_PRIVATE(com);
     560                 :             : 
     561                 :       66320 :     priv->name_space = nullptr;
     562                 :       66320 :     priv->fullname = CACHE_INSERT("");
     563                 :       66320 :     priv->mnemonic = CACHE_INSERT("");
     564                 :       66320 :     priv->cusip = CACHE_INSERT("");
     565                 :       66320 :     priv->fraction = 10000;
     566                 :       66320 :     priv->quote_flag = 0;
     567                 :       66320 :     priv->quote_source = nullptr;
     568                 :       66320 :     priv->quote_tz = CACHE_INSERT("");
     569                 :             : 
     570                 :       66320 :     reset_printname(priv);
     571                 :       66320 :     reset_unique_name(priv);
     572                 :       66320 : }
     573                 :             : 
     574                 :             : static void
     575                 :       41510 : gnc_commodity_dispose(GObject *comp)
     576                 :             : {
     577                 :       41510 :     G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
     578                 :       41510 : }
     579                 :             : 
     580                 :             : static void
     581                 :       41510 : gnc_commodity_finalize(GObject* comp)
     582                 :             : {
     583                 :       41510 :     G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
     584                 :       41510 : }
     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                 :         318 : 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                 :         318 :     g_return_if_fail(GNC_IS_COMMODITY(object));
     601                 :             : 
     602                 :         318 :     commodity = GNC_COMMODITY(object);
     603                 :         318 :     priv = GET_PRIVATE(commodity);
     604                 :         318 :     switch (prop_id)
     605                 :             :     {
     606                 :           0 :     case PROP_NAMESPACE:
     607                 :           0 :         g_value_take_object(value, priv->name_space);
     608                 :           0 :         break;
     609                 :          53 :     case PROP_FULL_NAME:
     610                 :          53 :         g_value_set_string(value, priv->fullname);
     611                 :          53 :         break;
     612                 :          53 :     case PROP_MNEMONIC:
     613                 :          53 :         g_value_set_string(value, priv->mnemonic);
     614                 :          53 :         break;
     615                 :           0 :     case PROP_PRINTNAME:
     616                 :           0 :         g_value_set_string(value, priv->printname);
     617                 :           0 :         break;
     618                 :          53 :     case PROP_CUSIP:
     619                 :          53 :         g_value_set_string(value, priv->cusip);
     620                 :          53 :         break;
     621                 :          53 :     case PROP_FRACTION:
     622                 :          53 :         g_value_set_int(value, priv->fraction);
     623                 :          53 :         break;
     624                 :           0 :     case PROP_UNIQUE_NAME:
     625                 :           0 :         g_value_set_string(value, priv->unique_name);
     626                 :           0 :         break;
     627                 :          53 :     case PROP_QUOTE_FLAG:
     628                 :          53 :         g_value_set_boolean(value, priv->quote_flag);
     629                 :          53 :         break;
     630                 :           0 :     case PROP_QUOTE_SOURCE:
     631                 :           0 :         g_value_set_pointer(value, priv->quote_source);
     632                 :           0 :         break;
     633                 :          53 :     case PROP_QUOTE_TZ:
     634                 :          53 :         g_value_set_string(value, priv->quote_tz);
     635                 :          53 :         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                 :          48 : gnc_commodity_set_property (GObject         *object,
     644                 :             :                             guint            prop_id,
     645                 :             :                             const GValue    *value,
     646                 :             :                             GParamSpec      *pspec)
     647                 :             : {
     648                 :             :     gnc_commodity *commodity;
     649                 :             : 
     650                 :          48 :     g_return_if_fail(GNC_IS_COMMODITY(object));
     651                 :             : 
     652                 :          48 :     commodity = GNC_COMMODITY(object);
     653                 :          48 :     g_assert (qof_instance_get_editlevel(commodity));
     654                 :             : 
     655                 :          48 :     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                 :           8 :     case PROP_FULL_NAME:
     661                 :           8 :         gnc_commodity_set_fullname(commodity, g_value_get_string(value));
     662                 :           8 :         break;
     663                 :           8 :     case PROP_MNEMONIC:
     664                 :           8 :         gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
     665                 :           8 :         break;
     666                 :           8 :     case PROP_CUSIP:
     667                 :           8 :         gnc_commodity_set_cusip(commodity, g_value_get_string(value));
     668                 :           8 :         break;
     669                 :           8 :     case PROP_FRACTION:
     670                 :           8 :         gnc_commodity_set_fraction(commodity, g_value_get_int(value));
     671                 :           8 :         break;
     672                 :           8 :     case PROP_QUOTE_FLAG:
     673                 :           8 :         gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
     674                 :           8 :         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                 :           8 :     case PROP_QUOTE_TZ:
     679                 :           8 :         gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
     680                 :           8 :         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                 :          62 : gnc_commodity_class_init(struct _GncCommodityClass* klass)
     688                 :             : {
     689                 :          62 :     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     690                 :             : 
     691                 :          62 :     gobject_class->dispose = gnc_commodity_dispose;
     692                 :          62 :     gobject_class->finalize = gnc_commodity_finalize;
     693                 :          62 :     gobject_class->set_property = gnc_commodity_set_property;
     694                 :          62 :     gobject_class->get_property = gnc_commodity_get_property;
     695                 :             : 
     696                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 :     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                 :          62 : }
     775                 :             : 
     776                 :             : gnc_commodity *
     777                 :       66320 : gnc_commodity_new(QofBook *book, const char * fullname,
     778                 :             :                   const char * name_space, const char * mnemonic,
     779                 :             :                   const char * cusip, int fraction)
     780                 :             : {
     781                 :       66320 :     auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
     782                 :             : 
     783                 :       66320 :     qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
     784                 :       66320 :     gnc_commodity_begin_edit(retval);
     785                 :             : 
     786                 :       66320 :     if ( name_space != nullptr )
     787                 :             :     {
     788                 :             :         /* Prevent setting anything except template in namespace template. */
     789                 :       66464 :         if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
     790                 :         273 :             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                 :       66191 :         gnc_commodity_set_namespace(retval, name_space);
     797                 :       66191 :         if (gnc_commodity_namespace_is_iso(name_space))
     798                 :             :         {
     799                 :       62650 :             gnc_commodity_set_quote_source(retval,
     800                 :             :                                            gnc_quote_source_lookup_by_internal("currency") );
     801                 :             :         }
     802                 :             :     }
     803                 :       66320 :     gnc_commodity_set_fullname(retval, fullname);
     804                 :       66320 :     gnc_commodity_set_mnemonic(retval, mnemonic);
     805                 :       66320 :     gnc_commodity_set_cusip(retval, cusip);
     806                 :       66320 :     gnc_commodity_set_fraction(retval, fraction);
     807                 :       66320 :     mark_commodity_dirty (retval);
     808                 :       66320 :     gnc_commodity_commit_edit(retval);
     809                 :             : 
     810                 :       66320 :     qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
     811                 :             : 
     812                 :       66320 :     return retval;
     813                 :             : }
     814                 :             : 
     815                 :             : 
     816                 :             : /********************************************************************
     817                 :             :  * gnc_commodity_destroy
     818                 :             :  ********************************************************************/
     819                 :             : 
     820                 :             : static void
     821                 :       41354 : commodity_free(gnc_commodity * cm)
     822                 :             : {
     823                 :             :     QofBook *book;
     824                 :             :     gnc_commodity_table *table;
     825                 :             :     gnc_commodityPrivate* priv;
     826                 :             : 
     827                 :       41354 :     if (!cm) return;
     828                 :             : 
     829                 :       41354 :     book = qof_instance_get_book(&cm->inst);
     830                 :       41354 :     table = gnc_commodity_table_get_table(book);
     831                 :       41354 :     gnc_commodity_table_remove(table, cm);
     832                 :       41354 :     priv = GET_PRIVATE(cm);
     833                 :             : 
     834                 :       41354 :     qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
     835                 :             : 
     836                 :             :     /* Set at creation */
     837                 :       41354 :     CACHE_REMOVE (priv->fullname);
     838                 :       41354 :     CACHE_REMOVE (priv->cusip);
     839                 :       41354 :     CACHE_REMOVE (priv->mnemonic);
     840                 :       41354 :     CACHE_REMOVE (priv->quote_tz);
     841                 :       41354 :     priv->name_space = nullptr;
     842                 :             : 
     843                 :             :     /* Set through accessor functions */
     844                 :       41354 :     priv->quote_source = nullptr;
     845                 :             : 
     846                 :             :     /* Automatically generated */
     847                 :       41354 :     g_free(priv->printname);
     848                 :       41354 :     priv->printname = nullptr;
     849                 :             : 
     850                 :       41354 :     g_free(priv->unique_name);
     851                 :       41354 :     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                 :       41354 :     g_object_unref(cm);
     867                 :             : }
     868                 :             : 
     869                 :             : void
     870                 :       41354 : gnc_commodity_destroy(gnc_commodity * cm)
     871                 :             : {
     872                 :       41354 :     gnc_commodity_begin_edit(cm);
     873                 :       41354 :     qof_instance_set_destroying(cm, TRUE);
     874                 :       41354 :     gnc_commodity_commit_edit(cm);
     875                 :       41354 : }
     876                 :             : 
     877                 :             : void
     878                 :          61 : gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
     879                 :             : {
     880                 :          61 :     gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
     881                 :          61 :     gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
     882                 :             : 
     883                 :          61 :     gnc_commodity_set_fullname (dest, src_priv->fullname);
     884                 :          61 :     gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
     885                 :          61 :     dest_priv->name_space = src_priv->name_space;
     886                 :          61 :     gnc_commodity_set_fraction (dest, src_priv->fraction);
     887                 :          61 :     gnc_commodity_set_cusip (dest, src_priv->cusip);
     888                 :          61 :     gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
     889                 :          61 :     gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src));
     890                 :          61 :     gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
     891                 :          61 :     qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
     892                 :          61 : }
     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                 :      130251 : gnc_commodity_get_mnemonic(const gnc_commodity * cm)
     931                 :             : {
     932                 :      130251 :     if (!cm) return nullptr;
     933                 :      130251 :     return GET_PRIVATE(cm)->mnemonic;
     934                 :             : }
     935                 :             : 
     936                 :             : /********************************************************************
     937                 :             :  * gnc_commodity_get_printname
     938                 :             :  ********************************************************************/
     939                 :             : 
     940                 :             : const char *
     941                 :       31024 : gnc_commodity_get_printname(const gnc_commodity * cm)
     942                 :             : {
     943                 :       31024 :     if (!cm) return nullptr;
     944                 :       31024 :     return GET_PRIVATE(cm)->printname;
     945                 :             : }
     946                 :             : 
     947                 :             : 
     948                 :             : /********************************************************************
     949                 :             :  * gnc_commodity_get_namespace
     950                 :             :  ********************************************************************/
     951                 :             : 
     952                 :             : const char *
     953                 :       12743 : gnc_commodity_get_namespace(const gnc_commodity * cm)
     954                 :             : {
     955                 :       12743 :     if (!cm) return nullptr;
     956                 :       12743 :     return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
     957                 :             : }
     958                 :             : 
     959                 :             : gnc_commodity_namespace *
     960                 :           0 : gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
     961                 :             : {
     962                 :           0 :     if (!cm) return nullptr;
     963                 :           0 :     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                 :          82 : gnc_commodity_get_unique_name(const gnc_commodity * cm)
     984                 :             : {
     985                 :          82 :     if (!cm) return nullptr;
     986                 :          76 :     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                 :      181174 : gnc_commodity_get_fraction(const gnc_commodity * cm)
    1007                 :             : {
    1008                 :      181174 :     if (!cm) return 0;
    1009                 :      181031 :     return GET_PRIVATE(cm)->fraction;
    1010                 :             : }
    1011                 :             : 
    1012                 :             : /********************************************************************
    1013                 :             :  * gnc_commodity_get_auto_quote_control_flag
    1014                 :             :  ********************************************************************/
    1015                 :             : 
    1016                 :             : static gboolean
    1017                 :         864 : gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
    1018                 :             : {
    1019                 :         864 :     GValue v = G_VALUE_INIT;
    1020                 :         864 :     gboolean retval = TRUE;
    1021                 :             : 
    1022                 :         864 :     if (!cm) return FALSE;
    1023                 :         864 :     qof_instance_get_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
    1024                 :         864 :     if (G_VALUE_HOLDS_STRING (&v) &&
    1025                 :           0 :         strcmp(g_value_get_string (&v), "false") == 0)
    1026                 :           0 :         retval = FALSE;
    1027                 :         864 :     g_value_unset (&v);
    1028                 :         864 :     return retval;
    1029                 :             : }
    1030                 :             : 
    1031                 :             : /********************************************************************
    1032                 :             :  * gnc_commodity_get_quote_flag
    1033                 :             :  ********************************************************************/
    1034                 :             : 
    1035                 :             : gboolean
    1036                 :        1041 : gnc_commodity_get_quote_flag(const gnc_commodity *cm)
    1037                 :             : {
    1038                 :        1041 :     if (!cm) return FALSE;
    1039                 :        1041 :     return (GET_PRIVATE(cm)->quote_flag);
    1040                 :             : }
    1041                 :             : 
    1042                 :             : /********************************************************************
    1043                 :             :  * gnc_commodity_get_quote_source
    1044                 :             :  ********************************************************************/
    1045                 :             : 
    1046                 :             : gnc_quote_source*
    1047                 :         164 : gnc_commodity_get_quote_source(const gnc_commodity *cm)
    1048                 :             : {
    1049                 :             :     gnc_commodityPrivate* priv;
    1050                 :             : 
    1051                 :         164 :     if (!cm) return nullptr;
    1052                 :         164 :     priv = GET_PRIVATE(cm);
    1053                 :         164 :     if (!priv->quote_source && gnc_commodity_is_iso(cm))
    1054                 :           0 :         return &currency_quote_sources.front();
    1055                 :         164 :     return priv->quote_source;
    1056                 :             : }
    1057                 :             : 
    1058                 :             : gnc_quote_source*
    1059                 :         146 : gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
    1060                 :             : {
    1061                 :         146 :     if (cm && gnc_commodity_is_iso(cm))
    1062                 :         146 :         return &currency_quote_sources.front();
    1063                 :             :     /* Should make this a user option at some point. */
    1064                 :           0 :     return gnc_quote_source_lookup_by_internal("alphavantage");
    1065                 :             : }
    1066                 :             : 
    1067                 :             : /********************************************************************
    1068                 :             :  * gnc_commodity_get_quote_tz
    1069                 :             :  ********************************************************************/
    1070                 :             : 
    1071                 :             : const char*
    1072                 :           4 : gnc_commodity_get_quote_tz(const gnc_commodity *cm)
    1073                 :             : {
    1074                 :           4 :     if (!cm) return nullptr;
    1075                 :           4 :     return GET_PRIVATE(cm)->quote_tz;
    1076                 :             : }
    1077                 :             : 
    1078                 :             : /********************************************************************
    1079                 :             :  * gnc_commodity_get_user_symbol
    1080                 :             :  ********************************************************************/
    1081                 :             : const char*
    1082                 :       61569 : gnc_commodity_get_user_symbol(const gnc_commodity *cm)
    1083                 :             : {
    1084                 :       61569 :     g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
    1085                 :             : 
    1086                 :       61569 :     GValue v = G_VALUE_INIT;
    1087                 :       61569 :     qof_instance_get_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
    1088                 :       61569 :     const char *rv = G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : nullptr;
    1089                 :       61569 :     g_value_unset (&v);
    1090                 :       61569 :     return rv;
    1091                 :             : }
    1092                 :             : 
    1093                 :             : /********************************************************************
    1094                 :             :  * gnc_commodity_get_default_symbol
    1095                 :             :  *******************************************************************/
    1096                 :             : const char*
    1097                 :       61110 : gnc_commodity_get_default_symbol(const gnc_commodity *cm)
    1098                 :             : {
    1099                 :       61110 :     if (!cm) return nullptr;
    1100                 :       61110 :     return GET_PRIVATE(cm)->default_symbol;
    1101                 :             : }
    1102                 :             : 
    1103                 :             : /********************************************************************
    1104                 :             :  * gnc_commodity_get_nice_symbol
    1105                 :             :  *******************************************************************/
    1106                 :             : const char*
    1107                 :       61567 : gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
    1108                 :             : {
    1109                 :             :     const char *nice_symbol;
    1110                 :             :     struct lconv *lc;
    1111                 :       61567 :     if (!cm) return nullptr;
    1112                 :             : 
    1113                 :       61567 :     nice_symbol = gnc_commodity_get_user_symbol(cm);
    1114                 :       61567 :     if (nice_symbol && *nice_symbol)
    1115                 :         464 :         return nice_symbol;
    1116                 :             : 
    1117                 :       61103 :     lc = gnc_localeconv();
    1118                 :       61103 :     nice_symbol = lc->currency_symbol;
    1119                 :       61103 :     if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
    1120                 :           0 :         return nice_symbol;
    1121                 :             : 
    1122                 :       61103 :     nice_symbol = gnc_commodity_get_default_symbol(cm);
    1123                 :       61103 :     if (nice_symbol && *nice_symbol)
    1124                 :       60855 :         return nice_symbol;
    1125                 :             : 
    1126                 :         248 :     return gnc_commodity_get_mnemonic(cm);
    1127                 :             : }
    1128                 :             : 
    1129                 :             : /********************************************************************
    1130                 :             :  * gnc_commodity_set_mnemonic
    1131                 :             :  ********************************************************************/
    1132                 :             : 
    1133                 :             : void
    1134                 :       66510 : gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
    1135                 :             : {
    1136                 :             :     gnc_commodityPrivate* priv;
    1137                 :             : 
    1138                 :       66510 :     if (!cm) return;
    1139                 :       66510 :     priv = GET_PRIVATE(cm);
    1140                 :       66510 :     if (priv->mnemonic == mnemonic) return;
    1141                 :             : 
    1142                 :       66467 :     gnc_commodity_begin_edit(cm);
    1143                 :       66467 :     CACHE_REMOVE (priv->mnemonic);
    1144                 :       66467 :     priv->mnemonic = CACHE_INSERT(mnemonic);
    1145                 :             : 
    1146                 :       66467 :     mark_commodity_dirty (cm);
    1147                 :       66467 :     reset_printname(priv);
    1148                 :       66467 :     reset_unique_name(priv);
    1149                 :       66467 :     gnc_commodity_commit_edit(cm);
    1150                 :             : }
    1151                 :             : 
    1152                 :             : /********************************************************************
    1153                 :             :  * gnc_commodity_set_namespace
    1154                 :             :  ********************************************************************/
    1155                 :             : 
    1156                 :             : void
    1157                 :       66320 : gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
    1158                 :             : {
    1159                 :             :     QofBook *book;
    1160                 :             :     gnc_commodity_table *table;
    1161                 :             :     gnc_commodity_namespace *nsp;
    1162                 :             :     gnc_commodityPrivate* priv;
    1163                 :             : 
    1164                 :       66320 :     if (!cm) return;
    1165                 :       66320 :     priv = GET_PRIVATE(cm);
    1166                 :       66320 :     book = qof_instance_get_book (&cm->inst);
    1167                 :       66320 :     table = gnc_commodity_table_get_table(book);
    1168                 :       66320 :     nsp = gnc_commodity_table_add_namespace(table, name_space, book);
    1169                 :       66320 :     if (priv->name_space == nsp)
    1170                 :         348 :         return;
    1171                 :             : 
    1172                 :       65972 :     gnc_commodity_begin_edit(cm);
    1173                 :       65972 :     priv->name_space = nsp;
    1174                 :       65972 :     if (nsp->iso4217)
    1175                 :       62442 :         priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
    1176                 :       65972 :     mark_commodity_dirty(cm);
    1177                 :       65972 :     reset_printname(priv);
    1178                 :       65972 :     reset_unique_name(priv);
    1179                 :       65972 :     gnc_commodity_commit_edit(cm);
    1180                 :             : }
    1181                 :             : 
    1182                 :             : /********************************************************************
    1183                 :             :  * gnc_commodity_set_fullname
    1184                 :             :  ********************************************************************/
    1185                 :             : 
    1186                 :             : void
    1187                 :       66492 : gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
    1188                 :             : {
    1189                 :             :     gnc_commodityPrivate* priv;
    1190                 :             : 
    1191                 :       66492 :     if (!cm) return;
    1192                 :       66492 :     priv = GET_PRIVATE(cm);
    1193                 :       66492 :     if (priv->fullname == fullname) return;
    1194                 :             : 
    1195                 :       66449 :     CACHE_REMOVE (priv->fullname);
    1196                 :       66449 :     priv->fullname = CACHE_INSERT (fullname);
    1197                 :             : 
    1198                 :       66449 :     gnc_commodity_begin_edit(cm);
    1199                 :       66449 :     mark_commodity_dirty(cm);
    1200                 :       66449 :     reset_printname(priv);
    1201                 :       66449 :     gnc_commodity_commit_edit(cm);
    1202                 :             : }
    1203                 :             : 
    1204                 :             : /********************************************************************
    1205                 :             :  * gnc_commodity_set_cusip
    1206                 :             :  ********************************************************************/
    1207                 :             : 
    1208                 :             : void
    1209                 :       66435 : gnc_commodity_set_cusip(gnc_commodity * cm,
    1210                 :             :                         const char * cusip)
    1211                 :             : {
    1212                 :             :     gnc_commodityPrivate* priv;
    1213                 :             : 
    1214                 :       66435 :     if (!cm) return;
    1215                 :             : 
    1216                 :       66435 :     priv = GET_PRIVATE(cm);
    1217                 :       66435 :     if (priv->cusip == cusip) return;
    1218                 :             : 
    1219                 :       66392 :     gnc_commodity_begin_edit(cm);
    1220                 :       66392 :     CACHE_REMOVE (priv->cusip);
    1221                 :       66392 :     priv->cusip = CACHE_INSERT (cusip);
    1222                 :       66392 :     mark_commodity_dirty(cm);
    1223                 :       66392 :     gnc_commodity_commit_edit(cm);
    1224                 :             : }
    1225                 :             : 
    1226                 :             : /********************************************************************
    1227                 :             :  * gnc_commodity_set_fraction
    1228                 :             :  ********************************************************************/
    1229                 :             : 
    1230                 :             : void
    1231                 :       66492 : gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
    1232                 :             : {
    1233                 :       66492 :     if (!cm) return;
    1234                 :       66492 :     gnc_commodity_begin_edit(cm);
    1235                 :       66492 :     GET_PRIVATE(cm)->fraction = fraction;
    1236                 :       66492 :     mark_commodity_dirty(cm);
    1237                 :       66492 :     gnc_commodity_commit_edit(cm);
    1238                 :             : }
    1239                 :             : 
    1240                 :             : /********************************************************************
    1241                 :             :  * gnc_commodity_set_auto_quote_control_flag
    1242                 :             :  ********************************************************************/
    1243                 :             : 
    1244                 :             : static void
    1245                 :           0 : gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
    1246                 :             :         const gboolean flag)
    1247                 :             : {
    1248                 :           0 :     GValue v = G_VALUE_INIT;
    1249                 :           0 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1250                 :             : 
    1251                 :           0 :     if (!cm)
    1252                 :             :     {
    1253                 :           0 :         LEAVE("");
    1254                 :           0 :         return;
    1255                 :             :     }
    1256                 :           0 :     gnc_commodity_begin_edit(cm);
    1257                 :           0 :     if (flag)
    1258                 :           0 :         qof_instance_set_kvp (QOF_INSTANCE (cm), nullptr, 1, "auto_quote_control");
    1259                 :             :     else
    1260                 :             :     {
    1261                 :           0 :         g_value_init (&v, G_TYPE_STRING);
    1262                 :           0 :         g_value_set_string (&v, "false");
    1263                 :           0 :         qof_instance_set_kvp (QOF_INSTANCE (cm), &v, 1, "auto_quote_control");
    1264                 :             :     }
    1265                 :           0 :     g_value_unset (&v);
    1266                 :           0 :     mark_commodity_dirty(cm);
    1267                 :           0 :     gnc_commodity_commit_edit(cm);
    1268                 :           0 :     LEAVE("");
    1269                 :             : }
    1270                 :             : 
    1271                 :             : /********************************************************************
    1272                 :             :  * gnc_commodity_user_set_quote_flag
    1273                 :             :  ********************************************************************/
    1274                 :             : 
    1275                 :             : void
    1276                 :           0 : gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
    1277                 :             : {
    1278                 :             :     gnc_commodityPrivate* priv;
    1279                 :             : 
    1280                 :           0 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1281                 :             : 
    1282                 :           0 :     if (!cm)
    1283                 :             :     {
    1284                 :           0 :         LEAVE("");
    1285                 :           0 :         return;
    1286                 :             :     }
    1287                 :             : 
    1288                 :           0 :     priv = GET_PRIVATE(cm);
    1289                 :           0 :     gnc_commodity_begin_edit(cm);
    1290                 :           0 :     gnc_commodity_set_quote_flag(cm, flag);
    1291                 :           0 :     if (gnc_commodity_is_iso(cm))
    1292                 :             :     {
    1293                 :             :         /* For currencies, disable auto quote control if the quote flag is being
    1294                 :             :          * changed from its default value and enable it if the quote flag is being
    1295                 :             :          * reset to its default value.  The defaults for the quote flag are
    1296                 :             :          * disabled if no accounts are using the currency, and true otherwise.
    1297                 :             :          * Thus enable auto quote control if flag is FALSE and there are not any
    1298                 :             :          * accounts using this currency OR flag is TRUE and there are accounts
    1299                 :             :          * using this currency; otherwise disable auto quote control */
    1300                 :           0 :         gnc_commodity_set_auto_quote_control_flag(cm,
    1301                 :           0 :                 (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
    1302                 :             :     }
    1303                 :           0 :     gnc_commodity_commit_edit(cm);
    1304                 :           0 :     LEAVE("");
    1305                 :             : }
    1306                 :             : 
    1307                 :             : /********************************************************************
    1308                 :             :  * gnc_commodity_set_quote_flag
    1309                 :             :  ********************************************************************/
    1310                 :             : 
    1311                 :             : void
    1312                 :         375 : gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
    1313                 :             : {
    1314                 :         375 :     ENTER ("(cm=%p, flag=%d)", cm, flag);
    1315                 :             : 
    1316                 :         375 :     if (!cm) return;
    1317                 :         375 :     gnc_commodity_begin_edit(cm);
    1318                 :         375 :     GET_PRIVATE(cm)->quote_flag = flag;
    1319                 :         375 :     mark_commodity_dirty(cm);
    1320                 :         375 :     gnc_commodity_commit_edit(cm);
    1321                 :         375 :     LEAVE(" ");
    1322                 :             : }
    1323                 :             : 
    1324                 :             : /********************************************************************
    1325                 :             :  * gnc_commodity_set_quote_source
    1326                 :             :  ********************************************************************/
    1327                 :             : 
    1328                 :             : void
    1329                 :       62944 : gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
    1330                 :             : {
    1331                 :       62944 :     ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
    1332                 :             : 
    1333                 :       62944 :     if (!cm) return;
    1334                 :       62944 :     gnc_commodity_begin_edit(cm);
    1335                 :       62944 :     GET_PRIVATE(cm)->quote_source = src;
    1336                 :       62944 :     mark_commodity_dirty(cm);
    1337                 :       62944 :     gnc_commodity_commit_edit(cm);
    1338                 :       62944 :     LEAVE(" ");
    1339                 :             : }
    1340                 :             : 
    1341                 :             : /********************************************************************
    1342                 :             :  * gnc_commodity_set_quote_tz
    1343                 :             :  ********************************************************************/
    1344                 :             : 
    1345                 :             : void
    1346                 :          87 : gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
    1347                 :             : {
    1348                 :             :     gnc_commodityPrivate* priv;
    1349                 :             : 
    1350                 :          87 :     if (!cm) return;
    1351                 :             : 
    1352                 :          87 :     ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
    1353                 :             : 
    1354                 :          87 :     priv = GET_PRIVATE(cm);
    1355                 :             : 
    1356                 :          87 :     if (tz == priv->quote_tz)
    1357                 :             :     {
    1358                 :          61 :         LEAVE("Already correct TZ");
    1359                 :          61 :         return;
    1360                 :             :     }
    1361                 :             : 
    1362                 :          26 :     gnc_commodity_begin_edit(cm);
    1363                 :          26 :     CACHE_REMOVE (priv->quote_tz);
    1364                 :          26 :     priv->quote_tz = CACHE_INSERT (tz);
    1365                 :          26 :     mark_commodity_dirty(cm);
    1366                 :          26 :     gnc_commodity_commit_edit(cm);
    1367                 :          26 :     LEAVE(" ");
    1368                 :             : }
    1369                 :             : 
    1370                 :             : /********************************************************************
    1371                 :             :  * gnc_commodity_set_user_symbol
    1372                 :             :  ********************************************************************/
    1373                 :             : 
    1374                 :             : void
    1375                 :           7 : gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
    1376                 :             : {
    1377                 :             :     struct lconv *lc;
    1378                 :             : 
    1379                 :           7 :     if (!cm) return;
    1380                 :             : 
    1381                 :           7 :     ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
    1382                 :             : 
    1383                 :           7 :     lc = gnc_localeconv();
    1384                 :           7 :     if (!user_symbol || !*user_symbol)
    1385                 :           0 :         user_symbol = nullptr;
    1386                 :           7 :     else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
    1387                 :           0 :              !g_strcmp0(lc->currency_symbol, user_symbol))
    1388                 :             :         /* if the user gives the ISO symbol for the locale currency or the
    1389                 :             :          * default symbol, actually remove the user symbol */
    1390                 :           0 :         user_symbol = nullptr;
    1391                 :           7 :     else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
    1392                 :           0 :         user_symbol = nullptr;
    1393                 :             : 
    1394                 :           7 :     gnc_commodity_begin_edit (cm);
    1395                 :             : 
    1396                 :           7 :     if (user_symbol)
    1397                 :             :     {
    1398                 :           7 :         GValue v = G_VALUE_INIT;
    1399                 :           7 :         g_value_init (&v, G_TYPE_STRING);
    1400                 :           7 :         g_value_set_static_string (&v, user_symbol);
    1401                 :           7 :         qof_instance_set_kvp (QOF_INSTANCE(cm), &v, 1, "user_symbol");
    1402                 :           7 :         g_value_unset (&v);
    1403                 :             :     }
    1404                 :             :     else
    1405                 :             :     {
    1406                 :           0 :         qof_instance_set_kvp (QOF_INSTANCE(cm), nullptr, 1, "user_symbol");
    1407                 :             :     }
    1408                 :             : 
    1409                 :           7 :     mark_commodity_dirty(cm);
    1410                 :           7 :     gnc_commodity_commit_edit(cm);
    1411                 :             : 
    1412                 :           7 :     LEAVE(" ");
    1413                 :             : }
    1414                 :             : 
    1415                 :             : /********************************************************************
    1416                 :             :  * gnc_commodity_set_default_symbol
    1417                 :             :  * Not made visible in gnc-commodity.h, it is only called from
    1418                 :             :  * iso-4217-currencies.c at startup.
    1419                 :             :  ********************************************************************/
    1420                 :             : void
    1421                 :       60382 : gnc_commodity_set_default_symbol(gnc_commodity * cm,
    1422                 :             :                                  const char * default_symbol)
    1423                 :             : {
    1424                 :       60382 :     GET_PRIVATE(cm)->default_symbol = default_symbol;
    1425                 :       60382 : }
    1426                 :             : 
    1427                 :             : /********************************************************************
    1428                 :             :  * gnc_commodity_increment_usage_count
    1429                 :             :  ********************************************************************/
    1430                 :             : 
    1431                 :             : void
    1432                 :        3048 : gnc_commodity_increment_usage_count(gnc_commodity *cm)
    1433                 :             : {
    1434                 :             :     gnc_commodityPrivate* priv;
    1435                 :             : 
    1436                 :        3048 :     ENTER("(cm=%p)", cm);
    1437                 :             : 
    1438                 :        3048 :     if (!cm)
    1439                 :             :     {
    1440                 :           1 :         LEAVE("");
    1441                 :           1 :         return;
    1442                 :             :     }
    1443                 :             : 
    1444                 :        3047 :     priv = GET_PRIVATE(cm);
    1445                 :             : 
    1446                 :         806 :     if ((priv->usage_count == 0) && !priv->quote_flag
    1447                 :         781 :             && gnc_commodity_get_auto_quote_control_flag(cm)
    1448                 :        3853 :             && gnc_commodity_is_iso(cm))
    1449                 :             :     {
    1450                 :             :         /* compatibility hack - Gnucash 1.8 gets currency quotes when a
    1451                 :             :            non-default currency is assigned to an account.  */
    1452                 :         146 :         gnc_commodity_begin_edit(cm);
    1453                 :         146 :         gnc_commodity_set_quote_flag(cm, TRUE);
    1454                 :         146 :         gnc_commodity_set_quote_source(cm,
    1455                 :             :                                        gnc_commodity_get_default_quote_source(cm));
    1456                 :         146 :         gnc_commodity_commit_edit(cm);
    1457                 :             :     }
    1458                 :        3047 :     priv->usage_count++;
    1459                 :        3047 :     LEAVE("(usage_count=%d)", priv->usage_count);
    1460                 :             : }
    1461                 :             : 
    1462                 :             : /********************************************************************
    1463                 :             :  * gnc_commodity_decrement_usage_count
    1464                 :             :  ********************************************************************/
    1465                 :             : 
    1466                 :             : void
    1467                 :        5142 : gnc_commodity_decrement_usage_count(gnc_commodity *cm)
    1468                 :             : {
    1469                 :             :     gnc_commodityPrivate* priv;
    1470                 :             : 
    1471                 :        5142 :     ENTER("(cm=%p)", cm);
    1472                 :             : 
    1473                 :        5142 :     if (!cm)
    1474                 :             :     {
    1475                 :        3751 :         LEAVE("");
    1476                 :        3751 :         return;
    1477                 :             :     }
    1478                 :             : 
    1479                 :        1391 :     priv = GET_PRIVATE(cm);
    1480                 :             : 
    1481                 :        1391 :     if (priv->usage_count == 0)
    1482                 :             :     {
    1483                 :           0 :         PWARN("usage_count already zero");
    1484                 :           0 :         LEAVE("");
    1485                 :           0 :         return;
    1486                 :             :     }
    1487                 :             : 
    1488                 :        1391 :     priv->usage_count--;
    1489                 :         293 :     if ((priv->usage_count == 0) && priv->quote_flag
    1490                 :          83 :             && gnc_commodity_get_auto_quote_control_flag(cm)
    1491                 :        1684 :             && gnc_commodity_is_iso(cm))
    1492                 :             :     {
    1493                 :             :         /* if this is a currency with auto quote control enabled and no more
    1494                 :             :          * accounts reference this currency, disable quote retrieval */
    1495                 :          81 :         gnc_commodity_set_quote_flag(cm, FALSE);
    1496                 :             :     }
    1497                 :        1391 :     LEAVE("(usage_count=%d)", priv->usage_count);
    1498                 :             : }
    1499                 :             : 
    1500                 :             : /********************************************************************\
    1501                 :             : \********************************************************************/
    1502                 :             : 
    1503                 :             : 
    1504                 :             : /********************************************************************
    1505                 :             :  * gnc_commodity_equiv
    1506                 :             :  * are two commodities the same?
    1507                 :             :  ********************************************************************/
    1508                 :             : 
    1509                 :             : gboolean
    1510                 :      118562 : gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
    1511                 :             : {
    1512                 :             :     gnc_commodityPrivate* priv_a;
    1513                 :             :     gnc_commodityPrivate* priv_b;
    1514                 :             : 
    1515                 :      118562 :     if (a == b) return TRUE;
    1516                 :       48471 :     if (!a || !b) return FALSE;
    1517                 :             : 
    1518                 :       21535 :     priv_a = GET_PRIVATE(a);
    1519                 :       21535 :     priv_b = GET_PRIVATE(b);
    1520                 :       21535 :     if (priv_a->name_space != priv_b->name_space) return FALSE;
    1521                 :       17576 :     if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
    1522                 :             : 
    1523                 :          69 :     return TRUE;
    1524                 :             : }
    1525                 :             : 
    1526                 :             : gboolean
    1527                 :       14704 : gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
    1528                 :             : {
    1529                 :       14704 :     return gnc_commodity_compare(a, b) == 0;
    1530                 :             : }
    1531                 :             : 
    1532                 :             : // Used as a sorting callback for deleting old prices, so it needs to be
    1533                 :             : // stable but doesn't need to be in any particular order sensible to humans.
    1534                 :       14727 : int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
    1535                 :             : {
    1536                 :       14727 :     if (a == b) return 0;
    1537                 :        2449 :     if (a && !b) return 1;
    1538                 :        2441 :     if (b && !a) return -1;
    1539                 :        2417 :     return qof_instance_guid_compare(a, b);
    1540                 :             : }
    1541                 :             : 
    1542                 :             : // Used as a callback to g_list_find_custom, it should return 0
    1543                 :             : // when the commodities match.
    1544                 :           0 : int gnc_commodity_compare_void(const void * a, const void * b)
    1545                 :             : {
    1546                 :           0 :     return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
    1547                 :             : }
    1548                 :             : 
    1549                 :             : /************************************************************
    1550                 :             :  *                   Namespace functions                    *
    1551                 :             :  ************************************************************/
    1552                 :             : const char *
    1553                 :       83648 : gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
    1554                 :             : {
    1555                 :       83648 :     if (ns == nullptr)
    1556                 :          15 :         return nullptr;
    1557                 :       83633 :     return ns->name;
    1558                 :             : }
    1559                 :             : 
    1560                 :             : const char *
    1561                 :           0 : gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
    1562                 :             : {
    1563                 :           0 :     if (ns == nullptr)
    1564                 :           0 :         return nullptr;
    1565                 :           0 :     if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
    1566                 :           0 :         return GNC_COMMODITY_NS_ISO_GUI;
    1567                 :           0 :     return ns->name;
    1568                 :             : }
    1569                 :             : 
    1570                 :             : GList *
    1571                 :           0 : gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
    1572                 :             : {
    1573                 :           0 :     if (!name_space)
    1574                 :           0 :         return nullptr;
    1575                 :             : 
    1576                 :           0 :     return g_list_copy (name_space->cm_list);
    1577                 :             : }
    1578                 :             : 
    1579                 :             : gboolean
    1580                 :       67220 : gnc_commodity_namespace_is_iso(const char *name_space)
    1581                 :             : {
    1582                 :       72132 :     return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
    1583                 :       72132 :             (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
    1584                 :             : }
    1585                 :             : 
    1586                 :             : static const gchar *
    1587                 :      335999 : gnc_commodity_table_map_namespace(const char * name_space)
    1588                 :             : {
    1589                 :      335999 :     if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
    1590                 :       62215 :         return GNC_COMMODITY_NS_CURRENCY;
    1591                 :      273784 :     return name_space;
    1592                 :             : }
    1593                 :             : 
    1594                 :             : /********************************************************************
    1595                 :             :  * gnc_commodity_table_new
    1596                 :             :  * make a new commodity table
    1597                 :             :  ********************************************************************/
    1598                 :             : 
    1599                 :             : gnc_commodity_table *
    1600                 :         290 : gnc_commodity_table_new(void)
    1601                 :             : {
    1602                 :         290 :     gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
    1603                 :         290 :     retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
    1604                 :         290 :     retval->ns_list = nullptr;
    1605                 :         290 :     return retval;
    1606                 :             : }
    1607                 :             : 
    1608                 :             : /********************************************************************
    1609                 :             :  * book anchor functions
    1610                 :             :  ********************************************************************/
    1611                 :             : 
    1612                 :             : gnc_commodity_table *
    1613                 :      118729 : gnc_commodity_table_get_table(QofBook *book)
    1614                 :             : {
    1615                 :      118729 :     if (!book) return nullptr;
    1616                 :      118729 :     return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
    1617                 :             : }
    1618                 :             : 
    1619                 :             : gnc_commodity *
    1620                 :           1 : gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
    1621                 :             : {
    1622                 :             :     gnc_commodity *twin;
    1623                 :             :     const char * ucom;
    1624                 :             :     gnc_commodity_table * comtbl;
    1625                 :             : 
    1626                 :           1 :     if (!from) return nullptr;
    1627                 :           0 :     comtbl = gnc_commodity_table_get_table (book);
    1628                 :           0 :     if (!comtbl) return nullptr;
    1629                 :             : 
    1630                 :           0 :     ucom = gnc_commodity_get_unique_name (from);
    1631                 :           0 :     twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
    1632                 :           0 :     if (!twin)
    1633                 :             :     {
    1634                 :           0 :         twin = gnc_commodity_clone (from, book);
    1635                 :           0 :         twin = gnc_commodity_table_insert (comtbl, twin);
    1636                 :             :     }
    1637                 :           0 :     return twin;
    1638                 :             : }
    1639                 :             : 
    1640                 :             : /********************************************************************
    1641                 :             :  * gnc_commodity_table_get_size
    1642                 :             :  * get the size of the commodity table
    1643                 :             :  ********************************************************************/
    1644                 :             : 
    1645                 :             : static void
    1646                 :         172 : count_coms(gpointer key, gpointer value, gpointer user_data)
    1647                 :             : {
    1648                 :         172 :     GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
    1649                 :         172 :     guint *count = (guint*)user_data;
    1650                 :             : 
    1651                 :         172 :     if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
    1652                 :             :     {
    1653                 :             :         /* don't count default commodities */
    1654                 :           8 :         return;
    1655                 :             :     }
    1656                 :             : 
    1657                 :         164 :     if (!value) return;
    1658                 :             : 
    1659                 :         164 :     *count += g_hash_table_size(tbl);
    1660                 :             : }
    1661                 :             : 
    1662                 :             : guint
    1663                 :          49 : gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
    1664                 :             : {
    1665                 :          49 :     guint count = 0;
    1666                 :          49 :     g_return_val_if_fail(tbl, 0);
    1667                 :          49 :     g_return_val_if_fail(tbl->ns_table, 0);
    1668                 :             : 
    1669                 :          49 :     g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
    1670                 :             : 
    1671                 :          49 :     return count;
    1672                 :             : }
    1673                 :             : 
    1674                 :             : /********************************************************************
    1675                 :             :  * gnc_commodity_table_lookup
    1676                 :             :  * locate a commodity by namespace and mnemonic.
    1677                 :             :  ********************************************************************/
    1678                 :             : 
    1679                 :             : gnc_commodity *
    1680                 :       77833 : gnc_commodity_table_lookup(const gnc_commodity_table * table,
    1681                 :             :                            const char * name_space, const char * mnemonic)
    1682                 :             : {
    1683                 :       77833 :     gnc_commodity_namespace * nsp = nullptr;
    1684                 :             : 
    1685                 :       77833 :     if (!table || !name_space || !mnemonic) return nullptr;
    1686                 :             : 
    1687                 :       77792 :     nsp = gnc_commodity_table_find_namespace(table, name_space);
    1688                 :             : 
    1689                 :       77792 :     if (nsp)
    1690                 :             :     {
    1691                 :             :         /*
    1692                 :             :          * Backward compatibility support for currencies that have
    1693                 :             :          * recently changed.
    1694                 :             :          */
    1695                 :       76651 :         if (nsp->iso4217)
    1696                 :             :         {
    1697                 :       69200 :             auto it = gnc_new_iso_codes.find (mnemonic);
    1698                 :       69200 :             if (it != gnc_new_iso_codes.end())
    1699                 :           0 :                 mnemonic = it->second.c_str();
    1700                 :             :         }
    1701                 :       76651 :         return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
    1702                 :             :     }
    1703                 :             :     else
    1704                 :             :     {
    1705                 :        1141 :         return nullptr;
    1706                 :             :     }
    1707                 :             : }
    1708                 :             : 
    1709                 :             : /********************************************************************
    1710                 :             :  * gnc_commodity_table_lookup
    1711                 :             :  * locate a commodity by unique name.
    1712                 :             :  ********************************************************************/
    1713                 :             : 
    1714                 :             : gnc_commodity *
    1715                 :           0 : gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
    1716                 :             :                                   const char * unique_name)
    1717                 :             : {
    1718                 :             :     char *name_space;
    1719                 :             :     char *mnemonic;
    1720                 :             :     gnc_commodity *commodity;
    1721                 :             : 
    1722                 :           0 :     if (!table || !unique_name) return nullptr;
    1723                 :             : 
    1724                 :           0 :     name_space = g_strdup (unique_name);
    1725                 :           0 :     mnemonic = strstr (name_space, "::");
    1726                 :           0 :     if (!mnemonic)
    1727                 :             :     {
    1728                 :           0 :         g_free (name_space);
    1729                 :           0 :         return nullptr;
    1730                 :             :     }
    1731                 :             : 
    1732                 :           0 :     *mnemonic = '\0';
    1733                 :           0 :     mnemonic += 2;
    1734                 :             : 
    1735                 :           0 :     commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
    1736                 :             : 
    1737                 :           0 :     g_free (name_space);
    1738                 :             : 
    1739                 :           0 :     return commodity;
    1740                 :             : }
    1741                 :             : 
    1742                 :             : /********************************************************************
    1743                 :             :  * gnc_commodity_table_find_full
    1744                 :             :  * locate a commodity by namespace and printable name
    1745                 :             :  ********************************************************************/
    1746                 :             : 
    1747                 :             : gnc_commodity *
    1748                 :           0 : gnc_commodity_table_find_full(const gnc_commodity_table * table,
    1749                 :             :                               const char * name_space,
    1750                 :             :                               const char * fullname)
    1751                 :             : {
    1752                 :           0 :     gnc_commodity * retval = nullptr;
    1753                 :             :     GList         * all;
    1754                 :             :     GList         * iterator;
    1755                 :             : 
    1756                 :           0 :     if (!fullname || (fullname[0] == '\0'))
    1757                 :           0 :         return nullptr;
    1758                 :             : 
    1759                 :           0 :     all = gnc_commodity_table_get_commodities(table, name_space);
    1760                 :             : 
    1761                 :           0 :     for (iterator = all; iterator; iterator = iterator->next)
    1762                 :             :     {
    1763                 :           0 :         auto commodity = GNC_COMMODITY (iterator->data);
    1764                 :           0 :         if (!strcmp(fullname,
    1765                 :             :                     gnc_commodity_get_printname(commodity)))
    1766                 :             :         {
    1767                 :           0 :             retval = commodity;
    1768                 :           0 :             break;
    1769                 :             :         }
    1770                 :             :     }
    1771                 :             : 
    1772                 :           0 :     g_list_free (all);
    1773                 :             : 
    1774                 :           0 :     return retval;
    1775                 :             : }
    1776                 :             : 
    1777                 :             : 
    1778                 :             : /********************************************************************
    1779                 :             :  * gnc_commodity_table_insert
    1780                 :             :  * add a commodity to the table.
    1781                 :             :  ********************************************************************/
    1782                 :             : 
    1783                 :             : gnc_commodity *
    1784                 :       62451 : gnc_commodity_table_insert(gnc_commodity_table * table,
    1785                 :             :                            gnc_commodity * comm)
    1786                 :             : {
    1787                 :       62451 :     gnc_commodity_namespace * nsp = nullptr;
    1788                 :             :     gnc_commodity *c;
    1789                 :             :     const char *ns_name;
    1790                 :             :     gnc_commodityPrivate* priv;
    1791                 :             :     QofBook *book;
    1792                 :             : 
    1793                 :       62451 :     if (!table) return nullptr;
    1794                 :       62451 :     if (!comm) return nullptr;
    1795                 :             : 
    1796                 :       62451 :     priv = GET_PRIVATE(comm);
    1797                 :             : 
    1798                 :       62451 :     ENTER ("(table=%p, comm=%p) %s %s", table, comm,
    1799                 :             :            (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
    1800                 :             :            (priv->fullname == nullptr ? "(null)" : priv->fullname));
    1801                 :       62451 :     ns_name = gnc_commodity_namespace_get_name(priv->name_space);
    1802                 :       62451 :     c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
    1803                 :             : 
    1804                 :       62451 :     if (c)
    1805                 :             :     {
    1806                 :          46 :         if (c == comm)
    1807                 :             :         {
    1808                 :           3 :             LEAVE("already in table");
    1809                 :           3 :             return c;
    1810                 :             :         }
    1811                 :             : 
    1812                 :             :         /* Backward compatibility support for currencies that have
    1813                 :             :          * recently changed. */
    1814                 :          43 :         if (priv->name_space->iso4217)
    1815                 :             :         {
    1816                 :          24 :             auto it = gnc_new_iso_codes.find (priv->mnemonic);
    1817                 :          24 :             if (it != gnc_new_iso_codes.end())
    1818                 :           0 :                 gnc_commodity_set_mnemonic(comm, it->second.c_str());
    1819                 :             :         }
    1820                 :          43 :         gnc_commodity_copy (c, comm);
    1821                 :          43 :         gnc_commodity_destroy (comm);
    1822                 :          43 :         LEAVE("found at %p", c);
    1823                 :          43 :         return c;
    1824                 :             :     }
    1825                 :             : 
    1826                 :             :     /* Prevent setting anything except template in namespace template. */
    1827                 :       62671 :     if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
    1828                 :         266 :         g_strcmp0 (priv->mnemonic, "template") != 0)
    1829                 :             :     {
    1830                 :           0 :         PWARN("Converting commodity %s from namespace template to "
    1831                 :             :               "namespace User", priv->mnemonic);
    1832                 :           0 :         gnc_commodity_set_namespace (comm, "User");
    1833                 :           0 :         ns_name = "User";
    1834                 :           0 :         mark_commodity_dirty (comm);
    1835                 :             :     }
    1836                 :             : 
    1837                 :       62405 :     book = qof_instance_get_book (&comm->inst);
    1838                 :       62405 :     nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
    1839                 :             : 
    1840                 :       62405 :     PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
    1841                 :             :            nsp->cm_table, nsp->name);
    1842                 :       62405 :     g_hash_table_insert(nsp->cm_table,
    1843                 :       62405 :                         (gpointer)CACHE_INSERT(priv->mnemonic),
    1844                 :             :                         (gpointer)comm);
    1845                 :       62405 :     nsp->cm_list = g_list_append(nsp->cm_list, comm);
    1846                 :             : 
    1847                 :       62405 :     qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
    1848                 :       62405 :     LEAVE ("(table=%p, comm=%p)", table, comm);
    1849                 :       62405 :     return comm;
    1850                 :             : }
    1851                 :             : 
    1852                 :             : /********************************************************************
    1853                 :             :  * gnc_commodity_table_remove
    1854                 :             :  * remove a commodity from the table.
    1855                 :             :  ********************************************************************/
    1856                 :             : 
    1857                 :             : void
    1858                 :       41354 : gnc_commodity_table_remove(gnc_commodity_table * table,
    1859                 :             :                            gnc_commodity * comm)
    1860                 :             : {
    1861                 :             :     gnc_commodity_namespace * nsp;
    1862                 :             :     gnc_commodity *c;
    1863                 :             :     gnc_commodityPrivate* priv;
    1864                 :             :     const char *ns_name;
    1865                 :             : 
    1866                 :       41354 :     if (!table) return;
    1867                 :        4578 :     if (!comm) return;
    1868                 :             : 
    1869                 :        4578 :     priv = GET_PRIVATE(comm);
    1870                 :        4578 :     ns_name = gnc_commodity_namespace_get_name(priv->name_space);
    1871                 :        4578 :     c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
    1872                 :        4578 :     if (c != comm) return;
    1873                 :             : 
    1874                 :          65 :     qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
    1875                 :             : 
    1876                 :          65 :     nsp = gnc_commodity_table_find_namespace(table, ns_name);
    1877                 :          65 :     if (!nsp) return;
    1878                 :             : 
    1879                 :          65 :     nsp->cm_list = g_list_remove(nsp->cm_list, comm);
    1880                 :          65 :     g_hash_table_remove (nsp->cm_table, priv->mnemonic);
    1881                 :             :     /* XXX minor mem leak, should remove the key as well */
    1882                 :             : }
    1883                 :             : 
    1884                 :             : /********************************************************************
    1885                 :             :  * gnc_commodity_table_has_namespace
    1886                 :             :  * see if the commodities namespace exists. May have zero commodities.
    1887                 :             :  ********************************************************************/
    1888                 :             : 
    1889                 :             : int
    1890                 :          20 : gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
    1891                 :             :                                   const char * name_space)
    1892                 :             : {
    1893                 :          20 :     gnc_commodity_namespace * nsp = nullptr;
    1894                 :             : 
    1895                 :          20 :     if (!table || !name_space)
    1896                 :             :     {
    1897                 :           0 :         return 0;
    1898                 :             :     }
    1899                 :             : 
    1900                 :          20 :     nsp = gnc_commodity_table_find_namespace(table, name_space);
    1901                 :          20 :     if (nsp)
    1902                 :             :     {
    1903                 :          20 :         return 1;
    1904                 :             :     }
    1905                 :             :     else
    1906                 :             :     {
    1907                 :           0 :         return 0;
    1908                 :             :     }
    1909                 :             : }
    1910                 :             : 
    1911                 :             : static void
    1912                 :        1642 : hash_keys_helper(gpointer key, gpointer value, gpointer data)
    1913                 :             : {
    1914                 :        1642 :     auto l = (GList**)data;
    1915                 :        1642 :     *l = g_list_prepend(*l, key);
    1916                 :        1642 : }
    1917                 :             : 
    1918                 :             : static GList *
    1919                 :         241 : g_hash_table_keys(GHashTable * table)
    1920                 :             : {
    1921                 :         241 :     GList * l = nullptr;
    1922                 :         241 :     g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
    1923                 :         241 :     return l;
    1924                 :             : }
    1925                 :             : 
    1926                 :             : static void
    1927                 :       10164 : hash_values_helper(gpointer key, gpointer value, gpointer data)
    1928                 :             : {
    1929                 :       10164 :     auto l = (GList**)data;
    1930                 :       10164 :     *l = g_list_prepend(*l, value);
    1931                 :       10164 : }
    1932                 :             : 
    1933                 :             : static GList *
    1934                 :         240 : g_hash_table_values(GHashTable * table)
    1935                 :             : {
    1936                 :         240 :     GList * l = nullptr;
    1937                 :         240 :     g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
    1938                 :         240 :     return l;
    1939                 :             : }
    1940                 :             : 
    1941                 :             : /********************************************************************
    1942                 :             :  * gnc_commodity_table_get_namespaces
    1943                 :             :  * see if any commodities in the namespace exist
    1944                 :             :  ********************************************************************/
    1945                 :             : 
    1946                 :             : GList *
    1947                 :         241 : gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
    1948                 :             : {
    1949                 :         241 :     if (!table)
    1950                 :           0 :         return nullptr;
    1951                 :             : 
    1952                 :         241 :     return g_hash_table_keys(table->ns_table);
    1953                 :             : }
    1954                 :             : 
    1955                 :             : GList *
    1956                 :           2 : gnc_commodity_table_get_namespaces_list(const gnc_commodity_table * table)
    1957                 :             : {
    1958                 :           2 :     if (!table)
    1959                 :           0 :         return nullptr;
    1960                 :             : 
    1961                 :           2 :     return g_list_copy (table->ns_list);
    1962                 :             : }
    1963                 :             : 
    1964                 :             : /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
    1965                 :             :    GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
    1966                 :             :    either of these, the net result is that the iso4217 bit is set only
    1967                 :             :    for GNC_COMMODITY_NS_CURRENCY.  This means that gnc_commodity_is_iso is
    1968                 :             :    a subset of gnc_commodity_is_currency.  Most callers seem to use
    1969                 :             :    gnc_commodity_is_iso. */
    1970                 :             : gboolean
    1971                 :      174634 : gnc_commodity_is_iso(const gnc_commodity * cm)
    1972                 :             : {
    1973                 :             :     gnc_commodityPrivate* priv;
    1974                 :             : 
    1975                 :      174634 :     if (!cm) return FALSE;
    1976                 :             : 
    1977                 :      174634 :     priv = GET_PRIVATE(cm);
    1978                 :      174634 :     if ( !priv->name_space) return FALSE;
    1979                 :      174448 :     return priv->name_space->iso4217;
    1980                 :             : }
    1981                 :             : 
    1982                 :             : gboolean
    1983                 :        3875 : gnc_commodity_is_currency(const gnc_commodity *cm)
    1984                 :             : {
    1985                 :             :     const char *ns_name;
    1986                 :        3875 :     if (!cm) return FALSE;
    1987                 :             : 
    1988                 :        3872 :     ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
    1989                 :        7744 :     return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
    1990                 :        7744 :             !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
    1991                 :             : }
    1992                 :             : 
    1993                 :             : /********************************************************************
    1994                 :             :  * gnc_commodity_table_get_commodities
    1995                 :             :  * list commodities in a given namespace
    1996                 :             :  ********************************************************************/
    1997                 :             : 
    1998                 :             : static CommodityList*
    1999                 :           0 : commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
    2000                 :             : {
    2001                 :           0 :     GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
    2002                 :           0 :     CommodityList *retval = nullptr;
    2003                 :           0 :     for (node = nslist; node; node=g_list_next(node))
    2004                 :             :     {
    2005                 :           0 :         gnc_commodity_namespace *ns = nullptr;
    2006                 :           0 :         if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
    2007                 :           0 :             || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
    2008                 :           0 :             continue;
    2009                 :           0 :         ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
    2010                 :           0 :         if (!ns)
    2011                 :           0 :             continue;
    2012                 :           0 :         retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
    2013                 :             :     }
    2014                 :           0 :     g_list_free(nslist);
    2015                 :           0 :     return retval;
    2016                 :             : }
    2017                 :             : 
    2018                 :             : CommodityList *
    2019                 :         240 : gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
    2020                 :             :                                     const char * name_space)
    2021                 :             : {
    2022                 :         240 :     gnc_commodity_namespace * ns = nullptr;
    2023                 :             : 
    2024                 :         240 :     if (!table)
    2025                 :           0 :         return nullptr;
    2026                 :         240 :     if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
    2027                 :           0 :         return commodity_table_get_all_noncurrency_commodities(table);
    2028                 :         240 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2029                 :         240 :     if (!ns)
    2030                 :           0 :         return nullptr;
    2031                 :             : 
    2032                 :         240 :     return g_hash_table_values(ns->cm_table);
    2033                 :             : }
    2034                 :             : 
    2035                 :             : /********************************************************************
    2036                 :             :  * gnc_commodity_table_get_quotable_commodities
    2037                 :             :  * list commodities in a given namespace that get price quotes
    2038                 :             :  ********************************************************************/
    2039                 :             : 
    2040                 :             : static void
    2041                 :           0 : get_quotables_helper1(gpointer key, gpointer value, gpointer data)
    2042                 :             : {
    2043                 :           0 :     auto comm = GNC_COMMODITY(value);
    2044                 :           0 :     gnc_commodityPrivate* priv = GET_PRIVATE(comm);
    2045                 :           0 :     auto l = static_cast<GList**>(data);
    2046                 :             : 
    2047                 :           0 :     if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
    2048                 :           0 :         return;
    2049                 :           0 :     *l = g_list_prepend(*l, value);
    2050                 :             : }
    2051                 :             : 
    2052                 :             : static gboolean
    2053                 :           0 : get_quotables_helper2 (gnc_commodity *comm, gpointer data)
    2054                 :             : {
    2055                 :           0 :     auto l = static_cast<GList**>(data);
    2056                 :           0 :     gnc_commodityPrivate* priv = GET_PRIVATE(comm);
    2057                 :             : 
    2058                 :           0 :     if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
    2059                 :           0 :         return TRUE;
    2060                 :           0 :     *l = g_list_prepend(*l, comm);
    2061                 :           0 :     return TRUE;
    2062                 :             : }
    2063                 :             : 
    2064                 :             : CommodityList *
    2065                 :           0 : gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table * table)
    2066                 :             : {
    2067                 :           0 :     gnc_commodity_namespace * ns = nullptr;
    2068                 :             :     const char *name_space;
    2069                 :             :     GList * nslist, * tmp;
    2070                 :           0 :     GList * l = nullptr;
    2071                 :             :     regex_t pattern;
    2072                 :           0 :     const char *expression = gnc_prefs_get_namespace_regexp();
    2073                 :             : 
    2074                 :           0 :     ENTER("table=%p, expression=%s", table, expression);
    2075                 :           0 :     if (!table)
    2076                 :           0 :         return nullptr;
    2077                 :             : 
    2078                 :           0 :     if (expression && *expression)
    2079                 :             :     {
    2080                 :           0 :         if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
    2081                 :             :         {
    2082                 :           0 :             LEAVE("Cannot compile regex");
    2083                 :           0 :             return nullptr;
    2084                 :             :         }
    2085                 :             : 
    2086                 :           0 :         nslist = gnc_commodity_table_get_namespaces(table);
    2087                 :           0 :         for (tmp = nslist; tmp; tmp = tmp->next)
    2088                 :             :         {
    2089                 :           0 :             name_space = static_cast<const char*>(tmp->data);
    2090                 :           0 :             if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
    2091                 :             :             {
    2092                 :           0 :                 DEBUG("Running list of %s commodities", name_space);
    2093                 :           0 :                 ns = gnc_commodity_table_find_namespace(table, name_space);
    2094                 :           0 :                 if (ns)
    2095                 :             :                 {
    2096                 :           0 :                     g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
    2097                 :             :                 }
    2098                 :             :             }
    2099                 :             :         }
    2100                 :           0 :         g_list_free(nslist);
    2101                 :           0 :         regfree(&pattern);
    2102                 :             :     }
    2103                 :             :     else
    2104                 :             :     {
    2105                 :           0 :         gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
    2106                 :             :                                               (gpointer) &l);
    2107                 :             :     }
    2108                 :           0 :     LEAVE("list head %p", l);
    2109                 :           0 :     return l;
    2110                 :             : }
    2111                 :             : 
    2112                 :             : /********************************************************************
    2113                 :             :  * gnc_commodity_table_add_namespace
    2114                 :             :  * add an empty namespace if it does not exist
    2115                 :             :  ********************************************************************/
    2116                 :             : 
    2117                 :             : /* GObject Initialization */
    2118                 :        2276 : QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
    2119                 :             : 
    2120                 :             : static void
    2121                 :         909 : gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
    2122                 :             : {
    2123                 :         909 : }
    2124                 :             : 
    2125                 :             : static void
    2126                 :         560 : gnc_commodity_namespace_dispose_real (GObject *nsp)
    2127                 :             : {
    2128                 :         560 : }
    2129                 :             : 
    2130                 :             : static void
    2131                 :         560 : gnc_commodity_namespace_finalize_real(GObject* nsp)
    2132                 :             : {
    2133                 :         560 : }
    2134                 :             : 
    2135                 :             : gnc_commodity_namespace *
    2136                 :      128991 : gnc_commodity_table_add_namespace(gnc_commodity_table * table,
    2137                 :             :                                   const char * name_space,
    2138                 :             :                                   QofBook *book)
    2139                 :             : {
    2140                 :      128991 :     gnc_commodity_namespace * ns = nullptr;
    2141                 :             : 
    2142                 :      128991 :     if (!table) return nullptr;
    2143                 :             : 
    2144                 :      128661 :     name_space = gnc_commodity_table_map_namespace(name_space);
    2145                 :      128661 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2146                 :      128661 :     if (!ns)
    2147                 :             :     {
    2148                 :         909 :         ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
    2149                 :         909 :         ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
    2150                 :         909 :         ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
    2151                 :         909 :         ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
    2152                 :         909 :         qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
    2153                 :         909 :         qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
    2154                 :             : 
    2155                 :         909 :         g_hash_table_insert(table->ns_table,
    2156                 :         909 :                             (gpointer) ns->name,
    2157                 :             :                             (gpointer) ns);
    2158                 :         909 :         table->ns_list = g_list_append(table->ns_list, ns);
    2159                 :         909 :         qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
    2160                 :             :     }
    2161                 :      128661 :     return ns;
    2162                 :             : }
    2163                 :             : 
    2164                 :             : 
    2165                 :             : gnc_commodity_namespace *
    2166                 :      207338 : gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
    2167                 :             :                                    const char * name_space)
    2168                 :             : {
    2169                 :      207338 :     if (!table || !name_space)
    2170                 :           0 :         return nullptr;
    2171                 :             : 
    2172                 :      207338 :     name_space = gnc_commodity_table_map_namespace(name_space);
    2173                 :      207338 :     return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
    2174                 :             : }
    2175                 :             : 
    2176                 :             : 
    2177                 :             : gnc_commodity *
    2178                 :          38 : gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
    2179                 :             : {
    2180                 :             :     QofCollection *col;
    2181                 :          38 :     if (!guid || !book) return nullptr;
    2182                 :          38 :     col = qof_book_get_collection (book, GNC_ID_COMMODITY);
    2183                 :          38 :     return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
    2184                 :             : }
    2185                 :             : 
    2186                 :             : /********************************************************************
    2187                 :             :  * gnc_commodity_table_delete_namespace
    2188                 :             :  * delete a namespace
    2189                 :             :  ********************************************************************/
    2190                 :             : 
    2191                 :             : static int
    2192                 :       37685 : ns_helper(gpointer key, gpointer value, gpointer user_data)
    2193                 :             : {
    2194                 :       37685 :     auto c = GNC_COMMODITY(value);
    2195                 :       37685 :     gnc_commodity_destroy(c);
    2196                 :       37685 :     CACHE_REMOVE(static_cast<char*>(key));  /* key is commodity mnemonic */
    2197                 :       37685 :     return TRUE;
    2198                 :             : }
    2199                 :             : 
    2200                 :             : void
    2201                 :         560 : gnc_commodity_table_delete_namespace(gnc_commodity_table * table,
    2202                 :             :                                      const char * name_space)
    2203                 :             : {
    2204                 :             :     gnc_commodity_namespace * ns;
    2205                 :             : 
    2206                 :         560 :     if (!table) return;
    2207                 :             : 
    2208                 :         560 :     ns = gnc_commodity_table_find_namespace(table, name_space);
    2209                 :         560 :     if (!ns)
    2210                 :           0 :         return;
    2211                 :             : 
    2212                 :         560 :     qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
    2213                 :         560 :     g_hash_table_remove(table->ns_table, name_space);
    2214                 :         560 :     table->ns_list = g_list_remove(table->ns_list, ns);
    2215                 :             : 
    2216                 :         560 :     g_list_free(ns->cm_list);
    2217                 :         560 :     ns->cm_list = nullptr;
    2218                 :             : 
    2219                 :         560 :     g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
    2220                 :         560 :     g_hash_table_destroy(ns->cm_table);
    2221                 :         560 :     CACHE_REMOVE(ns->name);
    2222                 :             : 
    2223                 :         560 :     qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
    2224                 :             :     /* qof_instance_release(&ns->inst); */
    2225                 :         560 :     g_object_unref(ns);
    2226                 :             : }
    2227                 :             : 
    2228                 :             : /********************************************************************
    2229                 :             :  * gnc_commodity_table_foreach_commodity
    2230                 :             :  * call user-defined function once for every commodity in every
    2231                 :             :  * namespace
    2232                 :             :  ********************************************************************/
    2233                 :             : 
    2234                 :             : typedef struct
    2235                 :             : {
    2236                 :             :     gboolean ok;
    2237                 :             :     gboolean (*func)(gnc_commodity *, gpointer);
    2238                 :             :     gpointer user_data;
    2239                 :             : } IterData;
    2240                 :             : 
    2241                 :             : static void
    2242                 :        4872 : iter_commodity (gpointer key, gpointer value, gpointer user_data)
    2243                 :             : {
    2244                 :        4872 :     IterData *iter_data = (IterData *) user_data;
    2245                 :        4872 :     gnc_commodity *cm = (gnc_commodity *) value;
    2246                 :             : 
    2247                 :        4872 :     if (iter_data->ok)
    2248                 :             :     {
    2249                 :        4872 :         iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
    2250                 :             :     }
    2251                 :        4872 : }
    2252                 :             : 
    2253                 :             : static void
    2254                 :          65 : iter_namespace (gpointer key, gpointer value, gpointer user_data)
    2255                 :             : {
    2256                 :          65 :     GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
    2257                 :          65 :     g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
    2258                 :          65 : }
    2259                 :             : 
    2260                 :             : gboolean
    2261                 :          25 : gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
    2262                 :             :                                        gboolean (*f)(gnc_commodity *, gpointer),
    2263                 :             :                                        gpointer user_data)
    2264                 :             : {
    2265                 :             :     IterData iter_data;
    2266                 :             : 
    2267                 :          25 :     if (!tbl || !f) return FALSE;
    2268                 :             : 
    2269                 :          25 :     iter_data.ok = TRUE;
    2270                 :          25 :     iter_data.func = f;
    2271                 :          25 :     iter_data.user_data = user_data;
    2272                 :             : 
    2273                 :          25 :     g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
    2274                 :             : 
    2275                 :          25 :     return iter_data.ok;
    2276                 :             : }
    2277                 :             : 
    2278                 :             : /********************************************************************
    2279                 :             :  * gnc_commodity_table_destroy
    2280                 :             :  * cleanup and free.
    2281                 :             :  ********************************************************************/
    2282                 :             : 
    2283                 :             : void
    2284                 :         186 : gnc_commodity_table_destroy(gnc_commodity_table * t)
    2285                 :             : {
    2286                 :             :     gnc_commodity_namespace * ns;
    2287                 :             :     GList *item, *next;
    2288                 :             : 
    2289                 :         186 :     if (!t) return;
    2290                 :         186 :     ENTER ("table=%p", t);
    2291                 :             : 
    2292                 :         746 :     for (item = t->ns_list; item; item = next)
    2293                 :             :     {
    2294                 :         560 :         next = g_list_next(item);
    2295                 :         560 :         ns = static_cast<gnc_commodity_namespace*>(item->data);
    2296                 :         560 :         gnc_commodity_table_delete_namespace(t, ns->name);
    2297                 :             :     }
    2298                 :             : 
    2299                 :         186 :     g_list_free(t->ns_list);
    2300                 :         186 :     t->ns_list = nullptr;
    2301                 :         186 :     g_hash_table_destroy(t->ns_table);
    2302                 :         186 :     t->ns_table = nullptr;
    2303                 :         186 :     LEAVE ("table=%p", t);
    2304                 :         186 :     g_free(t);
    2305                 :             : }
    2306                 :             : 
    2307                 :             : /* =========================================================== */
    2308                 :             : 
    2309                 :             : /********************************************************************
    2310                 :             :  * gnc_commodity_table_add_default_data
    2311                 :             :  ********************************************************************/
    2312                 :             : 
    2313                 :             : #define CUR_I18N(String) dgettext ("iso_4217", String)
    2314                 :             : 
    2315                 :             : gboolean
    2316                 :         266 : gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
    2317                 :             : {
    2318                 :             :     QofCollection *col;
    2319                 :             :     gnc_commodity* c;
    2320                 :             : 
    2321                 :         266 :     ENTER ("table=%p", table);
    2322                 :         266 :     gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
    2323                 :         266 :     c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
    2324                 :         266 :     gnc_commodity_table_insert(table, c);
    2325                 :             : 
    2326                 :             : #include "iso-4217-currencies.c"
    2327                 :             : 
    2328                 :             :     /* We've just created the default namespaces and currencies.  Mark
    2329                 :             :      * these collections as clean because there is no USER entered data
    2330                 :             :      * in these collections as of yet. */
    2331                 :         266 :     col = qof_book_get_collection(book, GNC_ID_COMMODITY);
    2332                 :         266 :     qof_collection_mark_clean(col);
    2333                 :         266 :     col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
    2334                 :         266 :     qof_collection_mark_clean(col);
    2335                 :             : 
    2336                 :         266 :     LEAVE ("table=%p", table);
    2337                 :         266 :     return TRUE;
    2338                 :             : }
    2339                 :             : 
    2340                 :             : /********************************************************************
    2341                 :             :  ********************************************************************/
    2342                 :             : /* QofObject function implementation and registration */
    2343                 :             : 
    2344                 :             : #ifdef _MSC_VER
    2345                 :             : /* MSVC compiler doesn't have C99 "designated initializers"
    2346                 :             :  * so we wrap them in a macro that is empty on MSVC. */
    2347                 :             : # define DI(x) /* */
    2348                 :             : #else
    2349                 :             : # define DI(x) x
    2350                 :             : #endif
    2351                 :             : static QofObject commodity_object_def =
    2352                 :             : {
    2353                 :             :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2354                 :             :     DI(.e_type            = ) GNC_ID_COMMODITY,
    2355                 :             :     DI(.type_label        = ) "Commodity",
    2356                 :             :     DI(.create            = ) nullptr,
    2357                 :             :     DI(.book_begin        = ) nullptr,
    2358                 :             :     DI(.book_end          = ) nullptr,
    2359                 :             :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2360                 :             :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2361                 :             :     DI(.foreach           = ) qof_collection_foreach,
    2362                 :             :     DI(.printable         = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
    2363                 :             : };
    2364                 :             : 
    2365                 :             : static QofObject namespace_object_def =
    2366                 :             : {
    2367                 :             :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2368                 :             :     DI(.e_type            = ) GNC_ID_COMMODITY_NAMESPACE,
    2369                 :             :     DI(.type_label        = ) "Namespace",
    2370                 :             :     DI(.create            = ) nullptr,
    2371                 :             :     DI(.book_begin        = ) nullptr,
    2372                 :             :     DI(.book_end          = ) nullptr,
    2373                 :             :     DI(.is_dirty          = ) nullptr,
    2374                 :             :     DI(.mark_clean        = ) nullptr,
    2375                 :             :     DI(.foreach           = ) nullptr,
    2376                 :             :     DI(.printable         = ) nullptr,
    2377                 :             : };
    2378                 :             : 
    2379                 :             : static void
    2380                 :         277 : commodity_table_book_begin (QofBook *book)
    2381                 :             : {
    2382                 :             :     gnc_commodity_table *ct;
    2383                 :         277 :     ENTER ("book=%p", book);
    2384                 :             : 
    2385                 :         277 :     if (gnc_commodity_table_get_table(book))
    2386                 :          15 :         return;
    2387                 :             : 
    2388                 :         262 :     ct = gnc_commodity_table_new ();
    2389                 :         262 :     qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
    2390                 :             : 
    2391                 :         262 :     if (!gnc_commodity_table_add_default_data(ct, book))
    2392                 :             :     {
    2393                 :           0 :         PWARN("unable to initialize book's commodity_table");
    2394                 :             :     }
    2395                 :             : 
    2396                 :         262 :     LEAVE ("book=%p", book);
    2397                 :             : }
    2398                 :             : 
    2399                 :             : static void
    2400                 :         173 : commodity_table_book_end (QofBook *book)
    2401                 :             : {
    2402                 :             :     gnc_commodity_table *ct;
    2403                 :             : 
    2404                 :         173 :     ct = gnc_commodity_table_get_table (book);
    2405                 :         173 :     qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
    2406                 :         173 :     gnc_commodity_table_destroy (ct);
    2407                 :         173 : }
    2408                 :             : 
    2409                 :             : static QofObject commodity_table_object_def =
    2410                 :             : {
    2411                 :             :     DI(.interface_version = ) QOF_OBJECT_VERSION,
    2412                 :             :     DI(.e_type            = ) GNC_ID_COMMODITY_TABLE,
    2413                 :             :     DI(.type_label        = ) "CommodityTable",
    2414                 :             :     DI(.create            = ) nullptr,
    2415                 :             :     DI(.book_begin        = ) commodity_table_book_begin,
    2416                 :             :     DI(.book_end          = ) commodity_table_book_end,
    2417                 :             :     DI(.is_dirty          = ) qof_collection_is_dirty,
    2418                 :             :     DI(.mark_clean        = ) qof_collection_mark_clean,
    2419                 :             :     DI(.foreach           = ) nullptr,
    2420                 :             :     DI(.printable         = ) nullptr,
    2421                 :             :     DI(.version_cmp       = ) nullptr,
    2422                 :             : };
    2423                 :             : 
    2424                 :             : gboolean
    2425                 :          99 : gnc_commodity_table_register (void)
    2426                 :             : {
    2427                 :          99 :     if (!qof_object_register (&commodity_object_def))
    2428                 :           1 :         return FALSE;
    2429                 :          98 :     if (!qof_object_register (&namespace_object_def))
    2430                 :           0 :         return FALSE;
    2431                 :          98 :     return qof_object_register (&commodity_table_object_def);
    2432                 :             : }
    2433                 :             : 
    2434                 :             : /* *******************************************************************
    2435                 :             : *  gnc_monetary methods
    2436                 :             : ********************************************************************/
    2437                 :             : 
    2438                 :             : /** Add a gnc_monetary to the list */
    2439                 :             : MonetaryList *
    2440                 :          44 : gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
    2441                 :             : {
    2442                 :          44 :     MonetaryList *l = list, *tmp;
    2443                 :          70 :     for (tmp = list; tmp; tmp = tmp->next)
    2444                 :             :     {
    2445                 :          49 :         auto list_mon = static_cast<gnc_monetary*>(tmp->data);
    2446                 :          49 :         if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
    2447                 :             :         {
    2448                 :          23 :             list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
    2449                 :             :                                               GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
    2450                 :          23 :             break;
    2451                 :             :         }
    2452                 :             :     }
    2453                 :             : 
    2454                 :             :     /* See if we found an entry, and add one if not */
    2455                 :          44 :     if (tmp == nullptr)
    2456                 :             :     {
    2457                 :          21 :         auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
    2458                 :          21 :         *new_mon = add_mon;
    2459                 :          21 :         l = g_list_prepend(l, new_mon);
    2460                 :             :     }
    2461                 :             : 
    2462                 :          44 :     return l;
    2463                 :             : }
    2464                 :             : 
    2465                 :             : /** Delete all entries in the list that have zero value.  Return list
    2466                 :             :     pointer will be a null pointer if there are no non-zero entries **/
    2467                 :             : MonetaryList *
    2468                 :          12 : gnc_monetary_list_delete_zeros(MonetaryList *list)
    2469                 :             : {
    2470                 :             :     MonetaryList *node, *next;
    2471                 :          33 :     for (node = list; node; node = next)
    2472                 :             :     {
    2473                 :          21 :         auto mon = static_cast<gnc_monetary*>(node->data);
    2474                 :          21 :         next = node->next;
    2475                 :          21 :         if (gnc_numeric_zero_p(mon->value))
    2476                 :             :         {
    2477                 :          10 :             g_free(mon);
    2478                 :          10 :             list = g_list_delete_link(list, node);
    2479                 :             :         }
    2480                 :             :     }
    2481                 :          12 :     return list;
    2482                 :             : }
    2483                 :             : 
    2484                 :             : /** Free a MonetaryList and all the monetaries it points to */
    2485                 :             : void
    2486                 :          11 : gnc_monetary_list_free(MonetaryList *list)
    2487                 :             : {
    2488                 :             :     MonetaryList *tmp;
    2489                 :          22 :     for (tmp = list; tmp; tmp = tmp->next)
    2490                 :             :     {
    2491                 :          11 :         g_free(tmp->data);
    2492                 :             :     }
    2493                 :             : 
    2494                 :          11 :     g_list_free(list);
    2495                 :          11 : }
    2496                 :             : 
    2497                 :             : /* ========================= END OF FILE ============================== */
        

Generated by: LCOV version 2.0-1