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