FreeRDP
Loading...
Searching...
No Matches
TimeZoneIanaAbbrevMap.c
1
21#include "TimeZoneIanaAbbrevMap.h"
22
23#include <stdlib.h>
24#include <dirent.h>
25#include <time.h>
26#include <sys/stat.h>
27#include <unistd.h>
28
29#include <winpr/atexit.h>
30#include <winpr/string.h>
31#include <winpr/synch.h>
32#include "timezone.h"
33
34typedef struct
35{
36 char* Iana;
37 char* Abbrev;
38} TimeZoneInanaAbbrevMapEntry;
39
40const static char* zonepath = "/usr/share/zoneinfo";
41
42static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = nullptr;
43static size_t TimeZoneIanaAbbrevMapSize = 0;
44
45static void append(const char* iana, const char* sname)
46{
47 const size_t size = TimeZoneIanaAbbrevMapSize + 1;
48
49 TimeZoneInanaAbbrevMapEntry* tmp =
50 realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
51 if (!tmp)
52 return;
53 TimeZoneIanaAbbrevMap = tmp;
54 TimeZoneIanaAbbrevMapSize = size;
55
56 TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
57 cur->Abbrev = _strdup(sname);
58 cur->Iana = _strdup(iana);
59}
60
61static void append_timezone(const char* dir, const char* name)
62{
63 char* tz = nullptr;
64 if (!dir && !name)
65 return;
66 if (!dir)
67 {
68 size_t len = 0;
69 winpr_asprintf(&tz, &len, "%s", name);
70 }
71 else
72 {
73 size_t len = 0;
74 winpr_asprintf(&tz, &len, "%s/%s", dir, name);
75 }
76 if (!tz)
77 return;
78
79 char* oldtz = setNewAndSaveOldTZ(tz);
80
81 const time_t t = time(nullptr);
82 struct tm lt = WINPR_C_ARRAY_INIT;
83 (void)localtime_r(&t, &lt);
84 append(tz, lt.tm_zone);
85 restoreSavedTZ(oldtz);
86 free(tz);
87}
88
89static void handle_link(const char* base, const char* dir, const char* name);
90
91static char* topath(const char* base, const char* bname, const char* name)
92{
93 size_t plen = 0;
94 char* path = nullptr;
95
96 if (!base && !bname && !name)
97 return nullptr;
98
99 if (!base && !name)
100 return _strdup(bname);
101
102 if (!bname && !name)
103 return _strdup(base);
104
105 if (!base && !bname)
106 return _strdup(name);
107
108 if (!base)
109 winpr_asprintf(&path, &plen, "%s/%s", bname, name);
110 else if (!bname)
111 winpr_asprintf(&path, &plen, "%s/%s", base, name);
112 else if (!name)
113 winpr_asprintf(&path, &plen, "%s/%s", base, bname);
114 else
115 winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
116 return path;
117}
118
119static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
120{
121 char* path = topath(base, bname, name);
122 if (!path)
123 return;
124
125 DIR* d = opendir(path);
126 if (d)
127 {
128 struct dirent* dp = nullptr;
129 // NOLINTNEXTLINE(concurrency-mt-unsafe)
130 while ((dp = readdir(d)) != nullptr)
131 {
132 switch (dp->d_type)
133 {
134 case DT_DIR:
135 {
136 if (strcmp(dp->d_name, ".") == 0)
137 continue;
138 if (strcmp(dp->d_name, "..") == 0)
139 continue;
140 iterate_subdir_recursive(path, dp->d_name, nullptr);
141 }
142 break;
143 case DT_LNK:
144 handle_link(base, bname, dp->d_name);
145 break;
146 case DT_REG:
147 append_timezone(bname, dp->d_name);
148 break;
149 default:
150 break;
151 }
152 }
153 closedir(d);
154 }
155 free(path);
156}
157
158static char* get_link_target(const char* base, const char* dir, const char* name)
159{
160 char* apath = nullptr;
161 char* path = topath(base, dir, name);
162 if (!path)
163 return nullptr;
164
165 SSIZE_T rc = -1;
166 size_t size = 0;
167 char* target = nullptr;
168 do
169 {
170 size += 64;
171 char* tmp = realloc(target, size + 1);
172 if (!tmp)
173 goto fail;
174
175 target = tmp;
176
177 memset(target, 0, size + 1);
178 rc = readlink(path, target, size);
179 if (rc < 0)
180 goto fail;
181 } while ((size_t)rc >= size);
182
183 apath = topath(base, dir, target);
184fail:
185 free(target);
186 free(path);
187 return apath;
188}
189
190void handle_link(const char* base, const char* dir, const char* name)
191{
192 int isDir = -1;
193
194 char* target = get_link_target(base, dir, name);
195 if (target)
196 {
197 struct stat s = WINPR_C_ARRAY_INIT;
198 const int rc3 = stat(target, &s);
199 if (rc3 == 0)
200 isDir = S_ISDIR(s.st_mode);
201
202 free(target);
203 }
204
205 switch (isDir)
206 {
207 case 1:
208 iterate_subdir_recursive(base, dir, name);
209 break;
210 case 0:
211 append_timezone(dir, name);
212 break;
213 default:
214 break;
215 }
216}
217
218static void TimeZoneIanaAbbrevCleanup(void)
219{
220 if (!TimeZoneIanaAbbrevMap)
221 return;
222
223 for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
224 {
225 TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
226 free(entry->Iana);
227 free(entry->Abbrev);
228 }
229 free(TimeZoneIanaAbbrevMap);
230 TimeZoneIanaAbbrevMap = nullptr;
231 TimeZoneIanaAbbrevMapSize = 0;
232}
233
234static BOOL CALLBACK TimeZoneIanaAbbrevInitialize(WINPR_ATTR_UNUSED PINIT_ONCE once,
235 WINPR_ATTR_UNUSED PVOID param,
236 WINPR_ATTR_UNUSED PVOID* context)
237{
238 iterate_subdir_recursive(zonepath, nullptr, nullptr);
239 (void)winpr_atexit(TimeZoneIanaAbbrevCleanup);
240
241 return TRUE;
242}
243
244size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
245{
246 static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
247
248 if (!InitOnceExecuteOnce(&init_guard, TimeZoneIanaAbbrevInitialize, nullptr, nullptr))
249 return 0;
250
251 size_t rc = 0;
252 for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
253 {
254 const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
255 if (strcmp(abbrev, entry->Abbrev) == 0)
256 {
257 if (listsize > rc)
258 list[rc] = entry->Iana;
259 rc++;
260 }
261 }
262
263 return rc;
264}