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