GnuCash  5.6-150-g038405b370+
gnc-commodity.cpp
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 
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;
98 
99 #define GET_PRIVATE(o) \
100  ((gnc_commodityPrivate*)gnc_commodity_get_instance_private((gnc_commodity*)o))
101 
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 
111 {
112  QofInstance inst;
113 
114  const gchar *name;
115  gboolean iso4217;
116  GHashTable * cm_table;
117  GList * cm_list;
118 };
119 
121 {
122  QofInstanceClass parent_class;
123 };
124 
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 
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  bool get_supported () const { return m_supported; }
158  void set_supported (bool supported) { m_supported = supported; }
159  QuoteSourceType get_type () const { return m_type; }
160  const char* get_user_name () const { return m_user_name.c_str(); }
161  const char* get_internal_name () const { return m_internal_name.c_str(); }
162  gnc_quote_source_s (gboolean supported, QuoteSourceType type,
163  const char* username, const char* int_name)
164  : m_supported{supported}
165  , m_type{type}
166  , m_user_name{username ? username : ""}
167  , 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", "Swiss Fund Data AG, CH"), "swissfunddata" },
224  { false, SOURCE_SINGLE, NC_("FQ Source", "Tesouro Direto bonds, BR"), "tesouro_direto" },
225  { false, SOURCE_SINGLE, NC_("FQ Source", "Toronto Stock eXchange, CA"), "tsx" },
226  { false, SOURCE_SINGLE, NC_("FQ Source", "Tradegate, DE"), "tradegate" },
227  { false, SOURCE_SINGLE, NC_("FQ Source", "Treasury Direct bonds, US"), "treasurydirect" },
228  { false, SOURCE_SINGLE, NC_("FQ Source", "Twelve Data"), "twelvedata" },
229  { false, SOURCE_SINGLE, NC_("FQ Source", "Union Investment, DE"), "unionfunds" },
230  { false, SOURCE_SINGLE, NC_("FQ Source", "US Govt. Thrift Savings Plan"), "tsp" },
231  { false, SOURCE_SINGLE, NC_("FQ Source", "XETRA, DE"), "xetra" },
232  { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo as JSON"), "yahoo_json" },
233  { false, SOURCE_SINGLE, NC_("FQ Source", "Yahoo Web"), "yahooweb" },
234  { false, SOURCE_SINGLE, NC_("FQ Source", "YH Finance (FinanceAPI)"), "financeapi" },
235 };
236 
237 // Finance::Quote defines these as failover methods
238 static QuoteSourceList multiple_quote_sources =
239 {
240  { false, SOURCE_MULTI, NC_("FQ Source", "Canada (Alphavantage, TMX)"), "canada" },
241  { false, SOURCE_MULTI, NC_("FQ Source", "Europe (ASEGR, Bourso, …)"), "europe" },
242  { false, SOURCE_MULTI, NC_("FQ Source", "India (BSEIndia, NSEIndia)"), "india"},
243  { false, SOURCE_MULTI, NC_("FQ Source", "Nasdaq (Alphavantage, FinanceAPI, …)"), "nasdaq" },
244  { false, SOURCE_MULTI, NC_("FQ Source", "NYSE (Alphavantage, FinanceAPI, …)"), "nyse" },
245  { false, SOURCE_MULTI, NC_("FQ Source", "U.K. Funds (FTfunds, MorningstarUK)"), "ukfunds" },
246  { false, SOURCE_MULTI, NC_("FQ Source", "USA (Alphavantage, FinanceAPI, …)"), "usa" },
247 };
248 
249 static QuoteSourceList new_quote_sources;
250 
251 // cannot use map or unordered_map because order must be preserved
252 static const std::vector<std::pair<QuoteSourceType,QuoteSourceList&>> quote_sources_map =
253  {
254  { SOURCE_CURRENCY, currency_quote_sources },
255  { SOURCE_SINGLE, single_quote_sources },
256  { SOURCE_MULTI, multiple_quote_sources },
257  { SOURCE_UNKNOWN, new_quote_sources }
258  };
259 
260 /********************************************************************
261  * gnc_quote_source_fq_installed
262  *
263  * This function indicates whether or not the Finance::Quote module
264  * is installed on a users computer.
265  ********************************************************************/
266 gboolean
268 {
269  return (!fq_version.empty());
270 }
271 
272 
273 /********************************************************************
274  * gnc_quote_source_fq_version
275  *
276  * This function the version of the Finance::Quote module installed
277  * on a user's computer or nullptr if no installation is found.
278  ********************************************************************/
279 const char*
281 {
282  return fq_version.c_str();
283 }
284 
285 static QuoteSourceList&
286 get_quote_source_from_type (QuoteSourceType type)
287 {
288  auto quote_sources_it = std::find_if (quote_sources_map.begin(), quote_sources_map.end(),
289  [type] (const auto& qs) { return type == qs.first; });
290 
291  if (quote_sources_it != quote_sources_map.end())
292  return quote_sources_it->second;
293 
294  PWARN ("Invalid Quote Source %d, returning new_quote_sources", type);
295  return new_quote_sources;
296 }
297 
298 /********************************************************************
299  * gnc_quote_source_num_entries
300  *
301  * Return the number of entries for a given type of price source.
302  ********************************************************************/
304 {
305  auto source{get_quote_source_from_type(type)};
306  return std::distance(source.begin(), source.end());
307 }
308 
309 
310 
311 /********************************************************************
312  * gnc_quote_source_add_new
313  *
314  * Add a new price source. Called when unknown source names are found
315  * either in the F::Q installation (a newly available source) or in
316  * the user's data file (a source that has vanished but needs to be
317  * tracked.)
318  ********************************************************************/
319 gnc_quote_source *
320 gnc_quote_source_add_new (const char *source_name, gboolean supported)
321 {
322  DEBUG("Creating new source %s", (!source_name ? "(null)" : source_name));
323  /* This name can be changed if/when support for this price source is
324  * integrated into gnucash. */
325  /* This name is permanent and must be kept the same if/when support
326  * for this price source is integrated into gnucash (i.e. for a
327  * nice user name). */
328  return &new_quote_sources.emplace_back (supported, SOURCE_UNKNOWN, source_name, source_name);
329 }
330 
331 /********************************************************************
332  * gnc_quote_source_lookup_by_xxx
333  *
334  * Lookup a price source data structure based upon various criteria.
335  ********************************************************************/
336 gnc_quote_source *
338 {
339  ENTER("type/index is %d/%d", type, index);
340  auto& sources = get_quote_source_from_type (type);
341  if ((size_t) index < sources.size())
342  {
343  auto it = std::next(sources.begin(), index);
344  LEAVE("found %s", it->get_user_name());
345  return &*it;
346  }
347 
348  LEAVE("not found");
349  return nullptr;
350 }
351 
352 gnc_quote_source *
354 {
355  if (!name || !*name)
356  return nullptr;
357 
358  for (const auto& [_, sources] : quote_sources_map)
359  {
360  auto source_it = std::find_if (sources.begin(), sources.end(),
361  [name] (const auto& qs)
362  { return (g_strcmp0(name, qs.get_internal_name()) == 0); });
363  if (source_it != sources.end())
364  return &(*source_it);
365  }
366 
367  DEBUG("gnc_quote_source_lookup_by_internal: Unknown source %s", name);
368  return nullptr;
369 }
370 
371 /********************************************************************
372  * gnc_quote_source_get_xxx
373  *
374  * Accessor functions - get functions only. There are no set functions.
375  ********************************************************************/
377 gnc_quote_source_get_type (const gnc_quote_source *source)
378 {
379  ENTER("%p", source);
380  if (!source)
381  {
382  LEAVE("bad source");
383  return SOURCE_SINGLE;
384  }
385 
386  LEAVE("type is %d", source->get_type());
387  return source->get_type();
388 }
389 
390 gint
391 gnc_quote_source_get_index (const gnc_quote_source *source)
392 {
393  if (!source)
394  {
395  PWARN ("bad source");
396  return 0;
397  }
398 
399  auto& sources = get_quote_source_from_type (source->get_type());
400  auto is_source = [&source](const auto& findif_source)
401  { return &findif_source == source; };
402 
403  auto iter = std::find_if (sources.begin(), sources.end(), is_source);
404  if (iter != sources.end())
405  return std::distance (sources.begin(), iter);
406 
407  PWARN ("couldn't locate source");
408  return 0;
409 }
410 
411 gboolean
412 gnc_quote_source_get_supported (const gnc_quote_source *source)
413 {
414  ENTER("%p", source);
415  if (!source)
416  {
417  LEAVE("bad source");
418  return FALSE;
419  }
420 
421  LEAVE("%s supported", source && source->get_supported() ? "" : "not ");
422  return source->get_supported();
423 }
424 
425 const char *
426 gnc_quote_source_get_user_name (const gnc_quote_source *source)
427 {
428  ENTER("%p", source);
429  if (!source)
430  {
431  LEAVE("bad source");
432  return nullptr;
433  }
434  LEAVE("user name %s", source->get_user_name());
435  return source->get_user_name();
436 }
437 
438 const char *
439 gnc_quote_source_get_internal_name (const gnc_quote_source *source)
440 {
441  ENTER("%p", source);
442  if (!source)
443  {
444  LEAVE("bad source");
445  return nullptr;
446  }
447  LEAVE("internal name %s", source->get_internal_name());
448  return source->get_internal_name();
449 }
450 
451 
452 /********************************************************************
453  * gnc_quote_source_set_fq_installed
454  *
455  * Update gnucash internal tables on what Finance::Quote sources are
456  * installed.
457  ********************************************************************/
458 void
459 gnc_quote_source_set_fq_installed (const char* version_string,
460  const std::vector<std::string>& sources_list)
461 {
462  ENTER(" ");
463 
464  if (sources_list.empty())
465  return;
466 
467  if (version_string)
468  fq_version = version_string;
469  else
470  fq_version.clear();
471 
472  for (const auto& source_name_str : sources_list)
473  {
474  auto source_name = source_name_str.c_str();
475  auto source = gnc_quote_source_lookup_by_internal(source_name);
476 
477  if (source)
478  {
479  DEBUG("Found source %s: %s", source_name, source->get_user_name());
480  source->set_supported (true);
481  continue;
482  }
483 
484  gnc_quote_source_add_new(source_name, TRUE);
485  }
486  LEAVE(" ");
487 }
488 
489 /********************************************************************
490  * QoF Helpers
491  ********************************************************************/
492 
493 void
494 gnc_commodity_begin_edit (gnc_commodity *cm)
495 {
496  qof_begin_edit(&cm->inst);
497 }
498 
499 static void commit_err (QofInstance *inst, QofBackendError errcode)
500 {
501  PERR ("Failed to commit: %d", errcode);
502  gnc_engine_signal_commit_error( errcode );
503 }
504 
505 static void noop (QofInstance *inst) {}
506 
507 static void
508 comm_free(QofInstance* inst)
509 {
510  commodity_free( GNC_COMMODITY(inst) );
511 }
512 
513 void
514 gnc_commodity_commit_edit (gnc_commodity *cm)
515 {
516  if (!qof_commit_edit (QOF_INSTANCE(cm))) return;
517  qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free);
518 }
519 
520 /********************************************************************
521  * gnc_commodity_new
522  ********************************************************************/
523 
524 static void
525 mark_commodity_dirty (gnc_commodity *cm)
526 {
527  qof_instance_set_dirty(&cm->inst);
528  qof_event_gen (&cm->inst, QOF_EVENT_MODIFY, nullptr);
529 }
530 
531 static void
532 reset_printname(gnc_commodityPrivate *priv)
533 {
534  g_free(priv->printname);
535  priv->printname = g_strdup_printf("%s (%s)",
536  priv->mnemonic ? priv->mnemonic : "",
537  priv->fullname ? priv->fullname : "");
538 }
539 
540 static void
541 reset_unique_name(gnc_commodityPrivate *priv)
542 {
543  gnc_commodity_namespace *ns;
544 
545  g_free(priv->unique_name);
546  ns = priv->name_space;
547  priv->unique_name = g_strdup_printf("%s::%s",
548  ns ? ns->name : "",
549  priv->mnemonic ? priv->mnemonic : "");
550 }
551 
552 /* GObject Initialization */
553 G_DEFINE_TYPE_WITH_PRIVATE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE)
554 
555 static void
556 gnc_commodity_init(gnc_commodity* com)
557 {
558  gnc_commodityPrivate* priv;
559 
560  priv = GET_PRIVATE(com);
561 
562  priv->name_space = nullptr;
563  priv->fullname = CACHE_INSERT("");
564  priv->mnemonic = CACHE_INSERT("");
565  priv->cusip = CACHE_INSERT("");
566  priv->fraction = 10000;
567  priv->quote_flag = 0;
568  priv->quote_source = nullptr;
569  priv->quote_tz = CACHE_INSERT("");
570 
571  reset_printname(priv);
572  reset_unique_name(priv);
573 }
574 
575 static void
576 gnc_commodity_dispose(GObject *comp)
577 {
578  G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp);
579 }
580 
581 static void
582 gnc_commodity_finalize(GObject* comp)
583 {
584  G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp);
585 }
586 /* Note that g_value_set_object() refs the object, as does
587  * g_object_get(). But g_object_get() only unrefs once when it disgorges
588  * the object, leaving an unbalanced ref, which leaks. So instead of
589  * using g_value_set_object(), use g_value_take_object() which doesn't
590  * ref the object when used in get_property().
591  */
592 static void
593 gnc_commodity_get_property (GObject *object,
594  guint prop_id,
595  GValue *value,
596  GParamSpec *pspec)
597 {
598  gnc_commodity *commodity;
599  gnc_commodityPrivate* priv;
600 
601  g_return_if_fail(GNC_IS_COMMODITY(object));
602 
603  commodity = GNC_COMMODITY(object);
604  priv = GET_PRIVATE(commodity);
605  switch (prop_id)
606  {
607  case PROP_NAMESPACE:
608  g_value_take_object(value, priv->name_space);
609  break;
610  case PROP_FULL_NAME:
611  g_value_set_string(value, priv->fullname);
612  break;
613  case PROP_MNEMONIC:
614  g_value_set_string(value, priv->mnemonic);
615  break;
616  case PROP_PRINTNAME:
617  g_value_set_string(value, priv->printname);
618  break;
619  case PROP_CUSIP:
620  g_value_set_string(value, priv->cusip);
621  break;
622  case PROP_FRACTION:
623  g_value_set_int(value, priv->fraction);
624  break;
625  case PROP_UNIQUE_NAME:
626  g_value_set_string(value, priv->unique_name);
627  break;
628  case PROP_QUOTE_FLAG:
629  g_value_set_boolean(value, priv->quote_flag);
630  break;
631  case PROP_QUOTE_SOURCE:
632  g_value_set_pointer(value, priv->quote_source);
633  break;
634  case PROP_QUOTE_TZ:
635  g_value_set_string(value, priv->quote_tz);
636  break;
637  default:
638  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
639  break;
640  }
641 }
642 
643 static void
644 gnc_commodity_set_property (GObject *object,
645  guint prop_id,
646  const GValue *value,
647  GParamSpec *pspec)
648 {
649  gnc_commodity *commodity;
650 
651  g_return_if_fail(GNC_IS_COMMODITY(object));
652 
653  commodity = GNC_COMMODITY(object);
654  g_assert (qof_instance_get_editlevel(commodity));
655 
656  switch (prop_id)
657  {
658  case PROP_NAMESPACE:
659  gnc_commodity_set_namespace(commodity, static_cast<const char*>(g_value_get_object(value)));
660  break;
661  case PROP_FULL_NAME:
662  gnc_commodity_set_fullname(commodity, g_value_get_string(value));
663  break;
664  case PROP_MNEMONIC:
665  gnc_commodity_set_mnemonic(commodity, g_value_get_string(value));
666  break;
667  case PROP_CUSIP:
668  gnc_commodity_set_cusip(commodity, g_value_get_string(value));
669  break;
670  case PROP_FRACTION:
671  gnc_commodity_set_fraction(commodity, g_value_get_int(value));
672  break;
673  case PROP_QUOTE_FLAG:
674  gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value));
675  break;
676  case PROP_QUOTE_SOURCE:
677  gnc_commodity_set_quote_source(commodity, static_cast<gnc_quote_source*>(g_value_get_pointer(value)));
678  break;
679  case PROP_QUOTE_TZ:
680  gnc_commodity_set_quote_tz(commodity, g_value_get_string(value));
681  break;
682  default:
683  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
684  break;
685  }
686 }
687 static void
688 gnc_commodity_class_init(struct _GncCommodityClass* klass)
689 {
690  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
691 
692  gobject_class->dispose = gnc_commodity_dispose;
693  gobject_class->finalize = gnc_commodity_finalize;
694  gobject_class->set_property = gnc_commodity_set_property;
695  gobject_class->get_property = gnc_commodity_get_property;
696 
697  g_object_class_install_property(gobject_class,
698  PROP_NAMESPACE,
699  g_param_spec_object ("namespace",
700  "Namespace",
701  "The namespace field denotes the "
702  "namespace for this commodity, either "
703  "a currency or symbol from a quote source.",
704  GNC_TYPE_COMMODITY_NAMESPACE,
705  G_PARAM_READWRITE));
706  g_object_class_install_property(gobject_class,
707  PROP_FULL_NAME,
708  g_param_spec_string ("fullname",
709  "Full Commodity Name",
710  "The fullname is the official full name of"
711  "the currency.",
712  nullptr,
713  G_PARAM_READWRITE));
714  g_object_class_install_property(gobject_class,
715  PROP_MNEMONIC,
716  g_param_spec_string ("mnemonic",
717  "Commodity Mnemonic",
718  "The mnemonic is the official abbreviated"
719  "designation for the currency.",
720  nullptr,
721  G_PARAM_READWRITE));
722  g_object_class_install_property(gobject_class,
723  PROP_PRINTNAME,
724  g_param_spec_string ("printname",
725  "Commodity Print Name",
726  "Printable form of the commodity name.",
727  nullptr,
728  G_PARAM_READABLE));
729  g_object_class_install_property(gobject_class,
730  PROP_CUSIP,
731  g_param_spec_string ("cusip",
732  "Commodity CUSIP Code",
733  "?????",
734  nullptr,
735  G_PARAM_READWRITE));
736  g_object_class_install_property(gobject_class,
737  PROP_FRACTION,
738  g_param_spec_int ("fraction",
739  "Fraction",
740  "The fraction is the number of sub-units that "
741  "the basic commodity can be divided into.",
742  1,
744  1,
745  G_PARAM_READWRITE));
746  g_object_class_install_property(gobject_class,
747  PROP_UNIQUE_NAME,
748  g_param_spec_string ("unique-name",
749  "Commodity Unique Name",
750  "Unique form of the commodity name which combines "
751  "the namespace name and the commodity name.",
752  nullptr,
753  G_PARAM_READABLE));
754  g_object_class_install_property(gobject_class,
755  PROP_QUOTE_FLAG,
756  g_param_spec_boolean ("quote_flag",
757  "Quote Flag",
758  "TRUE if prices are to be downloaded for this "
759  "commodity from a quote source.",
760  FALSE,
761  G_PARAM_READWRITE));
762  g_object_class_install_property(gobject_class,
763  PROP_QUOTE_SOURCE,
764  g_param_spec_pointer("quote-source",
765  "Quote Source",
766  "The quote source from which prices are downloaded.",
767  G_PARAM_READWRITE));
768  g_object_class_install_property(gobject_class,
769  PROP_QUOTE_TZ,
770  g_param_spec_string ("quote-tz",
771  "Commodity Quote Timezone",
772  "?????",
773  nullptr,
774  G_PARAM_READWRITE));
775 }
776 
777 gnc_commodity *
778 gnc_commodity_new(QofBook *book, const char * fullname,
779  const char * name_space, const char * mnemonic,
780  const char * cusip, int fraction)
781 {
782  auto retval = GNC_COMMODITY(g_object_new(GNC_TYPE_COMMODITY, nullptr));
783 
784  qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book);
785  gnc_commodity_begin_edit(retval);
786 
787  if ( name_space != nullptr )
788  {
789  /* Prevent setting anything except template in namespace template. */
790  if (g_strcmp0 (name_space, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
791  g_strcmp0 (mnemonic, "template") != 0)
792  {
793  PWARN("Converting commodity %s from namespace template to "
794  "namespace User", mnemonic);
795  name_space = "User";
796  }
797  gnc_commodity_set_namespace(retval, name_space);
798  if (gnc_commodity_namespace_is_iso(name_space))
799  {
802  }
803  }
804  gnc_commodity_set_fullname(retval, fullname);
805  gnc_commodity_set_mnemonic(retval, mnemonic);
806  gnc_commodity_set_cusip(retval, cusip);
807  gnc_commodity_set_fraction(retval, fraction);
808  mark_commodity_dirty (retval);
809  gnc_commodity_commit_edit(retval);
810 
811  qof_event_gen (&retval->inst, QOF_EVENT_CREATE, nullptr);
812 
813  return retval;
814 }
815 
816 
817 /********************************************************************
818  * gnc_commodity_destroy
819  ********************************************************************/
820 
821 static void
822 commodity_free(gnc_commodity * cm)
823 {
824  QofBook *book;
825  gnc_commodity_table *table;
826  gnc_commodityPrivate* priv;
827 
828  if (!cm) return;
829 
830  book = qof_instance_get_book(&cm->inst);
833  priv = GET_PRIVATE(cm);
834 
835  qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, nullptr);
836 
837  /* Set at creation */
838  CACHE_REMOVE (priv->fullname);
839  CACHE_REMOVE (priv->cusip);
840  CACHE_REMOVE (priv->mnemonic);
841  CACHE_REMOVE (priv->quote_tz);
842  priv->name_space = nullptr;
843 
844  /* Set through accessor functions */
845  priv->quote_source = nullptr;
846 
847  /* Automatically generated */
848  g_free(priv->printname);
849  priv->printname = nullptr;
850 
851  g_free(priv->unique_name);
852  priv->unique_name = nullptr;
853 
854 #ifdef ACCOUNTS_CLEANED_UP
855  /* Account objects are not actually cleaned up when a book is closed (in fact
856  * a memory leak), but commodities are, so in currently this warning gets hit
857  * quite frequently. Disable the check until cleaning up of accounts objects
858  * on close is implemented. */
859  if (priv->usage_count != 0)
860  {
861  PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm,
862  priv->usage_count);
863  }
864 #endif
865 
866  /* qof_instance_release (&cm->inst); */
867  g_object_unref(cm);
868 }
869 
870 void
871 gnc_commodity_destroy(gnc_commodity * cm)
872 {
873  gnc_commodity_begin_edit(cm);
874  qof_instance_set_destroying(cm, TRUE);
875  gnc_commodity_commit_edit(cm);
876 }
877 
878 void
879 gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src)
880 {
881  gnc_commodityPrivate* src_priv = GET_PRIVATE(src);
882  gnc_commodityPrivate* dest_priv = GET_PRIVATE(dest);
883 
884  gnc_commodity_set_fullname (dest, src_priv->fullname);
885  gnc_commodity_set_mnemonic (dest, src_priv->mnemonic);
886  dest_priv->name_space = src_priv->name_space;
887  gnc_commodity_set_fraction (dest, src_priv->fraction);
888  gnc_commodity_set_cusip (dest, src_priv->cusip);
889  gnc_commodity_set_quote_flag (dest, src_priv->quote_flag);
891  gnc_commodity_set_quote_tz (dest, src_priv->quote_tz);
892  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
893 }
894 
895 gnc_commodity *
896 gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
897 {
898  gnc_commodityPrivate* src_priv;
899  gnc_commodityPrivate* dest_priv;
900 
901  auto dest = GNC_COMMODITY (g_object_new(GNC_TYPE_COMMODITY, nullptr));
902  qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book);
903  src_priv = GET_PRIVATE(src);
904  dest_priv = GET_PRIVATE(dest);
905 
906  dest_priv->fullname = CACHE_INSERT(src_priv->fullname);
907  dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic);
908  dest_priv->cusip = CACHE_INSERT(src_priv->cusip);
909  dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz);
910 
911  dest_priv->name_space = src_priv->name_space;
912 
913  dest_priv->fraction = src_priv->fraction;
914  dest_priv->quote_flag = src_priv->quote_flag;
915 
917 
918  qof_instance_copy_kvp (QOF_INSTANCE (dest), QOF_INSTANCE (src));
919 
920  reset_printname(dest_priv);
921  reset_unique_name(dest_priv);
922 
923  return dest;
924 }
925 
926 /********************************************************************
927  * gnc_commodity_get_mnemonic
928  ********************************************************************/
929 
930 const char *
931 gnc_commodity_get_mnemonic(const gnc_commodity * cm)
932 {
933  if (!cm) return nullptr;
934  return GET_PRIVATE(cm)->mnemonic;
935 }
936 
937 /********************************************************************
938  * gnc_commodity_get_printname
939  ********************************************************************/
940 
941 const char *
942 gnc_commodity_get_printname(const gnc_commodity * cm)
943 {
944  if (!cm) return nullptr;
945  return GET_PRIVATE(cm)->printname;
946 }
947 
948 
949 /********************************************************************
950  * gnc_commodity_get_namespace
951  ********************************************************************/
952 
953 const char *
954 gnc_commodity_get_namespace(const gnc_commodity * cm)
955 {
956  if (!cm) return nullptr;
957  return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
958 }
959 
960 gnc_commodity_namespace *
961 gnc_commodity_get_namespace_ds(const gnc_commodity * cm)
962 {
963  if (!cm) return nullptr;
964  return GET_PRIVATE(cm)->name_space;
965 }
966 
967 /********************************************************************
968  * gnc_commodity_get_fullname
969  ********************************************************************/
970 
971 const char *
972 gnc_commodity_get_fullname(const gnc_commodity * cm)
973 {
974  if (!cm) return nullptr;
975  return GET_PRIVATE(cm)->fullname;
976 }
977 
978 
979 /********************************************************************
980  * gnc_commodity_get_unique_name
981  ********************************************************************/
982 
983 const char *
984 gnc_commodity_get_unique_name(const gnc_commodity * cm)
985 {
986  if (!cm) return nullptr;
987  return GET_PRIVATE(cm)->unique_name;
988 }
989 
990 
991 /********************************************************************
992  * gnc_commodity_get_cusip
993  ********************************************************************/
994 
995 const char *
996 gnc_commodity_get_cusip(const gnc_commodity * cm)
997 {
998  if (!cm) return nullptr;
999  return GET_PRIVATE(cm)->cusip;
1000 }
1001 
1002 /********************************************************************
1003  * gnc_commodity_get_fraction
1004  ********************************************************************/
1005 
1006 int
1007 gnc_commodity_get_fraction(const gnc_commodity * cm)
1008 {
1009  if (!cm) return 0;
1010  return GET_PRIVATE(cm)->fraction;
1011 }
1012 
1013 /********************************************************************
1014  * gnc_commodity_get_auto_quote_control_flag
1015  ********************************************************************/
1016 
1017 gboolean
1018 gnc_commodity_get_auto_quote_control_flag(const gnc_commodity *cm)
1019 {
1020  if (!cm) return FALSE;
1021  auto str{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE (cm), {"auto_quote_control"})};
1022  return !str || g_strcmp0 (*str, "false");
1023 }
1024 
1025 /********************************************************************
1026  * gnc_commodity_get_quote_flag
1027  ********************************************************************/
1028 
1029 gboolean
1030 gnc_commodity_get_quote_flag(const gnc_commodity *cm)
1031 {
1032  if (!cm) return FALSE;
1033  return (GET_PRIVATE(cm)->quote_flag);
1034 }
1035 
1036 /********************************************************************
1037  * gnc_commodity_get_quote_source
1038  ********************************************************************/
1039 
1040 gnc_quote_source*
1041 gnc_commodity_get_quote_source(const gnc_commodity *cm)
1042 {
1043  gnc_commodityPrivate* priv;
1044 
1045  if (!cm) return nullptr;
1046  priv = GET_PRIVATE(cm);
1047  if (!priv->quote_source && gnc_commodity_is_iso(cm))
1048  return &currency_quote_sources.front();
1049  return priv->quote_source;
1050 }
1051 
1052 gnc_quote_source*
1053 gnc_commodity_get_default_quote_source(const gnc_commodity *cm)
1054 {
1055  if (cm && gnc_commodity_is_iso(cm))
1056  return &currency_quote_sources.front();
1057  /* Should make this a user option at some point. */
1058  return gnc_quote_source_lookup_by_internal("alphavantage");
1059 }
1060 
1061 /********************************************************************
1062  * gnc_commodity_get_quote_tz
1063  ********************************************************************/
1064 
1065 const char*
1066 gnc_commodity_get_quote_tz(const gnc_commodity *cm)
1067 {
1068  if (!cm) return nullptr;
1069  return GET_PRIVATE(cm)->quote_tz;
1070 }
1071 
1072 /********************************************************************
1073  * gnc_commodity_get_user_symbol
1074  ********************************************************************/
1075 const char*
1076 gnc_commodity_get_user_symbol(const gnc_commodity *cm)
1077 {
1078  g_return_val_if_fail (GNC_IS_COMMODITY (cm), nullptr);
1079 
1080  auto sym{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(cm), {"user_symbol"})};
1081  return sym ? *sym : nullptr;
1082 }
1083 
1084 /********************************************************************
1085  * gnc_commodity_get_default_symbol
1086  *******************************************************************/
1087 const char*
1088 gnc_commodity_get_default_symbol(const gnc_commodity *cm)
1089 {
1090  if (!cm) return nullptr;
1091  return GET_PRIVATE(cm)->default_symbol;
1092 }
1093 
1094 /********************************************************************
1095  * gnc_commodity_get_nice_symbol
1096  *******************************************************************/
1097 const char*
1098 gnc_commodity_get_nice_symbol (const gnc_commodity *cm)
1099 {
1100  const char *nice_symbol;
1101  struct lconv *lc;
1102  if (!cm) return nullptr;
1103 
1104  nice_symbol = gnc_commodity_get_user_symbol(cm);
1105  if (nice_symbol && *nice_symbol)
1106  return nice_symbol;
1107 
1108  lc = gnc_localeconv();
1109  nice_symbol = lc->currency_symbol;
1110  if (!g_strcmp0(gnc_commodity_get_mnemonic(cm), lc->int_curr_symbol))
1111  return nice_symbol;
1112 
1113  nice_symbol = gnc_commodity_get_default_symbol(cm);
1114  if (nice_symbol && *nice_symbol)
1115  return nice_symbol;
1116 
1117  return gnc_commodity_get_mnemonic(cm);
1118 }
1119 
1120 /********************************************************************
1121  * gnc_commodity_set_mnemonic
1122  ********************************************************************/
1123 
1124 void
1125 gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic)
1126 {
1127  gnc_commodityPrivate* priv;
1128 
1129  if (!cm) return;
1130  priv = GET_PRIVATE(cm);
1131  if (priv->mnemonic == mnemonic) return;
1132 
1133  gnc_commodity_begin_edit(cm);
1134  CACHE_REMOVE (priv->mnemonic);
1135  priv->mnemonic = CACHE_INSERT(mnemonic);
1136 
1137  mark_commodity_dirty (cm);
1138  reset_printname(priv);
1139  reset_unique_name(priv);
1140  gnc_commodity_commit_edit(cm);
1141 }
1142 
1143 /********************************************************************
1144  * gnc_commodity_set_namespace
1145  ********************************************************************/
1146 
1147 void
1148 gnc_commodity_set_namespace(gnc_commodity * cm, const char * name_space)
1149 {
1150  QofBook *book;
1151  gnc_commodity_table *table;
1152  gnc_commodity_namespace *nsp;
1153  gnc_commodityPrivate* priv;
1154 
1155  if (!cm) return;
1156  priv = GET_PRIVATE(cm);
1157  book = qof_instance_get_book (&cm->inst);
1159  nsp = gnc_commodity_table_add_namespace(table, name_space, book);
1160  if (priv->name_space == nsp)
1161  return;
1162 
1163  gnc_commodity_begin_edit(cm);
1164  priv->name_space = nsp;
1165  if (nsp->iso4217)
1166  priv->quote_source = gnc_quote_source_lookup_by_internal("currency");
1167  mark_commodity_dirty(cm);
1168  reset_printname(priv);
1169  reset_unique_name(priv);
1170  gnc_commodity_commit_edit(cm);
1171 }
1172 
1173 /********************************************************************
1174  * gnc_commodity_set_fullname
1175  ********************************************************************/
1176 
1177 void
1178 gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname)
1179 {
1180  gnc_commodityPrivate* priv;
1181 
1182  if (!cm) return;
1183  priv = GET_PRIVATE(cm);
1184  if (priv->fullname == fullname) return;
1185 
1186  CACHE_REMOVE (priv->fullname);
1187  priv->fullname = CACHE_INSERT (fullname);
1188 
1189  gnc_commodity_begin_edit(cm);
1190  mark_commodity_dirty(cm);
1191  reset_printname(priv);
1192  gnc_commodity_commit_edit(cm);
1193 }
1194 
1195 /********************************************************************
1196  * gnc_commodity_set_cusip
1197  ********************************************************************/
1198 
1199 void
1200 gnc_commodity_set_cusip(gnc_commodity * cm,
1201  const char * cusip)
1202 {
1203  gnc_commodityPrivate* priv;
1204 
1205  if (!cm) return;
1206 
1207  priv = GET_PRIVATE(cm);
1208  if (priv->cusip == cusip) return;
1209 
1210  gnc_commodity_begin_edit(cm);
1211  CACHE_REMOVE (priv->cusip);
1212  priv->cusip = CACHE_INSERT (cusip);
1213  mark_commodity_dirty(cm);
1214  gnc_commodity_commit_edit(cm);
1215 }
1216 
1217 /********************************************************************
1218  * gnc_commodity_set_fraction
1219  ********************************************************************/
1220 
1221 void
1222 gnc_commodity_set_fraction(gnc_commodity * cm, int fraction)
1223 {
1224  if (!cm) return;
1225  gnc_commodity_begin_edit(cm);
1226  GET_PRIVATE(cm)->fraction = fraction;
1227  mark_commodity_dirty(cm);
1228  gnc_commodity_commit_edit(cm);
1229 }
1230 
1231 /********************************************************************
1232  * gnc_commodity_set_auto_quote_control_flag
1233  ********************************************************************/
1234 
1235 void
1236 gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm,
1237  const gboolean flag)
1238 {
1239  ENTER ("(cm=%p, flag=%d)", cm, flag);
1240 
1241  if (!cm)
1242  {
1243  LEAVE("");
1244  return;
1245  }
1246  gnc_commodity_begin_edit(cm);
1247  auto val = flag ? std::nullopt : std::make_optional<const char*>(g_strdup("false"));
1248  qof_instance_set_path_kvp<const char*> (QOF_INSTANCE (cm), val, {"auto_quote_control"});
1249  mark_commodity_dirty(cm);
1250  gnc_commodity_commit_edit(cm);
1251  LEAVE("");
1252 }
1253 
1254 /********************************************************************
1255  * gnc_commodity_user_set_quote_flag
1256  ********************************************************************/
1257 
1258 void
1259 gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1260 {
1261  gnc_commodityPrivate* priv;
1262 
1263  ENTER ("(cm=%p, flag=%d)", cm, flag);
1264 
1265  if (!cm)
1266  {
1267  LEAVE("");
1268  return;
1269  }
1270 
1271  priv = GET_PRIVATE(cm);
1272  gnc_commodity_begin_edit(cm);
1273  gnc_commodity_set_quote_flag(cm, flag);
1274  if (gnc_commodity_is_iso(cm))
1275  {
1276  /* For currencies, disable auto quote control if the quote flag is being
1277  * changed from its default value and enable it if the quote flag is being
1278  * reset to its default value. The defaults for the quote flag are
1279  * disabled if no accounts are using the currency, and true otherwise.
1280  * Thus enable auto quote control if flag is FALSE and there are not any
1281  * accounts using this currency OR flag is TRUE and there are accounts
1282  * using this currency; otherwise disable auto quote control */
1283  gnc_commodity_set_auto_quote_control_flag(cm,
1284  (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0)));
1285  }
1286  gnc_commodity_commit_edit(cm);
1287  LEAVE("");
1288 }
1289 
1290 /********************************************************************
1291  * gnc_commodity_set_quote_flag
1292  ********************************************************************/
1293 
1294 void
1295 gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
1296 {
1297  ENTER ("(cm=%p, flag=%d)", cm, flag);
1298 
1299  if (!cm) return;
1300  gnc_commodity_begin_edit(cm);
1301  GET_PRIVATE(cm)->quote_flag = flag;
1302  mark_commodity_dirty(cm);
1303  gnc_commodity_commit_edit(cm);
1304  LEAVE(" ");
1305 }
1306 
1307 /********************************************************************
1308  * gnc_commodity_set_quote_source
1309  ********************************************************************/
1310 
1311 void
1312 gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
1313 {
1314  ENTER ("(cm=%p, src=%p(%s))", cm, src, src ? src->get_internal_name() : "unknown");
1315 
1316  if (!cm) return;
1317  gnc_commodity_begin_edit(cm);
1318  GET_PRIVATE(cm)->quote_source = src;
1319  mark_commodity_dirty(cm);
1320  gnc_commodity_commit_edit(cm);
1321  LEAVE(" ");
1322 }
1323 
1324 /********************************************************************
1325  * gnc_commodity_set_quote_tz
1326  ********************************************************************/
1327 
1328 void
1329 gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
1330 {
1331  gnc_commodityPrivate* priv;
1332 
1333  if (!cm) return;
1334 
1335  ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)");
1336 
1337  priv = GET_PRIVATE(cm);
1338 
1339  if (tz == priv->quote_tz)
1340  {
1341  LEAVE("Already correct TZ");
1342  return;
1343  }
1344 
1345  gnc_commodity_begin_edit(cm);
1346  CACHE_REMOVE (priv->quote_tz);
1347  priv->quote_tz = CACHE_INSERT (tz);
1348  mark_commodity_dirty(cm);
1349  gnc_commodity_commit_edit(cm);
1350  LEAVE(" ");
1351 }
1352 
1353 /********************************************************************
1354  * gnc_commodity_set_user_symbol
1355  ********************************************************************/
1356 
1357 void
1358 gnc_commodity_set_user_symbol(gnc_commodity * cm, const char * user_symbol)
1359 {
1360  struct lconv *lc;
1361 
1362  if (!cm) return;
1363 
1364  ENTER ("(cm=%p, symbol=%s)", cm, user_symbol ? user_symbol : "(null)");
1365 
1366  lc = gnc_localeconv();
1367  if (!user_symbol || !*user_symbol)
1368  user_symbol = nullptr;
1369  else if (!g_strcmp0(lc->int_curr_symbol, gnc_commodity_get_mnemonic(cm)) &&
1370  !g_strcmp0(lc->currency_symbol, user_symbol))
1371  /* if the user gives the ISO symbol for the locale currency or the
1372  * default symbol, actually remove the user symbol */
1373  user_symbol = nullptr;
1374  else if (!g_strcmp0(user_symbol, gnc_commodity_get_default_symbol(cm)))
1375  user_symbol = nullptr;
1376 
1377  gnc_commodity_begin_edit (cm);
1378 
1379  auto val = user_symbol ? std::make_optional<const char*>(g_strdup(user_symbol)) : std::nullopt;
1380  qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(cm), val, {"user_symbol"});
1381 
1382  mark_commodity_dirty(cm);
1383  gnc_commodity_commit_edit(cm);
1384 
1385  LEAVE(" ");
1386 }
1387 
1388 /********************************************************************
1389  * gnc_commodity_set_default_symbol
1390  * Not made visible in gnc-commodity.h, it is only called from
1391  * iso-4217-currencies.c at startup.
1392  ********************************************************************/
1393 void
1394 gnc_commodity_set_default_symbol(gnc_commodity * cm,
1395  const char * default_symbol)
1396 {
1397  GET_PRIVATE(cm)->default_symbol = default_symbol;
1398 }
1399 
1400 /********************************************************************
1401  * gnc_commodity_increment_usage_count
1402  ********************************************************************/
1403 
1404 void
1406 {
1407  gnc_commodityPrivate* priv;
1408 
1409  ENTER("(cm=%p)", cm);
1410 
1411  if (!cm)
1412  {
1413  LEAVE("");
1414  return;
1415  }
1416 
1417  priv = GET_PRIVATE(cm);
1418 
1419  if ((priv->usage_count == 0) && !priv->quote_flag
1420  && gnc_commodity_get_auto_quote_control_flag(cm)
1421  && gnc_commodity_is_iso(cm))
1422  {
1423  /* compatibility hack - Gnucash 1.8 gets currency quotes when a
1424  non-default currency is assigned to an account. */
1425  gnc_commodity_begin_edit(cm);
1426  gnc_commodity_set_quote_flag(cm, TRUE);
1428  gnc_commodity_get_default_quote_source(cm));
1429  gnc_commodity_commit_edit(cm);
1430  }
1431  priv->usage_count++;
1432  LEAVE("(usage_count=%d)", priv->usage_count);
1433 }
1434 
1435 /********************************************************************
1436  * gnc_commodity_decrement_usage_count
1437  ********************************************************************/
1438 
1439 void
1441 {
1442  gnc_commodityPrivate* priv;
1443 
1444  ENTER("(cm=%p)", cm);
1445 
1446  if (!cm)
1447  {
1448  LEAVE("");
1449  return;
1450  }
1451 
1452  priv = GET_PRIVATE(cm);
1453 
1454  if (priv->usage_count == 0)
1455  {
1456  PWARN("usage_count already zero");
1457  LEAVE("");
1458  return;
1459  }
1460 
1461  priv->usage_count--;
1462  if ((priv->usage_count == 0) && priv->quote_flag
1463  && gnc_commodity_get_auto_quote_control_flag(cm)
1464  && gnc_commodity_is_iso(cm))
1465  {
1466  /* if this is a currency with auto quote control enabled and no more
1467  * accounts reference this currency, disable quote retrieval */
1468  gnc_commodity_set_quote_flag(cm, FALSE);
1469  }
1470  LEAVE("(usage_count=%d)", priv->usage_count);
1471 }
1472 
1473 /********************************************************************\
1474 \********************************************************************/
1475 
1476 
1477 /********************************************************************
1478  * gnc_commodity_equiv
1479  * are two commodities the same?
1480  ********************************************************************/
1481 
1482 gboolean
1483 gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b)
1484 {
1485  gnc_commodityPrivate* priv_a;
1486  gnc_commodityPrivate* priv_b;
1487 
1488  if (a == b) return TRUE;
1489  if (!a || !b) return FALSE;
1490 
1491  priv_a = GET_PRIVATE(a);
1492  priv_b = GET_PRIVATE(b);
1493  if (priv_a->name_space != priv_b->name_space) return FALSE;
1494  if (g_strcmp0(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE;
1495 
1496  return TRUE;
1497 }
1498 
1499 gboolean
1500 gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b)
1501 {
1502  return gnc_commodity_compare(a, b) == 0;
1503 }
1504 
1505 // Used as a sorting callback for deleting old prices, so it needs to be
1506 // stable but doesn't need to be in any particular order sensible to humans.
1507 int gnc_commodity_compare(const gnc_commodity * a, const gnc_commodity * b)
1508 {
1509  if (a == b) return 0;
1510  if (a && !b) return 1;
1511  if (b && !a) return -1;
1512  return qof_instance_guid_compare(a, b);
1513 }
1514 
1515 // Used as a callback to g_list_find_custom, it should return 0
1516 // when the commodities match.
1517 int gnc_commodity_compare_void(const void * a, const void * b)
1518 {
1519  return gnc_commodity_compare(GNC_COMMODITY (a), GNC_COMMODITY (b));
1520 }
1521 
1522 /************************************************************
1523  * Namespace functions *
1524  ************************************************************/
1525 const char *
1526 gnc_commodity_namespace_get_name (const gnc_commodity_namespace *ns)
1527 {
1528  if (ns == nullptr)
1529  return nullptr;
1530  return ns->name;
1531 }
1532 
1533 const char *
1534 gnc_commodity_namespace_get_gui_name (const gnc_commodity_namespace *ns)
1535 {
1536  if (ns == nullptr)
1537  return nullptr;
1538  if (g_strcmp0 (ns->name, GNC_COMMODITY_NS_CURRENCY) == 0)
1539  return GNC_COMMODITY_NS_ISO_GUI;
1540  return ns->name;
1541 }
1542 
1543 GList *
1544 gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
1545 {
1546  if (!name_space)
1547  return nullptr;
1548 
1549  return g_list_copy (name_space->cm_list);
1550 }
1551 
1552 gboolean
1553 gnc_commodity_namespace_is_iso(const char *name_space)
1554 {
1555  return ((g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0) ||
1556  (g_strcmp0(name_space, GNC_COMMODITY_NS_CURRENCY) == 0));
1557 }
1558 
1559 static const gchar *
1560 gnc_commodity_table_map_namespace(const char * name_space)
1561 {
1562  if (g_strcmp0(name_space, GNC_COMMODITY_NS_ISO) == 0)
1563  return GNC_COMMODITY_NS_CURRENCY;
1564  return name_space;
1565 }
1566 
1567 /********************************************************************
1568  * gnc_commodity_table_new
1569  * make a new commodity table
1570  ********************************************************************/
1571 
1572 gnc_commodity_table *
1574 {
1575  gnc_commodity_table * retval = g_new0(gnc_commodity_table, 1);
1576  retval->ns_table = g_hash_table_new(&g_str_hash, &g_str_equal);
1577  retval->ns_list = nullptr;
1578  return retval;
1579 }
1580 
1581 /********************************************************************
1582  * book anchor functions
1583  ********************************************************************/
1584 
1585 gnc_commodity_table *
1587 {
1588  if (!book) return nullptr;
1589  return static_cast<gnc_commodity_table*>(qof_book_get_data (book, GNC_COMMODITY_TABLE));
1590 }
1591 
1592 gnc_commodity *
1593 gnc_commodity_obtain_twin (const gnc_commodity *from, QofBook *book)
1594 {
1595  gnc_commodity *twin;
1596  const char * ucom;
1597  gnc_commodity_table * comtbl;
1598 
1599  if (!from) return nullptr;
1600  comtbl = gnc_commodity_table_get_table (book);
1601  if (!comtbl) return nullptr;
1602 
1603  ucom = gnc_commodity_get_unique_name (from);
1604  twin = gnc_commodity_table_lookup_unique (comtbl, ucom);
1605  if (!twin)
1606  {
1607  twin = gnc_commodity_clone (from, book);
1608  twin = gnc_commodity_table_insert (comtbl, twin);
1609  }
1610  return twin;
1611 }
1612 
1613 /********************************************************************
1614  * gnc_commodity_table_get_size
1615  * get the size of the commodity table
1616  ********************************************************************/
1617 
1618 static void
1619 count_coms(gpointer key, gpointer value, gpointer user_data)
1620 {
1621  GHashTable *tbl = ((gnc_commodity_namespace*)value)->cm_table;
1622  guint *count = (guint*)user_data;
1623 
1624  if (g_strcmp0((char*)key, GNC_COMMODITY_NS_CURRENCY) == 0)
1625  {
1626  /* don't count default commodities */
1627  return;
1628  }
1629 
1630  if (!value) return;
1631 
1632  *count += g_hash_table_size(tbl);
1633 }
1634 
1635 guint
1636 gnc_commodity_table_get_size(const gnc_commodity_table* tbl)
1637 {
1638  guint count = 0;
1639  g_return_val_if_fail(tbl, 0);
1640  g_return_val_if_fail(tbl->ns_table, 0);
1641 
1642  g_hash_table_foreach(tbl->ns_table, count_coms, (gpointer)&count);
1643 
1644  return count;
1645 }
1646 
1647 /********************************************************************
1648  * gnc_commodity_table_lookup
1649  * locate a commodity by namespace and mnemonic.
1650  ********************************************************************/
1651 
1652 gnc_commodity *
1653 gnc_commodity_table_lookup(const gnc_commodity_table * table,
1654  const char * name_space, const char * mnemonic)
1655 {
1656  gnc_commodity_namespace * nsp = nullptr;
1657 
1658  if (!table || !name_space || !mnemonic) return nullptr;
1659 
1660  nsp = gnc_commodity_table_find_namespace(table, name_space);
1661 
1662  if (nsp)
1663  {
1664  /*
1665  * Backward compatibility support for currencies that have
1666  * recently changed.
1667  */
1668  if (nsp->iso4217)
1669  {
1670  auto it = gnc_new_iso_codes.find (mnemonic);
1671  if (it != gnc_new_iso_codes.end())
1672  mnemonic = it->second.c_str();
1673  }
1674  return GNC_COMMODITY(g_hash_table_lookup(nsp->cm_table, (gpointer)mnemonic));
1675  }
1676  else
1677  {
1678  return nullptr;
1679  }
1680 }
1681 
1682 /********************************************************************
1683  * gnc_commodity_table_lookup
1684  * locate a commodity by unique name.
1685  ********************************************************************/
1686 
1687 gnc_commodity *
1688 gnc_commodity_table_lookup_unique(const gnc_commodity_table *table,
1689  const char * unique_name)
1690 {
1691  char *name_space;
1692  char *mnemonic;
1693  gnc_commodity *commodity;
1694 
1695  if (!table || !unique_name) return nullptr;
1696 
1697  name_space = g_strdup (unique_name);
1698  mnemonic = strstr (name_space, "::");
1699  if (!mnemonic)
1700  {
1701  g_free (name_space);
1702  return nullptr;
1703  }
1704 
1705  *mnemonic = '\0';
1706  mnemonic += 2;
1707 
1708  commodity = gnc_commodity_table_lookup (table, name_space, mnemonic);
1709 
1710  g_free (name_space);
1711 
1712  return commodity;
1713 }
1714 
1715 /********************************************************************
1716  * gnc_commodity_table_find_full
1717  * locate a commodity by namespace and printable name
1718  ********************************************************************/
1719 
1720 gnc_commodity *
1721 gnc_commodity_table_find_full(const gnc_commodity_table * table,
1722  const char * name_space,
1723  const char * fullname)
1724 {
1725  gnc_commodity * retval = nullptr;
1726  GList * all;
1727  GList * iterator;
1728 
1729  if (!fullname || (fullname[0] == '\0'))
1730  return nullptr;
1731 
1732  all = gnc_commodity_table_get_commodities(table, name_space);
1733 
1734  for (iterator = all; iterator; iterator = iterator->next)
1735  {
1736  auto commodity = GNC_COMMODITY (iterator->data);
1737  if (!strcmp(fullname,
1738  gnc_commodity_get_printname(commodity)))
1739  {
1740  retval = commodity;
1741  break;
1742  }
1743  }
1744 
1745  g_list_free (all);
1746 
1747  return retval;
1748 }
1749 
1750 
1751 /********************************************************************
1752  * gnc_commodity_table_insert
1753  * add a commodity to the table.
1754  ********************************************************************/
1755 
1756 gnc_commodity *
1757 gnc_commodity_table_insert(gnc_commodity_table * table,
1758  gnc_commodity * comm)
1759 {
1760  gnc_commodity_namespace * nsp = nullptr;
1761  gnc_commodity *c;
1762  const char *ns_name;
1763  gnc_commodityPrivate* priv;
1764  QofBook *book;
1765 
1766  if (!table) return nullptr;
1767  if (!comm) return nullptr;
1768 
1769  priv = GET_PRIVATE(comm);
1770 
1771  ENTER ("(table=%p, comm=%p) %s %s", table, comm,
1772  (priv->mnemonic == nullptr ? "(null)" : priv->mnemonic),
1773  (priv->fullname == nullptr ? "(null)" : priv->fullname));
1774  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1775  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1776 
1777  if (c)
1778  {
1779  if (c == comm)
1780  {
1781  LEAVE("already in table");
1782  return c;
1783  }
1784 
1785  /* Backward compatibility support for currencies that have
1786  * recently changed. */
1787  if (priv->name_space->iso4217)
1788  {
1789  auto it = gnc_new_iso_codes.find (priv->mnemonic);
1790  if (it != gnc_new_iso_codes.end())
1791  gnc_commodity_set_mnemonic(comm, it->second.c_str());
1792  }
1793  gnc_commodity_copy (c, comm);
1794  gnc_commodity_destroy (comm);
1795  LEAVE("found at %p", c);
1796  return c;
1797  }
1798 
1799  /* Prevent setting anything except template in namespace template. */
1800  if (g_strcmp0 (ns_name, GNC_COMMODITY_NS_TEMPLATE) == 0 &&
1801  g_strcmp0 (priv->mnemonic, "template") != 0)
1802  {
1803  PWARN("Converting commodity %s from namespace template to "
1804  "namespace User", priv->mnemonic);
1805  gnc_commodity_set_namespace (comm, "User");
1806  ns_name = "User";
1807  mark_commodity_dirty (comm);
1808  }
1809 
1810  book = qof_instance_get_book (&comm->inst);
1811  nsp = gnc_commodity_table_add_namespace(table, ns_name, book);
1812 
1813  PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic,
1814  nsp->cm_table, nsp->name);
1815  g_hash_table_insert(nsp->cm_table,
1816  (gpointer)CACHE_INSERT(priv->mnemonic),
1817  (gpointer)comm);
1818  nsp->cm_list = g_list_append(nsp->cm_list, comm);
1819 
1820  qof_event_gen (&comm->inst, QOF_EVENT_ADD, nullptr);
1821  LEAVE ("(table=%p, comm=%p)", table, comm);
1822  return comm;
1823 }
1824 
1825 /********************************************************************
1826  * gnc_commodity_table_remove
1827  * remove a commodity from the table.
1828  ********************************************************************/
1829 
1830 void
1831 gnc_commodity_table_remove(gnc_commodity_table * table,
1832  gnc_commodity * comm)
1833 {
1834  gnc_commodity_namespace * nsp;
1835  gnc_commodity *c;
1836  gnc_commodityPrivate* priv;
1837  const char *ns_name;
1838 
1839  if (!table) return;
1840  if (!comm) return;
1841 
1842  priv = GET_PRIVATE(comm);
1843  ns_name = gnc_commodity_namespace_get_name(priv->name_space);
1844  c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic);
1845  if (c != comm) return;
1846 
1847  qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, nullptr);
1848 
1849  nsp = gnc_commodity_table_find_namespace(table, ns_name);
1850  if (!nsp) return;
1851 
1852  nsp->cm_list = g_list_remove(nsp->cm_list, comm);
1853  g_hash_table_remove (nsp->cm_table, priv->mnemonic);
1854  /* XXX minor mem leak, should remove the key as well */
1855 }
1856 
1857 /********************************************************************
1858  * gnc_commodity_table_has_namespace
1859  * see if the commodities namespace exists. May have zero commodities.
1860  ********************************************************************/
1861 
1862 int
1863 gnc_commodity_table_has_namespace(const gnc_commodity_table * table,
1864  const char * name_space)
1865 {
1866  gnc_commodity_namespace * nsp = nullptr;
1867 
1868  if (!table || !name_space)
1869  {
1870  return 0;
1871  }
1872 
1873  nsp = gnc_commodity_table_find_namespace(table, name_space);
1874  if (nsp)
1875  {
1876  return 1;
1877  }
1878  else
1879  {
1880  return 0;
1881  }
1882 }
1883 
1884 static void
1885 hash_keys_helper(gpointer key, gpointer value, gpointer data)
1886 {
1887  auto l = (GList**)data;
1888  *l = g_list_prepend(*l, key);
1889 }
1890 
1891 static GList *
1892 g_hash_table_keys(GHashTable * table)
1893 {
1894  GList * l = nullptr;
1895  g_hash_table_foreach(table, &hash_keys_helper, (gpointer) &l);
1896  return l;
1897 }
1898 
1899 static void
1900 hash_values_helper(gpointer key, gpointer value, gpointer data)
1901 {
1902  auto l = (GList**)data;
1903  *l = g_list_prepend(*l, value);
1904 }
1905 
1906 static GList *
1907 g_hash_table_values(GHashTable * table)
1908 {
1909  GList * l = nullptr;
1910  g_hash_table_foreach(table, &hash_values_helper, (gpointer) &l);
1911  return l;
1912 }
1913 
1914 /********************************************************************
1915  * gnc_commodity_table_get_namespaces
1916  * see if any commodities in the namespace exist
1917  ********************************************************************/
1918 
1919 GList *
1920 gnc_commodity_table_get_namespaces(const gnc_commodity_table * table)
1921 {
1922  if (!table)
1923  return nullptr;
1924 
1925  return g_hash_table_keys(table->ns_table);
1926 }
1927 
1928 GList *
1930 {
1931  if (!table)
1932  return nullptr;
1933 
1934  return g_list_copy (table->ns_list);
1935 }
1936 
1937 /* Because gnc_commodity_table_add_namespace maps GNC_COMMODITY_NS_ISO to
1938  GNC_COMMODITY_NS_CURRENCY and then sets iso4217 if the namespace is
1939  either of these, the net result is that the iso4217 bit is set only
1940  for GNC_COMMODITY_NS_CURRENCY. This means that gnc_commodity_is_iso is
1941  a subset of gnc_commodity_is_currency. Most callers seem to use
1942  gnc_commodity_is_iso. */
1943 gboolean
1944 gnc_commodity_is_iso(const gnc_commodity * cm)
1945 {
1946  gnc_commodityPrivate* priv;
1947 
1948  if (!cm) return FALSE;
1949 
1950  priv = GET_PRIVATE(cm);
1951  if ( !priv->name_space) return FALSE;
1952  return priv->name_space->iso4217;
1953 }
1954 
1955 gboolean
1956 gnc_commodity_is_currency(const gnc_commodity *cm)
1957 {
1958  const char *ns_name;
1959  if (!cm) return FALSE;
1960 
1961  ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->name_space);
1962  return (!g_strcmp0(ns_name, GNC_COMMODITY_NS_LEGACY) ||
1963  !g_strcmp0(ns_name, GNC_COMMODITY_NS_CURRENCY));
1964 }
1965 
1966 /********************************************************************
1967  * gnc_commodity_table_get_commodities
1968  * list commodities in a given namespace
1969  ********************************************************************/
1970 
1971 static CommodityList*
1972 commodity_table_get_all_noncurrency_commodities(const gnc_commodity_table* table)
1973 {
1974  GList *node = nullptr, *nslist = gnc_commodity_table_get_namespaces(table);
1975  CommodityList *retval = nullptr;
1976  for (node = nslist; node; node=g_list_next(node))
1977  {
1978  gnc_commodity_namespace *ns = nullptr;
1979  if (g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_CURRENCY) == 0
1980  || g_strcmp0((char*)(node->data), GNC_COMMODITY_NS_TEMPLATE) == 0)
1981  continue;
1982  ns = gnc_commodity_table_find_namespace(table, (char*)(node->data));
1983  if (!ns)
1984  continue;
1985  retval = g_list_concat(g_hash_table_values(ns->cm_table), retval);
1986  }
1987  g_list_free(nslist);
1988  return retval;
1989 }
1990 
1991 CommodityList *
1992 gnc_commodity_table_get_commodities(const gnc_commodity_table * table,
1993  const char * name_space)
1994 {
1995  gnc_commodity_namespace * ns = nullptr;
1996 
1997  if (!table)
1998  return nullptr;
1999  if (g_strcmp0(name_space, GNC_COMMODITY_NS_NONISO_GUI) == 0)
2000  return commodity_table_get_all_noncurrency_commodities(table);
2001  ns = gnc_commodity_table_find_namespace(table, name_space);
2002  if (!ns)
2003  return nullptr;
2004 
2005  return g_hash_table_values(ns->cm_table);
2006 }
2007 
2008 /********************************************************************
2009  * gnc_commodity_table_get_quotable_commodities
2010  * list commodities in a given namespace that get price quotes
2011  ********************************************************************/
2012 
2013 static void
2014 get_quotables_helper1(gpointer key, gpointer value, gpointer data)
2015 {
2016  auto comm = GNC_COMMODITY(value);
2017  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2018  auto l = static_cast<GList**>(data);
2019 
2020  if (!priv->quote_flag || !priv->quote_source || !priv->quote_source->get_supported())
2021  return;
2022  *l = g_list_prepend(*l, value);
2023 }
2024 
2025 static gboolean
2026 get_quotables_helper2 (gnc_commodity *comm, gpointer data)
2027 {
2028  auto l = static_cast<GList**>(data);
2029  gnc_commodityPrivate* priv = GET_PRIVATE(comm);
2030 
2031  if (!priv->quote_flag || priv->quote_source || !priv->quote_source->get_supported())
2032  return TRUE;
2033  *l = g_list_prepend(*l, comm);
2034  return TRUE;
2035 }
2036 
2037 CommodityList *
2039 {
2040  gnc_commodity_namespace * ns = nullptr;
2041  const char *name_space;
2042  GList * nslist, * tmp;
2043  GList * l = nullptr;
2044  regex_t pattern;
2045  const char *expression = gnc_prefs_get_namespace_regexp();
2046 
2047  ENTER("table=%p, expression=%s", table, expression);
2048  if (!table)
2049  return nullptr;
2050 
2051  if (expression && *expression)
2052  {
2053  if (regcomp(&pattern, expression, REG_EXTENDED | REG_ICASE) != 0)
2054  {
2055  LEAVE("Cannot compile regex");
2056  return nullptr;
2057  }
2058 
2060  for (tmp = nslist; tmp; tmp = tmp->next)
2061  {
2062  name_space = static_cast<const char*>(tmp->data);
2063  if (regexec(&pattern, name_space, 0, nullptr, 0) == 0)
2064  {
2065  DEBUG("Running list of %s commodities", name_space);
2066  ns = gnc_commodity_table_find_namespace(table, name_space);
2067  if (ns)
2068  {
2069  g_hash_table_foreach(ns->cm_table, &get_quotables_helper1, (gpointer) &l);
2070  }
2071  }
2072  }
2073  g_list_free(nslist);
2074  regfree(&pattern);
2075  }
2076  else
2077  {
2078  gnc_commodity_table_foreach_commodity(table, get_quotables_helper2,
2079  (gpointer) &l);
2080  }
2081  LEAVE("list head %p", l);
2082  return l;
2083 }
2084 
2085 /********************************************************************
2086  * gnc_commodity_table_add_namespace
2087  * add an empty namespace if it does not exist
2088  ********************************************************************/
2089 
2090 /* GObject Initialization */
2091 QOF_GOBJECT_IMPL(gnc_commodity_namespace, gnc_commodity_namespace, QOF_TYPE_INSTANCE)
2092 
2093 static void
2094 gnc_commodity_namespace_init(gnc_commodity_namespace* ns)
2095 {
2096 }
2097 
2098 static void
2099 gnc_commodity_namespace_dispose_real (GObject *nsp)
2100 {
2101 }
2102 
2103 static void
2104 gnc_commodity_namespace_finalize_real(GObject* nsp)
2105 {
2106 }
2107 
2108 gnc_commodity_namespace *
2110  const char * name_space,
2111  QofBook *book)
2112 {
2113  gnc_commodity_namespace * ns = nullptr;
2114 
2115  if (!table) return nullptr;
2116 
2117  name_space = gnc_commodity_table_map_namespace(name_space);
2118  ns = gnc_commodity_table_find_namespace(table, name_space);
2119  if (!ns)
2120  {
2121  ns = static_cast<gnc_commodity_namespace*>(g_object_new(GNC_TYPE_COMMODITY_NAMESPACE, nullptr));
2122  ns->cm_table = g_hash_table_new(g_str_hash, g_str_equal);
2123  ns->name = CACHE_INSERT(static_cast<const char*>(name_space));
2124  ns->iso4217 = gnc_commodity_namespace_is_iso(name_space);
2125  qof_instance_init_data (&ns->inst, GNC_ID_COMMODITY_NAMESPACE, book);
2126  qof_event_gen (&ns->inst, QOF_EVENT_CREATE, nullptr);
2127 
2128  g_hash_table_insert(table->ns_table,
2129  (gpointer) ns->name,
2130  (gpointer) ns);
2131  table->ns_list = g_list_append(table->ns_list, ns);
2132  qof_event_gen (&ns->inst, QOF_EVENT_ADD, nullptr);
2133  }
2134  return ns;
2135 }
2136 
2137 
2138 gnc_commodity_namespace *
2139 gnc_commodity_table_find_namespace(const gnc_commodity_table * table,
2140  const char * name_space)
2141 {
2142  if (!table || !name_space)
2143  return nullptr;
2144 
2145  name_space = gnc_commodity_table_map_namespace(name_space);
2146  return static_cast<gnc_commodity_namespace*>(g_hash_table_lookup(table->ns_table, (gpointer)name_space));
2147 }
2148 
2149 
2150 gnc_commodity *
2151 gnc_commodity_find_commodity_by_guid(const GncGUID *guid, QofBook *book)
2152 {
2153  QofCollection *col;
2154  if (!guid || !book) return nullptr;
2155  col = qof_book_get_collection (book, GNC_ID_COMMODITY);
2156  return (gnc_commodity *) qof_collection_lookup_entity (col, guid);
2157 }
2158 
2159 /********************************************************************
2160  * gnc_commodity_table_delete_namespace
2161  * delete a namespace
2162  ********************************************************************/
2163 
2164 static int
2165 ns_helper(gpointer key, gpointer value, gpointer user_data)
2166 {
2167  auto c = GNC_COMMODITY(value);
2169  CACHE_REMOVE(static_cast<char*>(key)); /* key is commodity mnemonic */
2170  return TRUE;
2171 }
2172 
2173 void
2175  const char * name_space)
2176 {
2177  gnc_commodity_namespace * ns;
2178 
2179  if (!table) return;
2180 
2181  ns = gnc_commodity_table_find_namespace(table, name_space);
2182  if (!ns)
2183  return;
2184 
2185  qof_event_gen (&ns->inst, QOF_EVENT_REMOVE, nullptr);
2186  g_hash_table_remove(table->ns_table, name_space);
2187  table->ns_list = g_list_remove(table->ns_list, ns);
2188 
2189  g_list_free(ns->cm_list);
2190  ns->cm_list = nullptr;
2191 
2192  g_hash_table_foreach_remove(ns->cm_table, ns_helper, nullptr);
2193  g_hash_table_destroy(ns->cm_table);
2194  CACHE_REMOVE(ns->name);
2195 
2196  qof_event_gen (&ns->inst, QOF_EVENT_DESTROY, nullptr);
2197  /* qof_instance_release(&ns->inst); */
2198  g_object_unref(ns);
2199 }
2200 
2201 /********************************************************************
2202  * gnc_commodity_table_foreach_commodity
2203  * call user-defined function once for every commodity in every
2204  * namespace
2205  ********************************************************************/
2206 
2207 typedef struct
2208 {
2209  gboolean ok;
2210  gboolean (*func)(gnc_commodity *, gpointer);
2211  gpointer user_data;
2212 } IterData;
2213 
2214 static void
2215 iter_commodity (gpointer key, gpointer value, gpointer user_data)
2216 {
2217  IterData *iter_data = (IterData *) user_data;
2218  gnc_commodity *cm = (gnc_commodity *) value;
2219 
2220  if (iter_data->ok)
2221  {
2222  iter_data->ok = (iter_data->func)(cm, iter_data->user_data);
2223  }
2224 }
2225 
2226 static void
2227 iter_namespace (gpointer key, gpointer value, gpointer user_data)
2228 {
2229  GHashTable *namespace_hash = ((gnc_commodity_namespace *) value)->cm_table;
2230  g_hash_table_foreach (namespace_hash, iter_commodity, user_data);
2231 }
2232 
2233 gboolean
2234 gnc_commodity_table_foreach_commodity (const gnc_commodity_table * tbl,
2235  gboolean (*f)(gnc_commodity *, gpointer),
2236  gpointer user_data)
2237 {
2238  IterData iter_data;
2239 
2240  if (!tbl || !f) return FALSE;
2241 
2242  iter_data.ok = TRUE;
2243  iter_data.func = f;
2244  iter_data.user_data = user_data;
2245 
2246  g_hash_table_foreach(tbl->ns_table, iter_namespace, (gpointer)&iter_data);
2247 
2248  return iter_data.ok;
2249 }
2250 
2251 /********************************************************************
2252  * gnc_commodity_table_destroy
2253  * cleanup and free.
2254  ********************************************************************/
2255 
2256 void
2257 gnc_commodity_table_destroy(gnc_commodity_table * t)
2258 {
2259  gnc_commodity_namespace * ns;
2260  GList *item, *next;
2261 
2262  if (!t) return;
2263  ENTER ("table=%p", t);
2264 
2265  for (item = t->ns_list; item; item = next)
2266  {
2267  next = g_list_next(item);
2268  ns = static_cast<gnc_commodity_namespace*>(item->data);
2270  }
2271 
2272  g_list_free(t->ns_list);
2273  t->ns_list = nullptr;
2274  g_hash_table_destroy(t->ns_table);
2275  t->ns_table = nullptr;
2276  LEAVE ("table=%p", t);
2277  g_free(t);
2278 }
2279 
2280 /* =========================================================== */
2281 
2282 /********************************************************************
2283  * gnc_commodity_table_add_default_data
2284  ********************************************************************/
2285 
2286 #define CUR_I18N(String) dgettext ("iso_4217", String)
2287 
2288 gboolean
2289 gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
2290 {
2291  QofCollection *col;
2292  gnc_commodity* c;
2293 
2294  ENTER ("table=%p", table);
2295  gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_TEMPLATE, book);
2296  c = gnc_commodity_new(book, "template", GNC_COMMODITY_NS_TEMPLATE, "template", "template", 1);
2298 
2299 #include "iso-4217-currencies.c"
2300 
2301  /* We've just created the default namespaces and currencies. Mark
2302  * these collections as clean because there is no USER entered data
2303  * in these collections as of yet. */
2304  col = qof_book_get_collection(book, GNC_ID_COMMODITY);
2306  col = qof_book_get_collection(book, GNC_ID_COMMODITY_NAMESPACE);
2308 
2309  LEAVE ("table=%p", table);
2310  return TRUE;
2311 }
2312 
2313 /********************************************************************
2314  ********************************************************************/
2315 /* QofObject function implementation and registration */
2316 
2317 #ifdef _MSC_VER
2318 /* MSVC compiler doesn't have C99 "designated initializers"
2319  * so we wrap them in a macro that is empty on MSVC. */
2320 # define DI(x) /* */
2321 #else
2322 # define DI(x) x
2323 #endif
2324 static QofObject commodity_object_def =
2325 {
2326  DI(.interface_version = ) QOF_OBJECT_VERSION,
2327  DI(.e_type = ) GNC_ID_COMMODITY,
2328  DI(.type_label = ) "Commodity",
2329  DI(.create = ) nullptr,
2330  DI(.book_begin = ) nullptr,
2331  DI(.book_end = ) nullptr,
2332  DI(.is_dirty = ) qof_collection_is_dirty,
2333  DI(.mark_clean = ) qof_collection_mark_clean,
2334  DI(.foreach = ) qof_collection_foreach,
2335  DI(.printable = ) (const char * (*)(gpointer)) gnc_commodity_get_fullname,
2336 };
2337 
2338 static QofObject namespace_object_def =
2339 {
2340  DI(.interface_version = ) QOF_OBJECT_VERSION,
2341  DI(.e_type = ) GNC_ID_COMMODITY_NAMESPACE,
2342  DI(.type_label = ) "Namespace",
2343  DI(.create = ) nullptr,
2344  DI(.book_begin = ) nullptr,
2345  DI(.book_end = ) nullptr,
2346  DI(.is_dirty = ) nullptr,
2347  DI(.mark_clean = ) nullptr,
2348  DI(.foreach = ) nullptr,
2349  DI(.printable = ) nullptr,
2350 };
2351 
2352 static void
2353 commodity_table_book_begin (QofBook *book)
2354 {
2355  gnc_commodity_table *ct;
2356  ENTER ("book=%p", book);
2357 
2359  return;
2360 
2361  ct = gnc_commodity_table_new ();
2362  qof_book_set_data (book, GNC_COMMODITY_TABLE, ct);
2363 
2364  if (!gnc_commodity_table_add_default_data(ct, book))
2365  {
2366  PWARN("unable to initialize book's commodity_table");
2367  }
2368 
2369  LEAVE ("book=%p", book);
2370 }
2371 
2372 static void
2373 commodity_table_book_end (QofBook *book)
2374 {
2375  gnc_commodity_table *ct;
2376 
2377  ct = gnc_commodity_table_get_table (book);
2378  qof_book_set_data (book, GNC_COMMODITY_TABLE, nullptr);
2379  gnc_commodity_table_destroy (ct);
2380 }
2381 
2382 static QofObject commodity_table_object_def =
2383 {
2384  DI(.interface_version = ) QOF_OBJECT_VERSION,
2385  DI(.e_type = ) GNC_ID_COMMODITY_TABLE,
2386  DI(.type_label = ) "CommodityTable",
2387  DI(.create = ) nullptr,
2388  DI(.book_begin = ) commodity_table_book_begin,
2389  DI(.book_end = ) commodity_table_book_end,
2390  DI(.is_dirty = ) qof_collection_is_dirty,
2391  DI(.mark_clean = ) qof_collection_mark_clean,
2392  DI(.foreach = ) nullptr,
2393  DI(.printable = ) nullptr,
2394  DI(.version_cmp = ) nullptr,
2395 };
2396 
2397 gboolean
2399 {
2400  if (!qof_object_register (&commodity_object_def))
2401  return FALSE;
2402  if (!qof_object_register (&namespace_object_def))
2403  return FALSE;
2404  return qof_object_register (&commodity_table_object_def);
2405 }
2406 
2407 /* *******************************************************************
2408 * gnc_monetary methods
2409 ********************************************************************/
2410 
2412 MonetaryList *
2413 gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
2414 {
2415  MonetaryList *l = list, *tmp;
2416  for (tmp = list; tmp; tmp = tmp->next)
2417  {
2418  auto list_mon = static_cast<gnc_monetary*>(tmp->data);
2419  if (gnc_commodity_equiv(list_mon->commodity, add_mon.commodity))
2420  {
2421  list_mon->value = gnc_numeric_add(list_mon->value, add_mon.value,
2423  break;
2424  }
2425  }
2426 
2427  /* See if we found an entry, and add one if not */
2428  if (tmp == nullptr)
2429  {
2430  auto new_mon = static_cast<gnc_monetary*>(g_new0(gnc_monetary, 1));
2431  *new_mon = add_mon;
2432  l = g_list_prepend(l, new_mon);
2433  }
2434 
2435  return l;
2436 }
2437 
2440 MonetaryList *
2442 {
2443  MonetaryList *node, *next;
2444  for (node = list; node; node = next)
2445  {
2446  auto mon = static_cast<gnc_monetary*>(node->data);
2447  next = node->next;
2448  if (gnc_numeric_zero_p(mon->value))
2449  {
2450  g_free(mon);
2451  list = g_list_delete_link(list, node);
2452  }
2453  }
2454  return list;
2455 }
2456 
2458 void
2459 gnc_monetary_list_free(MonetaryList *list)
2460 {
2461  MonetaryList *tmp;
2462  for (tmp = list; tmp; tmp = tmp->next)
2463  {
2464  g_free(tmp->data);
2465  }
2466 
2467  g_list_free(list);
2468 }
2469 
2470 /* ========================= END OF FILE ============================== */
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
const char * gnc_commodity_get_cusip(const gnc_commodity *cm)
Retrieve the &#39;exchange code&#39; for the specified commodity.
gboolean gnc_commodity_table_foreach_commodity(const gnc_commodity_table *table, gboolean(*f)(gnc_commodity *cm, gpointer user_data), gpointer user_data)
Call a function once for each commodity in the commodity table.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
A gnc_commodity_table is a database of commodity info.
gboolean gnc_commodity_is_currency(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency or a legacy currency...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gboolean gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book)
Add all the standard namespaces and currencies to the commodity table.
const char * gnc_quote_source_get_user_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the user friendly name of this quote source...
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
This quote source pulls from a single specific web site.
QofBook * qof_instance_get_book(gconstpointer inst)
Return the book pointer.
gboolean qof_collection_is_dirty(const QofCollection *col)
Return value of &#39;dirty&#39; flag on collection.
Definition: qofid.cpp:255
gnc_quote_source * gnc_quote_source_add_new(const char *source_name, gboolean supported)
Create a new quote source.
const char * gnc_commodity_namespace_get_gui_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure in a form suitable to present to the user...
QofInstance * qof_collection_lookup_entity(const QofCollection *col, const GncGUID *guid)
Find the entity going only from its guid.
Definition: qofid.cpp:212
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm)
Retrieve the automatic price quote flag for the specified commodity.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
const char * gnc_commodity_get_user_symbol(const gnc_commodity *cm)
Retrieve the user-defined symbol for the specified commodity.
void gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz)
Set the automatic price quote timezone for the specified commodity.
void gnc_commodity_decrement_usage_count(gnc_commodity *cm)
Decrement a commodity&#39;s internal counter that tracks how many accounts are using that commodity...
Commodity handling public routines (C++ api)
const char * gnc_commodity_get_quote_tz(const gnc_commodity *cm)
Retrieve the automatic price quote timezone for the specified commodity.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_commodity_set_fraction(gnc_commodity *cm, int fraction)
Set the fraction for the specified commodity.
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equal.
gboolean gnc_quote_source_get_supported(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the flag that indicates whether this particular quote...
globally unique ID User API
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
QuoteSourceType gnc_quote_source_get_type(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the type of this particular quote source...
The special currency quote source.
int gnc_commodity_compare_void(const void *a, const void *b)
A wrapper around gnc_commodity_compare() which offers the function declaration that is needed for g_l...
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Object instance holds common fields that most gnucash objects use.
gnc_commodity * gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book)
allocate and copy
This is a locally installed quote source that gnucash knows nothing about.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:188
A gnc_commodity_namespace is an collection of commodities.
void gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity.
#define QOF_OBJECT_VERSION
Defines the version of the core object object registration interface.
Definition: qofobject.h:63
QuoteSourceType
The quote source type enum account types are used to determine how the transaction data in the accoun...
gnc_quote_source * gnc_quote_source_lookup_by_ti(QuoteSourceType type, gint index)
Given the type/index of a quote source, find the data structure identified by this pair...
gboolean qof_commit_edit(QofInstance *inst)
commit_edit helpers
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag)
Set the automatic price quote flag for the specified commodity, based on user input.
gnc_commodity_namespace * gnc_commodity_table_add_namespace(gnc_commodity_table *table, const char *name_space, QofBook *book)
This function adds a new string to the list of commodity namespaces.
GList * gnc_commodity_namespace_get_commodity_list(const gnc_commodity_namespace *name_space)
Return a list of all commodity data structures in the specified namespace.
void gnc_commodity_set_user_symbol(gnc_commodity *cm, const char *user_symbol)
Set a user-defined symbol for the specified commodity.
const char * gnc_commodity_namespace_get_name(const gnc_commodity_namespace *ns)
Return the textual name of a namespace data structure.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void qof_instance_init_data(QofInstance *inst, QofIdType type, QofBook *book)
Initialise the settings associated with an instance.
MonetaryList * gnc_monetary_list_delete_zeros(MonetaryList *list)
Delete all entries in the list that have zero value.
gboolean qof_begin_edit(QofInstance *inst)
begin_edit
void gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src)
Set the automatic price quote source for the specified commodity.
gint gnc_quote_source_num_entries(QuoteSourceType type)
Return the number of entries for a given type of quote source.
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
gboolean gnc_commodity_table_register(void)
You should probably not be using gnc_commodity_table_register() It is an internal routine for registe...
void gnc_commodity_increment_usage_count(gnc_commodity *cm)
Increment a commodity&#39;s internal counter that tracks how many accounts are using that commodity...
void gnc_commodity_set_cusip(gnc_commodity *cm, const char *cusip)
Set the &#39;exchange code&#39; for the specified commodity.
void gnc_monetary_list_free(MonetaryList *list)
Free a MonetaryList and all the monetaries it points to.
gnc_commodity * gnc_commodity_new(QofBook *book, const char *fullname, const char *name_space, const char *mnemonic, const char *cusip, int fraction)
Create a new commodity.
int gnc_commodity_table_has_namespace(const gnc_commodity_table *table, const char *name_space)
Test to see if the indicated namespace exits in the commodity table.
void gnc_commodity_table_delete_namespace(gnc_commodity_table *table, const char *name_space)
This function deletes a string from the list of commodity namespaces.
gboolean gnc_commodity_namespace_is_iso(const char *name_space)
Checks to see if the specified commodity namespace is the namespace for ISO 4217 currencies.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
#define GNC_COMMODITY_NS_LEGACY
The commodity namespace definitions are used to tag a commodity by its type, or a stocks by the excha...
void qof_book_set_data(QofBook *book, const gchar *key, gpointer data)
The qof_book_set_data() allows arbitrary pointers to structs to be stored in QofBook.
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
part2 – deal with the backend
const char * gnc_commodity_get_nice_symbol(const gnc_commodity *cm)
Retrieve a symbol for the specified commodity, suitable for display to the user.
void qof_collection_mark_clean(QofCollection *)
reset value of dirty flag
Definition: qofid.cpp:261
void gnc_quote_source_set_fq_installed(const char *version_string, const std::vector< std::string > &sources_list)
Update gnucash internal tables based on what Finance::Quote sources are installed.
const char * gnc_quote_source_fq_version(void)
This function returns the version of the Finance::Quote module installed on a user&#39;s computer...
Generic api to store and retrieve preferences.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
gnc_quote_source * gnc_quote_source_lookup_by_internal(const char *name)
Given the internal (gnucash or F::Q) name of a quote source, find the data structure identified by th...
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Retrieve the &#39;print&#39; name for the specified commodity.
int gnc_commodity_compare(const gnc_commodity *a, const gnc_commodity *b)
This routine returns 0 if the two commodities are equal, 1 otherwise.
void gnc_commodity_set_fullname(gnc_commodity *cm, const char *fullname)
Set the full name for the specified commodity.
This quote source may pull from multiple web sites.
gnc_quote_source * gnc_commodity_get_quote_source(const gnc_commodity *cm)
Retrieve the automatic price quote source for the specified commodity.
gnc_commodity_namespace * gnc_commodity_table_find_namespace(const gnc_commodity_table *table, const char *name_space)
This function finds a commodity namespace in the set of existing commodity namespaces.
void gnc_commodity_set_mnemonic(gnc_commodity *cm, const char *mnemonic)
Set the mnemonic for the specified commodity.
const char * gnc_commodity_get_default_symbol(const gnc_commodity *cm)
Retrieve the default symbol for the specified commodity.
gint qof_instance_guid_compare(gconstpointer ptr1, gconstpointer ptr2)
Compare the GncGUID values of two instances.
CommodityList * gnc_commodity_table_get_quotable_commodities(const gnc_commodity_table *table)
This function returns a list of commodities for which price quotes should be retrieved.
gnc_commodity_table * gnc_commodity_table_new(void)
You probably shouldn&#39;t be using gnc_commodity_table_new() directly, it&#39;s for internal use only...
gint gnc_quote_source_get_index(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the index of this particular quote source within its ...
gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity *cm)
Retrieve the namespace data structure for the specified commodity.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the &#39;unique&#39; name for the specified commodity.
MonetaryList * gnc_monetary_list_add_monetary(MonetaryList *list, gnc_monetary add_mon)
Add a gnc_monetary to the list.
void gnc_commodity_table_remove(gnc_commodity_table *table, gnc_commodity *comm)
Remove a commodity from the commodity table.
void gnc_commodity_set_namespace(gnc_commodity *cm, const char *name_space)
Set the namespace for the specified commodity.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
Returns the number of commodities in the commodity table.
const char * gnc_quote_source_get_internal_name(const gnc_quote_source *source)
Given a gnc_quote_source data structure, return the internal name of this quote source.
gboolean qof_object_register(const QofObject *object)
Register new types of object objects.
Definition: qofobject.cpp:299
An article that is bought and sold.
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
The type used to store guids in C.
Definition: guid.h:75
void gnc_commodity_copy(gnc_commodity *dest, const gnc_commodity *src)
Copy src into dest.
gpointer qof_book_get_data(const QofBook *book, const gchar *key)
Retrieves arbitrary pointers to structs stored by qof_book_set_data.
GList * gnc_commodity_table_get_namespaces_list(const gnc_commodity_table *table)
Return a list of all namespace data structures in the commodity table.
gnc_commodity * gnc_commodity_obtain_twin(const gnc_commodity *from, QofBook *book)
Given the commodity &#39;findlike&#39;, this routine will find and return the equivalent commodity (commodity...
Commodity handling public routines.
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
gboolean gnc_quote_source_fq_installed(void)
This function indicates whether or not the Finance::Quote module is installed on a user&#39;s computer...