1 /*
2 Misc utilities.
3
4 Copyright (C) 2002-2014 Robert Lipe, robertlipe+source@gpsbabel.org
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 */
21
22 #include <algorithm> // for sort
23 #include <cctype> // for isspace, isalpha, ispunct, tolower, toupper
24 #include <cerrno> // for errno
25 #include <cmath> // for fabs, floor
26 #include <cstdarg> // for va_list, va_end, va_start, va_copy
27 #include <cstdio> // for size_t, vsnprintf, FILE, fopen, printf, sprintf, stderr, stdin, stdout
28 #include <cstdint> // for uint32_t
29 #include <cstdlib> // for abs, getenv, calloc, free, malloc, realloc
30 #include <cstring> // for strlen, strcat, strstr, memcpy, strcmp, strcpy, strdup, strchr, strerror
31 #include <ctime> // for mktime, localtime
32
33 #include <QtCore/QByteArray> // for QByteArray
34 #include <QtCore/QChar> // for QChar, operator<=, operator>=
35 #include <QtCore/QCharRef> // for QCharRef
36 #include <QtCore/QDateTime> // for QDateTime
37 #include <QtCore/QFileInfo> // for QFileInfo
38 #include <QtCore/QList> // for QList
39 #include <QtCore/QScopedPointer> // for QScopedPointer
40 #include <QtCore/QString> // for QString
41 #include <QtCore/QStringRef> // for QStringRef
42 #include <QtCore/QTextCodec> // for QTextCodec
43 #include <QtCore/QTextStream> // for operator<<, QTextStream, qSetFieldWidth, endl, QTextStream::AlignLeft
44 #include <QtCore/QXmlStreamAttribute> // for QXmlStreamAttribute
45 #include <QtCore/QXmlStreamAttributes> // for QXmlStreamAttributes
46 #include <QtCore/Qt> // for CaseInsensitive
47 #include <QtCore/QTimeZone> // for QTimeZone
48 #include <QtCore/QtGlobal> // for qAsConst, QAddConst<>::Type, qPrintable
49
50 #include "defs.h"
51 #include "cet.h" // for cet_utf8_to_ucs4
52 #include "src/core/datetime.h" // for DateTime
53 #include "src/core/logging.h" // for Warning
54 #include "src/core/xmltag.h" // for xml_tag, xml_attribute, xml_findfirst, xml_findnext
55
56 // First test Apple's clever macro that's really a runtime test so
57 // that our universal binaries work right.
58 #if defined __BIG_ENDIAN__
59 #define i_am_little_endian !__BIG_ENDIAN__
60 #else
61 #if defined WORDS_BIGENDIAN
62 # define i_am_little_endian 0
63 #else
64 # define i_am_little_endian 1
65 #endif
66 #endif
67
68 void*
xmalloc(size_t size)69 xmalloc(size_t size)
70 {
71 void* obj = malloc(size);
72
73 if (!obj) {
74 fatal("gpsbabel: Unable to allocate %ld bytes of memory.\n", (unsigned long) size);
75 }
76
77 return obj;
78 }
79
80 void*
xcalloc(size_t nmemb,size_t size)81 xcalloc(size_t nmemb, size_t size)
82 {
83 void* obj = calloc(nmemb, size);
84
85 if (!obj) {
86 fatal("gpsbabel: Unable to allocate %ld units of %ld bytes of memory.\n", (unsigned long) nmemb, (unsigned long) size);
87 }
88
89 return obj;
90 }
91
92 void
xfree(const void * mem)93 xfree(const void* mem)
94 {
95 free(const_cast<void*>(mem));
96 }
97
98 char*
xstrdup(const char * s)99 xstrdup(const char* s)
100 {
101 char* o = s ? strdup(s) : strdup("");
102
103 if (!o) {
104 fatal("gpsbabel: Unable to allocate %ld bytes of memory.\n", (unsigned long) strlen(s));
105 }
106
107 return o;
108 }
109
xstrdup(const QString & s)110 char* xstrdup(const QString& s)
111 {
112 return xstrdup(CSTR(s));
113 }
114
115 /*
116 * Duplicate at most sz bytes in str.
117 */
118 char*
xstrndup(const char * str,size_t sz)119 xstrndup(const char* str, size_t sz)
120 {
121 size_t newlen = 0;
122 const char* cin = str;
123
124 while ((newlen < sz) && (*cin != '\0')) {
125 newlen++;
126 cin++;
127 }
128
129 char* newstr = (char*) xmalloc(newlen + 1);
130 memcpy(newstr, str, newlen);
131 newstr[newlen] = 0;
132
133 return newstr;
134 }
135
136 void*
xrealloc(void * p,size_t s)137 xrealloc(void* p, size_t s)
138 {
139 char* o = (char*) realloc(p, s);
140
141 if (!o) {
142 fatal("gpsbabel: Unable to realloc %ld bytes of memory.\n", (unsigned long) s);
143 }
144
145 return o;
146 }
147
148 /*
149 * For an allocated string, realloc it and append 's'
150 */
151 char*
xstrappend(char * src,const char * newd)152 xstrappend(char* src, const char* newd)
153 {
154 if (!src) {
155 return xstrdup(newd);
156 }
157 if (!newd) {
158 return xstrdup(src);
159 }
160
161 size_t newsz = strlen(src) + strlen(newd) + 1;
162 src = (char*) xrealloc(src, newsz);
163 strcat(src, newd);
164
165 return src;
166 }
167
168 /*
169 * Wrapper for open that honours - for stdin, stdout, unifies error text.
170 */
171 FILE*
xfopen(const char * fname,const char * type,const char * errtxt)172 xfopen(const char* fname, const char* type, const char* errtxt)
173 {
174 int am_writing = strchr(type, 'w') != nullptr;
175
176 if (fname == nullptr) {
177 fatal("%s must have a filename specified for %s.\n",
178 errtxt, am_writing ? "write" : "read");
179 }
180
181 if (0 == strcmp(fname, "-")) {
182 return am_writing ? stdout : stdin;
183 }
184 FILE* f = ufopen(QString::fromUtf8(fname), type);
185 if (nullptr == f) {
186 fatal("%s cannot open '%s' for %s. Error was '%s'.\n",
187 errtxt, fname,
188 am_writing ? "write" : "read",
189 strerror(errno));
190 }
191 return f;
192 }
193
194 /*
195 * Thin wrapper around fopen() that supports UTF-8 fname on all platforms.
196 */
197 FILE*
ufopen(const QString & fname,const char * mode)198 ufopen(const QString& fname, const char* mode)
199 {
200 #if __WIN32__
201 // On Windows standard fopen() doesn't support UTF-8, so we have to convert
202 // to wchar_t* (UTF-16) and use the wide-char version of fopen(), _wfopen().
203 return _wfopen((const wchar_t*) fname.utf16(),
204 (const wchar_t*) QString(mode).utf16());
205 #else
206 // On other platforms, convert to native locale (UTF-8 or other 8-bit).
207 return fopen(qPrintable(fname), mode);
208 #endif
209 }
210
211 /*
212 * OS-abstracting wrapper for getting Unicode environment variables.
213 */
ugetenv(const char * env_var)214 QString ugetenv(const char* env_var) {
215 #ifdef __WIN32__
216 // Use QString to convert 8-bit env_var argument to wchar_t* for _wgetenv().
217 return QString::fromWCharArray(
218 _wgetenv((const wchar_t*) QString(env_var).utf16()));
219 #else
220 // Everyone else uses UTF-8 or some other locale-specific 8-bit encoding.
221 return QString::fromLocal8Bit(std::getenv(env_var));
222 #endif
223 }
224
225 /*
226 * Allocate a string using a format list with optional arguments
227 * Returns -1 on error.
228 * If return value is anything else, *strp will be populated with an
229 * allocated string containing the formatted buffer.
230 *
231 * Freeing that is the responsibility of the caller.
232 */
233
234 int
xasprintf(char ** strp,const char * fmt,...)235 xasprintf(char** strp, const char* fmt, ...)
236 {
237 va_list args;
238
239 va_start(args, fmt);
240 int res = xvasprintf(strp, fmt, args);
241 va_end(args);
242
243 return res;
244 }
245
246 int
xasprintf(QString * strp,const char * fmt,...)247 xasprintf(QString* strp, const char* fmt, ...)
248 {
249 va_list args;
250 va_start(args, fmt);
251 char* cstrp;
252 int res = xvasprintf(&cstrp, fmt, args);
253 *strp = cstrp;
254 xfree(cstrp);
255 va_end(args);
256
257 return res;
258 }
259
260 int
xasprintf(QScopedPointer<char,QScopedPointerPodDeleter> & strp,const char * fmt,...)261 xasprintf(QScopedPointer<char, QScopedPointerPodDeleter>& strp, const char* fmt, ...)
262 {
263 va_list args;
264
265 va_start(args, fmt);
266 char* cstrp;
267 int res = xvasprintf(&cstrp, fmt, args);
268 strp.reset(cstrp);
269 va_end(args);
270
271 return res;
272 }
273
274 int
xvasprintf(char ** strp,const char * fmt,va_list ap)275 xvasprintf(char** strp, const char* fmt, va_list ap)
276 {
277 /* From http://perfec.to/vsnprintf/pasprintf.c */
278 /* size of first buffer malloc; start small to exercise grow routines */
279 # define FIRSTSIZE 1
280 char* buf = nullptr;
281 char* newbuf;
282 size_t nextsize = 0;
283 int outsize;
284 va_list args;
285
286 int bufsize = 0;
287 for (;;) {
288 if (bufsize == 0) {
289 if ((buf = (char*) xmalloc(FIRSTSIZE)) == nullptr) {
290 *strp = nullptr;
291 return -1;
292 }
293 bufsize = FIRSTSIZE;
294 } else if ((newbuf = (char*) xrealloc(buf, nextsize)) != nullptr) {
295 buf = newbuf;
296 bufsize = nextsize;
297 } else {
298 xfree(buf);
299 *strp = nullptr;
300 return -1;
301 }
302
303 va_copy(args, ap);
304 outsize = vsnprintf(buf, bufsize, fmt, args);
305 va_end(args);
306
307 if (outsize == -1) {
308 /* Clear indication that output was truncated, but no
309 * clear indication of how big buffer needs to be, so
310 * simply double existing buffer size for next time.
311 */
312 nextsize = bufsize * 2;
313
314 } else if (outsize == bufsize) {
315 /* Output was truncated (since at least the \0 could
316 * not fit), but no indication of how big the buffer
317 * needs to be, so just double existing buffer size
318 * for next time.
319 */
320 nextsize = bufsize * 2;
321
322 } else if (outsize > bufsize) {
323 /* Output was truncated, but we were told exactly how
324 * big the buffer needs to be next time. Add two chars
325 * to the returned size. One for the \0, and one to
326 * prevent ambiguity in the next case below.
327 */
328 nextsize = outsize + 2;
329
330 } else if (outsize == bufsize - 1) {
331 /* This is ambiguous. May mean that the output string
332 * exactly fits, but on some systems the output string
333 * may have been truncated. We can't tell.
334 * Just double the buffer size for next time.
335 */
336 nextsize = bufsize * 2;
337
338 } else {
339 /* Output was not truncated */
340 break;
341 }
342 }
343 /* Prevent us from allocating millions of unused bytes. */
344 /* O.K.: I think this is not the final solution. */
345 if (bufsize > outsize + 1) {
346 const unsigned ptrsz = sizeof(buf);
347 if (((bufsize + ptrsz + 1) / ptrsz) > ((outsize + ptrsz + 1) / ptrsz)) {
348 buf = (char*) xrealloc(buf, outsize + 1);
349 }
350
351 }
352 *strp = buf;
353 return outsize;
354 }
355
356 void
rtrim(char * s)357 rtrim(char* s)
358 {
359 char* t = s;
360
361 if (!s || !*s) {
362 return;
363 }
364
365 while (*s) {
366 s++;
367 }
368
369 s--;
370 while ((s >= t) && isspace(*s)) {
371 *s = 0;
372 s--;
373 }
374 }
375
376 /*
377 * Like trim, but trims whitespace from both beginning and end.
378 */
379 char*
lrtrim(char * buff)380 lrtrim(char* buff)
381 {
382 if (buff[0] == '\0') {
383 return buff;
384 }
385
386 char* c = buff + strlen(buff);
387 while ((c >= buff) && ((unsigned char)*c <= ' ')) {
388 *c-- = '\0';
389 }
390
391 c = buff;
392 while ((*c != '\0') && ((unsigned char)*c <= ' ')) {
393 c++;
394 }
395
396 if (c != buff) {
397 char* src = c;
398 char* dst = buff;
399
400 while (*src) {
401 *dst++ = *src++;
402 }
403 *dst = '\0';
404 }
405
406 return buff;
407 }
408
409 /*
410 * compare str with match
411 * match may contain wildcards "*" and "?"
412 *
413 * examples:
414 * str_match("ABCDE", "*BC*") -> 1
415 * str_match("ABCDE", "A*C*E") -> 1
416 * str_match("?ABCDE", "\\?A*") -> 1
417 * str_match("", "*A") -> 0
418 */
419
420 int
str_match(const char * str,const char * match)421 str_match(const char* str, const char* match)
422 {
423 const char* s = str;
424 const char* m = match;
425
426 while (*m || *s) {
427 switch (*m) {
428
429 case '\0':
430 /* there is something left in s, FAIL */
431 return 0;
432
433 case '*':
434 /* skip all wildcards */
435 while ((*m == '*') || (*m == '?')) {
436 m++;
437 }
438 if (*m == '\0') {
439 return 1;
440 }
441
442 if (*m == '\\') { /* ? escaped ? */
443 m++;
444 if (*m == '\0') {
445 return 0;
446 }
447 }
448
449 do {
450 while (*s && (*s != *m)) {
451 s++;
452 }
453 if (*s == '\0') {
454 return 0;
455 }
456
457 const char* sx = s + 1;
458 const char* mx = m + 1;
459
460 while (*sx) {
461 if (*mx == '\\') { /* ? escaped ? */
462 mx++;
463 if (*mx == '\0') {
464 return 0;
465 }
466
467 }
468 if (*sx == *mx) {
469 sx++;
470 mx++;
471 } else {
472 break;
473 }
474 }
475 if (*mx == '\0') { /* end of match */
476 if (*sx == '\0') {
477 return 1;
478 }
479 s++;
480 } else if ((*mx == '?') || (*mx == '*')) {
481 s = sx;
482 m = mx;
483 break;
484 } else {
485 s++;
486 }
487 } while (*s);
488 break;
489
490 case '?':
491 if (*s == '\0') {
492 return 0; /* no character left */
493 }
494 m++;
495 s++;
496 break;
497
498 case '\\':
499 m++;
500 if (*m == '\0') {
501 return 0; /* incomplete escape sequence */
502 }
503 /* pass-through next character */
504 /* fallthrough */
505
506 default:
507 if (*m != *s) {
508 return 0;
509 }
510 m++;
511 s++;
512 }
513 }
514 return ((*s == '\0') && (*m == '\0'));
515 }
516
517 void
printposn(const double c,int is_lat)518 printposn(const double c, int is_lat)
519 {
520 char d;
521 if (is_lat) {
522 if (c < 0) {
523 d = 'S';
524 } else {
525 d = 'N';
526 }
527 } else {
528 if (c < 0) {
529 d = 'W';
530 } else {
531 d = 'E';
532 }
533 }
534 printf("%f%c ", fabs(c), d);
535 }
536
537 void
is_fatal(const int condition,const char * fmt,...)538 is_fatal(const int condition, const char* fmt, ...)
539 {
540 va_list args;
541 char buff[128];
542
543 if (condition == 0) {
544 return;
545 }
546
547 va_start(args, fmt);
548 vsnprintf(buff, sizeof(buff), fmt, args);
549 va_end(args);
550
551 fatal("%s\n", buff);
552 }
553
554 /*
555 * Read 4 bytes in big-endian. Return as "int" in native endianness.
556 */
557 signed int
be_read32(const void * ptr)558 be_read32(const void* ptr)
559 {
560 const auto* i = (const unsigned char*) ptr;
561 return i[0] << 24 | i[1] << 16 | i[2] << 8 | i[3];
562 }
563
564 signed int
be_read16(const void * ptr)565 be_read16(const void* ptr)
566 {
567 const auto* i = (const unsigned char*) ptr;
568 return i[0] << 8 | i[1];
569 }
570
571 unsigned int
be_readu16(const void * ptr)572 be_readu16(const void* ptr)
573 {
574 const auto* i = (const unsigned char*) ptr;
575 return i[0] << 8 | i[1];
576 }
577
578 void
be_write16(void * ptr,const unsigned value)579 be_write16(void* ptr, const unsigned value)
580 {
581 auto* p = (unsigned char*) ptr;
582 p[0] = value >> 8;
583 p[1] = value;
584 }
585
586 void
be_write32(void * ptr,const unsigned value)587 be_write32(void* ptr, const unsigned value)
588 {
589 auto* p = (unsigned char*) ptr;
590
591 p[0] = value >> 24;
592 p[1] = value >> 16;
593 p[2] = value >> 8;
594 p[3] = value;
595 }
596
597 signed int
le_read16(const void * ptr)598 le_read16(const void* ptr)
599 {
600 const auto* p = (const unsigned char*) ptr;
601 return p[0] | (p[1] << 8);
602 }
603
604 unsigned int
le_readu16(const void * ptr)605 le_readu16(const void* ptr)
606 {
607 const auto* p = (const unsigned char*) ptr;
608 return p[0] | (p[1] << 8);
609 }
610
611 signed int
le_read32(const void * ptr)612 le_read32(const void* ptr)
613 {
614 const auto* p = (const unsigned char*) ptr;
615 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
616 }
617
618 unsigned int
le_readu32(const void * ptr)619 le_readu32(const void* ptr)
620 {
621 const auto* p = (const unsigned char*) ptr;
622 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
623 }
624
625 /*
626 * Read a little-endian 64-bit value from 'src' and return it in 'dest'
627 * in host endianness.
628 */
629 void
le_read64(void * dest,const void * src)630 le_read64(void* dest, const void* src)
631 {
632 char* cdest = (char*) dest;
633 const char* csrc = (const char*) src;
634
635 if (i_am_little_endian) {
636 memcpy(dest, src, 8);
637 } else {
638 int i;
639 for (i = 0; i < 8; i++) {
640 cdest[i] = csrc[7-i];
641 }
642 }
643 }
644
645 void
le_write16(void * ptr,const unsigned value)646 le_write16(void* ptr, const unsigned value)
647 {
648 auto* p = (unsigned char*) ptr;
649 p[0] = value;
650 p[1] = value >> 8;
651 }
652
653 void
le_write32(void * ptr,const unsigned value)654 le_write32(void* ptr, const unsigned value)
655 {
656 auto* p = (unsigned char*) ptr;
657 p[0] = value;
658 p[1] = value >> 8;
659 p[2] = value >> 16;
660 p[3] = value >> 24;
661 }
662
663 signed int
si_round(double d)664 si_round(double d)
665 {
666 if (d < 0) {
667 return (signed int)(d-0.5);
668 } else {
669 return (signed int)(d+0.5);
670 }
671 }
672
673 /*
674 mkgmtime -- convert tm struct in UTC to time_t
675
676 works just like mktime but without all the mucking
677 around with timezones and daylight savings
678
679 obsoletes get_tz_offset()
680
681 Borrowed from lynx GPL source code
682 http://lynx.isc.org/release/lynx2-8-5/src/mktime.c
683
684 Written by Philippe De Muyter <phdm@macqel.be>.
685 */
686
687 time_t
mkgmtime(struct tm * t)688 mkgmtime(struct tm* t)
689 {
690 static int m_to_d[12] =
691 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
692
693 short month = t->tm_mon;
694 short year = t->tm_year + month / 12 + 1900;
695 month %= 12;
696 if (month < 0) {
697 year -= 1;
698 month += 12;
699 }
700 time_t result = (year - 1970) * 365 + m_to_d[month];
701 if (month <= 1) {
702 year -= 1;
703 }
704 result += (year - 1968) / 4;
705 result -= (year - 1900) / 100;
706 result += (year - 1600) / 400;
707 result += t->tm_mday;
708 result -= 1;
709 result *= 24;
710 result += t->tm_hour;
711 result *= 60;
712 result += t->tm_min;
713 result *= 60;
714 result += t->tm_sec;
715 return (result);
716 }
717
718 /*
719 * mklocaltime: same as mktime, but try to recover the "Summer time flag",
720 * which is evaluated by mktime
721 */
722 time_t
mklocaltime(struct tm * t)723 mklocaltime(struct tm* t)
724 {
725 time_t result;
726 struct tm check = *t;
727
728 check.tm_isdst = 0;
729 result = mktime(&check);
730 check = *localtime(&result);
731 if (check.tm_isdst == 1) { /* DST is in effect */
732 check = *t;
733 check.tm_isdst = 1;
734 result = mktime(&check);
735 }
736 return result;
737 }
738
739 bool
gpsbabel_testmode()740 gpsbabel_testmode()
741 {
742 static bool testmode = getenv("GPSBABEL_FREEZE_TIME") != nullptr;
743 return testmode;
744 }
745
746 /*
747 * Historically, when we were C, this was A wrapper for time(2) that
748 * allowed us to "freeze" time for testing. The UNIX epoch
749 * (1970-1-1-00:00:00UTC) was a convenient value for that. Now in the
750 * world of Qt, sub-second time is convenient, but regenerating all the
751 * reference files would be tedious, so we uphold that convention.
752 */
753 gpsbabel::DateTime
current_time()754 current_time()
755 {
756 if (gpsbabel_testmode()) {
757 return QDateTime::fromMSecsSinceEpoch(0, Qt::UTC);
758 }
759
760 return QDateTime::currentDateTimeUtc();
761 }
762
763 /*
764 * Return the (zero based) month number of the year or -1 for failure.
765 */
766 signed int
month_lookup(const char * m)767 month_lookup(const char* m)
768 {
769 static const char* months[] = {
770 "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
771 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", nullptr
772 };
773
774 for (const char** mp = months; *mp; mp++) {
775 if (0 == case_ignore_strcmp(*mp, m)) {
776 return mp - months;
777 }
778 }
779 return -1;
780 }
781
782 /*
783 * Microsoft dot net's time format is the number of 100 nanosecond intervals
784 * since midnight Jan 1, 0001. We have time_t deeply ingrained into our
785 * internals and since we're in the GPS biz, timestamps before 1/1/1970 aren't
786 * that interesting to us anyway.
787 */
788 #define EPOCH_TICKS 621355968000000000.0
dotnet_time_to_time_t(double dotnet,time_t * t,int * millisecs)789 void dotnet_time_to_time_t(double dotnet, time_t* t, int* millisecs)
790 {
791 // TODO: replace this with better interface with normal return values
792 // and called via a QDateTime.
793 *t = (dotnet - EPOCH_TICKS) / 10000000.;
794 #if LATER
795 // TODO: work out fractional seconds.
796 if (millisecs) {
797 *millisecs = dotnet % 10000;
798 }
799 #else
800 (void)millisecs;
801 #endif
802 }
803
804
805 /*
806 * Return a pointer to a constant string that is suitable for icon lookup
807 * based on geocache attributes. The strings used are those present in
808 * a GPX file from geocaching.com. Thus we sort of make all the other
809 * formats do lookups based on these strings.
810 */
811 const char*
get_cache_icon(const Waypoint * waypointp)812 get_cache_icon(const Waypoint* waypointp)
813 {
814 if (!global_opts.smart_icons) {
815 return nullptr;
816 }
817
818 /*
819 * For icons, type overwrites container. So a multi-micro will
820 * get the icons for "multi".
821 */
822 switch (waypointp->gc_data->type) {
823 case gt_virtual:
824 return "Virtual cache";
825 case gt_multi:
826 return "Multi-Cache";
827 case gt_event:
828 return "Event Cache";
829 case gt_surprise:
830 return "Unknown Cache";
831 case gt_webcam:
832 return "Webcam Cache";
833 default:
834 break;
835 }
836
837 switch (waypointp->gc_data->container) {
838 case gc_micro:
839 return "Micro-Cache";
840 break;
841 default:
842 break;
843 }
844
845 if (waypointp->gc_data->diff > 1) {
846 return "Geocache";
847 }
848
849 return nullptr;
850 }
851
852 double
endian_read_double(const void * ptr,int read_le)853 endian_read_double(const void* ptr, int read_le)
854 {
855 double ret;
856 char r[8];
857 const void* p;
858
859 if (i_am_little_endian == read_le) {
860 p = ptr;
861 } else {
862 for (int i = 0; i < 8; i++) {
863 r[i] = ((char*)ptr)[7-i];
864 }
865 p = r;
866 }
867
868 // Word order is different on arm, but not on arm-eabi.
869 #if defined(__arm__) && !defined(__ARM_EABI__)
870 memcpy(&ret, p + 4, 4);
871 memcpy(((void*)&ret) + 4, p, 4);
872 #else
873 memcpy(&ret, p, 8);
874 #endif
875
876 return ret;
877 }
878
879 float
endian_read_float(const void * ptr,int read_le)880 endian_read_float(const void* ptr, int read_le)
881 {
882 float ret;
883 char r[4];
884 const void* p;
885
886 if (i_am_little_endian == read_le) {
887 p = ptr;
888 } else {
889 for (int i = 0; i < 4; i++) {
890 r[i] = ((char*)ptr)[3-i];
891 }
892 p = r;
893 }
894
895 memcpy(&ret, p, 4);
896 return ret;
897 }
898
899 void
endian_write_double(void * ptr,double value,int write_le)900 endian_write_double(void* ptr, double value, int write_le)
901 {
902 char* optr = (char*) ptr;
903 // Word order is different on arm, but not on arm-eabi.
904 #if defined(__arm__) && !defined(__ARM_EABI__)
905 char r[8];
906 memcpy(r + 4, &value, 4);
907 memcpy(r, ((void*)&value) + 4, 4);
908 #else
909 char* r = (char*)(void*)&value;
910 #endif
911
912
913 if (i_am_little_endian == write_le) {
914 memcpy(ptr, r, 8);
915 } else {
916 for (int i = 0; i < 8; i++) {
917 *optr++ = r[7-i];
918 }
919 }
920 }
921
922 void
endian_write_float(void * ptr,float value,int write_le)923 endian_write_float(void* ptr, float value, int write_le)
924 {
925 char* r = (char*)(void*)&value;
926 char* optr = (char*) ptr;
927
928 if (i_am_little_endian == write_le) {
929 memcpy(ptr, &value, 4);
930 } else {
931 for (int i = 0; i < 4; i++) {
932 *optr++ = r[3-i];
933 }
934 }
935 }
936
937 float
le_read_float(const void * ptr)938 le_read_float(const void* ptr)
939 {
940 return endian_read_float(ptr, 1);
941 }
942
943 void
le_write_float(void * ptr,float value)944 le_write_float(void* ptr, float value)
945 {
946 endian_write_float(ptr, value, 1);
947 }
948
949 float
be_read_float(void * ptr)950 be_read_float(void* ptr)
951 {
952 return endian_read_float(ptr, 0);
953 }
954
955 void
be_write_float(void * ptr,float value)956 be_write_float(void* ptr, float value)
957 {
958 endian_write_float(ptr, value, 0);
959 }
960
961 double
le_read_double(const void * ptr)962 le_read_double(const void* ptr)
963 {
964 return endian_read_double(ptr, 1);
965 }
966
967 void
le_write_double(void * ptr,double value)968 le_write_double(void* ptr, double value)
969 {
970 endian_write_double(ptr, value, 1);
971 }
972
973 double
be_read_double(void * ptr)974 be_read_double(void* ptr)
975 {
976 return endian_read_double(ptr, 0);
977 }
978
979 void
be_write_double(void * ptr,double value)980 be_write_double(void* ptr, double value)
981 {
982 endian_write_double(ptr, value, 0);
983 }
984
985
986 /* Magellan and PCX formats use this DDMM.mm format */
ddmm2degrees(double pcx_val)987 double ddmm2degrees(double pcx_val)
988 {
989 auto deg = (signed int)(pcx_val / 100.0);
990 double minutes = (((pcx_val / 100.0) - deg) * 100.0) / 60.0;
991 return (double) deg + minutes;
992 }
993
degrees2ddmm(double deg_val)994 double degrees2ddmm(double deg_val)
995 {
996 auto deg = (signed int) deg_val;
997 return (deg * 100.0) + ((deg_val - deg) * 60.0);
998 }
999
1000 /*
1001 * replace a single occurrence of "search" in "s" with "replace".
1002 * Returns an allocated copy if substitution was made, otherwise returns NULL.
1003 * Doesn't try to make an optimally sized dest buffer.
1004 */
1005 char*
strsub(const char * s,const char * search,const char * replace)1006 strsub(const char* s, const char* search, const char* replace)
1007 {
1008 int len = strlen(s);
1009 int slen = strlen(search);
1010 int rlen = strlen(replace);
1011
1012 const char* p = strstr(s, search);
1013 if (!slen || !p) {
1014 return nullptr;
1015 }
1016
1017 char* d = (char*) xmalloc(len + rlen + 1);
1018
1019 /* Copy first part */
1020 len = p - s;
1021 memcpy(d, s, len);
1022 d[len] = 0;
1023
1024 /* Copy replacement */
1025 strcat(d, replace);
1026
1027 /* Copy last part */
1028 strcat(d, p + slen);
1029 return d;
1030 }
1031
1032 /*
1033 * As strsub, but do it globally.
1034 */
1035 char*
gstrsub(const char * s,const char * search,const char * replace)1036 gstrsub(const char* s, const char* search, const char* replace)
1037 {
1038 int ooffs = 0;
1039 const char* c;
1040 const char* src = s;
1041 int olen = strlen(src);
1042 int slen = strlen(search);
1043 int rlen = strlen(replace);
1044
1045 char* o = (char*) xmalloc(olen + 1);
1046
1047 while ((c = strstr(src, search))) {
1048 olen += (rlen - slen);
1049 o = (char*) xrealloc(o, olen + 1);
1050 memcpy(o + ooffs, src, c - src);
1051 ooffs += (c - src);
1052 src = c + slen;
1053 if (rlen) {
1054 memcpy(o + ooffs, replace, rlen);
1055 ooffs += rlen;
1056 }
1057 }
1058
1059 if (ooffs < olen) {
1060 memcpy(o + ooffs, src, olen - ooffs);
1061 }
1062 o[olen] = '\0';
1063 return o;
1064 }
1065
1066 /*
1067 *
1068 */
1069 char*
strupper(char * src)1070 strupper(char* src)
1071 {
1072 for (char* c = src; *c; c++) {
1073 *c = toupper(*c);
1074 }
1075 return src;
1076 }
1077
1078 /*
1079 *
1080 */
1081 char*
strlower(char * src)1082 strlower(char* src)
1083 {
1084 for (char* c = src; *c; c++) {
1085 *c = tolower(*c);
1086 }
1087 return src;
1088 }
1089
1090 QString
rot13(const QString & s)1091 rot13(const QString& s)
1092 {
1093 static const QChar A('A');
1094 static const QChar M('M');
1095 static const QChar N('N');
1096 static const QChar Z('Z');
1097 QString r = s;
1098 int i = r.length();
1099 while (i--) {
1100 QChar letter = r[i].toUpper();
1101 if (letter >= A && letter <= M) {
1102 r[i] = QChar(r[i].toLatin1() + 13);
1103 } else if (letter >= N && letter <= Z) {
1104 r[i] = QChar(r[i].toLatin1() - 13);
1105 }
1106 }
1107 return r;
1108 }
1109
1110 /*
1111 * Convert a human readable date format (i.e. "YYYY/MM/DD") into
1112 * a format usable for strftime and others
1113 */
1114
1115 char*
convert_human_date_format(const char * human_datef)1116 convert_human_date_format(const char* human_datef)
1117 {
1118 char* result = (char*) xcalloc((2*strlen(human_datef)) + 1, 1);
1119 char* cout = result;
1120 char prev = '\0';
1121 int ylen = 0;
1122
1123 for (const char* cin = human_datef; *cin; cin++) {
1124 char okay = 1;
1125
1126 if (toupper(*cin) != 'Y') {
1127 ylen = 0;
1128 }
1129 if (isalpha(*cin)) {
1130 switch (*cin) {
1131 case 'y':
1132 case 'Y':
1133 if (prev != 'Y') {
1134 strcat(cout, "%y");
1135 cout += 2;
1136 prev = 'Y';
1137 }
1138 ylen++;
1139 if (ylen > 2) {
1140 *(cout-1) = 'Y';
1141 }
1142 break;
1143 case 'm':
1144 case 'M':
1145 if (prev != 'M') {
1146 strcat(cout, "%m");
1147 cout += 2;
1148 prev = 'M';
1149 }
1150 break;
1151 case 'd':
1152 case 'D':
1153 if (prev != 'D') {
1154 strcat(cout, "%d");
1155 cout += 2;
1156 prev = 'D';
1157 }
1158 break;
1159 default:
1160 okay = 0;
1161 }
1162 } else if (ispunct(*cin)) {
1163 *cout++ = *cin;
1164 prev = '\0';
1165 } else {
1166 okay = 0;
1167 }
1168
1169 is_fatal(okay == 0, "Invalid character \"%c\" in date format!", *cin);
1170 }
1171 return result;
1172 }
1173
1174 /*
1175 * Convert a human readable time format (i.e. "HH:mm:ss") into
1176 * a format usable for strftime and others
1177 */
1178
1179 char*
convert_human_time_format(const char * human_timef)1180 convert_human_time_format(const char* human_timef)
1181 {
1182 char* result = (char*) xcalloc((2*strlen(human_timef)) + 1, 1);
1183 char* cout = result;
1184 char prev = '\0';
1185
1186 for (const char* cin = human_timef; *cin; cin++) {
1187 int okay = 1;
1188
1189 if (isalpha(*cin)) {
1190 switch (*cin) {
1191 case 'S':
1192 case 's':
1193 if (prev != 'S') {
1194 strcat(cout, "%S");
1195 cout += 2;
1196 prev = 'S';
1197 }
1198 break;
1199
1200 case 'M':
1201 case 'm':
1202 if (prev != 'M') {
1203 strcat(cout, "%M");
1204 cout += 2;
1205 prev = 'M';
1206 }
1207 break;
1208
1209 case 'h': /* 12-hour-clock */
1210 if (prev != 'H') {
1211 strcat(cout, "%l"); /* 1 .. 12 */
1212 cout += 2;
1213 prev = 'H';
1214 } else {
1215 *(cout-1) = 'I'; /* 01 .. 12 */
1216 }
1217 break;
1218
1219 case 'H': /* 24-hour-clock */
1220 if (prev != 'H') {
1221 strcat(cout, "%k");
1222 cout += 2;
1223 prev = 'H';
1224 } else {
1225 *(cout-1) = 'H';
1226 }
1227 break;
1228
1229 case 'x':
1230 if (prev != 'X') {
1231 strcat(cout, "%P");
1232 cout += 2;
1233 prev = 'X';
1234 } else {
1235 *(cout-1) = 'P';
1236 }
1237 break;
1238
1239 case 'X':
1240 if (prev != 'X') {
1241 strcat(cout, "%p");
1242 cout += 2;
1243 prev = 'X';
1244 } else {
1245 *(cout-1) = 'p';
1246 }
1247 break;
1248
1249 default:
1250 okay = 0;
1251 }
1252 } else if (ispunct(*cin) || isspace(*cin)) {
1253 *cout++ = *cin;
1254 prev = '\0';
1255 } else {
1256 okay = 0;
1257 }
1258
1259 is_fatal(okay == 0, "Invalid character \"%c\" in time format!", *cin);
1260 }
1261 return result;
1262 }
1263
1264
1265 /*
1266 * Return a decimal degree pair as
1267 * DD.DDDDD DD MM.MMM or DD MM SS.S
1268 * fmt = ['d', 'm', 's']
1269 * sep = string between lat and lon (separator)
1270 * html = 1 for html output otherwise text
1271 */
1272 char*
pretty_deg_format(double lat,double lon,char fmt,const char * sep,int html)1273 pretty_deg_format(double lat, double lon, char fmt, const char* sep, int html)
1274 {
1275 char* result;
1276 char latsig = lat < 0 ? 'S':'N';
1277 char lonsig = lon < 0 ? 'W':'E';
1278 int latint = abs((int) lat);
1279 int lonint = abs((int) lon);
1280 double latmin = 60.0 * (fabs(lat) - latint);
1281 double lonmin = 60.0 * (fabs(lon) - lonint);
1282 double latsec = 60.0 * (latmin - floor(latmin));
1283 double lonsec = 60.0 * (lonmin - floor(lonmin));
1284 if (sep == nullptr) {
1285 sep = " "; /* default " " */
1286 }
1287 if (fmt == 'd') { /* ddd */
1288 xasprintf(&result, "%c%6.5f%s%s%c%6.5f%s",
1289 latsig, fabs(lat), html?"°":"", sep,
1290 lonsig, fabs(lon), html?"°":"");
1291 } else if (fmt == 's') { /* dms */
1292 xasprintf(&result, "%c%d%s%02d'%04.1f\"%s%c%d%s%02d'%04.1f\"",
1293 latsig, latint, html?"°":" ", (int)latmin, latsec, sep,
1294 lonsig, lonint, html?"°":" ", (int)lonmin, lonsec);
1295 } else { /* default dmm */
1296 xasprintf(&result, "%c%d%s%06.3f%s%c%d%s%06.3f",
1297 latsig, latint, html?"°":" ", latmin, sep,
1298 lonsig, lonint, html?"°":" ", lonmin);
1299 }
1300 return result;
1301 }
1302
1303
1304
1305 /*
1306 * Get rid of potentially nasty HTML that would influence another record
1307 * that includes;
1308 * <body> - to stop backgrounds/background colours from being loaded
1309 * </body> and </html>- stop processing altogether
1310 * <style> </style> - stop overriding styles for everything
1311 */
1312 char*
strip_nastyhtml(const QString & in)1313 strip_nastyhtml(const QString& in)
1314 {
1315 char* returnstr;
1316 char* lcstr;
1317
1318 char* sp = returnstr = xstrdup(in);
1319 char* lcp = lcstr = strlower(xstrdup(in));
1320
1321 while (lcp = strstr(lcstr, "<body>"), nullptr != lcp) {
1322 sp = returnstr + (lcp - lcstr) ; /* becomes <! > */
1323 sp++;
1324 *sp++ = '!';
1325 *sp++ = ' ';
1326 *sp++ = ' ';
1327 *sp++ = ' ';
1328 *lcp = '*'; /* so we wont find it again */
1329 }
1330 while (lcp = strstr(lcstr, "<body"), lcp != nullptr) { /* becomes <!-- --> */
1331 sp = returnstr + (lcp - lcstr) ;
1332 sp++;
1333 *sp++ = '!';
1334 *sp++ = '-';
1335 *sp++ = '-';
1336 while ((*sp) && (*sp != '>')) {
1337 sp++;
1338 }
1339 *--sp = '-';
1340 *--sp = '-';
1341 *lcp = '*'; /* so we wont find it again */
1342 }
1343 while (lcp = strstr(lcstr, "</body>"), nullptr != lcp) {
1344 sp = returnstr + (lcp - lcstr) ; /* becomes <!---- */
1345 sp++;
1346 *sp++ = '!';
1347 *sp++ = '-';
1348 *sp++ = '-';
1349 *sp++ = '-';
1350 *sp++ = '-';
1351 *lcp = '*'; /* so we wont find it again */
1352 }
1353 while (lcp = strstr(lcstr, "</html>"), nullptr != lcp) {
1354 sp = returnstr + (lcp - lcstr) ; /* becomes </---- */
1355 sp++;
1356 *sp++ = '!';
1357 *sp++ = '-';
1358 *sp++ = '-';
1359 *sp++ = '-';
1360 *sp++ = '-';
1361 *lcp = '*'; /* so we wont find it again */
1362 }
1363 while (lcp = strstr(lcstr, "<style"), nullptr != lcp) {
1364 sp = returnstr + (lcp - lcstr) ; /* becomes <!-- */
1365 sp++;
1366 *sp++ = '!';
1367 *sp++ = '-';
1368 *sp++ = '-';
1369 *sp++ = ' ';
1370 *sp++ = ' ';
1371 *sp = ' ';
1372 *lcp = '*'; /* so we wont find it again */
1373 }
1374 while (lcp = strstr(lcstr, "</style>"), nullptr != lcp) {
1375 sp = returnstr + (lcp - lcstr) ; /* becomes --> */
1376 *sp++ = ' ';
1377 *sp++ = ' ';
1378 *sp++ = ' ';
1379 *sp++ = ' ';
1380 *sp++ = ' ';
1381 *sp++ = '-';
1382 *sp++ = '-';
1383 *lcp = '*'; /* so we wont find it again */
1384 }
1385 while (lcp = strstr(lcstr, "<image"), nullptr != lcp) {
1386 sp = returnstr + (lcp - lcstr) ; /* becomes <img */
1387 sp+=3;
1388 *sp++ = 'g';
1389 *sp++ = ' ';
1390 *sp++ = ' ';
1391 *lcp = '*';
1392 }
1393 xfree(lcstr);
1394 return (returnstr);
1395 }
1396
1397 /*
1398 * Without getting into all the complexity of technically legal HTML,
1399 * this function tries to strip "ugly" parts of it to make it more
1400 * pleasant for a human reader. Yes, this falls down in all kinds of
1401 * ways such as spaces within the tags, etc.
1402 */
1403 char*
strip_html(const utf_string * in)1404 strip_html(const utf_string* in)
1405 {
1406 #if 0
1407 // If we were willing to link core against QtGui (not out of the question)
1408 // we could just do...and either decide whether to add handling for [IMG]
1409 // or just say we don't do that any more.
1410 QTextDocument doc;
1411 doc.setHtml(in->utfstring);
1412 return xstrdup(CSTR(doc.toPlainText().simplified()));
1413 #else
1414 char* out;
1415 char* instr;
1416 char tag[8];
1417 unsigned short int taglen = 0;
1418
1419 char* incopy = instr = xstrdup(in->utfstring);
1420 if (!in->is_html) {
1421 return instr;
1422 }
1423 /*
1424 * We only shorten, so just dupe the input buf for space.
1425 */
1426 char* outstring = out = xstrdup(in->utfstring);
1427
1428 tag[0] = 0;
1429 while (*instr) {
1430 if ((*instr == '<') || (*instr == '&')) {
1431 tag[0] = *instr;
1432 taglen = 0;
1433 }
1434
1435 if (! tag[0]) {
1436 if (*instr == '\n') {
1437 *out++ = ' ';
1438 do {
1439 instr++;
1440 } while (isspace(*instr));
1441 continue;
1442 } else {
1443 *out++ = *instr;
1444 }
1445 } else {
1446 if (taglen < (sizeof(tag)-1)) {
1447 tag[taglen++] = tolower(*instr);
1448 tag[taglen] = 0;
1449 }
1450 }
1451
1452 if (((tag[0] == '<') && (*instr == '>')) ||
1453 ((tag[0] == '&') && (*instr == ';'))) {
1454 if (! strcmp(tag, "&")) {
1455 *out++ = '&';
1456 } else if (! strcmp(tag, "<")) {
1457 *out++ = '<';
1458 } else if (! strcmp(tag, ">")) {
1459 *out++ = '>';
1460 } else if (! strcmp(tag, """)) {
1461 *out++ = '"';
1462 } else if (! strcmp(tag, " ")) {
1463 *out++ = ' ';
1464 } else if (! strcmp(tag, "°")) {
1465 *out++ = 'd';
1466 *out++ = 'e';
1467 *out++ = 'g';
1468 } else if ((tag[0]=='<') && (tag[1]=='p')) {
1469 *out++ = '\n';
1470 } else if ((tag[0]=='<') && (tag[1]=='b') && (tag[2]=='r')) {
1471 *out++ = '\n';
1472 } else if ((tag[0]=='<') && (tag[1]=='/') && (tag[2]=='t') && (tag[3]=='r')) {
1473 *out++ = '\n';
1474 } else if ((tag[0]=='<') && (tag[1]=='/') && (tag[2]=='t') && (tag[3]=='d')) {
1475 *out++ = ' ';
1476 } else if ((tag[0]=='<') && (tag[1]=='i') && (tag[2]=='m') && (tag[3]=='g')) {
1477 *out++ = '[';
1478 *out++ = 'I';
1479 *out++ = 'M';
1480 *out++ = 'G';
1481 *out++ = ']';
1482 }
1483
1484 tag[0] = 0;
1485 }
1486 instr++;
1487 }
1488 *out++ = 0;
1489 if (incopy) {
1490 xfree(incopy);
1491 }
1492 return (outstring);
1493 #endif
1494 }
1495
1496 struct entity_types {
1497 const char* text;
1498 const char* entity;
1499 int not_html;
1500 };
1501
1502 static
1503 entity_types stdentities[] = {
1504 { "&", "&", 0 },
1505 { "'", "'", 1 },
1506 { "<", "<", 0 },
1507 { ">", ">", 0 },
1508 { "\"", """, 0 },
1509 { "\x01", " ", 1 }, // illegal xml 1.0 character
1510 { "\x02", " ", 1 }, // illegal xml 1.0 character
1511 { "\x03", " ", 1 }, // illegal xml 1.0 character
1512 { "\x04", " ", 1 }, // illegal xml 1.0 character
1513 { "\x05", " ", 1 }, // illegal xml 1.0 character
1514 { "\x06", " ", 1 }, // illegal xml 1.0 character
1515 { "\x07", " ", 1 }, // illegal xml 1.0 character
1516 { "\x08", " ", 1 }, // illegal xml 1.0 character
1517 // { "\x09", " ", 1 }, legal xml 1.0 character
1518 // { "\x0a", " ", 1 }, legal xml 1.0 character
1519 { "\x0b", " ", 1 }, // illegal xml 1.0 character
1520 { "\x0c", " ", 1 }, // illegal xml 1.0 character
1521 // { "\x0d", " ", 1 }, legal xml 1.0 character
1522 { "\x0e", " ", 1 }, // illegal xml 1.0 character
1523 { "\x0f", " ", 1 }, // illegal xml 1.0 character
1524 { "\x10", " ", 1 }, // illegal xml 1.0 character
1525 { "\x11", " ", 1 }, // illegal xml 1.0 character
1526 { "\x12", " ", 1 }, // illegal xml 1.0 character
1527 { "\x13", " ", 1 }, // illegal xml 1.0 character
1528 { "\x14", " ", 1 }, // illegal xml 1.0 character
1529 { "\x15", " ", 1 }, // illegal xml 1.0 character
1530 { "\x16", " ", 1 }, // illegal xml 1.0 character
1531 { "\x17", " ", 1 }, // illegal xml 1.0 character
1532 { "\x18", " ", 1 }, // illegal xml 1.0 character
1533 { "\x19", " ", 1 }, // illegal xml 1.0 character
1534 { "\x1a", " ", 1 }, // illegal xml 1.0 character
1535 { "\x1b", " ", 1 }, // illegal xml 1.0 character
1536 { "\x1c", " ", 1 }, // illegal xml 1.0 character
1537 { "\x1d", " ", 1 }, //illegal xml 1.0 character
1538 { "\x1e", " ", 1 }, //illegal xml 1.0 character
1539 { "\x1f", " ", 1 }, //illegal xml 1.0 character
1540 { nullptr, nullptr, 0 }
1541 };
1542
1543 static
1544 char*
entitize(const char * str,bool is_html)1545 entitize(const char* str, bool is_html)
1546 {
1547 int ecount;
1548 int nsecount;
1549 char* p;
1550 char* tmp;
1551 char* xstr;
1552
1553 int bytes = 0;
1554 int value = 0;
1555 entity_types* ep = stdentities;
1556 int elen = ecount = nsecount = 0;
1557
1558 /* figure # of entity replacements and additional size. */
1559 while (ep->text) {
1560 const char* cp = str;
1561 while ((cp = strstr(cp, ep->text)) != nullptr) {
1562 elen += strlen(ep->entity) - strlen(ep->text);
1563 ecount++;
1564 cp += strlen(ep->text);
1565 }
1566 ep++;
1567 }
1568
1569 /* figure the same for other than standard entities (i.e. anything
1570 * that isn't in the range U+0000 to U+007F */
1571
1572 #if 0
1573 for (cp = str; *cp; cp++) {
1574 if (*cp & 0x80) {
1575 cet_utf8_to_ucs4(cp, &bytes, &value);
1576 cp += bytes-1;
1577 elen += sprintf(tmpsub, "&#x%x;", value) - bytes;
1578 nsecount++;
1579 }
1580 }
1581 #endif
1582
1583 /* enough space for the whole string plus entity replacements, if any */
1584 tmp = (char*) xcalloc((strlen(str) + elen + 1), 1);
1585 strcpy(tmp, str);
1586
1587 /* no entity replacements */
1588 if (ecount == 0 && nsecount == 0) {
1589 return (tmp);
1590 }
1591
1592 if (ecount != 0) {
1593 for (ep = stdentities; ep->text; ep++) {
1594 p = tmp;
1595 if (is_html && ep->not_html) {
1596 continue;
1597 }
1598 while ((p = strstr(p, ep->text)) != nullptr) {
1599 elen = strlen(ep->entity);
1600
1601 xstr = xstrdup(p + strlen(ep->text));
1602
1603 strcpy(p, ep->entity);
1604 strcpy(p + elen, xstr);
1605
1606 xfree(xstr);
1607
1608 p += elen;
1609 }
1610 }
1611 }
1612
1613 if (nsecount != 0) {
1614 p = tmp;
1615 while (*p) {
1616 if (*p & 0x80) {
1617 cet_utf8_to_ucs4(p, &bytes, &value);
1618 if (p[bytes]) {
1619 xstr = xstrdup(p + bytes);
1620 } else {
1621 xstr = nullptr;
1622 }
1623 sprintf(p, "&#x%x;", value);
1624 p = p+strlen(p);
1625 if (xstr) {
1626 strcpy(p, xstr);
1627 xfree(xstr);
1628 }
1629 } else {
1630 p++;
1631 }
1632 }
1633 }
1634 return (tmp);
1635 }
1636
1637 /*
1638 * Public callers for the above to hide the absence of &apos from HTML
1639 */
1640
xml_entitize(const char * str)1641 char* xml_entitize(const char* str)
1642 {
1643 return entitize(str, false);
1644 }
1645
html_entitize(const char * str)1646 char* html_entitize(const char* str)
1647 {
1648 return entitize(str, true);
1649 }
html_entitize(const QString & str)1650 char* html_entitize(const QString& str)
1651 {
1652 return entitize(CSTR(str), true);
1653 }
1654
1655 /*
1656 * xml_tag utilities
1657 */
1658
xml_next(xml_tag * root,xml_tag * cur)1659 xml_tag* xml_next(xml_tag* root, xml_tag* cur)
1660 {
1661 if (cur->child) {
1662 cur = cur->child;
1663 } else if (cur->sibling) {
1664 cur = cur->sibling;
1665 } else {
1666 cur = cur->parent;
1667 if (cur == root) {
1668 cur = nullptr;
1669 }
1670 if (cur) {
1671 cur = cur->sibling;
1672 }
1673 }
1674 return cur;
1675 }
1676
xml_findnext(xml_tag * root,xml_tag * cur,const QString & tagname)1677 xml_tag* xml_findnext(xml_tag* root, xml_tag* cur, const QString& tagname)
1678 {
1679 xml_tag* result = cur;
1680 do {
1681 result = xml_next(root, result);
1682 } while (result && result->tagname.compare(tagname, Qt::CaseInsensitive));
1683 return result;
1684 }
1685
xml_findfirst(xml_tag * root,const QString & tagname)1686 xml_tag* xml_findfirst(xml_tag* root, const QString& tagname)
1687 {
1688 return xml_findnext(root, root, tagname);
1689 }
1690
xml_attribute(const QXmlStreamAttributes & attributes,const QString & attrname)1691 QString xml_attribute(const QXmlStreamAttributes& attributes, const QString& attrname)
1692 {
1693 for (const auto& attribute : attributes) {
1694 if (attribute.qualifiedName().compare(attrname, Qt::CaseInsensitive) == 0) {
1695 return attribute.value().toString();
1696 }
1697 }
1698 return QString();
1699 }
1700
get_filename(const QString & fname)1701 const QString get_filename(const QString& fname)
1702 {
1703 return QFileInfo(fname).fileName();
1704 }
1705
1706 /* bit manipulation functions */
1707
1708 /*
1709 * setbit: Set bit number [nr] of buffer [buf]
1710 */
gb_setbit(void * buf,const uint32_t nr)1711 void gb_setbit(void* buf, const uint32_t nr)
1712 {
1713 auto* bytes = (unsigned char*) buf;
1714 bytes[nr / 8] |= (1 << (nr % 8));
1715 }
1716
1717 /*
1718 * setbit: Get state of bit number [nr] of buffer [buf]
1719 */
gb_getbit(const void * buf,const uint32_t nr)1720 char gb_getbit(const void* buf, const uint32_t nr)
1721 {
1722 const auto* bytes = (const unsigned char*) buf;
1723 return (bytes[nr / 8] & (1 << (nr % 8)));
1724
1725 }
1726
1727 /*
1728 * gb_int2ptr: Needed, when sizeof(*void) != sizeof(int) ! compiler warning !
1729 */
gb_int2ptr(const int i)1730 void* gb_int2ptr(const int i)
1731 {
1732 union {
1733 void* p;
1734 int i;
1735 } x = { nullptr };
1736
1737 x.i = i;
1738 return x.p;
1739 }
1740
1741 /*
1742 * gb_ptr2int: Needed, when sizeof(*void) != sizeof(int) ! compiler warning !
1743 */
gb_ptr2int(const void * p)1744 int gb_ptr2int(const void* p)
1745 {
1746 union {
1747 const void* p;
1748 int i;
1749 } x = { p };
1750
1751 return x.i;
1752 }
1753
1754 void
list_codecs()1755 list_codecs()
1756 {
1757 QTextStream info(stderr);
1758 info.setFieldAlignment(QTextStream::AlignLeft);
1759 const auto mibs = QTextCodec::availableMibs();
1760 int maxlen = 0;
1761 for (auto mib : mibs) {
1762 auto codec = QTextCodec::codecForMib(mib);
1763 if (codec->name().size() > maxlen) {
1764 maxlen = codec->name().size();
1765 }
1766 }
1767 info << "Available Codecs:" << endl;
1768 info << qSetFieldWidth(8) << "MIBenum" << qSetFieldWidth(maxlen+1) << "Name" << qSetFieldWidth(0) << "Aliases" << endl;
1769 for (auto mib : mibs) {
1770 auto codec = QTextCodec::codecForMib(mib);
1771 info << qSetFieldWidth(8) << mib << qSetFieldWidth(maxlen+1) << codec->name() << qSetFieldWidth(0);
1772 bool first = true;
1773 const auto aliases = codec->aliases();
1774 for (const auto& alias : aliases) {
1775 if (first) {
1776 first = false;
1777 } else {
1778 info << ", ";
1779 }
1780 info << alias;
1781 }
1782 info << endl;
1783 }
1784 }
1785
list_timezones()1786 void list_timezones()
1787 {
1788 QList<QByteArray> zoneids = QTimeZone::availableTimeZoneIds();
1789 auto alpha = [](const QByteArray& a, const QByteArray& b)->bool {
1790 return QString::compare(a, b, Qt::CaseInsensitive) < 0;
1791 };
1792 std::sort(zoneids.begin(), zoneids.end(), alpha);
1793 Warning() << "Available timezones are:";
1794 for (const auto& id : qAsConst(zoneids)) {
1795 Warning() << id;
1796 }
1797 }
1798
1799