/src/libiec61850/src/common/conversions.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * conversions.c |
3 | | * |
4 | | * Copyright 2013 Michael Zillgith |
5 | | * |
6 | | * This file is part of libIEC61850. |
7 | | * |
8 | | * libIEC61850 is free software: you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation, either version 3 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * libIEC61850 is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with libIEC61850. If not, see <http://www.gnu.org/licenses/>. |
20 | | * |
21 | | * See COPYING file for the complete license text. |
22 | | */ |
23 | | |
24 | | #include "libiec61850_platform_includes.h" |
25 | | #include "conversions.h" |
26 | | |
27 | | #include <time.h> |
28 | | |
29 | | #if defined TARGET |
30 | | #if (TARGET == UCLINUX-WAGO) |
31 | | time_t timegm (struct tm *tm) |
32 | | { |
33 | | time_t ret; |
34 | | char *tz; |
35 | | |
36 | | tz = getenv ("TZ"); |
37 | | setenv ("TZ", "", 1); |
38 | | tzset (); |
39 | | ret = mktime (tm); |
40 | | if (tz) |
41 | | setenv ("TZ", tz, 1); |
42 | | else |
43 | | unsetenv ("TZ"); |
44 | | tzset (); |
45 | | return ret; |
46 | | } |
47 | | |
48 | | #endif |
49 | | #endif |
50 | | |
51 | | #ifdef _WIN32 |
52 | | |
53 | | #ifndef _MSC_VER |
54 | | /* Algorithm: http://howardhinnant.github.io/date_algorithms.html */ |
55 | | static int |
56 | | days_from_civil(int y, int m, int d) |
57 | | { |
58 | | y -= m <= 2; |
59 | | int era = y / 400; |
60 | | int yoe = y - era * 400; /* [0, 399] */ |
61 | | int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; /* [0, 365] */ |
62 | | int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; /* [0, 146096] */ |
63 | | return era * 146097 + doe - 719468; |
64 | | } |
65 | | |
66 | | /* from https://stackoverflow.com/questions/16647819/timegm-cross-platform */ |
67 | | time_t |
68 | | timegm(struct tm const* t) /* does not modify broken-down time */ |
69 | | { |
70 | | int year = t->tm_year + 1900; |
71 | | int month = t->tm_mon; /* 0-11 */ |
72 | | if (month > 11) |
73 | | { |
74 | | year += month / 12; |
75 | | month %= 12; |
76 | | } |
77 | | else if (month < 0) |
78 | | { |
79 | | int years_diff = (11 - month) / 12; |
80 | | year -= years_diff; |
81 | | month += 12 * years_diff; |
82 | | } |
83 | | int days_since_1970 = days_from_civil(year, month + 1, t->tm_mday); |
84 | | |
85 | | return 60 * (60 * (24L * days_since_1970 + t->tm_hour) + t->tm_min) + t->tm_sec; |
86 | | } |
87 | | #else |
88 | | #define timegm _mkgmtime |
89 | | #endif |
90 | | |
91 | | #if defined(__MINGW32__) |
92 | | |
93 | | static inline /* assuming gmtime is thread safe in windows! */ |
94 | | struct tm* gmtime_r(const time_t* timep, struct tm* result) |
95 | | { |
96 | | struct tm* t; |
97 | | |
98 | | t = gmtime(timep); |
99 | | |
100 | | if (t != NULL) |
101 | | memcpy(result, t, sizeof (struct tm)); |
102 | | |
103 | | return result; |
104 | | } |
105 | | |
106 | | #else |
107 | | |
108 | | #if defined(_MSC_VER) |
109 | | |
110 | | static inline |
111 | | struct tm* gmtime_r(const time_t* timep, struct tm* result) |
112 | | { |
113 | | gmtime_s(result, timep); |
114 | | |
115 | | return result; |
116 | | } |
117 | | |
118 | | #else |
119 | | #error "No gmtime_r available for platform!" |
120 | | #endif |
121 | | |
122 | | |
123 | | #endif |
124 | | |
125 | | |
126 | | #endif |
127 | | |
128 | | void |
129 | | Conversions_intToStringBuffer(int intValue, int numberOfDigits, uint8_t* buffer) |
130 | 0 | { |
131 | 0 | int digitBase = 1; |
132 | |
|
133 | 0 | int i = 1; |
134 | |
|
135 | 0 | while (i < numberOfDigits) { |
136 | 0 | digitBase = digitBase * 10; |
137 | 0 | i++; |
138 | 0 | } |
139 | |
|
140 | 0 | int remainder = intValue; |
141 | |
|
142 | 0 | for (i = 0; i < numberOfDigits; i++) { |
143 | 0 | int digit = remainder / digitBase; |
144 | |
|
145 | 0 | buffer[i] = (uint8_t) (digit + 48); |
146 | |
|
147 | 0 | remainder = remainder % digitBase; |
148 | |
|
149 | 0 | digitBase = digitBase / 10; |
150 | 0 | } |
151 | |
|
152 | 0 | buffer[i] = 0; |
153 | 0 | } |
154 | | |
155 | | void |
156 | | Conversions_msTimeToGeneralizedTime(uint64_t msTime, uint8_t* buffer) |
157 | 0 | { |
158 | 0 | int msPart = (msTime % 1000); |
159 | |
|
160 | 0 | time_t unixTime = (msTime / 1000); |
161 | |
|
162 | 0 | struct tm tmTime; |
163 | |
|
164 | 0 | gmtime_r(&unixTime, &tmTime); |
165 | |
|
166 | 0 | Conversions_intToStringBuffer(tmTime.tm_year + 1900, 4, buffer); |
167 | |
|
168 | 0 | Conversions_intToStringBuffer(tmTime.tm_mon + 1, 2, buffer + 4); |
169 | 0 | Conversions_intToStringBuffer(tmTime.tm_mday, 2, buffer + 6); |
170 | 0 | Conversions_intToStringBuffer(tmTime.tm_hour, 2, buffer + 8); |
171 | 0 | Conversions_intToStringBuffer(tmTime.tm_min, 2, buffer + 10); |
172 | 0 | Conversions_intToStringBuffer(tmTime.tm_sec, 2, buffer + 12); |
173 | |
|
174 | 0 | buffer[14] = '.'; |
175 | |
|
176 | 0 | Conversions_intToStringBuffer(msPart, 3, buffer + 15); |
177 | |
|
178 | 0 | buffer[18] = 'Z'; |
179 | |
|
180 | 0 | buffer[19] = 0; |
181 | 0 | } |
182 | | |
183 | | static int |
184 | | getSecondsOffset(const char* offsetString) |
185 | 0 | { |
186 | 0 | int hourOffset = StringUtils_digitsToInt(offsetString, 2); |
187 | |
|
188 | 0 | if (hourOffset < 0) |
189 | 0 | return -1; |
190 | | |
191 | 0 | int minOffset = StringUtils_digitsToInt(offsetString + 2, 2); |
192 | |
|
193 | 0 | if (minOffset < 0) |
194 | 0 | return -1; |
195 | | |
196 | 0 | int secondsOffset = (hourOffset * (60 * 60)) + (minOffset * 60); |
197 | |
|
198 | 0 | return secondsOffset; |
199 | 0 | } |
200 | | |
201 | | uint64_t |
202 | | Conversions_generalizedTimeToMsTime(const char* gtString) |
203 | 0 | { |
204 | 0 | int gtStringLen = (int) strlen(gtString); |
205 | |
|
206 | 0 | if (gtStringLen < 14) return -1; |
207 | | |
208 | 0 | int year = StringUtils_digitsToInt(gtString, 4); |
209 | |
|
210 | 0 | if (year < 0) return -1; |
211 | | |
212 | 0 | int month = StringUtils_digitsToInt(gtString + 4, 2); |
213 | |
|
214 | 0 | if (month < 0) return -1; |
215 | | |
216 | 0 | int day = StringUtils_digitsToInt(gtString + 6, 2); |
217 | |
|
218 | 0 | if (day < 0) return -1; |
219 | | |
220 | 0 | int hour = StringUtils_digitsToInt(gtString + 8, 2); |
221 | |
|
222 | 0 | if (hour < 0) return -1; |
223 | | |
224 | 0 | int min = StringUtils_digitsToInt(gtString + 10, 2); |
225 | |
|
226 | 0 | if (min < 0) return -1; |
227 | | |
228 | 0 | int seconds = StringUtils_digitsToInt(gtString + 12, 2); |
229 | 0 | if (seconds < 0) return -1; |
230 | | |
231 | 0 | struct tm tmTime; |
232 | 0 | tmTime.tm_year = year - 1900; |
233 | 0 | tmTime.tm_mon = month - 1; |
234 | 0 | tmTime.tm_mday = day; |
235 | 0 | tmTime.tm_hour = hour; |
236 | 0 | tmTime.tm_min = min; |
237 | 0 | tmTime.tm_sec = seconds; |
238 | |
|
239 | 0 | int msOffset = 0; |
240 | |
|
241 | 0 | const char* parsePos = gtString + 14; |
242 | | |
243 | | /* parse optional fraction of second field */ |
244 | 0 | if (*(parsePos) == '.') { |
245 | 0 | parsePos++; |
246 | 0 | const char* fractionOfSecondStart = parsePos; |
247 | |
|
248 | 0 | int fractionOfSecondLen = 0; |
249 | |
|
250 | 0 | int secondValue = 1; |
251 | |
|
252 | 0 | while (StringUtils_isDigit(fractionOfSecondStart[fractionOfSecondLen])) { |
253 | 0 | fractionOfSecondLen++; |
254 | |
|
255 | 0 | secondValue = secondValue * 10; |
256 | 0 | } |
257 | |
|
258 | 0 | if (fractionOfSecondLen > 0) { |
259 | 0 | int fractionOfSecond = StringUtils_digitsToInt(fractionOfSecondStart, fractionOfSecondLen); |
260 | 0 | msOffset = (fractionOfSecond * 1000) / secondValue; |
261 | 0 | } |
262 | |
|
263 | 0 | parsePos += fractionOfSecondLen; |
264 | 0 | } |
265 | | |
266 | |
|
267 | 0 | time_t t = 0; |
268 | |
|
269 | 0 | switch (*parsePos) { |
270 | 0 | case 0: /* treat time as localtime */ |
271 | 0 | t = mktime(&tmTime); |
272 | 0 | break; |
273 | 0 | case 'Z': /* treat time as GMT(UTC) time */ |
274 | 0 | t = timegm(&tmTime); |
275 | 0 | break; |
276 | 0 | case '+': /* subtract offset */ |
277 | 0 | { |
278 | 0 | t = timegm(&tmTime); |
279 | 0 | int secondsOffset = getSecondsOffset(parsePos + 1); |
280 | 0 | t = t - secondsOffset; |
281 | 0 | } |
282 | 0 | break; |
283 | 0 | case '-': /* add offset */ |
284 | 0 | { |
285 | 0 | t = timegm(&tmTime); |
286 | 0 | int secondsOffset = getSecondsOffset(parsePos + 1); |
287 | 0 | t = t + secondsOffset; |
288 | 0 | } |
289 | 0 | break; |
290 | 0 | default: |
291 | 0 | return -1; |
292 | 0 | } |
293 | | |
294 | 0 | uint64_t msTime = (uint64_t) t * 1000LL; |
295 | |
|
296 | 0 | msTime += msOffset; |
297 | |
|
298 | 0 | return msTime; |
299 | 0 | } |
300 | | |
301 | | void |
302 | | memcpyReverseByteOrder(uint8_t* dst, const uint8_t* src, int size) |
303 | 0 | { |
304 | 0 | int i = 0; |
305 | 0 | for (i = 0; i < size; i++) { |
306 | 0 | dst[i] = src[size - i - 1]; |
307 | 0 | } |
308 | 0 | } |