Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/timezone/TimeZoneIanaAbbrevMap.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Time Zone
4
 *
5
 * Copyright 2024 Armin Novak <anovak@thincast.com>
6
 * Copyright 2024 Thincast Technologies GmbH
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
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
32
typedef struct
33
{
34
  char* Iana;
35
  char* Abbrev;
36
} TimeZoneInanaAbbrevMapEntry;
37
38
const static char* zonepath = "/usr/share/zoneinfo";
39
40
static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = NULL;
41
static size_t TimeZoneIanaAbbrevMapSize = 0;
42
43
static void append(const char* iana, const char* sname)
44
0
{
45
0
  const size_t size = TimeZoneIanaAbbrevMapSize + 1;
46
47
0
  TimeZoneInanaAbbrevMapEntry* tmp =
48
0
      realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
49
0
  if (!tmp)
50
0
    return;
51
0
  TimeZoneIanaAbbrevMap = tmp;
52
0
  TimeZoneIanaAbbrevMapSize = size;
53
54
0
  TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
55
0
  cur->Abbrev = _strdup(sname);
56
0
  cur->Iana = _strdup(iana);
57
0
}
58
59
static void append_timezone(const char* dir, const char* name)
60
0
{
61
0
  char* tz = NULL;
62
0
  if (!dir && !name)
63
0
    return;
64
0
  if (!dir)
65
0
  {
66
0
    size_t len = 0;
67
0
    winpr_asprintf(&tz, &len, "%s", name);
68
0
  }
69
0
  else
70
0
  {
71
0
    size_t len = 0;
72
0
    winpr_asprintf(&tz, &len, "%s/%s", dir, name);
73
0
  }
74
0
  if (!tz)
75
0
    return;
76
77
0
  char* oldtz = setNewAndSaveOldTZ(tz);
78
79
0
  const time_t t = time(NULL);
80
0
  struct tm lt = { 0 };
81
0
  (void)localtime_r(&t, &lt);
82
0
  append(tz, lt.tm_zone);
83
0
  restoreSavedTZ(oldtz);
84
0
  free(tz);
85
0
}
86
87
static void handle_link(const char* base, const char* dir, const char* name);
88
89
static char* topath(const char* base, const char* bname, const char* name)
90
0
{
91
0
  size_t plen = 0;
92
0
  char* path = NULL;
93
94
0
  if (!base && !bname && !name)
95
0
    return NULL;
96
97
0
  if (!base && !name)
98
0
    return _strdup(bname);
99
100
0
  if (!bname && !name)
101
0
    return _strdup(base);
102
103
0
  if (!base && !bname)
104
0
    return _strdup(name);
105
106
0
  if (!base)
107
0
    winpr_asprintf(&path, &plen, "%s/%s", bname, name);
108
0
  else if (!bname)
109
0
    winpr_asprintf(&path, &plen, "%s/%s", base, name);
110
0
  else if (!name)
111
0
    winpr_asprintf(&path, &plen, "%s/%s", base, bname);
112
0
  else
113
0
    winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
114
0
  return path;
115
0
}
116
117
static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
118
0
{
119
0
  char* path = topath(base, bname, name);
120
0
  if (!path)
121
0
    return;
122
123
0
  DIR* d = opendir(path);
124
0
  if (d)
125
0
  {
126
0
    struct dirent* dp = NULL;
127
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
128
0
    while ((dp = readdir(d)) != NULL)
129
0
    {
130
0
      switch (dp->d_type)
131
0
      {
132
0
        case DT_DIR:
133
0
        {
134
0
          if (strcmp(dp->d_name, ".") == 0)
135
0
            continue;
136
0
          if (strcmp(dp->d_name, "..") == 0)
137
0
            continue;
138
0
          iterate_subdir_recursive(path, dp->d_name, NULL);
139
0
        }
140
0
        break;
141
0
        case DT_LNK:
142
0
          handle_link(base, bname, dp->d_name);
143
0
          break;
144
0
        case DT_REG:
145
0
          append_timezone(bname, dp->d_name);
146
0
          break;
147
0
        default:
148
0
          break;
149
0
      }
150
0
    }
151
0
    closedir(d);
152
0
  }
153
0
  free(path);
154
0
}
155
156
static char* get_link_target(const char* base, const char* dir, const char* name)
157
0
{
158
0
  char* apath = NULL;
159
0
  char* path = topath(base, dir, name);
160
0
  if (!path)
161
0
    return NULL;
162
163
0
  SSIZE_T rc = -1;
164
0
  size_t size = 0;
165
0
  char* target = NULL;
166
0
  do
167
0
  {
168
0
    size += 64;
169
0
    char* tmp = realloc(target, size + 1);
170
0
    if (!tmp)
171
0
      goto fail;
172
173
0
    target = tmp;
174
175
0
    memset(target, 0, size + 1);
176
0
    rc = readlink(path, target, size);
177
0
    if (rc < 0)
178
0
      goto fail;
179
0
  } while ((size_t)rc >= size);
180
181
0
  apath = topath(base, dir, target);
182
0
fail:
183
0
  free(target);
184
0
  free(path);
185
0
  return apath;
186
0
}
187
188
void handle_link(const char* base, const char* dir, const char* name)
189
0
{
190
0
  int isDir = -1;
191
192
0
  char* target = get_link_target(base, dir, name);
193
0
  if (target)
194
0
  {
195
0
    struct stat s = { 0 };
196
0
    const int rc3 = stat(target, &s);
197
0
    if (rc3 == 0)
198
0
      isDir = S_ISDIR(s.st_mode);
199
200
0
    free(target);
201
0
  }
202
203
0
  switch (isDir)
204
0
  {
205
0
    case 1:
206
0
      iterate_subdir_recursive(base, dir, name);
207
0
      break;
208
0
    case 0:
209
0
      append_timezone(dir, name);
210
0
      break;
211
0
    default:
212
0
      break;
213
0
  }
214
0
}
215
216
static void TimeZoneIanaAbbrevCleanup(void)
217
0
{
218
0
  if (!TimeZoneIanaAbbrevMap)
219
0
    return;
220
221
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
222
0
  {
223
0
    TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
224
0
    free(entry->Iana);
225
0
    free(entry->Abbrev);
226
0
  }
227
0
  free(TimeZoneIanaAbbrevMap);
228
0
  TimeZoneIanaAbbrevMap = NULL;
229
0
  TimeZoneIanaAbbrevMapSize = 0;
230
0
}
231
232
static void TimeZoneIanaAbbrevInitialize(void)
233
0
{
234
0
  static BOOL initialized = FALSE;
235
0
  if (initialized)
236
0
    return;
237
238
0
  iterate_subdir_recursive(zonepath, NULL, NULL);
239
0
  (void)atexit(TimeZoneIanaAbbrevCleanup);
240
0
  initialized = TRUE;
241
0
}
242
243
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
244
0
{
245
0
  TimeZoneIanaAbbrevInitialize();
246
247
0
  size_t rc = 0;
248
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
249
0
  {
250
0
    const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
251
0
    if (strcmp(abbrev, entry->Abbrev) == 0)
252
0
    {
253
0
      if (listsize > rc)
254
0
        list[rc] = entry->Iana;
255
0
      rc++;
256
0
    }
257
0
  }
258
259
0
  return rc;
260
0
}