LCOV - code coverage report
Current view: top level - libgnucash/app-utils - gnc-ui-util.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 40.3 % 1019 411
Test Date: 2025-02-07 16:25:45 Functions: 39.2 % 74 29
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************\
       2                 :             :  * gnc-ui-util.c -- utility functions for the GnuCash UI            *
       3                 :             :  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com>              *
       4                 :             :  *                                                                  *
       5                 :             :  * This program is free software; you can redistribute it and/or    *
       6                 :             :  * modify it under the terms of the GNU General Public License as   *
       7                 :             :  * published by the Free Software Foundation; either version 2 of   *
       8                 :             :  * the License, or (at your option) any later version.              *
       9                 :             :  *                                                                  *
      10                 :             :  * This program is distributed in the hope that it will be useful,  *
      11                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      12                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      13                 :             :  * GNU General Public License for more details.                     *
      14                 :             :  *                                                                  *
      15                 :             :  * You should have received a copy of the GNU General Public License*
      16                 :             :  * along with this program; if not, contact:                        *
      17                 :             :  *                                                                  *
      18                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      19                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      20                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      21                 :             : \********************************************************************/
      22                 :             : 
      23                 :             : #include <config.h>
      24                 :             : 
      25                 :             : #ifdef __MINGW32__
      26                 :             : #define __USE_MINGW_ANSI_STDIO 1
      27                 :             : #endif
      28                 :             : #include "gnc-ui-util.h"
      29                 :             : #include <glib.h>
      30                 :             : #include <glib/gi18n.h>
      31                 :             : #include <gio/gio.h>
      32                 :             : #include <ctype.h>
      33                 :             : #include <errno.h>
      34                 :             : #include <limits.h>
      35                 :             : #include <locale.h>
      36                 :             : #include <math.h>
      37                 :             : #if defined(G_OS_WIN32) && !defined(_MSC_VER)
      38                 :             : # include <pow.h>
      39                 :             : #endif
      40                 :             : #include <stdarg.h>
      41                 :             : #include <stdlib.h>
      42                 :             : #include <stdio.h>
      43                 :             : #include <string.h>
      44                 :             : #include <cinttypes>
      45                 :             : #include <unicode/listformatter.h>
      46                 :             : 
      47                 :             : #include "qof.h"
      48                 :             : #include "gnc-prefs.h"
      49                 :             : #include "Account.h"
      50                 :             : #include "Transaction.h"
      51                 :             : #include "gnc-engine.h"
      52                 :             : #include "gnc-features.h"
      53                 :             : #include "gnc-hooks.h"
      54                 :             : #include "gnc-session.h"
      55                 :             : #include "engine-helpers.h"
      56                 :             : #include "gnc-locale-utils.h"
      57                 :             : 
      58                 :             : #define GNC_PREF_CURRENCY_CHOICE_LOCALE "currency-choice-locale"
      59                 :             : #define GNC_PREF_CURRENCY_CHOICE_OTHER  "currency-choice-other"
      60                 :             : #define GNC_PREF_CURRENCY_OTHER         "currency-other"
      61                 :             : #define GNC_PREF_REVERSED_ACCTS_NONE    "reversed-accounts-none"
      62                 :             : #define GNC_PREF_REVERSED_ACCTS_CREDIT  "reversed-accounts-credit"
      63                 :             : #define GNC_PREF_REVERSED_ACCTS_INC_EXP "reversed-accounts-incomeexpense"
      64                 :             : #define GNC_PREF_PRICES_FORCE_DECIMAL   "force-price-decimal"
      65                 :             : 
      66                 :             : using UniStr = icu::UnicodeString;
      67                 :             : 
      68                 :             : static QofLogModule log_module = GNC_MOD_GUI;
      69                 :             : 
      70                 :             : static bool auto_decimal_enabled = false;
      71                 :             : static int auto_decimal_places = 2;    /* default, can be changed */
      72                 :             : 
      73                 :             : static bool reverse_balance_inited = false;
      74                 :             : static bool reverse_type[NUM_ACCOUNT_TYPES];
      75                 :             : 
      76                 :             : /* Cache currency ISO codes and only look them up in the settings when
      77                 :             :  * absolutely necessary. Can't cache a pointer to the data structure
      78                 :             :  * as that will change any time the book changes. */
      79                 :             : static char* user_default_currency = nullptr;
      80                 :             : static char* user_report_currency = nullptr;
      81                 :             : constexpr int maximum_decimals = 15;
      82                 :             : constexpr int64_t pow_10[] = {1, 10, 100, 1000, 10000, 100000, 1000000,
      83                 :             :                                10000000, 100000000, 1000000000, 10000000000,
      84                 :             :                                100000000000, 1000000000000, 10000000000000,
      85                 :             :                                100000000000000, 1000000000000000};
      86                 :             : 
      87                 :             : char*
      88                 :           0 : gnc_normalize_account_separator (const gchar* separator)
      89                 :             : {
      90                 :           0 :     char* new_sep=nullptr;
      91                 :             : 
      92                 :           0 :     if (!separator || !*separator || g_strcmp0(separator, "colon") == 0)
      93                 :           0 :             new_sep = g_strdup (":");
      94                 :           0 :         else if (g_strcmp0(separator, "slash") == 0)
      95                 :           0 :             new_sep = g_strdup ("/");
      96                 :           0 :         else if (g_strcmp0(separator, "backslash") == 0)
      97                 :           0 :             new_sep = g_strdup ("\\");
      98                 :           0 :         else if (g_strcmp0(separator, "dash") == 0)
      99                 :           0 :             new_sep = g_strdup ("-");
     100                 :           0 :         else if (g_strcmp0(separator, "period") == 0)
     101                 :           0 :             new_sep = g_strdup (".");
     102                 :             :         else
     103                 :           0 :             new_sep = g_strdup (separator);
     104                 :             : 
     105                 :           0 :     return new_sep;
     106                 :             : }
     107                 :             : /********************************************************************\
     108                 :             :  * gnc_configure_account_separator                                  *
     109                 :             :  *   updates the current account separator character                *
     110                 :             :  *                                                                  *
     111                 :             :  * Args: none                                                       *
     112                 :             :  \*******************************************************************/
     113                 :             : static void
     114                 :           0 : gnc_configure_account_separator (void)
     115                 :             : {
     116                 :           0 :     auto string = gnc_prefs_get_string(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR);
     117                 :           0 :     auto separator = gnc_normalize_account_separator (string);
     118                 :             : 
     119                 :           0 :     gnc_set_account_separator(separator);
     120                 :             : 
     121                 :           0 :     g_free(string);
     122                 :           0 :     g_free(separator);
     123                 :           0 : }
     124                 :             : 
     125                 :             : 
     126                 :             : static void
     127                 :           8 : gnc_configure_reverse_balance (void)
     128                 :             : {
     129                 :         128 :     for (auto i = 0; i < NUM_ACCOUNT_TYPES; i++)
     130                 :         120 :         reverse_type[i] = false;
     131                 :             : 
     132                 :           8 :     if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP))
     133                 :             :     {
     134                 :           0 :         reverse_type[ACCT_TYPE_INCOME]  = true;
     135                 :           0 :         reverse_type[ACCT_TYPE_EXPENSE] = true;
     136                 :             :     }
     137                 :           8 :     else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT))
     138                 :             :     {
     139                 :           0 :         reverse_type[ACCT_TYPE_LIABILITY] = true;
     140                 :           0 :         reverse_type[ACCT_TYPE_PAYABLE]   = true;
     141                 :           0 :         reverse_type[ACCT_TYPE_EQUITY]    = true;
     142                 :           0 :         reverse_type[ACCT_TYPE_INCOME]    = true;
     143                 :           0 :         reverse_type[ACCT_TYPE_CREDIT]    = true;
     144                 :             :     }
     145                 :           8 :     else if (!gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE))
     146                 :           8 :         PWARN("no reversed account preference set, using none");
     147                 :             : 
     148                 :           8 : }
     149                 :             : 
     150                 :             : static void
     151                 :           8 : gnc_reverse_balance_init (void)
     152                 :             : {
     153                 :           8 :     gnc_configure_reverse_balance ();
     154                 :           8 :     reverse_balance_inited = TRUE;
     155                 :           8 : }
     156                 :             : 
     157                 :             : gboolean
     158                 :        3450 : gnc_reverse_balance (const Account *account)
     159                 :             : {
     160                 :        3450 :     if (account == nullptr)
     161                 :           0 :         return FALSE;
     162                 :             : 
     163                 :        3450 :     auto type = xaccAccountGetType (account);
     164                 :        3450 :     if ((type < 0) || (type >= NUM_ACCOUNT_TYPES))
     165                 :           0 :         return FALSE;
     166                 :             : 
     167                 :        3450 :     if (!reverse_balance_inited)
     168                 :           8 :         gnc_reverse_balance_init ();
     169                 :             : 
     170                 :        3450 :     return reverse_type[type];
     171                 :             : }
     172                 :             : 
     173                 :           0 : gboolean gnc_using_equity_type_opening_balance_account (QofBook* book)
     174                 :             : {
     175                 :           0 :     return gnc_features_check_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
     176                 :             : }
     177                 :             : 
     178                 :           0 : void gnc_set_use_equity_type_opening_balance_account (QofBook* book)
     179                 :             : {
     180                 :           0 :     gnc_features_set_used (book, GNC_FEATURE_EQUITY_TYPE_OPENING_BALANCE);
     181                 :           0 : }
     182                 :             : 
     183                 :             : char*
     184                 :           0 : gnc_get_default_directory (const char* section)
     185                 :             : {
     186                 :           0 :     auto dir = gnc_prefs_get_string (section, GNC_PREF_LAST_PATH);
     187                 :           0 :     if (!(dir && *dir))
     188                 :             :     {
     189                 :           0 :         g_free (dir); // if it's ""
     190                 :             : #ifdef G_OS_WIN32
     191                 :             :         dir = g_strdup (g_get_user_data_dir ()); /* equivalent of "My Documents" */
     192                 :             : #else
     193                 :           0 :         dir = g_strdup (g_get_home_dir ());
     194                 :             : #endif
     195                 :             :     }
     196                 :           0 :     return dir;
     197                 :             : }
     198                 :             : 
     199                 :             : void
     200                 :           0 : gnc_set_default_directory (const char* section, const char* directory)
     201                 :             : {
     202                 :           0 :     gnc_prefs_set_string(section, GNC_PREF_LAST_PATH, directory);
     203                 :           0 : }
     204                 :             : 
     205                 :             : QofBook *
     206                 :       33021 : gnc_get_current_book (void)
     207                 :             : {
     208                 :       33021 :     return qof_session_get_book (gnc_get_current_session ());
     209                 :             : }
     210                 :             : 
     211                 :             : /* If there is no current session, there is no book and we must be dealing
     212                 :             :  * with a new book. When gnucash is started with --nofile, there is
     213                 :             :  * initially no session (and no book), but by the time we check, one
     214                 :             :  * could have been created (for example, if an empty account tree tab is
     215                 :             :  * opened, a session is created which creates a new, but empty, book).
     216                 :             :  * A session is created and a book is loaded from a backend when gnucash is
     217                 :             :  * started with a file, but selecting 'new file' keeps a session open. So we
     218                 :             :  * need to check as well for a book with no accounts (root with no children). */
     219                 :             : gboolean
     220                 :           0 : gnc_is_new_book (void)
     221                 :             : {
     222                 :           0 :     return (!gnc_current_session_exist() ||
     223                 :           0 :             gnc_account_n_children (gnc_book_get_root_account (gnc_get_current_book ())) == 0);
     224                 :             : }
     225                 :             : 
     226                 :             : #define OPTION_TAXUS_NAME "tax_US/name"
     227                 :             : #define OPTION_TAXUS_TYPE "tax_US/type"
     228                 :             : #define OLD_OPTION_TAXUS_NAME "book/tax_US/name"
     229                 :             : #define OLD_OPTION_TAXUS_TYPE "book/tax_US/type"
     230                 :             : 
     231                 :             : void
     232                 :           0 : gnc_set_current_book_tax_name_type (gboolean name_changed, const char* tax_name,
     233                 :             :                                     gboolean type_changed, const char* tax_type)
     234                 :             : {
     235                 :           0 :     if (name_changed)
     236                 :             :     {
     237                 :           0 :         if (type_changed)
     238                 :             :         {
     239                 :           0 :             auto book = gnc_get_current_book();
     240                 :           0 :             if ((g_strcmp0 (tax_name, "") == 0) ||
     241                 :             :                 (tax_name == nullptr))
     242                 :             :             { /* change to no name */
     243                 :           0 :                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
     244                 :           0 :                     (g_strcmp0 (tax_type, "") == 0) ||
     245                 :             :                     (tax_type == nullptr))
     246                 :             :                 { /* need to delete both name and type and the "tax_US" frame */
     247                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
     248                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
     249                 :           0 :                     qof_book_option_frame_delete(book, "tax_US");
     250                 :             :                 }
     251                 :             :                 else
     252                 :             :                 { /* delete the name & change the type; keep the "tax_US" frame */
     253                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
     254                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
     255                 :             :                 }
     256                 :             :             }
     257                 :             :             else /* new name */
     258                 :             :             {
     259                 :           0 :                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
     260                 :           0 :                     (g_strcmp0 (tax_type, "") == 0) ||
     261                 :             :                     (tax_type == nullptr))
     262                 :             :                 { /* delete the type & change the name; keep the "tax_US" frame */
     263                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
     264                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
     265                 :             :                 }
     266                 :             :                 else /* and new type */
     267                 :             :                 { /* change the name & change the type */
     268                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
     269                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
     270                 :             :                 }
     271                 :             :             }
     272                 :             :         }
     273                 :             :         else /* no type change but name changed */
     274                 :             :         {
     275                 :           0 :             auto book = gnc_get_current_book();
     276                 :           0 :             if ((g_strcmp0 (tax_name, "") == 0) ||
     277                 :             :                 (tax_name == nullptr))
     278                 :             :             { /* change to no name */
     279                 :           0 :                 if ((g_strcmp0 (tax_type, "Other") == 0) ||
     280                 :           0 :                     (g_strcmp0 (tax_type, "") == 0) ||
     281                 :             :                     (tax_type == nullptr))
     282                 :             :                 { /* delete the name; there is no type; deleted the "tax_US" frame */
     283                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
     284                 :           0 :                     qof_book_option_frame_delete(book, "tax_US");
     285                 :             :                 }
     286                 :             :                 else
     287                 :             :                 { /* need to delete the name and keep "tax_US" frame */
     288                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_NAME, nullptr);
     289                 :             :                 }
     290                 :             :             }
     291                 :             :             else
     292                 :             :             { /* change the name & keep "tax_US" frame */
     293                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, tax_name);
     294                 :             :             }
     295                 :             :         }
     296                 :             :    }
     297                 :             :    else /* no name change */
     298                 :             :    {
     299                 :           0 :         if (type_changed)
     300                 :             :         {
     301                 :           0 :             auto book = gnc_get_current_book();
     302                 :           0 :             if ((g_strcmp0 (tax_type, "Other") == 0) ||
     303                 :           0 :                 (g_strcmp0 (tax_type, "") == 0) ||
     304                 :             :                 (tax_type == nullptr))
     305                 :             :             {
     306                 :           0 :                 if ((g_strcmp0 (tax_name, "") == 0) ||
     307                 :             :                     (tax_name == nullptr))
     308                 :             :                 {/* delete the type; there is no name; delete the "tax_US" frame */
     309                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
     310                 :           0 :                     qof_book_option_frame_delete(book, "tax_US");
     311                 :             :                 }
     312                 :             :                 else
     313                 :             :                 { /* need to delete the type and keep "tax_US" frame */
     314                 :           0 :                     qof_book_set_string_option(book, OPTION_TAXUS_TYPE, nullptr);
     315                 :             :                 }
     316                 :             :             }
     317                 :             :             else
     318                 :             :             { /* change the type & keep "tax_US" frame */
     319                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, tax_type);
     320                 :             :             }
     321                 :             :         } /*else no name and no type change - do nothing */
     322                 :             :    }
     323                 :           0 : }
     324                 :             : 
     325                 :             : const char*
     326                 :          66 : gnc_get_current_book_tax_name (void)
     327                 :             : {
     328                 :          66 :     auto book = gnc_get_current_book();
     329                 :          66 :     auto tax_name = qof_book_get_string_option(book, OPTION_TAXUS_NAME);
     330                 :          66 :     if (tax_name)
     331                 :             :     {
     332                 :           0 :         return tax_name;
     333                 :             :     }
     334                 :             :     else
     335                 :             :     {
     336                 :             :         const char* old_option_taxus_name =
     337                 :          66 :             qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
     338                 :          66 :         if (old_option_taxus_name)
     339                 :             :         {
     340                 :           0 :             char* taxus_name = g_strdup(old_option_taxus_name);
     341                 :             :             const char* old_option_taxus_type =
     342                 :           0 :                 qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
     343                 :           0 :             if (old_option_taxus_type)
     344                 :             :             { /* switch both name and type and remove unused frames */
     345                 :           0 :                 char* taxus_type = g_strdup(old_option_taxus_type);
     346                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
     347                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
     348                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
     349                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
     350                 :           0 :                 qof_book_option_frame_delete(book, "book/tax_US");
     351                 :           0 :                 qof_book_option_frame_delete(book, "book");
     352                 :           0 :                 g_free (taxus_type);
     353                 :             :             }
     354                 :             :             else
     355                 :             :             { /* switch just name and remove unused frames */
     356                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
     357                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
     358                 :           0 :                 qof_book_option_frame_delete(book, "book/tax_US");
     359                 :           0 :                 qof_book_option_frame_delete(book, "book");
     360                 :             :             }
     361                 :           0 :             g_free (taxus_name);
     362                 :           0 :             return qof_book_get_string_option(book, OPTION_TAXUS_NAME);
     363                 :             :         }
     364                 :          66 :         return nullptr;
     365                 :             :     }
     366                 :             : }
     367                 :             : 
     368                 :             : const char*
     369                 :          88 : gnc_get_current_book_tax_type (void)
     370                 :             : {
     371                 :          88 :     auto book = gnc_get_current_book();
     372                 :             :     auto tax_type =
     373                 :          88 :         qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
     374                 :          88 :     if (tax_type)
     375                 :             :     {
     376                 :           0 :         return tax_type;
     377                 :             :     }
     378                 :             :     else
     379                 :             :     {
     380                 :             :         auto old_option_taxus_type =
     381                 :          88 :             qof_book_get_string_option(book, OLD_OPTION_TAXUS_TYPE);
     382                 :          88 :         if (old_option_taxus_type)
     383                 :             :         {
     384                 :           0 :             auto taxus_type = g_strdup(old_option_taxus_type);
     385                 :             :             auto old_option_taxus_name =
     386                 :           0 :                 qof_book_get_string_option(book, OLD_OPTION_TAXUS_NAME);
     387                 :           0 :             if (old_option_taxus_name)
     388                 :             :             { /* switch both name and type and remove unused frames */
     389                 :           0 :                 auto taxus_name = g_strdup(old_option_taxus_name);
     390                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_NAME, taxus_name);
     391                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_NAME, nullptr);
     392                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
     393                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
     394                 :           0 :                 qof_book_option_frame_delete(book, "book/tax_US");
     395                 :           0 :                 qof_book_option_frame_delete(book, "book");
     396                 :           0 :                 g_free (taxus_name);
     397                 :             :             }
     398                 :             :             else
     399                 :             :             { /* switch just type and remove unused frames */
     400                 :           0 :                 qof_book_set_string_option(book, OPTION_TAXUS_TYPE, taxus_type);
     401                 :           0 :                 qof_book_set_string_option(book, OLD_OPTION_TAXUS_TYPE, nullptr);
     402                 :           0 :                 qof_book_option_frame_delete(book, "book/tax_US");
     403                 :           0 :                 qof_book_option_frame_delete(book, "book");
     404                 :             :             }
     405                 :           0 :             g_free (taxus_type);
     406                 :           0 :             return qof_book_get_string_option(book, OPTION_TAXUS_TYPE);
     407                 :             :         }
     408                 :          88 :         return nullptr;
     409                 :             :     }
     410                 :             : }
     411                 :             : 
     412                 :             : Account *
     413                 :       11684 : gnc_get_current_root_account (void)
     414                 :             : {
     415                 :       11684 :     return gnc_book_get_root_account (gnc_get_current_book ());
     416                 :             : }
     417                 :             : 
     418                 :             : gnc_commodity_table *
     419                 :        3837 : gnc_get_current_commodities (void)
     420                 :             : {
     421                 :        3837 :      if (gnc_current_session_exist())
     422                 :        3797 :           return gnc_commodity_table_get_table (gnc_get_current_book ());
     423                 :          40 :      return nullptr;
     424                 :             : }
     425                 :             : 
     426                 :             : char*
     427                 :           0 : gnc_get_account_name_for_split_register(const Account *account,
     428                 :             :                                         gboolean show_leaf_accounts)
     429                 :             : {
     430                 :           0 :     if (show_leaf_accounts)
     431                 :           0 :         return g_strdup (xaccAccountGetName (account));
     432                 :             :     else
     433                 :           0 :         return gnc_account_get_full_name (account);
     434                 :             : }
     435                 :             : 
     436                 :             : char*
     437                 :           0 : gnc_get_account_name_for_register(const Account *account)
     438                 :             : {
     439                 :             :     auto show_leaf_accounts =
     440                 :           0 :         gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
     441                 :             :                            GNC_PREF_SHOW_LEAF_ACCT_NAMES);
     442                 :             : 
     443                 :           0 :     return gnc_get_account_name_for_split_register(account, show_leaf_accounts);
     444                 :             : }
     445                 :             : 
     446                 :             : Account *
     447                 :           0 : gnc_account_lookup_for_register(const Account *base_account, const char* name)
     448                 :             : {
     449                 :             :     auto show_leaf_accounts =
     450                 :           0 :         gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
     451                 :             :                            GNC_PREF_SHOW_LEAF_ACCT_NAMES);
     452                 :             : 
     453                 :           0 :     if (show_leaf_accounts)
     454                 :           0 :         return gnc_account_lookup_by_name (base_account, name);
     455                 :             :     else
     456                 :           0 :         return gnc_account_lookup_by_full_name (base_account, name);
     457                 :             : }
     458                 :             : 
     459                 :             : /********************************************************************\
     460                 :             :  * gnc_get_reconcile_str                                            *
     461                 :             :  *   return the i18n'd string for the given reconciled flag         *
     462                 :             :  *                                                                  *
     463                 :             :  * Args: reconciled_flag - the flag to convert into a string        *
     464                 :             :  * Returns: the i18n'd reconciled string                            *
     465                 :             : \********************************************************************/
     466                 :             : const char*
     467                 :           0 : gnc_get_reconcile_str (char reconciled_flag)
     468                 :             : {
     469                 :           0 :     switch (reconciled_flag)
     470                 :             :     {
     471                 :           0 :     case NREC:
     472                 :           0 :         return C_("Reconciled flag 'not cleared'", "n");
     473                 :           0 :     case CREC:
     474                 :           0 :         return C_("Reconciled flag 'cleared'", "c");
     475                 :           0 :     case YREC:
     476                 :           0 :         return C_("Reconciled flag 'reconciled'", "y");
     477                 :           0 :     case FREC:
     478                 :           0 :         return C_("Reconciled flag 'frozen'", "f");
     479                 :           0 :     case VREC:
     480                 :           0 :         return C_("Reconciled flag 'void'", "v");
     481                 :           0 :     default:
     482                 :           0 :         PERR("Bad reconciled flag\n");
     483                 :           0 :         return nullptr;
     484                 :             :     }
     485                 :             : }
     486                 :             : 
     487                 :             : /********************************************************************\
     488                 :             :  * gnc_get_reconcile_valid_flags                                    *
     489                 :             :  *   return a string containing the list of reconciled flags        *
     490                 :             :  *                                                                  *
     491                 :             :  * Returns: the i18n'd reconciled flags string                      *
     492                 :             : \********************************************************************/
     493                 :             : const char*
     494                 :           0 : gnc_get_reconcile_valid_flags (void)
     495                 :             : {
     496                 :             :     static const char flags[] = { NREC, CREC, YREC, FREC, VREC, 0 };
     497                 :           0 :     return flags;
     498                 :             : }
     499                 :             : 
     500                 :             : /********************************************************************\
     501                 :             :  * gnc_get_reconcile_flag_order                                     *
     502                 :             :  *   return a string containing the reconciled-flag change order    *
     503                 :             :  *                                                                  *
     504                 :             :  * Args: reconciled_flag - the flag to convert into a string        *
     505                 :             :  * Returns: the i18n'd reconciled string                            *
     506                 :             : \********************************************************************/
     507                 :             : const char*
     508                 :           0 : gnc_get_reconcile_flag_order (void)
     509                 :             : {
     510                 :             :     static const char flags[] = { NREC, CREC, 0 };
     511                 :           0 :     return flags;
     512                 :             : }
     513                 :             : 
     514                 :             : const char*
     515                 :           0 : gnc_get_doclink_str (char link_flag)
     516                 :             : {
     517                 :           0 :     switch (link_flag)
     518                 :             :     {
     519                 :           0 :     case WLINK:
     520                 :           0 :         return C_("Document Link flag for 'web'", "w");
     521                 :           0 :     case FLINK:
     522                 :           0 :         return C_("Document Link flag for 'file'", "f");
     523                 :           0 :     case ' ':
     524                 :           0 :         return " ";
     525                 :           0 :     default:
     526                 :           0 :         PERR("Bad link flag");
     527                 :           0 :         return nullptr;
     528                 :             :     }
     529                 :             : }
     530                 :             : 
     531                 :             : const char*
     532                 :           0 : gnc_get_doclink_valid_flags (void)
     533                 :             : {
     534                 :             :     static const char flags[] = { FLINK, WLINK, ' ', 0 };
     535                 :           0 :     return flags;
     536                 :             : }
     537                 :             : 
     538                 :             : const char*
     539                 :           0 : gnc_get_doclink_flag_order (void)
     540                 :             : {
     541                 :             :     static const char flags[] = { FLINK, WLINK, ' ', 0 };
     542                 :           0 :     return flags;
     543                 :             : }
     544                 :             : 
     545                 :             : static const char*
     546                 :           0 : equity_base_name (GNCEquityType equity_type)
     547                 :             : {
     548                 :           0 :     switch (equity_type)
     549                 :             :     {
     550                 :           0 :     case EQUITY_OPENING_BALANCE:
     551                 :           0 :         return N_("Opening Balances");
     552                 :             : 
     553                 :           0 :     case EQUITY_RETAINED_EARNINGS:
     554                 :           0 :         return N_("Retained Earnings");
     555                 :             : 
     556                 :           0 :     default:
     557                 :           0 :         return nullptr;
     558                 :             :     }
     559                 :             : }
     560                 :             : 
     561                 :             : Account *
     562                 :           0 : gnc_find_or_create_equity_account (Account *root,
     563                 :             :                                    GNCEquityType equity_type,
     564                 :             :                                    gnc_commodity *currency)
     565                 :             : {
     566                 :           0 :     g_return_val_if_fail (equity_type >= 0, nullptr);
     567                 :           0 :     g_return_val_if_fail (equity_type < NUM_EQUITY_TYPES, nullptr);
     568                 :           0 :     g_return_val_if_fail (currency != nullptr, nullptr);
     569                 :           0 :     g_return_val_if_fail (root != nullptr, nullptr);
     570                 :           0 :     g_return_val_if_fail (gnc_commodity_is_currency(currency), nullptr);
     571                 :             : 
     572                 :           0 :     auto use_eq_op_feature = equity_type == EQUITY_OPENING_BALANCE && gnc_using_equity_type_opening_balance_account (gnc_get_current_book ());
     573                 :             : 
     574                 :           0 :     if (use_eq_op_feature)
     575                 :             :     {
     576                 :           0 :         auto account = gnc_account_lookup_by_opening_balance (root, currency);
     577                 :           0 :         if (account)
     578                 :           0 :             return account;
     579                 :             :     }
     580                 :             : 
     581                 :           0 :     auto base_name = equity_base_name (equity_type);
     582                 :             : 
     583                 :           0 :     auto account = gnc_account_lookup_by_name(root, base_name);
     584                 :           0 :     if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
     585                 :           0 :         account = nullptr;
     586                 :             : 
     587                 :           0 :     if (!account)
     588                 :             :     {
     589                 :           0 :         base_name = base_name && *base_name ? _(base_name) : "";
     590                 :             : 
     591                 :           0 :         account = gnc_account_lookup_by_name(root, base_name);
     592                 :           0 :         if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
     593                 :           0 :             account = nullptr;
     594                 :             :     }
     595                 :             : 
     596                 :           0 :     auto base_name_exists = (account != nullptr);
     597                 :             : 
     598                 :           0 :     if (account &&
     599                 :           0 :             gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
     600                 :             :     {
     601                 :           0 :         if (use_eq_op_feature)
     602                 :           0 :             xaccAccountSetIsOpeningBalance (account, TRUE);
     603                 :           0 :         return account;
     604                 :             :     }
     605                 :             : 
     606                 :           0 :     auto name = g_strconcat (base_name, " - ",
     607                 :             :                         gnc_commodity_get_mnemonic (currency), nullptr);
     608                 :           0 :     account = gnc_account_lookup_by_name(root, name);
     609                 :           0 :     if (account && xaccAccountGetType (account) != ACCT_TYPE_EQUITY)
     610                 :           0 :         account = nullptr;
     611                 :             : 
     612                 :           0 :     auto name_exists = (account != nullptr);
     613                 :             : 
     614                 :           0 :     if (account &&
     615                 :           0 :             gnc_commodity_equiv (currency, xaccAccountGetCommodity (account)))
     616                 :             :     {
     617                 :           0 :         if (use_eq_op_feature)
     618                 :           0 :             xaccAccountSetIsOpeningBalance (account, TRUE);
     619                 :           0 :         return account;
     620                 :             :     }
     621                 :             : 
     622                 :             :     /* Couldn't find one, so create it */
     623                 :           0 :     if (name_exists && base_name_exists)
     624                 :             :     {
     625                 :           0 :         PWARN ("equity account with unexpected currency");
     626                 :           0 :         g_free (name);
     627                 :           0 :         return nullptr;
     628                 :             :     }
     629                 :             : 
     630                 :           0 :     if (!base_name_exists &&
     631                 :           0 :             gnc_commodity_equiv (currency, gnc_default_currency ()))
     632                 :             :     {
     633                 :           0 :         g_free (name);
     634                 :           0 :         name = g_strdup (base_name);
     635                 :             :     }
     636                 :             : 
     637                 :           0 :     auto parent = gnc_account_lookup_by_name(root, _("Equity"));
     638                 :           0 :     if (!parent || xaccAccountGetType (parent) != ACCT_TYPE_EQUITY)
     639                 :           0 :         parent = root;
     640                 :           0 :     g_assert(parent);
     641                 :             : 
     642                 :           0 :     account = xaccMallocAccount (gnc_account_get_book(root));
     643                 :             : 
     644                 :           0 :     xaccAccountBeginEdit (account);
     645                 :             : 
     646                 :           0 :     xaccAccountSetName (account, name);
     647                 :           0 :     xaccAccountSetType (account, ACCT_TYPE_EQUITY);
     648                 :           0 :     xaccAccountSetCommodity (account, currency);
     649                 :             : 
     650                 :           0 :     if (use_eq_op_feature)
     651                 :           0 :         xaccAccountSetIsOpeningBalance (account, TRUE);
     652                 :             : 
     653                 :           0 :     xaccAccountBeginEdit (parent);
     654                 :           0 :     gnc_account_append_child (parent, account);
     655                 :           0 :     xaccAccountCommitEdit (parent);
     656                 :             : 
     657                 :           0 :     xaccAccountCommitEdit (account);
     658                 :             : 
     659                 :           0 :     g_free (name);
     660                 :             : 
     661                 :           0 :     return account;
     662                 :             : }
     663                 :             : 
     664                 :             : gboolean
     665                 :           0 : gnc_account_create_opening_balance (Account *account,
     666                 :             :                                     gnc_numeric balance,
     667                 :             :                                     time64 date,
     668                 :             :                                     QofBook *book)
     669                 :             : {
     670                 :           0 :     if (gnc_numeric_zero_p (balance))
     671                 :           0 :         return TRUE;
     672                 :             : 
     673                 :           0 :     g_return_val_if_fail (account != nullptr, FALSE);
     674                 :           0 :     auto commodity = xaccAccountGetCommodity (account);
     675                 :           0 :     g_return_val_if_fail (gnc_commodity_is_currency (commodity), FALSE);
     676                 :             : 
     677                 :             :     auto equity_account =
     678                 :           0 :         gnc_find_or_create_equity_account (gnc_account_get_root(account),
     679                 :             :                                            EQUITY_OPENING_BALANCE,
     680                 :             :                                            commodity);
     681                 :           0 :     if (!equity_account)
     682                 :           0 :         return FALSE;
     683                 :             : 
     684                 :           0 :     xaccAccountBeginEdit (account);
     685                 :           0 :     xaccAccountBeginEdit (equity_account);
     686                 :             : 
     687                 :           0 :     auto trans = xaccMallocTransaction (book);
     688                 :             : 
     689                 :           0 :     xaccTransBeginEdit (trans);
     690                 :             : 
     691                 :           0 :     xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, nullptr));
     692                 :           0 :     xaccTransSetDatePostedSecsNormalized (trans, date);
     693                 :           0 :     xaccTransSetDescription (trans, _("Opening Balance"));
     694                 :             : 
     695                 :           0 :     auto split = xaccMallocSplit (book);
     696                 :             : 
     697                 :           0 :     xaccTransAppendSplit (trans, split);
     698                 :           0 :     xaccAccountInsertSplit (account, split);
     699                 :             : 
     700                 :           0 :     xaccSplitSetAmount (split, balance);
     701                 :           0 :     xaccSplitSetValue (split, balance);
     702                 :             : 
     703                 :           0 :     balance = gnc_numeric_neg (balance);
     704                 :             : 
     705                 :           0 :     split = xaccMallocSplit (book);
     706                 :             : 
     707                 :           0 :     xaccTransAppendSplit (trans, split);
     708                 :           0 :     xaccAccountInsertSplit (equity_account, split);
     709                 :             : 
     710                 :           0 :     xaccSplitSetAmount (split, balance);
     711                 :           0 :     xaccSplitSetValue (split, balance);
     712                 :             : 
     713                 :           0 :     xaccTransCommitEdit (trans);
     714                 :           0 :     xaccAccountCommitEdit (equity_account);
     715                 :           0 :     xaccAccountCommitEdit (account);
     716                 :             : 
     717                 :           0 :     return TRUE;
     718                 :             : }
     719                 :             : 
     720                 :             : 
     721                 :             : gnc_commodity *
     722                 :        3817 : gnc_locale_default_currency_nodefault (void)
     723                 :             : {
     724                 :        3817 :     auto table = gnc_get_current_commodities ();
     725                 :        3817 :     auto code = gnc_locale_default_iso_currency_code ();
     726                 :             : 
     727                 :        3817 :     auto currency = gnc_commodity_table_lookup (table,
     728                 :             :                                                 GNC_COMMODITY_NS_CURRENCY,
     729                 :             :                                                 code);
     730                 :             : 
     731                 :        3817 :     return (currency ? currency : nullptr);
     732                 :             : }
     733                 :             : 
     734                 :             : gnc_commodity*
     735                 :        3817 : gnc_locale_default_currency (void)
     736                 :             : {
     737                 :        3817 :     auto currency = gnc_locale_default_currency_nodefault ();
     738                 :             : 
     739                 :        3817 :     return (currency ? currency :
     740                 :          20 :             gnc_commodity_table_lookup (gnc_get_current_commodities (),
     741                 :        3817 :                                         GNC_COMMODITY_NS_CURRENCY, "USD"));
     742                 :             : }
     743                 :             : 
     744                 :             : 
     745                 :             : static gnc_commodity*
     746                 :        3817 : gnc_default_currency_common (char* requested_currency,
     747                 :             :                              const char* section)
     748                 :             : {
     749                 :        3817 :     gnc_commodity *currency = nullptr;
     750                 :             : 
     751                 :        3817 :     if (requested_currency)
     752                 :           0 :         return gnc_commodity_table_lookup(gnc_get_current_commodities(),
     753                 :             :                                           GNC_COMMODITY_NS_CURRENCY,
     754                 :           0 :                                           requested_currency);
     755                 :             : 
     756                 :        7614 :     if (gnc_current_session_exist() &&
     757                 :        3797 :         gnc_prefs_get_bool (section, GNC_PREF_CURRENCY_CHOICE_OTHER))
     758                 :             :     {
     759                 :           0 :         auto mnemonic = gnc_prefs_get_string(section, GNC_PREF_CURRENCY_OTHER);
     760                 :           0 :         currency = gnc_commodity_table_lookup(gnc_get_current_commodities(),
     761                 :             :                                               GNC_COMMODITY_NS_CURRENCY,
     762                 :             :                                               mnemonic);
     763                 :           0 :         DEBUG("mnemonic %s, result %p",
     764                 :             :               mnemonic && *mnemonic ? mnemonic : "(null)", currency);
     765                 :           0 :         g_free(mnemonic);
     766                 :             :     }
     767                 :             : 
     768                 :        3817 :     if (!currency)
     769                 :        3817 :         currency = gnc_locale_default_currency ();
     770                 :             : 
     771                 :        3817 :     if (currency)
     772                 :             :     {
     773                 :        3797 :         g_free (requested_currency);
     774                 :             :     }
     775                 :             : 
     776                 :        3817 :     return currency;
     777                 :             : }
     778                 :             : 
     779                 :             : gnc_commodity*
     780                 :         687 : gnc_default_currency (void)
     781                 :             : {
     782                 :         687 :     return gnc_default_currency_common (user_default_currency,
     783                 :         687 :                                         GNC_PREFS_GROUP_GENERAL);
     784                 :             : }
     785                 :             : 
     786                 :             : gnc_commodity*
     787                 :           0 : gnc_account_or_default_currency(const Account* account,
     788                 :             :                                 gboolean * currency_from_account_found)
     789                 :             : {
     790                 :             :     gnc_commodity *currency;
     791                 :           0 :     if (!account)
     792                 :             :     {
     793                 :           0 :         if (currency_from_account_found)
     794                 :           0 :             *currency_from_account_found = FALSE;
     795                 :           0 :         return gnc_default_currency();
     796                 :             :     }
     797                 :             : 
     798                 :           0 :     currency = gnc_account_get_currency_or_parent(account);
     799                 :           0 :     if (currency)
     800                 :             :     {
     801                 :           0 :         if (currency_from_account_found)
     802                 :           0 :             *currency_from_account_found = TRUE;
     803                 :             :     }
     804                 :             :     else
     805                 :             :     {
     806                 :           0 :         if (currency_from_account_found)
     807                 :           0 :             *currency_from_account_found = FALSE;
     808                 :           0 :         currency = gnc_default_currency();
     809                 :             :     }
     810                 :           0 :     return currency;
     811                 :             : }
     812                 :             : 
     813                 :             : 
     814                 :             : 
     815                 :             : gnc_commodity*
     816                 :        3130 : gnc_default_report_currency (void)
     817                 :             : {
     818                 :        3130 :     return gnc_default_currency_common (user_report_currency,
     819                 :        3130 :                                         GNC_PREFS_GROUP_GENERAL_REPORT);
     820                 :             : }
     821                 :             : 
     822                 :             : static void
     823                 :           0 : gnc_currency_changed_cb (GSettings *settings, char* key, gpointer user_data)
     824                 :             : {
     825                 :           0 :     user_default_currency = nullptr;
     826                 :           0 :     user_report_currency = nullptr;
     827                 :           0 :     gnc_hook_run(HOOK_CURRENCY_CHANGED, nullptr);
     828                 :           0 : }
     829                 :             : 
     830                 :             : 
     831                 :             : GNCPrintAmountInfo
     832                 :         681 : gnc_default_print_info (gboolean use_symbol)
     833                 :             : {
     834                 :             :     static GNCPrintAmountInfo info;
     835                 :             :     static bool got_it = false;
     836                 :             : 
     837                 :             :     /* These must be updated each time. */
     838                 :         681 :     info.use_symbol = use_symbol ? 1 : 0;
     839                 :         681 :     info.commodity = gnc_default_currency ();
     840                 :             : 
     841                 :         681 :     if (got_it)
     842                 :         676 :         return info;
     843                 :             : 
     844                 :           5 :     auto lc = gnc_localeconv ();
     845                 :             : 
     846                 :           5 :     info.max_decimal_places = lc->frac_digits;
     847                 :           5 :     info.min_decimal_places = lc->frac_digits;
     848                 :             : 
     849                 :           5 :     info.use_separators = 1;
     850                 :           5 :     info.use_locale = 1;
     851                 :           5 :     info.monetary = 1;
     852                 :           5 :     info.force_fit = 0;
     853                 :           5 :     info.round = 0;
     854                 :             : 
     855                 :           5 :     got_it = TRUE;
     856                 :             : 
     857                 :           5 :     return info;
     858                 :             : }
     859                 :             : 
     860                 :             : static bool
     861                 :       60529 : is_decimal_fraction (int fraction, uint8_t *max_decimal_places_p)
     862                 :             : {
     863                 :       60529 :     uint8_t max_decimal_places = 0;
     864                 :             : 
     865                 :       60529 :     if (fraction <= 0)
     866                 :           0 :         return false;
     867                 :             : 
     868                 :      181754 :     while (fraction != 1)
     869                 :             :     {
     870                 :      121225 :         if (fraction % 10 != 0)
     871                 :           0 :             return false;
     872                 :             : 
     873                 :      121225 :         fraction = fraction / 10;
     874                 :      121225 :         max_decimal_places += 1;
     875                 :             :     }
     876                 :             : 
     877                 :       60529 :     if (max_decimal_places_p)
     878                 :       60529 :         *max_decimal_places_p = max_decimal_places;
     879                 :             : 
     880                 :       60529 :     return true;
     881                 :             : }
     882                 :             : 
     883                 :             : GNCPrintAmountInfo
     884                 :       60860 : gnc_commodity_print_info (const gnc_commodity *commodity,
     885                 :             :                           gboolean use_symbol)
     886                 :             : {
     887                 :             :     GNCPrintAmountInfo info;
     888                 :             : 
     889                 :       60860 :     if (commodity == nullptr)
     890                 :         331 :         return gnc_default_print_info (use_symbol);
     891                 :             : 
     892                 :       60529 :     info.commodity = commodity;
     893                 :             : 
     894                 :       60529 :     auto is_iso = gnc_commodity_is_iso (commodity);
     895                 :             : 
     896                 :       60529 :     if (is_decimal_fraction (gnc_commodity_get_fraction (commodity),
     897                 :             :                              &info.max_decimal_places))
     898                 :             :     {
     899                 :       60529 :         if (is_iso)
     900                 :       60236 :             info.min_decimal_places = info.max_decimal_places;
     901                 :             :         else
     902                 :         293 :             info.min_decimal_places = 0;
     903                 :             :     }
     904                 :             :     else
     905                 :           0 :         info.max_decimal_places = info.min_decimal_places = 0;
     906                 :             : 
     907                 :       60529 :     info.use_separators = 1;
     908                 :       60529 :     info.use_symbol = use_symbol ? 1 : 0;
     909                 :       60529 :     info.use_locale = is_iso ? 1 : 0;
     910                 :       60529 :     info.monetary = 1;
     911                 :       60529 :     info.force_fit = 0;
     912                 :       60529 :     info.round = 0;
     913                 :             : 
     914                 :       60529 :     return info;
     915                 :             : }
     916                 :             : 
     917                 :             : static GNCPrintAmountInfo
     918                 :           0 : gnc_account_print_info_helper(const Account *account, gboolean use_symbol,
     919                 :             :                               gnc_commodity * (*efffunc)(const Account *),
     920                 :             :                               int (*scufunc)(const Account*))
     921                 :             : {
     922                 :             :     GNCPrintAmountInfo info;
     923                 :             : 
     924                 :           0 :     if (account == nullptr)
     925                 :           0 :         return gnc_default_print_info (use_symbol);
     926                 :             : 
     927                 :           0 :     info.commodity = efffunc (account);
     928                 :             : 
     929                 :           0 :     auto is_iso = gnc_commodity_is_iso (info.commodity);
     930                 :             : 
     931                 :           0 :     auto scu = scufunc (account);
     932                 :             : 
     933                 :           0 :     if (is_decimal_fraction (scu, &info.max_decimal_places))
     934                 :             :     {
     935                 :           0 :         if (is_iso)
     936                 :           0 :             info.min_decimal_places = info.max_decimal_places;
     937                 :             :         else
     938                 :           0 :             info.min_decimal_places = 0;
     939                 :             :     }
     940                 :             :     else
     941                 :           0 :         info.max_decimal_places = info.min_decimal_places = 0;
     942                 :             : 
     943                 :           0 :     info.use_separators = 1;
     944                 :           0 :     info.use_symbol = use_symbol ? 1 : 0;
     945                 :           0 :     info.use_locale = is_iso ? 1 : 0;
     946                 :           0 :     info.monetary = 1;
     947                 :           0 :     info.force_fit = 0;
     948                 :           0 :     info.round = 0;
     949                 :             : 
     950                 :           0 :     return info;
     951                 :             : }
     952                 :             : 
     953                 :             : GNCPrintAmountInfo
     954                 :           0 : gnc_account_print_info (const Account *account, gboolean use_symbol)
     955                 :             : {
     956                 :           0 :     return gnc_account_print_info_helper(account, use_symbol,
     957                 :             :                                          xaccAccountGetCommodity,
     958                 :           0 :                                          xaccAccountGetCommoditySCU);
     959                 :             : }
     960                 :             : 
     961                 :             : GNCPrintAmountInfo
     962                 :           0 : gnc_split_amount_print_info (Split *split, gboolean use_symbol)
     963                 :             : {
     964                 :           0 :     if (!split)
     965                 :             :     {
     966                 :           0 :         GNCPrintAmountInfo info = gnc_default_share_print_info ();
     967                 :           0 :         info.use_symbol = use_symbol;
     968                 :           0 :         return info;
     969                 :             :     }
     970                 :             : 
     971                 :           0 :     return gnc_account_print_info (xaccSplitGetAccount (split), use_symbol);
     972                 :             : }
     973                 :             : 
     974                 :             : static GNCPrintAmountInfo
     975                 :           2 : gnc_default_print_info_helper (int decplaces)
     976                 :             : {
     977                 :             :     GNCPrintAmountInfo info;
     978                 :             : 
     979                 :           2 :     info.commodity = nullptr;
     980                 :             : 
     981                 :           2 :     info.max_decimal_places = decplaces;
     982                 :           2 :     info.min_decimal_places = 0;
     983                 :             : 
     984                 :           2 :     info.use_separators = 1;
     985                 :           2 :     info.use_symbol = 0;
     986                 :           2 :     info.use_locale = 1;
     987                 :           2 :     info.monetary = 1;
     988                 :           2 :     info.force_fit = 0;
     989                 :           2 :     info.round = 0;
     990                 :             : 
     991                 :           2 :     return info;
     992                 :             : }
     993                 :             : 
     994                 :             : GNCPrintAmountInfo
     995                 :          17 : gnc_default_share_print_info (void)
     996                 :             : {
     997                 :             :     static GNCPrintAmountInfo info;
     998                 :             :     static bool got_it = false;
     999                 :             : 
    1000                 :          17 :     if (!got_it)
    1001                 :             :     {
    1002                 :           2 :         info = gnc_default_print_info_helper (5);
    1003                 :           2 :         info.monetary = 0;
    1004                 :           2 :         got_it = TRUE;
    1005                 :             :     }
    1006                 :             : 
    1007                 :          17 :     return info;
    1008                 :             : }
    1009                 :             : 
    1010                 :             : GNCPrintAmountInfo
    1011                 :          17 : gnc_share_print_info_places (int decplaces)
    1012                 :             : {
    1013                 :             :     GNCPrintAmountInfo info;
    1014                 :             : 
    1015                 :          17 :     info = gnc_default_share_print_info ();
    1016                 :          17 :     info.max_decimal_places = decplaces;
    1017                 :          17 :     info.min_decimal_places = decplaces;
    1018                 :          17 :     info.force_fit = 1;
    1019                 :          17 :     info.round = 1;
    1020                 :          17 :     return info;
    1021                 :             : }
    1022                 :             : 
    1023                 :             : GNCPrintAmountInfo
    1024                 :         654 : gnc_price_print_info (const gnc_commodity *curr, gboolean use_symbol)
    1025                 :             : {
    1026                 :             :     GNCPrintAmountInfo info;
    1027                 :         654 :     auto force = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL,
    1028                 :             :                                      GNC_PREF_PRICES_FORCE_DECIMAL);
    1029                 :         654 :     info.commodity = curr;
    1030                 :             : 
    1031                 :         654 :     if (info.commodity)
    1032                 :             :     {
    1033                 :         654 :         int frac = gnc_commodity_get_fraction (curr);
    1034                 :         654 :         guint8 decplaces = 2;
    1035                 :        1962 :         while (frac != 1 && (frac % 10) == 0 && (frac /= 10)) ++decplaces;
    1036                 :         654 :         info.max_decimal_places = decplaces;
    1037                 :         654 :         info.min_decimal_places = decplaces;
    1038                 :             :     }
    1039                 :             :     else
    1040                 :             :     {
    1041                 :           0 :         info.max_decimal_places = 6;
    1042                 :           0 :         info.min_decimal_places = 0;
    1043                 :             :     }
    1044                 :             : 
    1045                 :         654 :     info.use_separators = 1;
    1046                 :         654 :     info.use_symbol = use_symbol ? 1 : 0;
    1047                 :         654 :     info.use_locale = 1;
    1048                 :         654 :     info.monetary = 1;
    1049                 :             : 
    1050                 :         654 :     info.force_fit = force;
    1051                 :         654 :     info.round = force;
    1052                 :         654 :     return info;
    1053                 :             : }
    1054                 :             : 
    1055                 :             : GNCPrintAmountInfo
    1056                 :           0 : gnc_default_price_print_info (const gnc_commodity *curr)
    1057                 :             : {
    1058                 :           0 :     return gnc_price_print_info (curr, FALSE);
    1059                 :             : }
    1060                 :             : 
    1061                 :             : 
    1062                 :             : GNCPrintAmountInfo
    1063                 :           0 : gnc_integral_print_info (void)
    1064                 :             : {
    1065                 :             :     static GNCPrintAmountInfo info;
    1066                 :             :     static bool got_it = false;
    1067                 :             : 
    1068                 :           0 :     if (!got_it)
    1069                 :             :     {
    1070                 :           0 :         info = gnc_default_print_info_helper (0);
    1071                 :           0 :         got_it = TRUE;
    1072                 :             :     }
    1073                 :             : 
    1074                 :           0 :     return info;
    1075                 :             : }
    1076                 :             : 
    1077                 :             : /* Utility function for printing non-negative amounts */
    1078                 :             : static int
    1079                 :       64813 : PrintAmountInternal(char* buf, gnc_numeric val, const GNCPrintAmountInfo *info)
    1080                 :             : {
    1081                 :       64813 :     auto *lc = gnc_localeconv();
    1082                 :       64813 :     constexpr size_t buf_size = 128;
    1083                 :             :     char temp_buf[buf_size];
    1084                 :             : 
    1085                 :       64813 :     g_return_val_if_fail (info != nullptr, 0);
    1086                 :             : 
    1087                 :       64813 :     if (gnc_numeric_check (val))
    1088                 :             :     {
    1089                 :           0 :         PWARN ("Bad numeric: %s.",
    1090                 :             :                gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
    1091                 :           0 :         *buf = '\0';
    1092                 :           0 :         return 0;
    1093                 :             :     }
    1094                 :             : 
    1095                 :             :     /* Print the absolute value, but remember sign */
    1096                 :       64813 :     auto value_is_negative = gnc_numeric_negative_p (val);
    1097                 :       64813 :     val = gnc_numeric_abs (val);
    1098                 :             : 
    1099                 :             :     /* Try to print as decimal. */
    1100                 :       64813 :     auto value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
    1101                 :       64813 :     if (!value_is_decimal && info->force_fit && info->round)
    1102                 :             :     {
    1103                 :             :         /* if there's a commodity use 100x the commodity's fraction. N.B. This
    1104                 :             :          * assumes that commodity fractions are multiples of 10, a reasonable
    1105                 :             :          * assumption in 2018. Otherwise, if there's a reasonable
    1106                 :             :          * max_decimal_places, use that.
    1107                 :             :          */
    1108                 :           0 :         const int64_t denom = info->commodity ?
    1109                 :           0 :              gnc_commodity_get_fraction(info->commodity) * 100 :
    1110                 :           0 :              (info->max_decimal_places &&
    1111                 :           0 :                info->max_decimal_places <= maximum_decimals) ?
    1112                 :           0 :              pow_10[info->max_decimal_places] : pow_10[maximum_decimals];
    1113                 :           0 :         val = gnc_numeric_convert(val, denom, GNC_HOW_RND_ROUND_HALF_UP);
    1114                 :           0 :         value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
    1115                 :             :     }
    1116                 :       64813 :     auto min_dp = info->min_decimal_places;
    1117                 :       64813 :     auto max_dp = info->max_decimal_places;
    1118                 :             : 
    1119                 :             :     /* Don to limit the number of decimal places _UNLESS_ force_fit is
    1120                 :             :      * true. */
    1121                 :       64813 :     if (!info->force_fit)
    1122                 :       63777 :         max_dp = 99;
    1123                 :             : 
    1124                 :             :     /* rounding? -- can only ROUND if force_fit is also true */
    1125                 :       64813 :     if (value_is_decimal && info->round && info->force_fit)
    1126                 :             :     {
    1127                 :             :         /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */
    1128                 :         524 :         gnc_numeric rounding{5, pow_10[max_dp + 1]};
    1129                 :             : 
    1130                 :         524 :         val = gnc_numeric_add(val, rounding, val.denom,
    1131                 :             :                               GNC_HOW_DENOM_EXACT | GNC_HOW_RND_TRUNC);
    1132                 :             : 
    1133                 :         524 :         if (gnc_numeric_check(val))
    1134                 :             :         {
    1135                 :           0 :             PWARN ("Bad numeric from rounding: %s.",
    1136                 :             :                    gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
    1137                 :           0 :             *buf = '\0';
    1138                 :           0 :             return 0;
    1139                 :             :         }
    1140                 :             :     }
    1141                 :             : 
    1142                 :             :     /* calculate the integer part and the remainder */
    1143                 :       64813 :     auto whole = gnc_numeric_convert(val, 1, GNC_HOW_RND_TRUNC);
    1144                 :       64813 :     val = gnc_numeric_sub (val, whole, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER);
    1145                 :       64813 :     if (gnc_numeric_check (val))
    1146                 :             :     {
    1147                 :           0 :         PWARN ("Problem with remainder: %s.",
    1148                 :             :                gnc_numeric_errorCode_to_string(gnc_numeric_check (val)));
    1149                 :           0 :         *buf = '\0';
    1150                 :           0 :         return 0;
    1151                 :             :     }
    1152                 :             : 
    1153                 :             :     // Value may now be decimal, for example if the factional part is zero
    1154                 :       64813 :     value_is_decimal = gnc_numeric_to_decimal(&val, nullptr);
    1155                 :             :     /* print the integer part without separators */
    1156                 :       64813 :     snprintf(temp_buf, buf_size, "%" G_GINT64_FORMAT, whole.num);
    1157                 :       64813 :     auto num_whole_digits = strlen (temp_buf);
    1158                 :             : 
    1159                 :       64813 :     if (!info->use_separators)
    1160                 :        2048 :         strcpy (buf, temp_buf);
    1161                 :             :     else
    1162                 :             :     {
    1163                 :             :         char* separator;
    1164                 :             :         char* group;
    1165                 :             : 
    1166                 :       62765 :         if (info->monetary)
    1167                 :             :         {
    1168                 :       62241 :             separator = lc->mon_thousands_sep;
    1169                 :       62241 :             group = lc->mon_grouping;
    1170                 :             :         }
    1171                 :             :         else
    1172                 :             :         {
    1173                 :         524 :             separator = lc->thousands_sep;
    1174                 :         524 :             group = lc->grouping;
    1175                 :             :         }
    1176                 :             : 
    1177                 :       62765 :         auto buf_ptr = buf;
    1178                 :       62765 :         auto temp_ptr = &temp_buf[num_whole_digits - 1];
    1179                 :       62765 :         auto group_count = 0;
    1180                 :             : 
    1181                 :       92833 :         while (temp_ptr != temp_buf)
    1182                 :             :         {
    1183                 :       30068 :             *buf_ptr++ = *temp_ptr--;
    1184                 :             : 
    1185                 :       30068 :             if (*group != CHAR_MAX)
    1186                 :             :             {
    1187                 :       30068 :                 group_count++;
    1188                 :             : 
    1189                 :       30068 :                 if (group_count == *group)
    1190                 :             :                 {
    1191                 :        4280 :                     g_utf8_strncpy(buf_ptr, separator, 1);
    1192                 :        4280 :                     buf_ptr = g_utf8_find_next_char(buf_ptr, nullptr);
    1193                 :        4280 :                     group_count = 0;
    1194                 :             : 
    1195                 :             :                     /* Peek ahead at the next group code */
    1196                 :        4280 :                     switch (group[1])
    1197                 :             :                     {
    1198                 :             :                         /* A null char means repeat the last group indefinitely */
    1199                 :        4280 :                     case '\0':
    1200                 :        4280 :                         break;
    1201                 :             :                         /* CHAR_MAX means no more grouping allowed */
    1202                 :           0 :                     case CHAR_MAX:
    1203                 :             :                         /* fall through */
    1204                 :             :                         /* Anything else means another group size */
    1205                 :             :                     default:
    1206                 :           0 :                         group++;
    1207                 :           0 :                         break;
    1208                 :             :                     }
    1209                 :             :                 }
    1210                 :             :             }
    1211                 :             :         }
    1212                 :             : 
    1213                 :             :         /* We built the string backwards, now reverse */
    1214                 :       62765 :         *buf_ptr++ = *temp_ptr;
    1215                 :       62765 :         *buf_ptr = '\0';
    1216                 :       62765 :         auto rev_buf = g_utf8_strreverse(buf, -1);
    1217                 :       62765 :         strcpy (buf, rev_buf);
    1218                 :       62765 :         g_free(rev_buf);
    1219                 :             :     } /* endif */
    1220                 :             : 
    1221                 :             :     /* at this point, buf contains the whole part of the number */
    1222                 :             : 
    1223                 :             :     /* If it's not decimal, print the fraction as an expression. */
    1224                 :       64813 :     if (!value_is_decimal)
    1225                 :             :     {
    1226                 :          43 :         val = gnc_numeric_reduce (val);
    1227                 :             : 
    1228                 :          43 :         if (val.denom > 0)
    1229                 :          43 :             snprintf (temp_buf, buf_size, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
    1230                 :             :                      val.num, val.denom);
    1231                 :             :         else
    1232                 :           0 :             snprintf (temp_buf, buf_size, "%" G_GINT64_FORMAT " * %" G_GINT64_FORMAT,
    1233                 :           0 :                      val.num, -val.denom);
    1234                 :             : 
    1235                 :          43 :         if (whole.num == 0)
    1236                 :           1 :             *buf = '\0';
    1237                 :          42 :         else if (value_is_negative)
    1238                 :           0 :             strcat(buf, " - ");
    1239                 :             :         else
    1240                 :          42 :             strcat(buf, " + ");
    1241                 :             : 
    1242                 :          43 :         strcat (buf, temp_buf);
    1243                 :             :     }
    1244                 :             :     else
    1245                 :             :     {
    1246                 :       64770 :         guint8 num_decimal_places = 0;
    1247                 :       64770 :         char* temp_ptr = temp_buf;
    1248                 :             : 
    1249                 :      129540 :         auto decimal_point = info->monetary
    1250                 :       64770 :                              ? lc->mon_decimal_point
    1251                 :             :                              : lc->decimal_point;
    1252                 :       64770 :         g_utf8_strncpy(temp_ptr, decimal_point, 1);
    1253                 :       64770 :         temp_ptr = g_utf8_find_next_char(temp_ptr, nullptr);
    1254                 :             : 
    1255                 :       78925 :         while (!gnc_numeric_zero_p (val)
    1256                 :       14155 :                 && (val.denom != 1)
    1257                 :       93080 :                 && (num_decimal_places < max_dp))
    1258                 :             :         {
    1259                 :             :             gint64 digit;
    1260                 :             : 
    1261                 :       14155 :             val.denom = val.denom / 10;
    1262                 :             : 
    1263                 :       14155 :             digit = val.num / val.denom;
    1264                 :             : 
    1265                 :       14155 :             *temp_ptr++ = digit + '0';
    1266                 :       14155 :             num_decimal_places++;
    1267                 :             : 
    1268                 :       14155 :             val.num = val.num - (digit * val.denom);
    1269                 :             :         }
    1270                 :             : 
    1271                 :      185918 :         while (num_decimal_places < min_dp)
    1272                 :             :         {
    1273                 :      121148 :             *temp_ptr++ = '0';
    1274                 :      121148 :             num_decimal_places++;
    1275                 :             :         }
    1276                 :             : 
    1277                 :             :         /* cap the end and move to the last character */
    1278                 :       64770 :         *temp_ptr-- = '\0';
    1279                 :             : 
    1280                 :             :         /* Here we strip off trailing decimal zeros per the argument. */
    1281                 :       64770 :         while (*temp_ptr == '0' && num_decimal_places > min_dp)
    1282                 :             :         {
    1283                 :           0 :             *temp_ptr-- = '\0';
    1284                 :           0 :             num_decimal_places--;
    1285                 :             :         }
    1286                 :             : 
    1287                 :       64770 :         if (num_decimal_places > max_dp)
    1288                 :             :         {
    1289                 :           0 :             PWARN ("max_decimal_places too small; limit %d, value %s%s",
    1290                 :             :                    info->max_decimal_places, buf, temp_buf);
    1291                 :             :         }
    1292                 :             : 
    1293                 :       64770 :         if (num_decimal_places > 0)
    1294                 :       64426 :             strcat (buf, temp_buf);
    1295                 :             :     }
    1296                 :             : 
    1297                 :       64813 :     return strlen(buf);
    1298                 :             : }
    1299                 :             : 
    1300                 :             : /**
    1301                 :             :  * @param bufp Should be at least 64 chars.
    1302                 :             :  **/
    1303                 :             : int
    1304                 :       64813 : xaccSPrintAmount (char*  bufp, gnc_numeric val, GNCPrintAmountInfo info)
    1305                 :             : {
    1306                 :       64813 :     auto orig_bufp = bufp;
    1307                 :       64813 :     auto currency_symbol = "";
    1308                 :             :     const char* sign;
    1309                 :             : 
    1310                 :             :     char cs_precedes;
    1311                 :             :     char sep_by_space;
    1312                 :             :     char sign_posn;
    1313                 :             : 
    1314                 :       64813 :     bool print_sign = true;
    1315                 :       64813 :     bool print_absolute = false;
    1316                 :             : 
    1317                 :       64813 :     if (!bufp)
    1318                 :           0 :         return 0;
    1319                 :             : 
    1320                 :       64813 :     auto lc = gnc_localeconv();
    1321                 :       64813 :     if (info.use_locale)
    1322                 :       64565 :         if (gnc_numeric_negative_p (val))
    1323                 :             :         {
    1324                 :       10745 :             cs_precedes  = lc->n_cs_precedes;
    1325                 :       10745 :             sep_by_space = lc->n_sep_by_space;
    1326                 :             :         }
    1327                 :             :         else
    1328                 :             :         {
    1329                 :       53820 :             cs_precedes  = lc->p_cs_precedes;
    1330                 :       53820 :             sep_by_space = lc->p_sep_by_space;
    1331                 :             :         }
    1332                 :             :     else
    1333                 :             :     {
    1334                 :         248 :         cs_precedes = TRUE;
    1335                 :         248 :         sep_by_space = TRUE;
    1336                 :             :     }
    1337                 :             : 
    1338                 :       64813 :     if (info.commodity && info.use_symbol)
    1339                 :             :     {
    1340                 :       61379 :         currency_symbol = gnc_commodity_get_nice_symbol (info.commodity);
    1341                 :       61379 :         if (!gnc_commodity_is_iso (info.commodity))
    1342                 :             :         {
    1343                 :         248 :             cs_precedes  = FALSE;
    1344                 :         248 :             sep_by_space = TRUE;
    1345                 :             :         }
    1346                 :             :     }
    1347                 :             : 
    1348                 :       64813 :     if (gnc_numeric_negative_p (val))
    1349                 :             :     {
    1350                 :       10777 :         sign = lc->negative_sign;
    1351                 :       10777 :         sign_posn = lc->n_sign_posn;
    1352                 :             :     }
    1353                 :             :     else
    1354                 :             :     {
    1355                 :       54036 :         sign = lc->positive_sign;
    1356                 :       54036 :         sign_posn = lc->p_sign_posn;
    1357                 :             :     }
    1358                 :             : 
    1359                 :       64813 :     if (gnc_numeric_zero_p (val) || (sign == nullptr) || (sign[0] == 0))
    1360                 :       54036 :         print_sign = FALSE;
    1361                 :             : 
    1362                 :             :     /* See if we print sign now */
    1363                 :       64813 :     if (print_sign && (sign_posn == 1))
    1364                 :       10777 :         bufp = g_stpcpy(bufp, sign);
    1365                 :             : 
    1366                 :             :     /* Now see if we print currency */
    1367                 :       64813 :     if (cs_precedes)
    1368                 :             :     {
    1369                 :             :         /* See if we print sign now */
    1370                 :       64565 :         if (print_sign && (sign_posn == 3))
    1371                 :           0 :             bufp = g_stpcpy(bufp, sign);
    1372                 :             : 
    1373                 :       64565 :         if (info.use_symbol)
    1374                 :             :         {
    1375                 :       61131 :             bufp = g_stpcpy(bufp, currency_symbol);
    1376                 :       61131 :             if (sep_by_space)
    1377                 :           0 :                 bufp = g_stpcpy(bufp, " ");
    1378                 :             :         }
    1379                 :             : 
    1380                 :             :         /* See if we print sign now */
    1381                 :       64565 :         if (print_sign && (sign_posn == 4))
    1382                 :           0 :             bufp = g_stpcpy(bufp, sign);
    1383                 :             :     }
    1384                 :             : 
    1385                 :             :     /* Now see if we print parentheses */
    1386                 :       64813 :     if (print_sign && (sign_posn == 0))
    1387                 :             :     {
    1388                 :           0 :         bufp = g_stpcpy(bufp, "(");
    1389                 :           0 :         print_absolute = TRUE;
    1390                 :             :     }
    1391                 :             : 
    1392                 :             :     /* Now print the value */
    1393                 :       64813 :     bufp += PrintAmountInternal(bufp,
    1394                 :           0 :                                 print_absolute ? gnc_numeric_abs(val) : val,
    1395                 :             :                                 &info);
    1396                 :             : 
    1397                 :             :     /* Now see if we print parentheses */
    1398                 :       64813 :     if (print_sign && (sign_posn == 0))
    1399                 :           0 :         bufp = g_stpcpy(bufp, ")");
    1400                 :             : 
    1401                 :             :     /* Now see if we print currency */
    1402                 :       64813 :     if (!cs_precedes)
    1403                 :             :     {
    1404                 :             :         /* See if we print sign now */
    1405                 :         248 :         if (print_sign && (sign_posn == 3))
    1406                 :           0 :             bufp = g_stpcpy(bufp, sign);
    1407                 :             : 
    1408                 :         248 :         if (info.use_symbol)
    1409                 :             :         {
    1410                 :         248 :             if (sep_by_space)
    1411                 :         248 :                 bufp = g_stpcpy(bufp, " ");
    1412                 :         248 :             bufp = g_stpcpy(bufp, currency_symbol);
    1413                 :             :         }
    1414                 :             : 
    1415                 :             :         /* See if we print sign now */
    1416                 :         248 :         if (print_sign && (sign_posn == 4))
    1417                 :           0 :             bufp = g_stpcpy(bufp, sign);
    1418                 :             :     }
    1419                 :             : 
    1420                 :             :     /* See if we print sign now */
    1421                 :       64813 :     if (print_sign && (sign_posn == 2))
    1422                 :           0 :         bufp = g_stpcpy(bufp, sign);
    1423                 :             : 
    1424                 :             :     /* return length of printed string */
    1425                 :       64813 :     return (bufp - orig_bufp);
    1426                 :             : }
    1427                 :             : 
    1428                 :             : #define BUFLEN 1024
    1429                 :             : 
    1430                 :             : const char*
    1431                 :       64813 : xaccPrintAmount (gnc_numeric val, GNCPrintAmountInfo info)
    1432                 :             : {
    1433                 :             :     /* hack alert -- this is not thread safe ... */
    1434                 :             :     static char buf[BUFLEN];
    1435                 :             : 
    1436                 :       64813 :     if (!xaccSPrintAmount (buf, val, info))
    1437                 :           0 :         buf[0] = '\0';
    1438                 :             : 
    1439                 :             :     /* its OK to return buf, since we declared it static */
    1440                 :       64813 :     return buf;
    1441                 :             : }
    1442                 :             : 
    1443                 :             : const char*
    1444                 :           0 : gnc_print_amount_with_bidi_ltr_isolate (gnc_numeric val, GNCPrintAmountInfo info)
    1445                 :             : {
    1446                 :             :     /* hack alert -- this is not thread safe ... */
    1447                 :             :     static char buf[BUFLEN];
    1448                 :             :     static const char ltr_isolate[] = { '\xe2', '\x81', '\xa6' };
    1449                 :             :     static const char ltr_pop_isolate[] = { '\xe2', '\x81', '\xa9' };
    1450                 :           0 :     auto offset = info.use_symbol ? 3 : 0;
    1451                 :             : 
    1452                 :           0 :     if (!gnc_commodity_is_currency (info.commodity))
    1453                 :           0 :         offset = 0;
    1454                 :             : 
    1455                 :           0 :     memset (buf, 0, BUFLEN);
    1456                 :           0 :     if (!xaccSPrintAmount (buf + offset, val, info))
    1457                 :             :     {
    1458                 :           0 :         buf[0] = '\0';
    1459                 :           0 :         return buf;
    1460                 :             :     };
    1461                 :             : 
    1462                 :           0 :     if (offset == 0)
    1463                 :           0 :         return buf;
    1464                 :             : 
    1465                 :           0 :     memcpy (buf, ltr_isolate, 3);
    1466                 :             : 
    1467                 :           0 :     if (buf[BUFLEN - 4] == '\0')
    1468                 :             :     {
    1469                 :           0 :         auto length = strlen (buf);
    1470                 :           0 :         memcpy (buf + length, ltr_pop_isolate, 3);
    1471                 :             :     }
    1472                 :             :     else
    1473                 :             :     {
    1474                 :           0 :         buf[BUFLEN - 1] = '\0';
    1475                 :           0 :         memcpy (buf + BUFLEN - 4, ltr_pop_isolate, 3);
    1476                 :             : 
    1477                 :           0 :         PWARN("buffer length %d exceeded, string truncated was %s", BUFLEN, buf);
    1478                 :             :     }
    1479                 :             :     /* its OK to return buf, since we declared it static
    1480                 :             :        and is immediately g_strdup'd */
    1481                 :           0 :     return buf;
    1482                 :             : }
    1483                 :             : 
    1484                 :             : char*
    1485                 :           0 : gnc_wrap_text_with_bidi_ltr_isolate (const char* text)
    1486                 :             : {
    1487                 :             :     static const char* ltr = "\u2066"; // ltr isolate
    1488                 :             :     static const char* pop = "\u2069"; // pop directional formatting
    1489                 :             : 
    1490                 :           0 :     if (!text)
    1491                 :           0 :         return nullptr;
    1492                 :             : 
    1493                 :           0 :     return g_strconcat (ltr, text, pop, nullptr);
    1494                 :             : }
    1495                 :             : 
    1496                 :             : /********************************************************************\
    1497                 :             :  ********************************************************************/
    1498                 :             : 
    1499                 :             : #define FUDGE .00001
    1500                 :             : 
    1501                 :             : /* This function is basically untranslatable. I'd
    1502                 :             :    guess out of the 29 translations we have, 20 will have their number
    1503                 :             :    wordings in a totally different way than English has (not to
    1504                 :             :    mention gender-dependent number endings). Which means this
    1505                 :             :    word-by-word translation will be useless or even plain
    1506                 :             :    wrong. For this reason, we don't even start to pretend a
    1507                 :             :    word-by-word translation would be of any use, so we don't mark any
    1508                 :             :    of these strings for translation. cstim, 2007-04-15. */
    1509                 :             : static const char* small_numbers[] =
    1510                 :             : {
    1511                 :             :     /* Translators: This section is for generating the "amount, in
    1512                 :             :        words" field when printing a check. This function gets the
    1513                 :             :        wording right for English, but unfortunately not for most other
    1514                 :             :        languages. Decide for yourself whether the check printing is
    1515                 :             :        actually needed in your language; if not, you can safely skip the
    1516                 :             :        translation of all of these strings.  */
    1517                 :             :     "Zero", "One", "Two", "Three", "Four",
    1518                 :             :     "Five", "Six", "Seven", "Eight", "Nine",
    1519                 :             :     "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
    1520                 :             :     "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen",
    1521                 :             :     "Twenty"
    1522                 :             : };
    1523                 :             : static const char* medium_numbers[] =
    1524                 :             : {
    1525                 :             :     "Zero", "Ten", "Twenty", "Thirty", "Forty",
    1526                 :             :     "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"
    1527                 :             : };
    1528                 :             : static const char* big_numbers[] =
    1529                 :             : {
    1530                 :             :     /* Translators: This is the word for the number 10^2 */
    1531                 :             :     "Hundred",
    1532                 :             :     /* Translators: This is the word for the number 10^3 */
    1533                 :             :     "Thousand",
    1534                 :             :     /* Translators: This is the word for the number 10^6, one thousand
    1535                 :             :        thousands. */
    1536                 :             :     "Million",
    1537                 :             :     /* Translators: This is the word for the number 10^9, one thousand
    1538                 :             :        millions. WATCH OUT: In British English and many other languages
    1539                 :             :        this word is used for 10^12 which is one million millions! In
    1540                 :             :        contrast to this, here in GnuCash this is used in the American
    1541                 :             :        English meaning of 10^9.  */
    1542                 :             :     "Billion",
    1543                 :             :     /* Translators: This is the word for the number 10^12, one million
    1544                 :             :        millions. */
    1545                 :             :     "Trillion",
    1546                 :             :     /* Translators: This is the word for the number 10^15 */
    1547                 :             :     "Quadrillion",
    1548                 :             :     /* Translators: This is the word for the number 10^18 */
    1549                 :             :     "Quintillion"
    1550                 :             : };
    1551                 :             : 
    1552                 :             : static char*
    1553                 :           0 : integer_to_words(gint64 val)
    1554                 :             : {
    1555                 :           0 :     if (val == 0)
    1556                 :           0 :         return g_strdup("zero");
    1557                 :           0 :     if (val < 0)
    1558                 :           0 :         val = -val;
    1559                 :             : 
    1560                 :           0 :     auto result = g_string_sized_new(100);
    1561                 :             : 
    1562                 :           0 :     while (val >= 1000)
    1563                 :             :     {
    1564                 :           0 :         int log_val = log10(val) / 3 + FUDGE;
    1565                 :           0 :         int pow_val = exp(log_val * 3 * G_LN10) + FUDGE;
    1566                 :           0 :         int this_part = val / pow_val;
    1567                 :           0 :         val -= this_part * pow_val;
    1568                 :           0 :         auto tmp = integer_to_words(this_part);
    1569                 :           0 :         g_string_append_printf(result, "%s %s ", tmp, gettext(big_numbers[log_val]));
    1570                 :           0 :         g_free(tmp);
    1571                 :             :     }
    1572                 :             : 
    1573                 :           0 :     if (val >= 100)
    1574                 :             :     {
    1575                 :           0 :         int this_part = val / 100;
    1576                 :           0 :         val -= this_part * 100;
    1577                 :           0 :         g_string_append_printf(result, "%s %s ",
    1578                 :             :                                gettext(small_numbers[this_part]),
    1579                 :             :                                gettext(big_numbers[0]));
    1580                 :             :     }
    1581                 :             : 
    1582                 :           0 :     if (val > 20)
    1583                 :             :     {
    1584                 :           0 :         int this_part = val / 10;
    1585                 :           0 :         val -= this_part * 10;
    1586                 :           0 :         g_string_append(result, gettext(medium_numbers[this_part]));
    1587                 :             :         g_string_append_c(result, ' ');
    1588                 :             :     }
    1589                 :             : 
    1590                 :           0 :     if (val > 0)
    1591                 :             :     {
    1592                 :           0 :         int this_part = val;
    1593                 :           0 :         g_string_append(result, gettext(small_numbers[this_part]));
    1594                 :             :         g_string_append_c(result, ' ');
    1595                 :             :     }
    1596                 :             : 
    1597                 :           0 :     result = g_string_truncate(result, result->len - 1);
    1598                 :           0 :     return g_string_free(result, FALSE);
    1599                 :             : }
    1600                 :             : 
    1601                 :             : #ifdef _MSC_VER
    1602                 :             : static double round(double x)
    1603                 :             : {
    1604                 :             :     // A simple round() implementation because MSVC doesn't seem to have that
    1605                 :             :     return floor(x + 0.5);
    1606                 :             : }
    1607                 :             : #endif
    1608                 :             : 
    1609                 :             : char*
    1610                 :           0 : number_to_words(double val, int64_t denom)
    1611                 :             : {
    1612                 :           0 :     if (val < 0) val = -val;
    1613                 :           0 :     if (denom < 0) denom = -denom;
    1614                 :             : 
    1615                 :           0 :     auto int_part = floor(val);
    1616                 :           0 :     auto frac_part = static_cast<int64_t>(round((val - int_part) * denom));
    1617                 :             : 
    1618                 :           0 :     auto int_string = integer_to_words(int_part);
    1619                 :             :     /* Inside of the gettext macro _(...) we must not use any macros but
    1620                 :             :        only plain string literals. For this reason, convert the strings
    1621                 :             :        separately. */
    1622                 :           0 :     auto nomin_string = g_strdup_printf("%02" PRId64, frac_part);
    1623                 :           0 :     auto denom_string = g_strdup_printf("%" PRId64, denom);
    1624                 :             :     auto full_string =
    1625                 :             :         /* Translators: This is for the "amount, in words" field in check
    1626                 :             :            printing. The first %s is the integer amount of dollars (or
    1627                 :             :            whatever currency), the second and third %s the cent amount as
    1628                 :             :            a fraction, e.g. 47/100.  */
    1629                 :           0 :         g_strdup_printf("%s and %s/%s",
    1630                 :             :                         int_string, nomin_string, denom_string);
    1631                 :           0 :     g_free(int_string);
    1632                 :           0 :     g_free(nomin_string);
    1633                 :           0 :     g_free(denom_string);
    1634                 :           0 :     return full_string;
    1635                 :             : }
    1636                 :             : 
    1637                 :             : char*
    1638                 :           0 : numeric_to_words(gnc_numeric val)
    1639                 :             : {
    1640                 :           0 :     return number_to_words(gnc_numeric_to_double(val),
    1641                 :           0 :                            gnc_numeric_denom(val));
    1642                 :             : }
    1643                 :             : 
    1644                 :             : const char*
    1645                 :           0 : printable_value (double val, int denom)
    1646                 :             : {
    1647                 :           0 :     auto num = gnc_numeric_create(round(val * denom), denom);
    1648                 :           0 :     auto info = gnc_share_print_info_places(log10(denom));
    1649                 :           0 :     return xaccPrintAmount (num, info);
    1650                 :             : }
    1651                 :             : 
    1652                 :             : /********************************************************************\
    1653                 :             :  * xaccParseAmount                                                  *
    1654                 :             :  *   parses amount strings using locale data                        *
    1655                 :             :  *                                                                  *
    1656                 :             :  * Args: in_str   -- pointer to string rep of num                   *
    1657                 :             :  *       monetary -- boolean indicating whether value is monetary   *
    1658                 :             :  *       result   -- pointer to result location, may be nullptr        *
    1659                 :             :  *       endstr   -- used to store first digit not used in parsing  *
    1660                 :             :  * Return: gboolean -- TRUE if a number found and parsed            *
    1661                 :             :  *                     If FALSE, result is not changed              *
    1662                 :             : \********************************************************************/
    1663                 :             : 
    1664                 :             : /* Parsing state machine states */
    1665                 :             : enum ParseState
    1666                 :             : {
    1667                 :             :     START_ST,       /* Parsing initial whitespace */
    1668                 :             :     NEG_ST,         /* Parsed a negative sign or a left paren */
    1669                 :             :     NUMER_ST,   /* Parsing digits before grouping and decimal characters */
    1670                 :             :     FRAC_ST,        /* Parsing the fractional portion of a number */
    1671                 :             :     DONE_ST,        /* Finished, number is correct module grouping constraints */
    1672                 :             :     NO_NUM_ST       /* Finished, number was malformed */
    1673                 :             : };
    1674                 :             : 
    1675                 :             : #define done_state(state) (((state) == DONE_ST) || ((state) == NO_NUM_ST))
    1676                 :             : 
    1677                 :             : static inline int64_t
    1678                 :        3011 : multiplier (int num_decimals)
    1679                 :             : {
    1680                 :        3011 :     switch (num_decimals)
    1681                 :             :     {
    1682                 :           0 :     case 12:
    1683                 :           0 :         return 1000000000000;
    1684                 :           0 :     case 11:
    1685                 :           0 :         return 100000000000;
    1686                 :           0 :     case 10:
    1687                 :           0 :         return 10000000000;
    1688                 :           0 :     case 9:
    1689                 :           0 :         return 1000000000;
    1690                 :         192 :     case 8:
    1691                 :         192 :         return 100000000;
    1692                 :         276 :     case 7:
    1693                 :         276 :         return 10000000;
    1694                 :         294 :     case 6:
    1695                 :         294 :         return 1000000;
    1696                 :         258 :     case 5:
    1697                 :         258 :         return 100000;
    1698                 :         465 :     case 4:
    1699                 :         465 :         return 10000;
    1700                 :         369 :     case 3:
    1701                 :         369 :         return 1000;
    1702                 :         687 :     case 2:
    1703                 :         687 :         return 100;
    1704                 :         470 :     case 1:
    1705                 :         470 :         return 10;
    1706                 :           0 :     case 0:
    1707                 :           0 :          return 1;
    1708                 :           0 :     default:
    1709                 :           0 :         PERR("bad fraction length");
    1710                 :           0 :         g_assert_not_reached();
    1711                 :             :         break;
    1712                 :             :     }
    1713                 :             : 
    1714                 :             :     return 1;
    1715                 :             : }
    1716                 :             : 
    1717                 :             : static gboolean
    1718                 :        3226 : xaccParseAmountInternal (const char*  in_str, gboolean monetary,
    1719                 :             :                          gunichar negative_sign, gunichar decimal_point,
    1720                 :             :                          gunichar group_separator, const char* ignore_list,
    1721                 :             :                          gboolean use_auto_decimal,
    1722                 :             :                          gnc_numeric *result, char** endstr)
    1723                 :             : {
    1724                 :             :     /* Initialize *endstr to in_str */
    1725                 :        3226 :     if (endstr)
    1726                 :         140 :         *endstr = (char* ) in_str;
    1727                 :             : 
    1728                 :        3226 :     if (!in_str)
    1729                 :           0 :         return FALSE;
    1730                 :             : 
    1731                 :             :     const char* in;
    1732                 :        3226 :     if (!g_utf8_validate(in_str, -1, &in))
    1733                 :             :     {
    1734                 :           0 :         printf("Invalid utf8 string '%s'. Bad character at position %ld.\n",
    1735                 :             :                in_str, g_utf8_pointer_to_offset (in_str, in));
    1736                 :           0 :         return FALSE;
    1737                 :             :     }
    1738                 :             : 
    1739                 :             :     /* 'out_str' will be used to store digits for numeric conversion.
    1740                 :             :      * 'out' will be used to traverse out_str. */
    1741                 :        3226 :     auto out_str = g_new(gchar, strlen(in_str) + 128);
    1742                 :        3226 :     auto out = out_str;
    1743                 :             : 
    1744                 :             :     /* 'in' is used to traverse 'in_str'. */
    1745                 :        3226 :     in = in_str;
    1746                 :             : 
    1747                 :        3226 :     auto is_negative = false;
    1748                 :        3226 :     auto got_decimal = false;
    1749                 :        3226 :     auto need_paren = false;
    1750                 :        3226 :     int64_t numer = 0;
    1751                 :        3226 :     int64_t denom = 1;
    1752                 :             : 
    1753                 :             :     /* Initialize the state machine */
    1754                 :        3226 :     auto state = START_ST;
    1755                 :             : 
    1756                 :             :     /* This while loop implements a state machine for parsing numbers. */
    1757                 :             :     while (TRUE)
    1758                 :             :     {
    1759                 :       41007 :         auto next_state = state;
    1760                 :       41007 :         auto uc = g_utf8_get_char(in);
    1761                 :             : 
    1762                 :             :         /* Ignore anything in the 'ignore list' */
    1763                 :       41007 :         if (ignore_list && uc && g_utf8_strchr(ignore_list, -1, uc))
    1764                 :             :         {
    1765                 :           5 :             in = g_utf8_next_char(in);
    1766                 :           5 :             continue;
    1767                 :             :         }
    1768                 :             : 
    1769                 :             :         /* Note we never need to check for the end of 'in_str' explicitly.
    1770                 :             :          * The 'else' clauses on all the state transitions will handle that. */
    1771                 :       41002 :         switch (state)
    1772                 :             :         {
    1773                 :             :             /* START_ST means we have parsed 0 or more whitespace characters */
    1774                 :        3232 :             case START_ST:
    1775                 :        3232 :                 if (g_unichar_isdigit(uc))
    1776                 :             :                 {
    1777                 :        3214 :                     int count = g_unichar_to_utf8(uc, out);
    1778                 :        3214 :                     out += count; /* we record the digits themselves in out_str
    1779                 :             :                     * for later conversion by libc routines */
    1780                 :        3214 :                     next_state = NUMER_ST;
    1781                 :             :                 }
    1782                 :          18 :                 else if (uc == decimal_point)
    1783                 :           0 :                     next_state = FRAC_ST;
    1784                 :          18 :                 else if (g_unichar_isspace(uc))
    1785                 :             :                     ;
    1786                 :          12 :                 else if (uc == negative_sign)
    1787                 :             :                 {
    1788                 :           6 :                     is_negative = TRUE;
    1789                 :           6 :                     next_state = NEG_ST;
    1790                 :             :                 }
    1791                 :           6 :                 else if (uc == '(')
    1792                 :             :                 {
    1793                 :           0 :                     is_negative = TRUE;
    1794                 :           0 :                     need_paren = TRUE;
    1795                 :           0 :                     next_state = NEG_ST;
    1796                 :             :                 }
    1797                 :             :                 else
    1798                 :           6 :                     next_state = NO_NUM_ST;
    1799                 :             : 
    1800                 :        3232 :                 break;
    1801                 :             : 
    1802                 :             :             /* NEG_ST means we have just parsed a negative sign. For now,
    1803                 :             :              * we only recognize formats where the negative sign comes first. */
    1804                 :           6 :             case NEG_ST:
    1805                 :           6 :                 if (g_unichar_isdigit(uc))
    1806                 :             :                 {
    1807                 :           6 :                     int count = g_unichar_to_utf8(uc, out);
    1808                 :           6 :                     out += count;
    1809                 :           6 :                     next_state = NUMER_ST;
    1810                 :             :                 }
    1811                 :           0 :                 else if (uc == decimal_point)
    1812                 :           0 :                     next_state = FRAC_ST;
    1813                 :           0 :                 else if (g_unichar_isspace(uc))
    1814                 :             :                     ;
    1815                 :             :                 else
    1816                 :           0 :                     next_state = NO_NUM_ST;
    1817                 :             : 
    1818                 :           6 :                 break;
    1819                 :             : 
    1820                 :             :             /* NUMER_ST means we have started parsing the number, but
    1821                 :             :              * have not encountered a decimal separator. */
    1822                 :       23417 :             case NUMER_ST:
    1823                 :       23417 :                 if (g_unichar_isdigit(uc))
    1824                 :             :                 {
    1825                 :       18250 :                     int count = g_unichar_to_utf8(uc, out);
    1826                 :       18250 :                     out += count;
    1827                 :             :                 }
    1828                 :        5167 :                 else if (uc == decimal_point)
    1829                 :        3012 :                     next_state = FRAC_ST;
    1830                 :        2155 :                 else if (uc == group_separator)
    1831                 :             :                     ; //ignore it
    1832                 :         208 :                 else if (uc == ')' && need_paren)
    1833                 :             :                 {
    1834                 :           0 :                     next_state = DONE_ST;
    1835                 :           0 :                     need_paren = FALSE;
    1836                 :             :                 }
    1837                 :             :                 else
    1838                 :         208 :                     next_state = DONE_ST;
    1839                 :             : 
    1840                 :       23417 :                 break;
    1841                 :             : 
    1842                 :             :             /* FRAC_ST means we are now parsing fractional digits. */
    1843                 :       14347 :             case FRAC_ST:
    1844                 :       14347 :                 if (g_unichar_isdigit(uc))
    1845                 :             :                 {
    1846                 :       11335 :                     int count = g_unichar_to_utf8(uc, out);
    1847                 :       11335 :                     out += count;
    1848                 :             :                 }
    1849                 :        3012 :                 else if (uc == decimal_point)
    1850                 :             :                 {
    1851                 :             :                     /* If a subsequent decimal point is also whitespace,
    1852                 :             :                         * assume it was intended as such and stop parsing.
    1853                 :             :                         * Otherwise, there is a problem. */
    1854                 :           1 :                     if (g_unichar_isspace(decimal_point))
    1855                 :           0 :                         next_state = DONE_ST;
    1856                 :             :                     else
    1857                 :           1 :                         next_state = NO_NUM_ST;
    1858                 :             :                 }
    1859                 :        3011 :                 else if (uc == group_separator)
    1860                 :             :                 {
    1861                 :             :                     /* If a subsequent group separator is also whitespace,
    1862                 :             :                         * assume it was intended as such and stop parsing.
    1863                 :             :                         * Otherwise ignore it. */
    1864                 :           0 :                     if (g_unichar_isspace(group_separator))
    1865                 :           0 :                         next_state = DONE_ST;
    1866                 :             :                 }
    1867                 :        3011 :                 else if (uc == ')' && need_paren)
    1868                 :             :                 {
    1869                 :           0 :                     next_state = DONE_ST;
    1870                 :           0 :                     need_paren = FALSE;
    1871                 :             :                 }
    1872                 :             :                 else
    1873                 :        3011 :                     next_state = DONE_ST;
    1874                 :             : 
    1875                 :       14347 :                 break;
    1876                 :             : 
    1877                 :           0 :             default:
    1878                 :           0 :                 PERR("bad state");
    1879                 :           0 :                 g_assert_not_reached();
    1880                 :             :                 break;
    1881                 :             :         }
    1882                 :             : 
    1883                 :             :         /* If we're moving into the FRAC_ST or out of the machine
    1884                 :             :          * without going through FRAC_ST, record the integral value. */
    1885                 :       41002 :         if (((next_state == FRAC_ST) && (state != FRAC_ST)) ||
    1886                 :        3219 :             ((next_state == DONE_ST) && !got_decimal))
    1887                 :             :         {
    1888                 :        3220 :             *out = '\0';
    1889                 :             : 
    1890                 :        3220 :             if (*out_str && sscanf(out_str, "%" SCNd64, &numer) < 1)
    1891                 :           0 :                 next_state = NO_NUM_ST;
    1892                 :        3220 :             else if (next_state == FRAC_ST)
    1893                 :             :             {
    1894                 :             :                 /* reset the out pointer to record the fraction */
    1895                 :        3012 :                 out = out_str;
    1896                 :        3012 :                 *out = '\0';
    1897                 :             : 
    1898                 :        3012 :                 got_decimal = TRUE;
    1899                 :             :             }
    1900                 :             :         }
    1901                 :             : 
    1902                 :       41002 :         state = next_state;
    1903                 :       41002 :         if (done_state (state))
    1904                 :             :             break;
    1905                 :             : 
    1906                 :       37776 :         in = g_utf8_next_char(in);
    1907                 :       37781 :     }
    1908                 :             : 
    1909                 :             :     /* If there was an error, just quit */
    1910                 :        3226 :     if (need_paren || (state == NO_NUM_ST))
    1911                 :             :     {
    1912                 :           7 :         g_free(out_str);
    1913                 :           7 :         return FALSE;
    1914                 :             :     }
    1915                 :             : 
    1916                 :             :     /* Cap the end of the fraction string, if any */
    1917                 :        3219 :     *out = '\0';
    1918                 :             : 
    1919                 :             :     /* Add in fractional value */
    1920                 :        3219 :     if (got_decimal && *out_str)
    1921                 :             :     {
    1922                 :             : 
    1923                 :        3011 :         auto len = strlen(out_str);
    1924                 :             : 
    1925                 :        3011 :         if (len > 12)
    1926                 :             :         {
    1927                 :           0 :             out_str[12] = '\0';
    1928                 :           0 :             len = 12;
    1929                 :             :         }
    1930                 :             : 
    1931                 :             :         int64_t fraction;
    1932                 :        3011 :         if (sscanf (out_str, "%" SCNd64 , &fraction) < 1)
    1933                 :             :         {
    1934                 :           0 :             g_free(out_str);
    1935                 :           0 :             return FALSE;
    1936                 :             :         }
    1937                 :             : 
    1938                 :        3011 :         denom = multiplier(len);
    1939                 :        3011 :         numer *= denom;
    1940                 :        3011 :         numer += fraction;
    1941                 :        3011 :     }
    1942                 :         208 :     else if (monetary && use_auto_decimal && !got_decimal)
    1943                 :             :     {
    1944                 :           0 :         if ((auto_decimal_places > 0) && (auto_decimal_places <= 12))
    1945                 :             :         {
    1946                 :           0 :             denom = multiplier(auto_decimal_places);
    1947                 :             : 
    1948                 :             :             /* No need to multiply numer by denom at this point,
    1949                 :             :              * since by specifying the auto decimal places the
    1950                 :             :              * user has effectively determined the scaling factor
    1951                 :             :              * for the numerator they entered.
    1952                 :             :              */
    1953                 :             :         }
    1954                 :             :     }
    1955                 :             : 
    1956                 :        3219 :     if (result)
    1957                 :             :     {
    1958                 :        3219 :         *result = gnc_numeric_create (numer, denom);
    1959                 :        3219 :         if (is_negative)
    1960                 :           6 :             *result = gnc_numeric_neg (*result);
    1961                 :             :     }
    1962                 :             : 
    1963                 :        3219 :     if (endstr)
    1964                 :         133 :         *endstr = (char* ) in;
    1965                 :             : 
    1966                 :        3219 :     g_free (out_str);
    1967                 :             : 
    1968                 :        3219 :     return TRUE;
    1969                 :             : }
    1970                 :             : 
    1971                 :             : 
    1972                 :             : static gboolean
    1973                 :        3206 : xaccParseAmountBasicInternal (const char*  in_str, gboolean monetary,
    1974                 :             :                               gboolean use_auto_decimal, gnc_numeric *result,
    1975                 :             :                               char** endstr, gboolean skip)
    1976                 :             : {
    1977                 :        3206 :     struct lconv *lc = gnc_localeconv();
    1978                 :        3206 :     gunichar negative_sign = g_utf8_get_char(lc->negative_sign);
    1979                 :             : 
    1980                 :             :     gunichar decimal_point;
    1981                 :             :     gunichar group_separator;
    1982                 :        3206 :     if (monetary)
    1983                 :             :     {
    1984                 :         646 :         group_separator = g_utf8_get_char(lc->mon_thousands_sep);
    1985                 :         646 :         decimal_point = g_utf8_get_char(lc->mon_decimal_point);
    1986                 :             :     }
    1987                 :             :     else
    1988                 :             :     {
    1989                 :        2560 :         group_separator = g_utf8_get_char(lc->thousands_sep);
    1990                 :        2560 :         decimal_point = g_utf8_get_char(lc->decimal_point);
    1991                 :             :     }
    1992                 :             : 
    1993                 :        3206 :     const char* ignore = nullptr;
    1994                 :        3206 :     if (skip)
    1995                 :             :     {
    1996                 :             :         /* We want the locale's positive sign to be ignored.
    1997                 :             :          * If the locale doesn't specify one, we assume "+" as
    1998                 :             :          * an optional positive sign and ignore that */
    1999                 :           0 :         ignore = lc->positive_sign;
    2000                 :           0 :         if (!ignore || !*ignore)
    2001                 :           0 :             ignore = "+";
    2002                 :             :     }
    2003                 :             : 
    2004                 :        3206 :     return xaccParseAmountInternal(in_str, monetary, negative_sign,
    2005                 :             :                                    decimal_point, group_separator,
    2006                 :             :                                    ignore, use_auto_decimal,
    2007                 :        3206 :                                    result, endstr);
    2008                 :             : }
    2009                 :             : 
    2010                 :             : 
    2011                 :             : gboolean
    2012                 :        3206 : xaccParseAmount (const char*  in_str, gboolean monetary, gnc_numeric *result,
    2013                 :             :                  char** endstr)
    2014                 :             : {
    2015                 :        3206 :     return xaccParseAmountBasicInternal (in_str, monetary, auto_decimal_enabled,
    2016                 :        3206 :                                          result, endstr, FALSE);
    2017                 :             : }
    2018                 :             : 
    2019                 :             : gboolean
    2020                 :           0 : xaccParseAmountImport (const char*  in_str, gboolean monetary,
    2021                 :             :                         gnc_numeric *result,
    2022                 :             :                         char** endstr, gboolean skip)
    2023                 :             : {
    2024                 :           0 :     return xaccParseAmountBasicInternal (in_str, monetary, FALSE,
    2025                 :           0 :                                          result, endstr, skip);
    2026                 :             : }
    2027                 :             : 
    2028                 :             : 
    2029                 :             : gboolean
    2030                 :          10 : xaccParseAmountExtended (const char*  in_str, gboolean monetary,
    2031                 :             :                          gunichar negative_sign, gunichar decimal_point,
    2032                 :             :                          gunichar group_separator, const char* ignore_list,
    2033                 :             :                          gnc_numeric *result, char** endstr)
    2034                 :             : {
    2035                 :          10 :     return xaccParseAmountInternal (in_str, monetary, negative_sign,
    2036                 :             :                                     decimal_point, group_separator,
    2037                 :             :                                     ignore_list, auto_decimal_enabled,
    2038                 :          10 :                                     result, endstr);
    2039                 :             : }
    2040                 :             : 
    2041                 :             : gboolean
    2042                 :          10 : xaccParseAmountExtImport (const char*  in_str, gboolean monetary,
    2043                 :             :                              gunichar negative_sign, gunichar decimal_point,
    2044                 :             :                              gunichar group_separator, const char* ignore_list,
    2045                 :             :                              gnc_numeric *result, char** endstr)
    2046                 :             : {
    2047                 :          10 :     return xaccParseAmountInternal (in_str, monetary, negative_sign,
    2048                 :             :                                     decimal_point, group_separator,
    2049                 :             :                                     ignore_list, FALSE,
    2050                 :          10 :                                     result, endstr);
    2051                 :             : }
    2052                 :             : 
    2053                 :             : 
    2054                 :             : /* enable/disable the auto_decimal_enabled option */
    2055                 :             : static void
    2056                 :           0 : gnc_set_auto_decimal_enabled (gpointer settings, char* key, gpointer user_data)
    2057                 :             : {
    2058                 :           0 :     auto_decimal_enabled =
    2059                 :           0 :             gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
    2060                 :           0 : }
    2061                 :             : 
    2062                 :             : /* set the number of auto decimal places to use */
    2063                 :             : static void
    2064                 :           0 : gnc_set_auto_decimal_places (gpointer settings, char* key, gpointer user_data)
    2065                 :             : {
    2066                 :           0 :     auto_decimal_places =
    2067                 :           0 :             gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
    2068                 :           0 : }
    2069                 :             : 
    2070                 :             : static void
    2071                 :           0 : gnc_auto_decimal_init (void)
    2072                 :             : {
    2073                 :           0 :     auto_decimal_enabled =
    2074                 :           0 :         gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT);
    2075                 :           0 :     auto_decimal_places =
    2076                 :           0 :         gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES);
    2077                 :           0 : }
    2078                 :             : 
    2079                 :             : void
    2080                 :           0 : gnc_ui_util_init (void)
    2081                 :             : {
    2082                 :           0 :     gnc_configure_account_separator ();
    2083                 :           0 :     gnc_auto_decimal_init();
    2084                 :             : 
    2085                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_SEPARATOR,
    2086                 :             :                           (void*)gnc_configure_account_separator, nullptr);
    2087                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_NONE,
    2088                 :             :                           (void*)gnc_configure_reverse_balance, nullptr);
    2089                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_CREDIT,
    2090                 :             :                           (void*)gnc_configure_reverse_balance, nullptr);
    2091                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_REVERSED_ACCTS_INC_EXP,
    2092                 :             :                           (void*)gnc_configure_reverse_balance, nullptr);
    2093                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_LOCALE,
    2094                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2095                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_CHOICE_OTHER,
    2096                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2097                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_CURRENCY_OTHER,
    2098                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2099                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_LOCALE,
    2100                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2101                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_CHOICE_OTHER,
    2102                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2103                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL_REPORT, GNC_PREF_CURRENCY_OTHER,
    2104                 :             :                           (void*)gnc_currency_changed_cb, nullptr);
    2105                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_POINT,
    2106                 :             :                           (void*)gnc_set_auto_decimal_enabled, nullptr);
    2107                 :           0 :     gnc_prefs_register_cb(GNC_PREFS_GROUP_GENERAL, GNC_PREF_AUTO_DECIMAL_PLACES,
    2108                 :             :                           (void*)gnc_set_auto_decimal_places, nullptr);
    2109                 :             : 
    2110                 :           0 : }
    2111                 :             : 
    2112                 :             : void
    2113                 :           0 : gnc_ui_util_remove_registered_prefs (void)
    2114                 :             : {
    2115                 :             :     // remove the registered pref call backs above
    2116                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2117                 :             :                                  GNC_PREF_ACCOUNT_SEPARATOR,
    2118                 :             :                                  (void*)gnc_configure_account_separator, nullptr);
    2119                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2120                 :             :                                  GNC_PREF_REVERSED_ACCTS_NONE,
    2121                 :             :                                  (void*)gnc_configure_reverse_balance, nullptr);
    2122                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2123                 :             :                                  GNC_PREF_REVERSED_ACCTS_CREDIT,
    2124                 :             :                                  (void*)gnc_configure_reverse_balance, nullptr);
    2125                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2126                 :             :                                  GNC_PREF_REVERSED_ACCTS_INC_EXP,
    2127                 :             :                                  (void*)gnc_configure_reverse_balance, nullptr);
    2128                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2129                 :             :                                  GNC_PREF_CURRENCY_CHOICE_LOCALE,
    2130                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2131                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2132                 :             :                                  GNC_PREF_CURRENCY_CHOICE_OTHER,
    2133                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2134                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2135                 :             :                                  GNC_PREF_CURRENCY_OTHER,
    2136                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2137                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
    2138                 :             :                                  GNC_PREF_CURRENCY_CHOICE_LOCALE,
    2139                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2140                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
    2141                 :             :                                  GNC_PREF_CURRENCY_CHOICE_OTHER,
    2142                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2143                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL_REPORT,
    2144                 :             :                                  GNC_PREF_CURRENCY_OTHER,
    2145                 :             :                                  (void*)gnc_currency_changed_cb, nullptr);
    2146                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2147                 :             :                                  GNC_PREF_AUTO_DECIMAL_POINT,
    2148                 :             :                                  (void*)gnc_set_auto_decimal_enabled, nullptr);
    2149                 :           0 :     gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
    2150                 :             :                                  GNC_PREF_AUTO_DECIMAL_PLACES,
    2151                 :             :                                  (void*)gnc_set_auto_decimal_places, nullptr);
    2152                 :           0 : }
    2153                 :             : 
    2154                 :             : static inline bool
    2155                 :           0 : unichar_is_cntrl (gunichar uc)
    2156                 :             : {
    2157                 :           0 :     return (uc < 0x20 || (uc > 0x7e && uc < 0xa0));
    2158                 :             : }
    2159                 :             : 
    2160                 :             : char*
    2161                 :           0 : gnc_filter_text_for_control_chars (const char* text)
    2162                 :             : {
    2163                 :           0 :     bool cntrl = false;
    2164                 :           0 :     bool text_found = false;
    2165                 :             : 
    2166                 :           0 :     if (!text)
    2167                 :           0 :         return nullptr;
    2168                 :             : 
    2169                 :           0 :     if (!g_utf8_validate (text, -1, nullptr))
    2170                 :           0 :         return nullptr;
    2171                 :             : 
    2172                 :           0 :     auto filtered = g_string_sized_new (strlen (text) + 1);
    2173                 :             : 
    2174                 :           0 :     auto ch = text;
    2175                 :             : 
    2176                 :           0 :     while (*ch)
    2177                 :             :     {
    2178                 :           0 :         auto uc = g_utf8_get_char (ch);
    2179                 :             : 
    2180                 :             :         // check for starting with control characters
    2181                 :           0 :         if (unichar_is_cntrl (uc) && !text_found)
    2182                 :             :         {
    2183                 :           0 :             ch = g_utf8_next_char (ch);
    2184                 :           0 :             continue;
    2185                 :             :         }
    2186                 :             :         // check for alpha, num and punctuation
    2187                 :           0 :         if (!unichar_is_cntrl (uc))
    2188                 :             :         {
    2189                 :           0 :             filtered = g_string_append_unichar (filtered, uc);
    2190                 :           0 :             text_found = true;
    2191                 :             :         }
    2192                 :             :         // check for control characters after text
    2193                 :           0 :         if (unichar_is_cntrl (uc))
    2194                 :           0 :             cntrl = true;
    2195                 :             : 
    2196                 :           0 :         ch = g_utf8_next_char (ch);
    2197                 :             : 
    2198                 :           0 :         if (cntrl) // if control characters in text replace with space
    2199                 :             :         {
    2200                 :           0 :             auto uc2 = g_utf8_get_char (ch);
    2201                 :             : 
    2202                 :           0 :             if (!unichar_is_cntrl (uc2))
    2203                 :           0 :                 filtered = g_string_append_unichar (filtered, ' ');
    2204                 :             :         }
    2205                 :           0 :         cntrl = false;
    2206                 :             :     }
    2207                 :           0 :     return g_string_free (filtered, FALSE);
    2208                 :             : }
    2209                 :             : 
    2210                 :             : void
    2211                 :           0 : gnc_filter_text_set_cursor_position (const char* incoming_text,
    2212                 :             :                                      const char* symbol,
    2213                 :             :                                      gint *cursor_position)
    2214                 :             : {
    2215                 :           0 :     int num = 0;
    2216                 :             : 
    2217                 :           0 :     if (*cursor_position == 0)
    2218                 :           0 :         return;
    2219                 :             : 
    2220                 :           0 :     if (!incoming_text || !symbol)
    2221                 :           0 :         return;
    2222                 :             : 
    2223                 :           0 :     if (g_strrstr (incoming_text, symbol) == nullptr)
    2224                 :           0 :         return;
    2225                 :             : 
    2226                 :           0 :     auto text_len = g_utf8_strlen (incoming_text, -1);
    2227                 :             : 
    2228                 :           0 :     for (int x = 0; x < text_len; x++)
    2229                 :             :     {
    2230                 :           0 :         auto temp = g_utf8_offset_to_pointer (incoming_text, x);
    2231                 :             : 
    2232                 :           0 :         if (g_str_has_prefix (temp, symbol))
    2233                 :           0 :             num++;
    2234                 :             : 
    2235                 :           0 :         if (g_strrstr (temp, symbol) == nullptr)
    2236                 :           0 :             break;
    2237                 :             :     }
    2238                 :           0 :     *cursor_position = *cursor_position - (num * g_utf8_strlen (symbol, -1));
    2239                 :             : }
    2240                 :             : 
    2241                 :             : char*
    2242                 :           0 : gnc_filter_text_for_currency_symbol (const char* incoming_text,
    2243                 :             :                                      const char* symbol)
    2244                 :             : {
    2245                 :           0 :     if (!incoming_text)
    2246                 :           0 :         return nullptr;
    2247                 :             : 
    2248                 :           0 :     if (!symbol)
    2249                 :           0 :        return g_strdup (incoming_text);
    2250                 :             : 
    2251                 :           0 :     if (g_strrstr (incoming_text, symbol) == nullptr)
    2252                 :           0 :         return g_strdup (incoming_text);
    2253                 :             : 
    2254                 :           0 :     auto split = g_strsplit (incoming_text, symbol, -1);
    2255                 :             : 
    2256                 :           0 :     auto ret_text = g_strjoinv (nullptr, split);
    2257                 :             : 
    2258                 :           0 :     g_strfreev (split);
    2259                 :           0 :     return ret_text;
    2260                 :             : }
    2261                 :             : 
    2262                 :             : char*
    2263                 :           0 : gnc_filter_text_for_currency_commodity (const gnc_commodity *comm,
    2264                 :             :                                         const char* incoming_text,
    2265                 :             :                                         const char** symbol)
    2266                 :             : {
    2267                 :           0 :     if (!incoming_text)
    2268                 :             :     {
    2269                 :           0 :         *symbol = nullptr;
    2270                 :           0 :         return nullptr;
    2271                 :             :     }
    2272                 :             : 
    2273                 :           0 :     if (!gnc_commodity_is_currency (comm))
    2274                 :             :     {
    2275                 :           0 :         *symbol = nullptr;
    2276                 :           0 :         return g_strdup (incoming_text);
    2277                 :             :     }
    2278                 :             : 
    2279                 :           0 :     if (comm)
    2280                 :           0 :         *symbol = gnc_commodity_get_nice_symbol (comm);
    2281                 :             :     else
    2282                 :           0 :         *symbol = gnc_commodity_get_nice_symbol (gnc_default_currency ());
    2283                 :             : 
    2284                 :           0 :     return gnc_filter_text_for_currency_symbol (incoming_text, *symbol);
    2285                 :             : }
    2286                 :             : 
    2287                 :             : gchar*
    2288                 :           0 : gnc_list_formatter (GList *strings)
    2289                 :             : {
    2290                 :           0 :     g_return_val_if_fail (strings, nullptr);
    2291                 :             : 
    2292                 :           0 :     UErrorCode status = U_ZERO_ERROR;
    2293                 :           0 :     auto formatter = icu::ListFormatter::createInstance(status);
    2294                 :           0 :     std::vector<UniStr> strvec;
    2295                 :           0 :     UniStr result;
    2296                 :           0 :     std::string retval;
    2297                 :             : 
    2298                 :           0 :     for (auto n = strings; n; n = g_list_next (n))
    2299                 :             :     {
    2300                 :           0 :         auto utf8_str{static_cast<const char*>(n->data)};
    2301                 :           0 :         strvec.push_back (UniStr::fromUTF8(utf8_str));
    2302                 :             :     }
    2303                 :             : 
    2304                 :           0 :     formatter->format (strvec.data(), strvec.size(), result, status);
    2305                 :             : 
    2306                 :           0 :     if (U_FAILURE(status))
    2307                 :           0 :         PERR ("Unicode error");
    2308                 :             :     else
    2309                 :           0 :         result.toUTF8String(retval);
    2310                 :             : 
    2311                 :           0 :     delete formatter;
    2312                 :           0 :     return g_strdup (retval.c_str());
    2313                 :           0 : }
        

Generated by: LCOV version 2.0-1