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