we always only use the "C" locale when saving.
[ardour.git] / libs / pbd / locale_guard.cc
index aa5a57552188b3a38f74b53367cee3d4a6eb5512..8007ea001b60c7fcd9b7d24f07ad559146d6b400 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Paul Davis 
+    Copyright (C) 2012 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 #include <locale.h>
 
 #include "pbd/locale_guard.h"
+#include "pbd/error.h"
 
 using namespace PBD;
 
-// try to avoid calling setlocale() recursively.  this is not thread-safe.
-std::string PBD::LocaleGuard::current;
+/* The initial C++ locale is "C" regardless of the user's preferred locale.
+ * The C locale from setlocale() matches the user's preferred locale
+ *
+ * Setting the C++ locale will change the C locale, but not the other way 'round.
+ * and some plugin may change either behind our back.
+ */
 
-LocaleGuard::LocaleGuard (const char* str)
-       : old(0)
+LocaleGuard::LocaleGuard ()
+       : old_c (0)
 {
-       if (current != str) {
-               old = strdup (setlocale (LC_NUMERIC, NULL));
-               if (strcmp (old, str)) {
-                       if (setlocale (LC_NUMERIC, str)) {
-                               current = str;
-                       }
-               }
+       char* actual = setlocale (LC_NUMERIC, NULL);
+       if (strcmp ("C", actual)) {
+               /* purpose of LocaleGuard is to make sure we're using "C" for
+                  the numeric locale during its lifetime, so make it so.
+               */
+               old_c = strdup (actual);
+               /* this changes both C++ and C locale */
+               std::locale::global (std::locale (std::locale::classic(), "C", std::locale::numeric));
+       }
+       if (old_cpp != std::locale::classic ()) {
+               PBD::error << "LocaleGuard: initial C++ locale is not 'C'. Expect non-portable session files.\n";
        }
 }
 
 LocaleGuard::~LocaleGuard ()
 {
-       if (old) {
-               if (setlocale (LC_NUMERIC, old)) {
-                       current = old;
-               }
+       char* actual = setlocale (LC_NUMERIC, NULL);
+       std::locale current;
 
-               free (old);
+       if (current != old_cpp) {
+               /* the C++ locale should always be "C", that's the default
+                * at application start, and ardour never changes it to
+                * anything but "C".
+                *
+                * if it's not: some plugin meddled with it.
+                */
+               if (old_cpp != std::locale::classic ()) {
+                       PBD::error << "LocaleGuard: someone (a plugin) changed the C++ locale, expect non-portable session files.\n";
+               }
+               std::locale::global (old_cpp);
+       }
+       if (old_c && strcmp (old_c, actual)) {
+               setlocale (LC_NUMERIC, old_c);
        }
+       free (old_c);
 }
-