libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
helpers.c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license in the file COPYING
10 * or http://www.opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file COPYING.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2023 Saso Kiselkov. All rights reserved.
24 */
25
26#include <errno.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdio.h>
30#include <math.h>
31#include <ctype.h>
32#include <time.h>
33#include <zlib.h>
34
35#if IBM
36#include <windows.h>
37#include <strsafe.h>
38#else /* !IBM */
39#include <sys/types.h>
40#include <sys/time.h>
41#include <sys/stat.h>
42#include <dirent.h>
43#include <unistd.h>
44#endif /* !IBM */
45
46#include "acfutils/assert.h"
47#include "acfutils/helpers.h"
48#include "acfutils/log.h"
49#include "acfutils/safe_alloc.h"
50#include "acfutils/stat.h"
51#include "acfutils/time.h"
52
53/*
54 * The single-letter versions of the IDs need to go after the two-letter ones
55 * to make sure we pick up the two-letter versions first.
56 */
57static const char *const icao_country_codes[] = {
58 "AN", "AY",
59 "BG", "BI", "BK",
60 "C",
61 "DA", "DB", "DF", "DG", "DI", "DN", "DR", "DT", "DX",
62 "EB", "ED", "EE", "EF", "EG", "EH", "EI", "EK", "EL",
63 "EN", "EP", "ES", "ET", "EV", "EY",
64 "FA", "FB", "FC", "FD", "FE", "FG", "FH", "FI", "FJ",
65 "FK", "FL", "FM", "FN", "FO", "FP", "FQ", "FS", "FT",
66 "FV", "FW", "FX", "FY", "FZ",
67 "GA", "GB", "GC", "GE", "GF", "GG", "GL", "GM", "GO",
68 "GQ", "GS", "GU", "GV",
69 "HA", "HB", "HC", "HD", "HE", "HH", "HK", "HL", "HR",
70 "HS", "HT", "HU",
71 "K",
72 "LA", "LB", "LC", "LD", "LE", "LF", "LG", "LH", "LI",
73 "LJ", "LK", "LL", "LM", "LN", "LO", "LP", "LQ", "LR",
74 "LS", "LT", "LU", "LV", "LW", "LX", "LY", "LZ",
75 "MB", "MD", "MG", "MH", "MK", "MM", "MN", "MP", "MR",
76 "MS", "MT", "MU", "MW", "MY", "MZ",
77 "NC", "NF", "NG", "NI", "NL", "NS", "NT", "NV", "NW",
78 "NZ",
79 "OA", "OB", "OE", "OI", "OJ", "OK", "OL", "OM", "OO",
80 "OP", "OR", "OS", "OT", "OY",
81 "PA", "PB", "PC", "PF", "PG", "PH", "PJ", "PK", "PL",
82 "PM", "PO", "PP", "PT", "PW",
83 "RC", "RJ", "RK", "RO", "RP",
84 "SA", "SB", "SC", "SD", "SE", "SF", "SG", "SH", "SI",
85 "SJ", "SK", "SL", "SM", "SN", "SO", "SP", "SS", "SU",
86 "SV", "SW", "SY",
87 "TA", "TB", "TD", "TF", "TG", "TI", "TJ", "TK", "TL",
88 "TN", "TQ", "TR", "TT", "TU", "TV", "TX",
89 "UA", "UB", "UC", "UD", "UG", "UK", "UM", "UT",
90 "U",
91 "VA", "VC", "VD", "VE", "VG", "VH", "VI", "VL", "VM",
92 "VN", "VO", "VQ", "VR", "VT", "VV", "VY",
93 "WA", "WB", "WI", "WM", "WP", "WQ", "WR", "WS",
94 "Y",
95 "ZK", "ZM",
96 "Z",
97 NULL
98};
99
100static const char *months[12] = {
101 "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
102 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
103};
104
105static struct {
106 int cycle;
107 const char *start;
108} airac_eff_dates[] = {
109 /* year 2015 */
110 {1501, "08-JAN"}, {1502, "05-FEB"}, {1503, "05-MAR"}, {1504, "02-APR"},
111 {1505, "30-APR"}, {1506, "28-MAY"}, {1507, "25-JUN"}, {1508, "23-JUL"},
112 {1509, "20-AUG"}, {1510, "17-SEP"}, {1511, "15-OCT"}, {1512, "12-NOV"},
113 {1513, "10-DEC"},
114 /* year 2016 */
115 {1601, "07-JAN"}, {1602, "04-FEB"}, {1603, "03-MAR"}, {1604, "31-MAR"},
116 {1605, "28-APR"}, {1606, "26-MAY"}, {1607, "23-JUN"}, {1608, "21-JUL"},
117 {1609, "18-AUG"}, {1610, "15-SEP"}, {1611, "13-OCT"}, {1612, "10-NOV"},
118 {1613, "08-DEC"},
119 /* year 2017 */
120 {1701, "05-JAN"}, {1702, "02-FEB"}, {1703, "02-MAR"}, {1704, "30-MAR"},
121 {1705, "27-APR"}, {1706, "25-MAY"}, {1707, "22-JUN"}, {1708, "20-JUL"},
122 {1709, "17-AUG"}, {1710, "14-SEP"}, {1711, "12-OCT"}, {1712, "09-NOV"},
123 {1713, "07-DEC"},
124 /* year 2018 */
125 {1801, "04-JAN"}, {1802, "01-FEB"}, {1803, "01-MAR"}, {1804, "29-MAR"},
126 {1805, "26-APR"}, {1806, "24-MAY"}, {1807, "21-JUN"}, {1808, "19-JUL"},
127 {1809, "16-AUG"}, {1810, "13-SEP"}, {1811, "11-OCT"}, {1812, "08-NOV"},
128 {1813, "06-DEC"},
129 /* year 2019 */
130 {1901, "03-JAN"}, {1902, "31-JAN"}, {1903, "28-FEB"}, {1904, "28-MAR"},
131 {1905, "25-APR"}, {1906, "23-MAY"}, {1907, "20-JUN"}, {1908, "18-JUL"},
132 {1909, "15-AUG"}, {1910, "12-SEP"}, {1911, "10-OCT"}, {1912, "07-NOV"},
133 {1913, "05-DEC"},
134 /* year 2020 */
135 {2001, "02-JAN"}, {2002, "30-JAN"}, {2003, "27-FEB"}, {2004, "26-MAR"},
136 {2005, "23-APR"}, {2006, "21-MAY"}, {2007, "18-JUN"}, {2008, "16-JUL"},
137 {2009, "13-AUG"}, {2010, "10-SEP"}, {2011, "08-OCT"}, {2012, "05-NOV"},
138 {2013, "03-DEC"}, {2014, "31-DEC"},
139 /* year 2021 */
140 {2101, "28-JAN"}, {2102, "25-FEB"}, {2103, "25-MAR"}, {2104, "22-APR"},
141 {2105, "20-MAY"}, {2106, "17-JUN"}, {2107, "15-JUL"}, {2108, "12-AUG"},
142 {2109, "09-SEP"}, {2110, "07-OCT"}, {2111, "04-NOV"}, {2112, "02-DEC"},
143 {2113, "30-DEC"},
144 /* year 2022 */
145 {2201, "27-JAN"}, {2202, "24-FEB"}, {2203, "24-MAR"}, {2204, "21-APR"},
146 {2205, "19-MAY"}, {2206, "16-JUN"}, {2207, "14-JUL"}, {2208, "11-AUG"},
147 {2209, "08-SEP"}, {2210, "06-OCT"}, {2211, "03-NOV"}, {2212, "01-DEC"},
148 {2213, "29-DEC"},
149 /* year 2023 */
150 {2301, "26-JAN"}, {2302, "23-FEB"}, {2303, "23-MAR"}, {2304, "20-APR"},
151 {2305, "18-MAY"}, {2306, "15-JUN"}, {2307, "13-JUL"}, {2308, "10-AUG"},
152 {2309, "07-SEP"}, {2310, "05-OCT"}, {2311, "02-NOV"}, {2312, "30-NOV"},
153 {2313, "28-DEC"},
154 /* year 2024 */
155 {2401, "25-JAN"}, {2402, "22-FEB"}, {2403, "21-MAR"}, {2404, "18-APR"},
156 {2405, "16-MAY"}, {2406, "13-JUN"}, {2407, "11-JUL"}, {2408, "08-AUG"},
157 {2409, "05-SEP"}, {2410, "03-OCT"}, {2411, "31-OCT"}, {2412, "28-NOV"},
158 {2413, "26-DEC"},
159 /* year 2025 */
160 {2501, "23-JAN"}, {2502, "20-FEB"}, {2503, "20-MAR"}, {2504, "17-APR"},
161 {2505, "15-MAY"}, {2506, "12-JUN"}, {2507, "10-JUL"}, {2508, "07-AUG"},
162 {2509, "04-SEP"}, {2510, "02-OCT"}, {2511, "30-OCT"}, {2512, "27-NOV"},
163 {2513, "25-DEC"},
164 /* year 2026 */
165 {2601, "22-JAN"}, {2602, "19-FEB"}, {2603, "19-MAR"}, {2604, "16-APR"},
166 {2605, "14-MAY"}, {2606, "11-JUN"}, {2607, "09-JUL"}, {2608, "06-AUG"},
167 {2609, "03-SEP"}, {2610, "01-OCT"}, {2611, "29-OCT"}, {2612, "26-NOV"},
168 {2613, "24-DEC"},
169 /* year 2027 */
170 {2701, "21-JAN"}, {2702, "18-FEB"}, {2703, "18-MAR"}, {2704, "15-APR"},
171 {2705, "13-MAY"}, {2706, "10-JUN"}, {2707, "08-JUL"}, {2708, "05-AUG"},
172 {2709, "02-SEP"}, {2710, "30-SEP"}, {2711, "28-OCT"}, {2712, "25-NOV"},
173 {2713, "23-DEC"},
174 /* year 2028 */
175 {2801, "20-JAN"}, {2802, "17-FEB"}, {2803, "16-MAR"}, {2804, "13-APR"},
176 {2805, "11-MAY"}, {2806, "08-JUN"}, {2807, "06-JUL"}, {2808, "03-AUG"},
177 {2809, "31-AUG"}, {2810, "28-SEP"}, {2811, "26-OCT"}, {2812, "23-NOV"},
178 {2813, "21-DEC"},
179 /* year 2029 */
180 {2901, "18-JAN"}, {2902, "15-FEB"}, {2903, "15-MAR"}, {2904, "12-APR"},
181 {2905, "10-MAY"}, {2906, "07-JUN"}, {2907, "05-JUL"}, {2908, "02-AUG"},
182 {2909, "30-AUG"}, {2910, "27-SEP"}, {2911, "25-OCT"}, {2912, "22-NOV"},
183 {2912, "20-DEC"},
184 /* end of list */
185 { .cycle = -1 }
186};
187
188/*
189 * How to turn to get from hdg1 to hdg2 with positive being right and negative
190 * being left. Always turns the shortest way around (<= 180 degrees).
191 */
193double
194rel_hdg_impl(double hdg1, double hdg2, const char *file, int line)
195{
196 ASSERT_MSG(is_valid_hdg(hdg1) && is_valid_hdg(hdg2),
197 "from: %s:%d %f -> %f", file, line, hdg1, hdg2);
198 if (hdg1 > hdg2) {
199 if (hdg1 > hdg2 + 180)
200 return (360 - hdg1 + hdg2);
201 else
202 return (-(hdg1 - hdg2));
203 } else {
204 if (hdg2 > hdg1 + 180)
205 return (-(360 - hdg2 + hdg1));
206 else
207 return (hdg2 - hdg1);
208 }
209}
210
216API_EXPORT bool_t
218{
219 return (code >= 0 && code <= 7777 &&
220 (code % 10) <= 7 &&
221 ((code / 10) % 10) <= 7 &&
222 ((code / 100) % 10) <= 7 &&
223 ((code / 1000) % 10) <= 7);
224}
225
232bool_t
233is_valid_vor_freq(double freq_mhz)
234{
235 unsigned freq_khz = freq_mhz * 1000;
236
237 /* Check correct frequency band */
238 if (freq_khz < 108000 || freq_khz > 117950)
239 return (B_FALSE);
240 /*
241 * Check the LOC band - freq must be multiple of 200 kHz or
242 * remainder must be 50 kHz.
243 */
244 if (freq_khz >= 108000 && freq_khz <= 112000 &&
245 freq_khz % 200 != 0 && freq_khz % 200 != 50)
246 return (B_FALSE);
247 /* Above 112 MHz, frequency must be multiple of 50 kHz */
248 if (freq_khz % 50 != 0)
249 return (B_FALSE);
250
251 return (B_TRUE);
252}
253
260bool_t
261is_valid_loc_freq(double freq_mhz)
262{
263 unsigned freq_khz = freq_mhz * 1000;
264
265 /* Check correct frequency band */
266 if (freq_khz < 108100 || freq_khz > 111950)
267 return (B_FALSE);
268 /* Check 200 kHz spacing with 100 kHz or 150 kHz remainder. */
269 if (freq_khz % 200 != 100 && freq_khz % 200 != 150)
270 return (B_FALSE);
271
272 return (B_TRUE);
273}
274
279bool_t
280is_valid_tacan_freq(double freq_mhz)
281{
282 unsigned freq_khz = freq_mhz * 1000;
283
284 /* this is quite a guess! */
285 if (freq_khz < 133000 || freq_khz > 136000 ||
286 freq_khz % 100 != 0)
287 return (B_FALSE);
288 return (B_TRUE);
289}
290
296bool_t
297is_valid_ndb_freq(double freq_khz)
298{
299 unsigned freq_hz = freq_khz * 1000;
300 /* 177 kHz for an NDB is the lowest I've ever seen */
301 return (freq_hz >= 177000 && freq_hz <= 1750000);
302}
303
311bool_t
312is_valid_icao_code(const char *icao)
313{
314 if (strlen(icao) != 4)
315 return (B_FALSE);
316 for (int i = 0; i < 4; i++) {
317 if ((icao[i] < 'A' || icao[i] > 'Z') &&
318 (icao[i] < '0' || icao[i] > '9')) {
319 return (B_FALSE);
320 }
321 }
322 if (icao[0] == 'I' || icao[0] == 'J' || icao[0] == 'Q' ||
323 icao[0] == 'X')
324 return (B_FALSE);
325 return (B_TRUE);
326}
327
335bool_t
336is_valid_iata_code(const char *iata)
337{
338 if (strlen(iata) != 3)
339 return (B_FALSE);
340 for (int i = 0; i < 3; i++) {
341 if (iata[i] < 'A' || iata[i] > 'Z')
342 return (B_FALSE);
343 }
344 if (iata[0] == 'Q')
345 return (B_FALSE);
346 return (B_TRUE);
347}
348
357const char *
359{
360 if (!is_valid_icao_code(icao))
361 return (NULL);
362
363 for (int i = 0; icao_country_codes[i] != NULL; i++) {
364 if (strncmp(icao, icao_country_codes[i],
365 strlen(icao_country_codes[i])) == 0)
366 return (icao_country_codes[i]);
367 }
368 return (NULL);
369}
370
378bool_t
379is_valid_rwy_ID(const char *rwy_ID)
380{
381 int hdg;
382 int len = strlen(rwy_ID);
383
384 if (len < 2 || len > 3 || !isdigit(rwy_ID[0]) || !isdigit(rwy_ID[1]))
385 return (B_FALSE);
386 hdg = atoi(rwy_ID);
387 if (hdg == 0 || hdg > 36)
388 return (B_FALSE);
389 if (len == 3) {
390 if (rwy_ID[2] != 'R' && rwy_ID[2] != 'L' && rwy_ID[2] != 'C' &&
391 rwy_ID[2] != 'T') {
392 return (B_FALSE);
393 }
394 }
395
396 return (B_TRUE);
397}
398
410void
411copy_rwy_ID(const char *src, char dst[4])
412{
413 int len;
414
415 /* make sure dst is populated with *something* */
416 memset(dst, 0, 4);
417 lacf_strlcpy(dst, src, 4);
418
419 /*
420 * Strip an optional trailing true hdg indicator. If the runway had
421 * a suffix (e.g. '08LT'), the previous copy step will have already
422 * stripped it.
423 */
424 len = strlen(dst);
425 if (len > 0 && dst[len - 1] == 'T') {
426 dst[len - 1] = '\0';
427 len--;
428 }
429
430 /* check if the identifier is a US runway number and translate it */
431 if (dst[0] >= '1' && dst[0] <= '9' &&
432 (len == 1 || (len == 2 && !isdigit(dst[1])))) {
433 memmove(dst + 1, dst, len + 1);
434 dst[0] = '0';
435 }
436}
437
438static int
439month2nr(const char *month)
440{
441 for (int i = 0; i < 12; i++) {
442 if (strcmp(month, months[i]) == 0)
443 return (i);
444 }
445 VERIFY_MSG(0, "Invalid month specified: \"%s\"", month);
446}
447
448static const char *
449nr2month(unsigned tm_mon)
450{
451 VERIFY3S(tm_mon, <, 12);
452 return (months[tm_mon]);
453}
454
455static time_t
456cycle2start(int i)
457{
458 struct tm cyc_tm = { .tm_year = 0 };
459 char day[4], month[4];
460 int year = (airac_eff_dates[i].cycle / 100) + 100;
461 const char *start = airac_eff_dates[i].start;
462
463 snprintf(day, sizeof (day), "%c%c", start[0], start[1]);
464 snprintf(month, sizeof (month), "%c%c%c", start[3], start[4], start[5]);
465
466 cyc_tm.tm_year = year;
467 cyc_tm.tm_mday = atoi(day);
468 cyc_tm.tm_mon = month2nr(month);
469 cyc_tm.tm_wday = -1;
470 cyc_tm.tm_yday = -1;
471 cyc_tm.tm_isdst = -1;
472
473 return (lacf_timegm(&cyc_tm));
474}
475
484const char *
486{
487 for (int i = 0; airac_eff_dates[i].cycle != -1; i++) {
488 if (airac_eff_dates[i].cycle == cycle)
489 return (airac_eff_dates[i].start);
490 }
491 return (NULL);
492}
493
498time_t
500 for (int i = 0; airac_eff_dates[i].cycle != -1; i++) {
501 if (airac_eff_dates[i].cycle == cycle)
502 return (cycle2start(i));
503 }
504 return (-1u);
505}
506
525bool_t
526airac_cycle2exp_date(int cycle, char buf[16], time_t *cycle_end_p)
527{
528 for (int i = 0; airac_eff_dates[i].cycle != -1; i++) {
529 if (airac_eff_dates[i].cycle == cycle) {
530 /*
531 * Grab the effective date of the next cycle and
532 * subtract one day second. The end time will be
533 * the previous day at 23:59:59.
534 */
535 time_t cycle_end_t = cycle2start(i) + 28 * 86400;
536 struct tm end_tm;
537
538 lacf_gmtime_r(&cycle_end_t, &end_tm);
539 if (buf != NULL) {
540 snprintf(buf, 16, "%02d-%s", end_tm.tm_mday,
541 nr2month(end_tm.tm_mon));
542 }
543 if (cycle_end_p != NULL)
544 *cycle_end_p = cycle_end_t;
545
546 return (B_TRUE);
547 }
548 }
549 return (B_FALSE);
550}
551
556int
558{
559 for (int i = 0; airac_eff_dates[i].cycle != -1; i++) {
560 if (cycle2start(i) > t && i > 0)
561 return (airac_eff_dates[i - 1].cycle);
562 }
563
564 return (-1);
565}
566
587ssize_t
588explode_line(char *line, char delim, char **comps, size_t capacity)
589{
590 ssize_t i = 1;
591 bool_t toomany = B_FALSE;
592
593 ASSERT(capacity != 0);
594 comps[0] = line;
595 for (char *p = line; *p != 0; p++) {
596 if (*p == delim) {
597 *p = 0;
598 if (i < (ssize_t)capacity)
599 comps[i] = p + 1;
600 else
601 toomany = B_TRUE;
602 i++;
603 }
604 }
605
606 return (toomany ? -i : i);
607}
608
649char **
650strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
651{
652 char **result;
653 size_t i = 0, n = 0;
654 size_t seplen = strlen(sep);
655
656 for (const char *a = input, *b = strstr(a, sep);
657 a != NULL; a = b + seplen, b = strstr(a, sep)) {
658 if (b == NULL) {
659 b = input + strlen(input);
660 if (a != b || !skip_empty)
661 n++;
662 break;
663 }
664 if (a == b && skip_empty)
665 continue;
666 n++;
667 }
668
669 result = safe_calloc(n, sizeof (char *));
670
671 for (const char *a = input, *b = strstr(a, sep);
672 a != NULL; a = b + seplen, b = strstr(a, sep)) {
673 if (b == NULL) {
674 b = input + strlen(input);
675 if (a != b || !skip_empty) {
676 result[i] = safe_calloc(b - a + 1,
677 sizeof (char));
678 memcpy(result[i], a, b - a);
679 }
680 break;
681 }
682 if (a == b && skip_empty)
683 continue;
684 ASSERT(i < n);
685 result[i] = safe_calloc(b - a + 1, sizeof (char));
686 memcpy(result[i], a, b - a);
687 i++;
688 }
689
690 if (num != NULL)
691 *num = n;
692
693 return (result);
694}
695
702void
703free_strlist(char **comps, size_t num)
704{
705 if (comps == NULL)
706 return;
707 for (size_t i = 0; i < num; i++)
708 free(comps[i]);
709 free(comps);
710}
711
730void
731append_format(char **str, size_t *sz, const char *format, ...)
732{
733 va_list ap;
734 int needed;
735
736 va_start(ap, format);
737 needed = vsnprintf(NULL, 0, format, ap);
738 va_end(ap);
739
740 va_start(ap, format);
741 *str = safe_realloc(*str, *sz + needed + 1);
742 (void) vsnprintf(*str + *sz, needed + 1, format, ap);
743 *sz += needed;
744 va_end(ap);
745}
746
752void
754{
755 for (int i = 0, n = strlen(str); i + 2 < n; i++) {
756 /* Skip non-parseable hex digits */
757 if (str[i] == '%' && isxdigit(str[i + 1]) &&
758 isxdigit(str[i + 2])) {
759 char dig[3] = { str[i + 1], str[i + 2], 0 };
760 unsigned val;
761 sscanf(dig, "%x", &val);
762 str[i] = val;
763 memmove(&str[i + 1], &str[i + 3], (n - i) - 2);
764 n -= 2;
765 }
766 }
767}
768
772API_EXPORT void
773strtolower(char *str)
774{
775 for (int i = 0, n = strlen(str); i < n; i++)
776 str[i] = tolower(str[i]);
777}
778
782API_EXPORT void
783strtoupper(char *str)
784{
785 for (int i = 0, n = strlen(str); i < n; i++)
786 str[i] = toupper(str[i]);
787}
788
796API_EXPORT size_t
798{
799 if ((str[0] & 0xe0) == 0xc0 && str[1] != 0)
800 return (2);
801 if ((str[0] & 0xf0) == 0xe0 && str[1] != 0 && str[2] != 0)
802 return (3);
803 if ((str[0] & 0xf8) == 0xf0 && str[1] != 0 && str[2] != 0 &&
804 str[3] != 0)
805 return (4);
806 return (1);
807}
808
815API_EXPORT size_t
816utf8_get_num_chars(const char *str)
817{
818 size_t num_chars = 0;
819 for (const char *s = str; s[0] != 0; num_chars++)
820 s += utf8_char_get_num_bytes(str);
821 return (num_chars);
822}
823
832char *
833mkpathname(const char *comp, ...)
834{
835 char *res;
836 va_list ap;
837
838 va_start(ap, comp);
839 res = mkpathname_v(comp, ap);
840 va_end(ap);
841
842 return (res);
843}
844
849char *
850mkpathname_v(const char *comp, va_list ap)
851{
852 size_t n = 0, len = 0;
853 char *str;
854 va_list ap2;
855
856 ASSERT(ap != NULL);
857
858 va_copy(ap2, ap);
859 len = strlen(comp);
860 for (const char *c = va_arg(ap2, const char *); c != NULL;
861 c = va_arg(ap2, const char *)) {
862 len += 1 + strlen(c);
863 }
864 va_end(ap2);
865
866 str = safe_malloc(len + 1);
867 n += snprintf(str, len + 1, "%s", comp);
868 for (const char *c = va_arg(ap, const char *); c != NULL;
869 c = va_arg(ap, const char *)) {
870 ASSERT(n < len);
871 n += snprintf(&str[n], len - n + 1, "%c%s", DIRSEP, c);
872 /* kill a trailing directory separator */
873 if (str[n - 1] == DIRSEP) {
874 str[n - 1] = 0;
875 n--;
876 }
877 }
878 /* eliminate duplicate and flipped path separators */
879 fix_pathsep(str);
880
881 return (str);
882}
883
891void
892fix_pathsep(char *str)
893{
894 for (int i = 0, n = strlen(str); i < n; i++) {
895#if IBM
896 if (str[i] == '/')
897 str[i] = '\\';
898#else /* !IBM */
899 if (str[i] == '\\')
900 str[i] = '/';
901#endif /* !IBM */
902 }
903 /* Eliminate duplicate '//' and '\\' */
904 for (char *elem = str; *elem != '\0';) {
905 if (elem[0] == DIRSEP && elem[1] == DIRSEP) {
906 /* Move the rest up one, to delete the duplicate */
907 memmove(elem, &elem[1], strlen(&elem[1]) + 1);
908 } else {
909 elem++;
910 }
911 }
912}
913
923char *
924path_last_comp_subst(const char *path, const char *replace)
925{
926 const char *last_sep;
927 char *tmp, *result;
928
929 ASSERT(path != NULL);
930 ASSERT(replace != NULL);
931
932 last_sep = MAX(strrchr(path, '/'), strrchr(path, '\\'));
933 if (last_sep == NULL) {
934 /* No path separator? Just return a copy of the replacement. */
935 return (strdup(replace));
936 }
937 tmp = safe_malloc(last_sep - path + 1);
938 lacf_strlcpy(tmp, path, last_sep - path + 1);
939 result = mkpathname(tmp, replace, NULL);
940 free(tmp);
941 path_normalize(result);
942
943 return (result);
944}
945
951char *
952path_last_comp(const char *path)
953{
954 char *last;
955
956 ASSERT(path != NULL);
957 last = MAX(strrchr(path, '/'), strrchr(path, '\\'));
958 if (last != NULL)
959 last++;
960 else
961 last = (char *)path;
962
963 return (last);
964}
965
972char *
973path_ext_subst(const char *path, const char *ext)
974{
975 const char *period;
976 char *str;
977 int l, k;
978
979 ASSERT(path != NULL);
980 ASSERT(ext != NULL);
981 period = strrchr(path, '.');
982 if (period == NULL)
983 return (sprintf_alloc("%s.%s", path, ext));
984 l = period - path;
985 k = strlen(ext);
986 str = safe_malloc(l + strlen(ext) + 2);
987 strlcpy(str, path, l + 1);
988 strlcpy(&str[l], ".", 2);
989 strlcpy(&str[l + 1], ext, k + 1);
990
991 return (str);
992}
993
994static char *
995find_prev_path_sep(char *path, char *elem)
996{
997 ASSERT(path != NULL);
998 ASSERT(elem != NULL);
999 for (elem--; elem >= path; elem--) {
1000 if (*elem == DIRSEP)
1001 return (elem);
1002 }
1003 return (NULL);
1004}
1005
1012void
1014{
1015 char *elem;
1016
1017 ASSERT(path != NULL);
1018
1019 fix_pathsep(path);
1020 /* Resolve directory returns */
1021 while ((elem = strstr(path, DIRSEP_S ".." DIRSEP_S)) != NULL ||
1022 ((elem = strstr(path, DIRSEP_S "..")) != NULL && elem[3] == '\0')) {
1023 char *prev_comp = find_prev_path_sep(path, elem);
1024 if (prev_comp == NULL) {
1025 /* Reached start of string - cannot remove anymore */
1026 break;
1027 }
1028 memmove(prev_comp, &elem[3], strlen(&elem[3]) + 1);
1029 }
1030}
1031
1035char *
1036file2str(const char *comp, ...)
1037{
1038 va_list ap;
1039 char *filename, *str;
1040 long len;
1041
1042 va_start(ap, comp);
1043 filename = mkpathname_v(comp, ap);
1044 va_end(ap);
1045 str = file2str_name(&len, filename);
1046 free(filename);
1047
1048 return (str);
1049}
1050
1054char *
1055file2str_ext(long *len_p, const char *comp, ...)
1056{
1057 va_list ap;
1058 char *filename, *str;
1059
1060 va_start(ap, comp);
1061 filename = mkpathname_v(comp, ap);
1062 va_end(ap);
1063 str = file2str_name(len_p, filename);
1064 free(filename);
1065
1066 return (str);
1067}
1068
1086char *
1087file2str_name(long *len_p, const char *filename)
1088{
1089#define MAX_FILESIZE (256 << 20) /* 256MB */
1090 char *contents;
1091 FILE *fp;
1092 long len;
1093
1094 fp = fopen(filename, "rb");
1095 if (fp == NULL)
1096 return (NULL);
1097
1098 fseek(fp, 0, SEEK_END);
1099 len = ftell(fp);
1100 if (len < 0 || len > MAX_FILESIZE) {
1101 fclose(fp);
1102 return (NULL);
1103 }
1104 fseek(fp, 0, SEEK_SET);
1105 contents = safe_malloc(len + 1);
1106 contents[len] = 0;
1107 if (fread(contents, 1, len, fp) != (size_t)len) {
1108 fclose(fp);
1109 free(contents);
1110 return (NULL);
1111 }
1112 fclose(fp);
1113 *len_p = len;
1114
1115 return (contents);
1116}
1117
1133API_EXPORT void *
1134file2buf(const char *filename, size_t *bufsz)
1135{
1136 ssize_t s;
1137 void *buf;
1138 FILE *fp = fopen(filename, "rb");
1139
1140 if (fp == NULL) {
1141 *bufsz = 0;
1142 return (NULL);
1143 }
1144 fseek(fp, 0, SEEK_END);
1145 s = ftell(fp);
1146 if (s <= 0) {
1147 *bufsz = 0;
1148 fclose(fp);
1149 return (NULL);
1150 }
1151 fseek(fp, 0, SEEK_SET);
1152
1153 buf = safe_malloc(s);
1154 if (fread(buf, 1, s, fp) < (size_t)s) {
1155 *bufsz = 0;
1156 free(buf);
1157 fclose(fp);
1158 return (NULL);
1159 }
1160
1161 fclose(fp);
1162 *bufsz = s;
1163
1164 return (buf);
1165}
1166
1173ssize_t
1174filesz(const char *filename)
1175{
1176 struct stat st;
1177
1178 ASSERT(filename != NULL);
1179 if (!file_exists(filename, NULL) || stat(filename, &st) != 0)
1180 return (-1);
1181 return (st.st_size);
1182}
1183
1184#if IBM
1185
1186void
1187win_perror(DWORD err, const char *fmt, ...)
1188{
1189 va_list ap;
1190 LPSTR win_msg = NULL;
1191 char *caller_msg;
1192 int len;
1193
1194 va_start(ap, fmt);
1195 len = vsnprintf(NULL, 0, fmt, ap);
1196 va_end(ap);
1197 caller_msg = safe_malloc(len + 1);
1198 va_start(ap, fmt);
1199 vsnprintf(caller_msg, len + 1, fmt, ap);
1200 va_end(ap);
1201
1202 (void) FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
1203 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1204 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1205 (LPSTR)&win_msg, 0, NULL);
1206 strip_space(win_msg);
1207 logMsg("%s: %s", caller_msg, win_msg);
1208
1209 free(caller_msg);
1210 LocalFree(win_msg);
1211}
1212
1213#endif /* IBM */
1214
1221bool_t
1222file_exists(const char *filename, bool_t *isdir)
1223{
1224#if IBM
1225 unsigned len = strlen(filename);
1226 TCHAR *filenameT = safe_calloc(len + 1, sizeof (*filenameT));
1227 DWORD attr;
1228
1229 MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameT, len + 1);
1230 attr = GetFileAttributes(filenameT);
1231 if (isdir != NULL)
1232 *isdir = !!(attr & FILE_ATTRIBUTE_DIRECTORY);
1233 if (attr == INVALID_FILE_ATTRIBUTES) {
1234 errno = ENOENT; /* fake an errno value */
1235 free(filenameT);
1236 return (B_FALSE);
1237 }
1238 free(filenameT);
1239 return (B_TRUE);
1240#else /* !IBM */
1241 struct stat st;
1242
1243 if (isdir != NULL)
1244 *isdir = B_FALSE;
1245 if (stat(filename, &st) < 0) {
1246 if (errno != ENOENT)
1247 logMsg("Error checking if file %s exists: %s",
1248 filename, strerror(errno));
1249 return (B_FALSE);
1250 }
1251 if (isdir != NULL)
1252 *isdir = S_ISDIR(st.st_mode);
1253 return (B_TRUE);
1254#endif /* !IBM */
1255}
1256
1260bool_t
1261create_directory(const char *dirname)
1262{
1263 ASSERT(dirname != NULL);
1264#if IBM
1265 DWORD err;
1266 unsigned len = strlen(dirname);
1267 WCHAR *dirnameW = safe_calloc(len + 1, sizeof (*dirnameW));
1268
1269 MultiByteToWideChar(CP_UTF8, 0, dirname, -1, dirnameW, len + 1);
1270 if (!CreateDirectory(dirnameW, NULL) &&
1271 (err = GetLastError()) != ERROR_ALREADY_EXISTS) {
1272 win_perror(err, "Error creating directory %s", dirname);
1273 free(dirnameW);
1274 return (B_FALSE);
1275 }
1276 free(dirnameW);
1277#else /* !IBM */
1278 if (mkdir(dirname, 0777) != 0 && errno != EEXIST) {
1279 logMsg("Error creating directory %s: %s", dirname,
1280 strerror(errno));
1281 return (B_FALSE);
1282 }
1283#endif /* !IBM */
1284 return (B_TRUE);
1285}
1286
1291bool_t
1292create_directory_recursive(const char *dirname)
1293{
1294 char *partname = safe_calloc(1, strlen(dirname) + 1);
1295
1296 for (const char *start = dirname, *end = strchr(&dirname[1], DIRSEP);
1297 end != NULL; start = end, end = strchr(&start[1], DIRSEP)) {
1298 strncat(partname, start, end - start);
1299 if (!create_directory(partname))
1300 return (B_FALSE);
1301 }
1302
1303 free(partname);
1304
1305 return (create_directory(dirname));
1306}
1307
1308#if IBM
1309
1310static bool_t
1311win_rmdir(const LPTSTR dirnameT)
1312{
1313 WIN32_FIND_DATA find_data;
1314 HANDLE h_find = INVALID_HANDLE_VALUE;
1315 unsigned dirname_len = wcslen(dirnameT);
1316 TCHAR *srchnameT = safe_calloc(dirname_len + 4, sizeof (*srchnameT));
1317
1318 StringCchPrintf(srchnameT, dirname_len + 4, TEXT("%s\\*"), dirnameT);
1319 h_find = FindFirstFile(srchnameT, &find_data);
1320 if (h_find == INVALID_HANDLE_VALUE) {
1321 int err = GetLastError();
1322 char dirname[MAX_PATH];
1323 WideCharToMultiByte(CP_UTF8, 0, dirnameT, -1, dirname,
1324 sizeof (dirname), NULL, NULL);
1325 win_perror(err, "Error listing directory %s", dirname);
1326 goto errout;
1327 }
1328 do {
1329 TCHAR filepathT[MAX_PATH];
1330 DWORD attrs;
1331
1332 if (wcscmp(find_data.cFileName, TEXT(".")) == 0 ||
1333 wcscmp(find_data.cFileName, TEXT("..")) == 0)
1334 continue;
1335
1336 StringCchPrintf(filepathT, MAX_PATH, TEXT("%s\\%s"), dirnameT,
1337 find_data.cFileName);
1338 attrs = GetFileAttributes(filepathT);
1339
1340 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
1341 if (!win_rmdir(filepathT))
1342 goto errout;
1343 } else {
1344 if (!DeleteFile(filepathT)) {
1345 char filepath[MAX_PATH];
1346 WideCharToMultiByte(CP_UTF8, 0, filepathT, -1,
1347 filepath, sizeof (filepath), NULL, NULL);
1348 win_perror(GetLastError(), "Error removing "
1349 "file %s", filepath);
1350 goto errout;
1351 }
1352 }
1353 } while (FindNextFile(h_find, &find_data));
1354
1355 if (!RemoveDirectory(dirnameT)) {
1356 char dirname[MAX_PATH];
1357 WideCharToMultiByte(CP_UTF8, 0, dirnameT, -1,
1358 dirname, sizeof (dirname), NULL, NULL);
1359 win_perror(GetLastError(), "Error removing directory %s",
1360 dirname);
1361 goto errout;
1362 }
1363 free(srchnameT);
1364 FindClose(h_find);
1365
1366 return (B_TRUE);
1367errout:
1368 if (h_find != INVALID_HANDLE_VALUE) {
1369 FindClose(h_find);
1370 }
1371 free(srchnameT);
1372 return (B_FALSE);
1373}
1374
1375#endif /* IBM */
1376
1381bool_t
1382remove_directory(const char *dirname)
1383{
1384#if IBM
1385 unsigned l = strlen(dirname) + 1;
1386 TCHAR *dirnameT = safe_calloc(l + 1, sizeof (*dirnameT));
1387
1388 MultiByteToWideChar(CP_UTF8, 0, dirname, -1, dirnameT, l);
1389 bool_t result = win_rmdir(dirnameT);
1390 free(dirnameT);
1391 return (result);
1392#else /* !IBM */
1393 DIR *dp;
1394 struct dirent *de;
1395
1396 if ((dp = opendir(dirname)) == NULL) {
1397 logMsg("Error removing directory %s: %s", dirname,
1398 strerror(errno));
1399 return (B_FALSE);
1400 }
1401 while ((de = readdir(dp)) != NULL) {
1402 char filename[FILENAME_MAX];
1403 int err;
1404 struct stat st;
1405
1406 if (strcmp(de->d_name, ".") == 0 ||
1407 strcmp(de->d_name, "..") == 0)
1408 continue;
1409
1410 if (snprintf(filename, sizeof (filename), "%s/%s", dirname,
1411 de->d_name) >= (ssize_t)sizeof (filename)) {
1412 logMsg("Error removing directory %s: path too long",
1413 dirname);
1414 goto errout;
1415 }
1416 if (lstat(filename, &st) < 0) {
1417 logMsg("Error removing directory %s: cannot stat "
1418 "file %s: %s", dirname, de->d_name,
1419 strerror(errno));
1420 goto errout;
1421 }
1422 if (S_ISDIR(st.st_mode)) {
1423 if (!remove_directory(filename))
1424 goto errout;
1425 err = 0;
1426 } else {
1427 err = unlink(filename);
1428 }
1429 if (err != 0) {
1430 logMsg("Error removing %s: %s", filename,
1431 strerror(errno));
1432 goto errout;
1433 }
1434 }
1435 closedir(dp);
1436 if (rmdir(dirname) != 0) {
1437 logMsg("Error removing %s: %s", dirname, strerror(errno));
1438 goto errout;
1439 }
1440 return (B_TRUE);
1441errout:
1442 closedir(dp);
1443 return (B_FALSE);
1444#endif /* !IBM */
1445}
1446
1458bool_t
1459remove_file(const char *filename, bool_t notfound_ok)
1460{
1461#if IBM
1462 unsigned l = strlen(filename) + 1;
1463 TCHAR *filenameT = safe_calloc(l, sizeof (*filenameT));
1464 DWORD error;
1465
1466 MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameT, l);
1467 if (!DeleteFile(filenameT) && ((error = GetLastError()) !=
1468 ERROR_FILE_NOT_FOUND || !notfound_ok)) {
1469 win_perror(error, "Cannot remove file %s", filename);
1470 free(filenameT);
1471 return (B_FALSE);
1472 }
1473 free(filenameT);
1474 return (B_TRUE);
1475#else /* !IBM */
1476 if (unlink(filename) < 0 && (errno != ENOENT || !notfound_ok)) {
1477 logMsg("Cannot remove file %s: %s", filename, strerror(errno));
1478 return (B_FALSE);
1479 }
1480 return (B_TRUE);
1481#endif /* !IBM */
1482}
1483
1488API_EXPORT char *
1489lacf_dirname(const char *filename)
1490{
1491 int l = strlen(filename);
1492 char *p1, *p2;
1493 char *str;
1494
1495 if (l == 0)
1496 return (strdup(""));
1497
1498 p1 = strrchr(filename, '/');
1499 p2 = strrchr(filename, '\\');
1500 p1 = MAX(p1, p2);
1501 if (p1 == NULL)
1502 return (strdup(""));
1503 str = safe_malloc((p1 - filename) + 1);
1504 lacf_strlcpy(str, filename, (p1 - filename) + 1);
1505
1506 return (str);
1507}
1508
1509#if IBM
1510
1511DIR *
1512opendir(const char *path)
1513{
1514 DIR *dirp = safe_calloc(1, sizeof (*dirp));
1515 unsigned l = strlen(path) + 3; /* For '\*' at the end */
1516 TCHAR *pathT = safe_calloc(l, sizeof (*pathT));
1517
1518 MultiByteToWideChar(CP_UTF8, 0, path, -1, pathT, l);
1519 StringCchCat(pathT, l, TEXT("\\*"));
1520 dirp->handle = FindFirstFile(pathT, &dirp->find_data);
1521 if (dirp->handle == INVALID_HANDLE_VALUE) {
1522 win_perror(GetLastError(), "Cannot open directory %s", path);
1523 free(dirp);
1524 free(pathT);
1525 return (NULL);
1526 }
1527 dirp->first = B_TRUE;
1528 free(pathT);
1529 return (dirp);
1530}
1531
1532struct dirent *
1533readdir(DIR *dirp)
1534{
1535 /* First call to readdir retrieves what we got from FindFirstFile */
1536 if (!dirp->first) {
1537 if (FindNextFile(dirp->handle, &dirp->find_data) == 0)
1538 return (NULL);
1539 } else {
1540 dirp->first = B_FALSE;
1541 }
1542 WideCharToMultiByte(CP_UTF8, 0, dirp->find_data.cFileName, -1,
1543 dirp->de.d_name, sizeof (dirp->de.d_name), NULL, NULL);
1544 return (&dirp->de);
1545}
1546
1547void
1548closedir(DIR *dirp)
1549{
1550 FindClose(dirp->handle);
1551 free(dirp);
1552}
1553
1554int
1555stat(const char *pathname, struct stat *buf)
1556{
1557 FILETIME wtime, atime;
1558 HANDLE fh = INVALID_HANDLE_VALUE;
1559 unsigned l = strlen(pathname) + 1;
1560 TCHAR *pathnameT = safe_calloc(l, sizeof (*pathnameT));
1561 LARGE_INTEGER sz;
1562 ULARGE_INTEGER ftime;
1563 bool_t isdir;
1564
1565 if (!file_exists(pathname, &isdir)) {
1566 errno = ENOENT;
1567 goto errout;
1568 }
1569 MultiByteToWideChar(CP_UTF8, 0, pathname, -1, pathnameT, l);
1570 fh = CreateFile(pathnameT, (!isdir ? FILE_READ_ATTRIBUTES : 0) |
1571 GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1572 if (fh == INVALID_HANDLE_VALUE ||
1573 !GetFileTime(fh, NULL, &atime, &wtime) ||
1574 (!isdir && !GetFileSizeEx(fh, &sz))) {
1575 win_perror(GetLastError(), "Cannot stat %s", pathname);
1576 errno = EACCES;
1577 goto errout;
1578 }
1579 if (isdir)
1580 buf->st_size = 0;
1581 else
1582 buf->st_size = sz.QuadPart;
1583
1584 ftime.LowPart = atime.dwLowDateTime;
1585 ftime.HighPart = atime.dwHighDateTime;
1586 buf->st_atime = ftime.QuadPart / 10000000ull - 11644473600ull;
1587
1588 ftime.LowPart = wtime.dwLowDateTime;
1589 ftime.HighPart = wtime.dwHighDateTime;
1590 buf->st_mtime = ftime.QuadPart / 10000000ull - 11644473600ull;
1591
1592 CloseHandle(fh);
1593 free(pathnameT);
1594 return (0);
1595errout:
1596 if (fh != INVALID_HANDLE_VALUE) {
1597 CloseHandle(fh);
1598 }
1599 free(pathnameT);
1600 return (-1);
1601}
1602
1603#endif /* IBM */
1604
1605static size_t
1606qsort_partition(void *base, long lo, long hi, size_t size,
1607 int (*compar)(const void *, const void *, void *), void *arg)
1608{
1609#define SWAP(i, j) \
1610 do { \
1611 memcpy(tmp, base + size * (i), size); \
1612 memcpy(base + size * (i), base + size * (j), size); \
1613 memcpy(base + size * (j), tmp, size); \
1614 } while (0)
1615 void *pivot = base + hi * size;
1616 long i = lo;
1617 uint8_t *tmp = safe_malloc(size * sizeof (*tmp));
1618
1619 for (long j = lo; j < hi; j++) {
1620 if (compar(base + j * size, pivot, arg) < 0) {
1621 SWAP(i, j);
1622 i++;
1623 }
1624 }
1625 SWAP(i, hi);
1626 free(tmp);
1627
1628 return (i);
1629#undef SWAP
1630}
1631
1632static void
1633qsort_r_impl(void *base, long lo, long hi, size_t size,
1634 int (*compar)(const void *, const void *, void *), void *arg)
1635{
1636 if (lo < hi) {
1637 long p = qsort_partition(base, lo, hi, size, compar, arg);
1638
1639 qsort_r_impl(base, lo, p - 1, size, compar, arg);
1640 qsort_r_impl(base, p + 1, hi, size, compar, arg);
1641 }
1642}
1643
1649void
1650lacf_qsort_r(void *base, size_t nmemb, size_t size,
1651 int (*compar)(const void *, const void *, void *), void *arg)
1652{
1653 ASSERT(compar != NULL);
1654 if (nmemb != 0) {
1655 ASSERT(base != NULL);
1656 qsort_r_impl(base, 0, nmemb - 1, size, compar, arg);
1657 }
1658}
1659
1666void
1667lacf_strlcpy(char *restrict dest, const char *restrict src, size_t cap)
1668{
1669 size_t l;
1670
1671 ASSERT(cap != 0);
1672 /*
1673 * We MUSTN'T use strlen here, because src may be SIGNIFICANTLY
1674 * larger than dest and we don't want to measure the ENTIRE body
1675 * of src. We only care for length UP TO the destination capacity.
1676 */
1677 for (l = 0; l + 1 < cap && src[l] != '\0'; l++)
1678 ;
1679 /*
1680 * Due to a bug in GCC, we can't use strncpy, as it sometimes throws
1681 * "call to __builtin___strncpy_chk will always overflow destination
1682 * buffer", even when it's absolutely NOT the case.
1683 */
1684 memcpy(dest, src, MIN(cap - 1, l + 1));
1685 /* Insure the string is ALWAYS terminated */
1686 dest[cap - 1] = '\0';
1687}
#define ASSERT_MSG(x, fmt,...)
Definition assert.h:214
#define ASSERT(x)
Definition assert.h:208
#define VERIFY3S(x, op, y)
Definition assert.h:125
#define VERIFY_MSG(x, fmt,...)
Definition assert.h:91
char * file2str_name(long *len_p, const char *filename)
Definition helpers.c:1087
char * lacf_dirname(const char *filename)
Definition helpers.c:1489
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
Definition helpers.c:650
ssize_t filesz(const char *filename)
Definition helpers.c:1174
void unescape_percent(char *str)
Definition helpers.c:753
int airac_time2cycle(time_t t)
Definition helpers.c:557
void fix_pathsep(char *str)
Definition helpers.c:892
char * mkpathname(const char *comp,...)
Definition helpers.c:833
void strtoupper(char *str)
Definition helpers.c:783
char * path_ext_subst(const char *path, const char *ext)
Definition helpers.c:973
void copy_rwy_ID(const char *src, char dst[4])
Definition helpers.c:411
char * file2str(const char *comp,...)
Definition helpers.c:1036
bool_t remove_directory(const char *dirname)
Definition helpers.c:1382
bool_t is_valid_icao_code(const char *icao)
Definition helpers.c:312
ssize_t explode_line(char *line, char delim, char **comps, size_t capacity)
Definition helpers.c:588
void path_normalize(char *path)
Definition helpers.c:1013
static bool_t lacf_gmtime_r(const time_t *tim, struct tm *tm)
Definition helpers.h:846
size_t utf8_get_num_chars(const char *str)
Definition helpers.c:816
bool_t is_valid_loc_freq(double freq_mhz)
Definition helpers.c:261
char * file2str_ext(long *len_p, const char *comp,...)
Definition helpers.c:1055
const char * extract_icao_country_code(const char *icao)
Definition helpers.c:358
bool_t is_valid_vor_freq(double freq_mhz)
Definition helpers.c:233
bool_t is_valid_ndb_freq(double freq_khz)
Definition helpers.c:297
static bool_t is_valid_hdg(double hdg)
Definition helpers.h:169
static char * sprintf_alloc(const char *fmt,...)
Definition helpers.h:572
const char * airac_cycle2eff_date(int cycle)
Definition helpers.c:485
bool_t create_directory(const char *dirname)
Definition helpers.c:1261
bool_t is_valid_tacan_freq(double freq_mhz)
Definition helpers.c:280
void * file2buf(const char *filename, size_t *bufsz)
Definition helpers.c:1134
bool_t airac_cycle2exp_date(int cycle, char buf[16], time_t *cycle_end_p)
Definition helpers.c:526
void append_format(char **str, size_t *sz, const char *format,...)
Definition helpers.c:731
void lacf_qsort_r(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
Definition helpers.c:1650
void lacf_strlcpy(char *dest, const char *src, size_t cap)
Definition helpers.c:1667
void free_strlist(char **comps, size_t num)
Definition helpers.c:703
double rel_hdg_impl(double hdg1, double hdg2, const char *file, int line)
Definition helpers.c:194
size_t utf8_char_get_num_bytes(const char *str)
Definition helpers.c:797
bool_t create_directory_recursive(const char *dirname)
Definition helpers.c:1292
bool_t is_valid_rwy_ID(const char *rwy_ID)
Definition helpers.c:379
char * path_last_comp(const char *path)
Definition helpers.c:952
void strtolower(char *str)
Definition helpers.c:773
bool_t remove_file(const char *filename, bool_t notfound_ok)
Definition helpers.c:1459
time_t airac_cycle2eff_date2(int cycle)
Definition helpers.c:499
bool_t is_valid_xpdr_code(int code)
Definition helpers.c:217
char * mkpathname_v(const char *comp, va_list ap)
Definition helpers.c:850
bool_t file_exists(const char *path, bool_t *isdir)
Definition helpers.c:1222
bool_t is_valid_iata_code(const char *iata)
Definition helpers.c:336
char * path_last_comp_subst(const char *path, const char *replace)
Definition helpers.c:924
#define logMsg(...)
Definition log.h:112
static void strip_space(char *line)
static void * safe_realloc(void *oldptr, size_t size)
Definition safe_alloc.h:86
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56