]> sourceware.org Git - newlib-cygwin.git/blob - winsup/utils/tzset.c
Cygwin: add 3.2.1 release file and add fixes up to this point
[newlib-cygwin.git] / winsup / utils / tzset.c
1 /* tzset.c: Convert current Windows timezone to POSIX timezone information.
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 #include <errno.h>
10 #include <stdio.h>
11 #include <inttypes.h>
12 #include <wchar.h>
13 #include <locale.h>
14 #include <getopt.h>
15 #include <cygwin/version.h>
16 #include <windows.h>
17
18 #ifndef GEOID_NOT_AVAILABLE
19 #define GEOID_NOT_AVAILABLE -1
20 #endif
21
22 /* The auto-generated tzmap.h contains the mapping table from Windows timezone
23 and country per ISO 3166-1 to POSIX timezone. For more info, see the file
24 itself. */
25 #include "tzmap.h"
26
27 #define TZMAP_SIZE (sizeof tzmap / sizeof tzmap[0])
28
29 static struct option longopts[] =
30 {
31 {"help", no_argument, NULL, 'h' },
32 {"version", no_argument, NULL, 'V'},
33 {NULL, 0, NULL, 0}
34 };
35
36 static char opts[] = "hV";
37
38 #define REG_TZINFO L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation"
39 #define REG_TZDB L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"
40
41 static inline HKEY
42 reg_open (HKEY pkey, PCWSTR path, const char *msg)
43 {
44 LONG ret;
45 HKEY hkey;
46
47 ret = RegOpenKeyExW (pkey, path, 0, KEY_READ, &hkey);
48 if (ret == ERROR_SUCCESS)
49 return hkey;
50 if (msg)
51 fprintf (stderr, "%s: cannot open registry %s, error code %" PRIu32 "\n",
52 program_invocation_short_name, msg, (unsigned int) ret);
53 return NULL;
54 }
55
56 /* For symmetry */
57 #define reg_close(hkey) RegCloseKey(hkey)
58
59 static inline BOOL
60 reg_query (HKEY hkey, PCWSTR value, PWCHAR buf, DWORD size, const char *msg)
61 {
62 LONG ret;
63 DWORD type;
64
65 ret = RegQueryValueExW (hkey, value, 0, &type, (LPBYTE) buf, &size);
66 if (ret == ERROR_SUCCESS)
67 return TRUE;
68 if (msg)
69 fprintf (stderr, "%s: cannot query registry %s, error code %" PRIu32 "\n",
70 program_invocation_short_name, msg, (unsigned int) ret);
71 return FALSE;
72 }
73
74 static inline BOOL
75 reg_enum (HKEY hkey, int idx, PWCHAR name, DWORD size)
76 {
77 return RegEnumKeyExW (hkey, idx, name, &size, NULL, NULL, NULL, NULL)
78 == ERROR_SUCCESS;
79 }
80
81 static void __attribute__ ((__noreturn__))
82 usage (FILE *stream)
83 {
84 fprintf (stream, ""
85 "Usage: %1$s [OPTION]\n"
86 "\n"
87 "Print POSIX-compatible timezone ID from current Windows timezone setting\n"
88 "\n"
89 "Options:\n"
90 " -h, --help output usage information and exit.\n"
91 " -V, --version output version information and exit.\n"
92 "\n"
93 "Use %1$s to set your TZ variable. In POSIX-compatible shells like bash,\n"
94 "dash, mksh, or zsh:\n"
95 "\n"
96 " export TZ=$(%1$s)\n"
97 "\n"
98 "In csh-compatible shells like tcsh:\n"
99 "\n"
100 " setenv TZ `%1$s`\n"
101 "\n", program_invocation_short_name);
102 exit (stream == stdout ? 0 : 1);
103 };
104
105 static void
106 print_version ()
107 {
108 printf ("tzset (cygwin) %d.%d.%d\n"
109 "POSIX-timezone generator\n"
110 "Copyright (C) 2012 - %s Cygwin Authors\n"
111 "This is free software; see the source for copying conditions. There is NO\n"
112 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
113 CYGWIN_VERSION_DLL_MAJOR / 1000,
114 CYGWIN_VERSION_DLL_MAJOR % 1000,
115 CYGWIN_VERSION_DLL_MINOR,
116 strrchr (__DATE__, ' ') + 1);
117 }
118
119 int
120 main (int argc, char **argv)
121 {
122 BOOL ret;
123 HKEY hkey, skey;
124 WCHAR keyname[256], stdname[256], std2name[256], country[10], *spc;
125 GEOID geo;
126 int opt, idx, gotit = -1;
127
128 setlocale (LC_ALL, "");
129 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
130 switch (opt)
131 {
132 case 'h':
133 usage (stdout);
134 case 'V':
135 print_version ();
136 return 0;
137 default:
138 fprintf (stderr, "Try `%s --help' for more information.\n",
139 program_invocation_short_name);
140 return 1;
141 }
142 if (optind < argc)
143 usage (stderr);
144
145 /* First fetch current timezone information from registry. */
146 hkey = reg_open (HKEY_LOCAL_MACHINE, REG_TZINFO, "timezone information");
147 if (!hkey)
148 return 1;
149 /* Vista introduced the TimeZoneKeyName value, which simplifies the
150 job a lot. */
151 if (!reg_query (hkey, L"TimeZoneKeyName", keyname, sizeof keyname, NULL))
152 {
153 /* Pre-Vista we have a lot more to do. First fetch the name of the
154 Standard (non-DST) timezone. If we can't get that, give up. */
155 if (!reg_query (hkey, L"StandardName", stdname, sizeof stdname,
156 "timezone information"))
157 {
158 reg_close (hkey);
159 return 1;
160 }
161 reg_close (hkey);
162 /* Now open the timezone database registry key. Every subkey is a
163 timezone. The key name is what we're after, but to find the right
164 one, we have to compare the name of the previously fetched
165 "StandardName" with the "Std" value in the timezone info... */
166 hkey = reg_open (HKEY_LOCAL_MACHINE, REG_TZDB, "timezone database");
167 if (!hkey)
168 return 1;
169 for (idx = 0; reg_enum (hkey, idx, keyname, sizeof keyname); ++idx)
170 {
171 skey = reg_open (hkey, keyname, NULL);
172 if (skey)
173 {
174 /* ...however, on MUI-enabled machines, the names are not stored
175 directly in the above StandardName, rather it is a resource
176 pointer into tzres.dll. This is stored in MUI_Std.
177 Fortunately it's easy to recognize this situation: If
178 StandardName starts with @, it's a resource pointer, otherwise
179 it's the cleartext value. */
180 ret = reg_query (skey, stdname[0] == L'@' ? L"MUI_Std" : L"Std",
181 std2name, sizeof std2name, NULL);
182 reg_close (skey);
183 if (ret && !wcscmp (stdname, std2name))
184 break;
185 }
186 }
187 }
188 reg_close (hkey);
189
190 /* We fetch the current Geo-location of the user and convert it to an
191 ISO 3166-1 compatible nation code. */
192 *country = L'\0';
193 geo = GetUserGeoID (GEOCLASS_NATION);
194 if (geo != GEOID_NOT_AVAILABLE)
195 GetGeoInfoW (geo, GEO_ISO2, country, sizeof country / sizeof (*country), 0);
196 /* If, for some reason, the Geo-location isn't available, we use the locale
197 setting instead. */
198 if (!*country)
199 GetLocaleInfoW (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME,
200 country, sizeof country);
201
202 /* Now iterate over the mapping table and find the right entry. */
203 for (idx = 0; idx < TZMAP_SIZE; ++idx)
204 {
205 if (!wcscasecmp (keyname, tzmap[idx].win_tzkey))
206 {
207 if (gotit < 0)
208 gotit = idx;
209 if (!wcscasecmp (country, tzmap[idx].country))
210 break;
211 }
212 else if (gotit >= 0)
213 {
214 idx = gotit;
215 break;
216 }
217 }
218 if (idx >= TZMAP_SIZE)
219 {
220 if (gotit < 0)
221 {
222 fprintf (stderr,
223 "%s: can't find matching POSIX timezone for "
224 "Windows timezone \"%ls\"\n",
225 program_invocation_short_name, keyname);
226 return 1;
227 }
228 idx = gotit;
229 }
230 /* Got one. Print it.
231 Note: The tzmap array is in the R/O data section on x86_64. Don't
232 try to overwrite the space, as the code did originally. */
233 spc = wcschr (tzmap[idx].posix_tzid, L' ');
234 if (!spc)
235 spc = wcschr (tzmap[idx].posix_tzid, L'\0');
236 printf ("%.*ls\n", (int) (spc - tzmap[idx].posix_tzid), tzmap[idx].posix_tzid);
237 return 0;
238 }
This page took 0.04712 seconds and 5 git commands to generate.