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