CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
builtin.c
1/*
2 * Copyright (C) 2014-2026 Jean-Luc Barriere
3 *
4 * This Program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * This Program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "builtin.h"
23
24#include <ctype.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <errno.h>
28
29int string_to_int64(const char *str, int64_t *num)
30{
31 uint64_t val = 0;
32 int sign = 1;
33 uint64_t limit = INT64_MAX;
34
35 if (str == NULL)
36 return -(EINVAL);
37 while (isspace(*str))
38 {
39 ++str;
40 }
41
42 if (*str && (*str == '-'))
43 {
44 ++str;
45 sign = -1;
46 }
47 while (*str && !isspace(*str))
48 {
49 if (!isdigit(*str))
50 return -(EINVAL);
51 val *= 10;
52 val += ((*str) - '0');
53 /*
54 * Check and make sure we are still under the limit (this is
55 * an absolute value limit, sign will be applied later).
56 */
57 if (val > limit)
58 return -(ERANGE);
59 str++;
60 }
61
62 *num = (int64_t) (sign * val);
63 return 0;
64}
65
66int string_to_int32(const char *str, int32_t *num)
67{
68 uint64_t val = 0;
69 int sign = 1;
70 uint64_t limit = INT32_MAX;
71
72 if (str == NULL)
73 return -(EINVAL);
74 while (isspace(*str))
75 {
76 ++str;
77 }
78
79 if (*str && (*str == '-'))
80 {
81 ++str;
82 sign = -1;
83 }
84 while (*str && !isspace(*str))
85 {
86 if (!isdigit(*str))
87 return -(EINVAL);
88 val *= 10;
89 val += ((*str) - '0');
90 /*
91 * Check and make sure we are still under the limit (this is
92 * an absolute value limit, sign will be applied later).
93 */
94 if (val > limit)
95 return -(ERANGE);
96 str++;
97 }
98
99 *num = (int32_t) (sign * val);
100 return 0;
101}
102
103int string_to_int16(const char *str, int16_t *num)
104{
105 int32_t val;
106 int err;
107
108 if ((err = string_to_int32(str, &val)))
109 return err;
110 if (val > 32767 || val < -32768)
111 return -(ERANGE);
112
113 *num = (int16_t) val;
114 return 0;
115}
116
117int string_to_int8(const char *str, int8_t *num)
118{
119 int32_t val;
120 int err;
121
122 if ((err = string_to_int32(str, &val)))
123 return err;
124 if (val > 127 || val < -128)
125 return -(ERANGE);
126
127 *num = (int8_t) val;
128 return 0;
129}
130
131int string_to_uint32(const char *str, uint32_t *num)
132{
133 uint64_t val = 0;
134 uint64_t limit = UINT32_MAX;
135
136 if (str == NULL)
137 return -(EINVAL);
138 while (isspace(*str))
139 {
140 ++str;
141 }
142
143 while (*str && !isspace(*str))
144 {
145 if (!isdigit(*str))
146 return -(EINVAL);
147 val *= 10;
148 val += ((*str) - '0');
149 /*
150 * Check and make sure we are still under the limit (this is
151 * an absolute value limit, sign will be applied later).
152 */
153 if (val > limit)
154 return -(ERANGE);
155 str++;
156 }
157
158 *num = (uint32_t) val;
159 return 0;
160}
161
162int string_to_uint16(const char *str, uint16_t *num)
163{
164 uint32_t val;
165 int err;
166
167 if ((err = string_to_uint32(str, &val)))
168 return err;
169 if (val > 65535)
170 return -(ERANGE);
171
172 *num = (uint16_t) val;
173 return 0;
174}
175
176int string_to_uint8(const char *str, uint8_t *num)
177{
178 uint32_t val;
179 int err;
180
181 if ((err = string_to_uint32(str, &val)))
182 return err;
183 if (val > 255)
184 return -(ERANGE);
185
186 *num = (uint8_t) val;
187 return 0;
188}
189
190int string_to_double(const char *str, double *dbl)
191{
192 char* pe;
193 *dbl = strtod(str, &pe);
194 if (pe == str)
195 return -(EINVAL);
196 return 0;
197}
198
199int string_to_timeout(const char *str, unsigned *ms)
200{
201 float q = 0.0;
202 char u[2] = { 0, 0 };
203 int n = sscanf(str, "%f%c%c", &q, u, u+1);
204 if (n < 2 || q < 0)
205 return -(EINVAL);
206 if (memcmp("ms", u, 2) == 0)
207 *ms = (unsigned)q;
208 else if (memcmp("s", u, 2) == 0)
209 *ms = (unsigned)(q * 1000);
210 else if (memcmp("m", u, 2) == 0)
211 *ms = (unsigned)(q * 60000);
212 else if (memcmp("h", u, 2) == 0)
213 *ms = (unsigned)(q * 3600000);
214 else if (memcmp("d", u, 2) == 0)
215 *ms = (unsigned)(q * 86400000);
216 else
217 return -(EINVAL);
218 return 0;
219}
220
221int string_to_size(const char *str, int64_t *sz)
222{
223 float q = 0.0;
224 char u[2] = { 0, 0 };
225 int n = sscanf(str, "%f%c%c", &q, u, u+1);
226 if (n < 1 || q < 0)
227 return -(EINVAL);
228 if (*u == 0)
229 *sz = (int64_t)q;
230 else if (memcmp("k", u, 2) == 0)
231 *sz = (int64_t)(q * (1<<10));
232 else if (memcmp("m", u, 2) == 0)
233 *sz = (int64_t)(q * (1<<20));
234 else if (memcmp("g", u, 2) == 0)
235 *sz = (int64_t)(q * (1<<30));
236 else
237 return -(EINVAL);
238 return 0;
239}
240
241int hex_to_num(const char *str, int *num)
242{
243 int val = 0;
244 while (*str)
245 {
246 if (*str >= '0' && *str <= '9')
247 val = (val << 4) + (*str - '0');
248 else if (*str >= 'A' && *str <= 'F')
249 val = (val << 4) + (*str - 'A' + 10);
250 else if (*str >= 'a' && *str <= 'f')
251 val = (val << 4) + (*str - 'a' + 10);
252 else
253 return -(EINVAL);
254 ++str;
255 }
256 *num = val;
257 return 0;
258}
259
260void char_to_hex(char c, BUILTIN_BUFFER *str)
261{
262 static const char g[16] = {
263 '0', '1', '2', '3', '4', '5', '6', '7',
264 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
265 };
266 char * p = str->data;
267 p[0] = g[(0xf & (c >> 4))];
268 p[1] = g[(0xf & (c))];
269 p[2] = '\0';
270}
271
272void char_to_uhex(char c, BUILTIN_BUFFER *str)
273{
274 static const char g[16] = {
275 '0', '1', '2', '3', '4', '5', '6', '7',
276 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
277 };
278 char * p = str->data;
279 p[0] = g[(0xf & (c >> 4))];
280 p[1] = g[(0xf & (c))];
281 p[2] = '\0';
282}
283
284unsigned uint_to_strdec(unsigned u, char *str, unsigned len, int pad)
285{
286 static const char g[10] = {
287 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
288 };
289 if (len)
290 {
291 char *ptr = str, *end = str + len;
292 unsigned n = u, n10 = u / 10;
293 do
294 {
295 *ptr++ = g[n - n10 * 10];
296 n = n10;
297 } while ((n10 = n / 10) > 0 && ptr < end);
298 if (ptr < end)
299 {
300 /* push last digit */
301 if (n > 0)
302 *ptr++ = g[n];
303 /* padding */
304 if (pad)
305 while (ptr < end) *ptr++ = '0';
306 }
307 len = ptr - str;
308 /* reorder digits */
309 while (--ptr > str)
310 {
311 char c = *str;
312 *str = *ptr;
313 *ptr = c;
314 ++str;
315 }
316 }
317 return len;
318}
319
320unsigned uint64_to_strdec(uint64_t ul, char *str, unsigned len, int pad)
321{
322 static const char g[10] = {
323 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
324 };
325 if (len)
326 {
327 char *ptr = str, *end = str + len;
328 uint64_t n = ul, n10 = ul / 10;
329 do
330 {
331 *ptr++ = g[n - n10 * 10];
332 n = n10;
333 } while ((n10 = n / 10) > 0 && ptr < end);
334 if (ptr < end)
335 {
336 /* push last digit */
337 if (n > 0)
338 *ptr++ = g[n];
339 /* padding */
340 if (pad)
341 while (ptr < end) *ptr++ = '0';
342 }
343 len = ptr - str;
344 /* reorder digits */
345 while (--ptr > str)
346 {
347 char c = *str;
348 *str = *ptr;
349 *ptr = c;
350 ++str;
351 }
352 }
353 return len;
354}
355
356time_t __timegm(struct tm *utctime_tm)
357{
358 time_t time;
359 struct tm adj_tm, chk_tm;
360
361 adj_tm.tm_year = utctime_tm->tm_year;
362 adj_tm.tm_mon = utctime_tm->tm_mon;
363 adj_tm.tm_mday = utctime_tm->tm_mday;
364 adj_tm.tm_hour = utctime_tm->tm_hour;
365 adj_tm.tm_min = utctime_tm->tm_min;
366 adj_tm.tm_sec = utctime_tm->tm_sec;
367 adj_tm.tm_isdst = -1;
368
369 for (;;)
370 {
371 time = mktime(&adj_tm);
372 if (time == INVALID_TIME)
373 return time;
374 if (NULL == gmtime_r(&time, &chk_tm))
375 return INVALID_TIME;
376 if (chk_tm.tm_min == utctime_tm->tm_min &&
377 chk_tm.tm_hour == utctime_tm->tm_hour &&
378 chk_tm.tm_mday == utctime_tm->tm_mday &&
379 chk_tm.tm_mon == utctime_tm->tm_mon &&
380 chk_tm.tm_year == utctime_tm->tm_year)
381 break;
382 adj_tm.tm_min += utctime_tm->tm_min - chk_tm.tm_min;
383 adj_tm.tm_hour += utctime_tm->tm_hour - chk_tm.tm_hour;
384 adj_tm.tm_mday += utctime_tm->tm_mday - chk_tm.tm_mday;
385 adj_tm.tm_mon += utctime_tm->tm_mon - chk_tm.tm_mon;
386 adj_tm.tm_year += utctime_tm->tm_year - chk_tm.tm_year;
387 }
388 return time;
389}
390
391int string_to_time(const char *str, time_t *time)
392{
393 struct tm time_tm;
394 int i, len, format, isutc;
395 char *yyyy, *MM, *dd, *hh, *mm, *ss;
396 char buf[TIMESTAMP_UTC_LEN + 1];
397
398 if (*str == '\0')
399 {
400 /* empty string */
401 *time = INVALID_TIME;
402 return 0;
403 }
404 memset(buf, 0, sizeof(buf));
405 strncpy(buf, str, sizeof(buf) - 1);
406 len = strlen(buf);
407
408 switch (len)
409 {
410 case TIMESTAMP_UTC_LEN:
411 if (((buf[4] != '-') || (buf[7] != '-') || (buf[10] != 'T') ||
412 (buf[13] != ':') || (buf[16] != ':') || (buf[19] != 'Z')))
413 {
414 fprintf(stderr, "%s: string is badly formed '%s'\n", __func__, buf);
415 goto err;
416 }
417 format = 1;
418 isutc = 1;
419 break;
420 case TIMESTAMP_LEN:
421 if (((buf[4] != '-') || (buf[7] != '-') || (buf[10] != 'T') ||
422 (buf[13] != ':') || (buf[16] != ':')))
423 {
424 fprintf(stderr, "%s: string is badly formed '%s'\n", __func__, buf);
425 goto err;
426 }
427 format = 2;
428 isutc = 0;
429 break;
430 case DATESTAMP_LEN:
431 if (((buf[4] != '-') || (buf[7] != '-')))
432 {
433 fprintf(stderr, "%s: string is badly formed '%s'\n", __func__, buf);
434 goto err;
435 }
436 format = 3;
437 isutc = 0;
438 break;
439 default:
440 fprintf(stderr, "%s: string is not a timestamp '%s'\n", __func__, buf);
441 goto err;
442 break;
443 }
444
445 yyyy = buf;
446 MM = buf + 5;
447 dd = buf + 8;
448 hh = buf + 11;
449 mm = buf + 14;
450 ss = buf + 17;
451
452 yyyy[4] = MM[2] = dd[2] = hh[2] = mm[2] = ss[2] = '\0';
453
454 for (i = 0; i < len; ++i)
455 {
456 if (buf[i] && !isdigit(buf[i]))
457 {
458 fprintf(stderr, "%s: expected numeral at '%s'[%d]\n", __func__, str, i);
459 goto err;
460 }
461 }
462
463 time_tm.tm_isdst = -1;
464 time_tm.tm_year = atoi(yyyy) - 1900;
465 time_tm.tm_mon = atoi(MM) - 1;
466 if (time_tm.tm_mon > 11)
467 {
468 fprintf(stderr, "%s: month value too big '%s'\n", __func__, str);
469 goto err;
470 }
471 time_tm.tm_mday = atoi(dd);
472 if (time_tm.tm_mday > 31)
473 {
474 fprintf(stderr, "%s: day value too big '%s'\n", __func__, str);
475 goto err;
476
477 }
478
479 if (format == 3)
480 {
481 time_tm.tm_hour = time_tm.tm_min = time_tm.tm_sec = 0;
482 *time = mktime(&time_tm);
483 return 0;
484 }
485
486 time_tm.tm_hour = atoi(hh);
487 if (time_tm.tm_hour > 23)
488 {
489 fprintf(stderr, "%s: hour value too big '%s'\n", __func__, str);
490 goto err;
491 }
492 time_tm.tm_min = atoi(mm);
493 if (time_tm.tm_min > 59)
494 {
495 fprintf(stderr, "%s: minute value too big '%s'\n", __func__, str);
496 goto err;
497 }
498 time_tm.tm_sec = atoi(ss);
499 if (time_tm.tm_sec > 59)
500 {
501 fprintf(stderr, "%s: second value too big '%s'\n", __func__, str);
502 goto err;
503 }
504
505 if (isutc)
506 *time = timegm(&time_tm);
507 else
508 *time = mktime(&time_tm);
509 return 0;
510
511err:
512 *time = INVALID_TIME;
513 return -(EINVAL);
514}
515
516void time_to_iso8601utc(time_t time, BUILTIN_BUFFER *str)
517{
518 struct tm time_tm;
519
520 if (time == INVALID_TIME || NULL == gmtime_r(&time, &time_tm))
521 {
522 str->data[0] = '\0';
523 return;
524 }
525 /* yyyy-MM-ddTHH:mm:ssZ */
526 uint_to_strdec(time_tm.tm_year + 1900, str->data, 4, 1);
527 str->data[4] = '-';
528 uint_to_strdec(time_tm.tm_mon + 1, str->data + 5, 2, 1);
529 str->data[7] = '-';
530 uint_to_strdec(time_tm.tm_mday, str->data + 8, 2, 1);
531 str->data[10] = 'T';
532 uint_to_strdec(time_tm.tm_hour, str->data + 11, 2, 1);
533 str->data[13] = ':';
534 uint_to_strdec(time_tm.tm_min, str->data + 14, 2, 1);
535 str->data[16] = ':';
536 uint_to_strdec(time_tm.tm_sec, str->data + 17, 2, 1);
537 str->data[19] = 'Z';
538 str->data[20] = '\0';
539}
540
541void time_to_iso8601(time_t time, BUILTIN_BUFFER *str)
542{
543 struct tm time_tm;
544
545 if (time == INVALID_TIME || NULL == localtime_r(&time, &time_tm))
546 {
547 str->data[0] = '\0';
548 return;
549 }
550 /* yyyy-MM-ddTHH:mm:ss */
551 uint_to_strdec(time_tm.tm_year + 1900, str->data, 4, 1);
552 str->data[4] = '-';
553 uint_to_strdec(time_tm.tm_mon + 1, str->data + 5, 2, 1);
554 str->data[7] = '-';
555 uint_to_strdec(time_tm.tm_mday, str->data + 8, 2, 1);
556 str->data[10] = 'T';
557 uint_to_strdec(time_tm.tm_hour, str->data + 11, 2, 1);
558 str->data[13] = ':';
559 uint_to_strdec(time_tm.tm_min, str->data + 14, 2, 1);
560 str->data[16] = ':';
561 uint_to_strdec(time_tm.tm_sec, str->data + 17, 2, 1);
562 str->data[19] = '\0';
563}
564
565void time_to_isodate(time_t time, BUILTIN_BUFFER *str)
566{
567 struct tm time_tm;
568
569 if (time == INVALID_TIME || NULL == localtime_r(&time, &time_tm))
570 {
571 str->data[0] = '\0';
572 return;
573 }
574 uint_to_strdec(time_tm.tm_year + 1900, str->data, 4, 1);
575 str->data[4] = '-';
576 uint_to_strdec(time_tm.tm_mon + 1, str->data + 5, 2, 1);
577 str->data[7] = '-';
578 uint_to_strdec(time_tm.tm_mday, str->data + 8, 2, 1);
579 str->data[10] = '\0';
580}
581
582void time_to_httptime(time_t time, BUILTIN_BUFFER *str)
583{
584 static const char* dw[] = {
585 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
586 };
587 static const char* my[] = {
588 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
589 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
590 };
591
592 struct tm time_tm;
593
594 if (time == INVALID_TIME || NULL == gmtime_r(&time, &time_tm))
595 {
596 str->data[0] = '\0';
597 return;
598 }
599 sprintf(str->data, "%s, %2.2d %s %4.4d %2.2d:%2.2d:%2.2d GMT",
600 dw[time_tm.tm_wday], time_tm.tm_mday, my[time_tm.tm_mon],
601 time_tm.tm_year + 1900, time_tm.tm_hour, time_tm.tm_min,
602 time_tm.tm_sec);
603}
604
605tz_t *time_tz(time_t time, tz_t* tz) {
606 struct tm loc;
607 localtime_r(&time, &loc);
608 struct tm gmt;
609 gmtime_r(&time, &gmt);
610 int minutes = ((loc.tm_hour * 60 + loc.tm_min) - (gmt.tm_hour * 60 + gmt.tm_min)) % 720;
611 tz->tz_dir = minutes < 0 ? (-1) : 1;
612 tz->tz_hour = tz->tz_dir * minutes / 60;
613 tz->tz_min = ( tz->tz_dir * minutes ) - tz->tz_hour * 60;
614 sprintf(tz->tz_str, "%+2.2d:%2.2d", tz->tz_dir * tz->tz_hour, (unsigned)(tz->tz_min) % 60);
615 return tz;
616}