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?"&deg;":"", sep,
1290               lonsig, fabs(lon), html?"&deg;":"");
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?"&deg;":" ", (int)latmin, latsec, sep,
1294               lonsig, lonint, html?"&deg;":" ", (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?"&deg;":" ", latmin, sep,
1298               lonsig, lonint, html?"&deg;":" ", 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, "&amp;")) {
1455         *out++ = '&';
1456       } else if (! strcmp(tag, "&lt;")) {
1457         *out++ = '<';
1458       } else if (! strcmp(tag, "&gt;")) {
1459         *out++ = '>';
1460       } else if (! strcmp(tag, "&quot;")) {
1461         *out++ = '"';
1462       } else if (! strcmp(tag, "&nbsp;")) {
1463         *out++ = ' ';
1464       } else if (! strcmp(tag, "&deg;")) {
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   { "&",	"&amp;", 0 },
1505   { "'",	"&apos;", 1 },
1506   { "<",	"&lt;", 0 },
1507   { ">",	"&gt;", 0 },
1508   { "\"",	"&quot;", 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