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