]> granicus.if.org Git - postgresql/blob - src/port/win32setlocale.c
Centralize single quote escaping in src/port/quotes.c
[postgresql] / src / port / win32setlocale.c
1 /*-------------------------------------------------------------------------
2  *
3  * win32setlocale.c
4  *              Wrapper to work around bugs in Windows setlocale() implementation
5  *
6  * Copyright (c) 2011-2013, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        src/port/win32setlocale.c
10  *
11  *
12  * Windows has a problem with locale names that have a dot in the country
13  * name. For example:
14  *
15  * "Chinese (Traditional)_Hong Kong S.A.R..950"
16  *
17  * For some reason, setlocale() doesn't accept that. Fortunately, Windows'
18  * setlocale() accepts various alternative names for such countries, so we
19  * provide a wrapper setlocale() function that maps the troublemaking locale
20  * names to accepted aliases.
21  *-------------------------------------------------------------------------
22  */
23
24 #include "c.h"
25
26 #undef setlocale
27
28 struct locale_map
29 {
30         const char *locale_name_part;           /* string in locale name to replace */
31         const char *replacement;        /* string to replace it with */
32 };
33
34 static const struct locale_map locale_map_list[] = {
35         /*
36          * "HKG" is listed here:
37          * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx
38          * (Country/Region Strings).
39          *
40          * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the
41          * above list, but seems to work anyway.
42          */
43         {"Hong Kong S.A.R.", "HKG"},
44         {"U.A.E.", "ARE"},
45
46         /*
47          * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't
48          * seem to recognize that. And Macau isn't listed in the table of accepted
49          * abbreviations linked above. Fortunately, "ZHM" seems to be accepted as
50          * an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm not sure
51          * where "ZHM" comes from, must be some legacy naming scheme. But hey, it
52          * works.
53          *
54          * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale
55          * name, not just the country part.
56          *
57          * Some versions of Windows spell it "Macau", others "Macao".
58          */
59         {"Chinese (Traditional)_Macau S.A.R..950", "ZHM"},
60         {"Chinese_Macau S.A.R..950", "ZHM"},
61         {"Chinese (Traditional)_Macao S.A.R..950", "ZHM"},
62         {"Chinese_Macao S.A.R..950", "ZHM"}
63 };
64
65 char *
66 pgwin32_setlocale(int category, const char *locale)
67 {
68         char       *result;
69         char       *alias;
70         int                     i;
71
72         if (locale == NULL)
73                 return setlocale(category, locale);
74
75         /* Check if the locale name matches any of the problematic ones. */
76         alias = NULL;
77         for (i = 0; i < lengthof(locale_map_list); i++)
78         {
79                 const char *needle = locale_map_list[i].locale_name_part;
80                 const char *replacement = locale_map_list[i].replacement;
81                 char       *match;
82
83                 match = strstr(locale, needle);
84                 if (match != NULL)
85                 {
86                         /* Found a match. Replace the matched string. */
87                         int                     matchpos = match - locale;
88                         int                     replacementlen = strlen(replacement);
89                         char       *rest = match + strlen(needle);
90                         int                     restlen = strlen(rest);
91
92                         alias = malloc(matchpos + replacementlen + restlen + 1);
93                         if (!alias)
94                                 return NULL;
95
96                         memcpy(&alias[0], &locale[0], matchpos);
97                         memcpy(&alias[matchpos], replacement, replacementlen);
98                         memcpy(&alias[matchpos + replacementlen], rest, restlen + 1);           /* includes null
99                                                                                                                                                                  * terminator */
100
101                         break;
102                 }
103         }
104
105         /* Call the real setlocale() function */
106         if (alias)
107         {
108                 result = setlocale(category, alias);
109                 free(alias);
110         }
111         else
112                 result = setlocale(category, locale);
113
114         return result;
115 }