1 /*
2 
3  gg_sqlaux.c -- SQL ancillary functions
4 
5  version 5.0, 2020 August 1
6 
7  Author: Sandro Furieri a.furieri@lqt.it
8 
9  -----------------------------------------------------------------------------
10 
11  Version: MPL 1.1/GPL 2.0/LGPL 2.1
12 
13  The contents of this file are subject to the Mozilla Public License Version
14  1.1 (the "License"); you may not use this file except in compliance with
15  the License. You may obtain a copy of the License at
16  http://www.mozilla.org/MPL/
17 
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22 
23 The Original Code is the SpatiaLite library
24 
25 The Initial Developer of the Original Code is Alessandro Furieri
26 
27 Portions created by the Initial Developer are Copyright (C) 2008-2021
28 the Initial Developer. All Rights Reserved.
29 
30 Contributor(s):
31 
32 Alternatively, the contents of this file may be used under the terms of
33 either the GNU General Public License Version 2 or later (the "GPL"), or
34 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 in which case the provisions of the GPL or the LGPL are applicable instead
36 of those above. If you wish to allow use of your version of this file only
37 under the terms of either the GPL or the LGPL, and not to allow others to
38 use your version of this file under the terms of the MPL, indicate your
39 decision by deleting the provisions above and replace them with the notice
40 and other provisions required by the GPL or the LGPL. If you do not delete
41 the provisions above, a recipient may use your version of this file under
42 the terms of any one of the MPL, the GPL or the LGPL.
43 
44 */
45 
46 #include <sys/types.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <math.h>
51 #include <ctype.h>
52 
53 #if defined(_WIN32) && !defined(__MINGW32__)
54 #include "config-msvc.h"
55 #else
56 #include "config.h"
57 #endif
58 
59 #include <spatialite/sqlite.h>
60 
61 #include <spatialite/gaiaaux.h>
62 #include <spatialite_private.h>
63 
64 #ifdef _WIN32
65 #define strcasecmp	_stricmp
66 #endif /* not WIN32 */
67 
68 /* 64 bit integer: portable format for printf() */
69 #if defined(_WIN32) && !defined(__MINGW32__)
70 #define FRMT64 "%I64d"
71 #else
72 #define FRMT64 "%lld"
73 #endif
74 
75 GAIAAUX_DECLARE int
gaiaIllegalSqlName(const char * name)76 gaiaIllegalSqlName (const char *name)
77 {
78 /* checks if column-name is an SQL illegal name */
79     int i;
80     int len;
81     if (!name)
82 	return 1;
83     len = strlen (name);
84     if (len == 0)
85 	return 1;
86     for (i = 0; i < len; i++)
87       {
88 	  if (name[i] >= 'a' && name[i] <= 'z')
89 	      continue;
90 	  if (name[i] >= 'A' && name[i] <= 'Z')
91 	      continue;
92 	  if (name[i] >= '0' && name[i] <= '9')
93 	      continue;
94 	  if (name[i] == '_')
95 	      continue;
96 	  /* the name contains an illegal char */
97 	  return 1;
98       }
99     if (name[0] >= 'a' && name[0] <= 'z')
100 	return 0;
101     if (name[0] >= 'A' && name[0] <= 'Z')
102 	return 0;
103 /* the first char in the name isn't a letter */
104     return 1;
105 }
106 
107 GAIAAUX_DECLARE int
gaiaIsReservedSqliteName(const char * name)108 gaiaIsReservedSqliteName (const char *name)
109 {
110 /* checks if column-name is an SQLite reserved keyword */
111     char *reserved[] = {
112 	"ALL",
113 	"ALTER",
114 	"AND",
115 	"AS",
116 	"AUTOINCREMENT",
117 	"BETWEEN",
118 	"BLOB",
119 	"BY",
120 	"CASE",
121 	"CHECK",
122 	"COLLATE",
123 	"COMMIT",
124 	"CONSTRAINT",
125 	"CREATE",
126 	"CROSS",
127 	"DATE",
128 	"DATETIME",
129 	"DEFAULT",
130 	"DEFERRABLE",
131 	"DELETE",
132 	"DISTINCT",
133 	"DOUBLE",
134 	"DROP",
135 	"ELSE",
136 	"ESCAPE",
137 	"EXCEPT",
138 	"FOREIGN",
139 	"FROM",
140 	"FULL",
141 	"GLOB",
142 	"GROUP",
143 	"HAVING",
144 	"IN",
145 	"INDEX",
146 	"INNER",
147 	"INSERT",
148 	"INTEGER",
149 	"INTERSECT",
150 	"INTO",
151 	"IS",
152 	"ISNULL",
153 	"JOIN",
154 	"KEY",
155 	"LEFT",
156 	"LIKE",
157 	"LIMIT",
158 	"MATCH",
159 	"NATURAL",
160 	"NOT",
161 	"NOTNULL",
162 	"NULL",
163 	"ON",
164 	"OR",
165 	"ORDER",
166 	"OUTER",
167 	"PRAGMA",
168 	"PRIMARY",
169 	"REFERENCES",
170 	"REPLACE",
171 	"RIGHT",
172 	"ROLLBACK",
173 	"SELECT",
174 	"SET",
175 	"TABLE",
176 	"TEMP",
177 	"TEMPORARY",
178 	"THEN",
179 	"TEXT",
180 	"TIMESTAMP",
181 	"TO",
182 	"TRANSACTION",
183 	"UNION",
184 	"UNIQUE",
185 	"UPDATE",
186 	"USING",
187 	"VALUES",
188 	"VIEW",
189 	"WHEN",
190 	"WHERE",
191 	NULL
192     };
193     char **pw = reserved;
194     while (*pw != NULL)
195       {
196 	  if (strcasecmp (name, *pw) == 0)
197 	      return 1;
198 	  pw++;
199       }
200     return 0;
201 }
202 
203 GAIAAUX_DECLARE int
gaiaIsReservedSqlName(const char * name)204 gaiaIsReservedSqlName (const char *name)
205 {
206 /* checks if column-name is an SQL reserved keyword */
207     char *reserved[] = {
208 	"ABSOLUTE",
209 	"ACTION",
210 	"ADD",
211 	"AFTER",
212 	"ALL",
213 	"ALLOCATE",
214 	"ALTER",
215 	"AND",
216 	"ANY",
217 	"ARE",
218 	"ARRAY",
219 	"AS",
220 	"ASC",
221 	"ASENSITIVE",
222 	"ASSERTION",
223 	"ASYMMETRIC",
224 	"AT",
225 	"ATOMIC",
226 	"AUTHORIZATION",
227 	"AVG",
228 	"BEFORE",
229 	"BEGIN",
230 	"BETWEEN",
231 	"BIGINT",
232 	"BINARY",
233 	"BIT",
234 	"BIT_LENGTH",
235 	"BLOB",
236 	"BOOLEAN",
237 	"BOTH",
238 	"BREADTH",
239 	"BY",
240 	"CALL",
241 	"CALLED",
242 	"CASCADE",
243 	"CASCADED",
244 	"CASE",
245 	"CAST",
246 	"CATALOG",
247 	"CHAR",
248 	"CHARACTER",
249 	"CHARACTER_LENGTH",
250 	"CHAR_LENGTH",
251 	"CHECK",
252 	"CLOB",
253 	"CLOSE",
254 	"COALESCE",
255 	"COLLATE",
256 	"COLLATION",
257 	"COLUMN",
258 	"COMMIT",
259 	"CONDITION",
260 	"CONNECT",
261 	"CONNECTION",
262 	"CONSTRAINT",
263 	"CONSTRAINTS",
264 	"CONSTRUCTOR",
265 	"CONTAINS",
266 	"CONTINUE",
267 	"CONVERT",
268 	"CORRESPONDING",
269 	"COUNT",
270 	"CREATE",
271 	"CROSS",
272 	"CUBE",
273 	"CURRENT",
274 	"CURRENT_DATE",
275 	"CURRENT_DEFAULT_TRANSFORM_GROUP",
276 	"CURRENT_PATH",
277 	"CURRENT_ROLE",
278 	"CURRENT_TIME",
279 	"CURRENT_TIMESTAMP",
280 	"CURRENT_TRANSFORM_GROUP_FOR_TYPE",
281 	"CURRENT_USER",
282 	"CURSOR",
283 	"CYCLE",
284 	"DATA",
285 	"DATE",
286 	"DAY",
287 	"DEALLOCATE",
288 	"DEC",
289 	"DECIMAL",
290 	"DECLARE",
291 	"DEFAULT",
292 	"DEFERRABLE",
293 	"DEFERRED",
294 	"DELETE",
295 	"DEPTH",
296 	"DEREF",
297 	"DESC",
298 	"DESCRIBE",
299 	"DESCRIPTOR",
300 	"DETERMINISTIC",
301 	"DIAGNOSTICS",
302 	"DISCONNECT",
303 	"DISTINCT",
304 	"DO",
305 	"DOMAIN",
306 	"DOUBLE",
307 	"DROP",
308 	"DYNAMIC",
309 	"EACH",
310 	"ELEMENT",
311 	"ELSE",
312 	"ELSEIF",
313 	"END",
314 	"EQUALS",
315 	"ESCAPE",
316 	"EXCEPT",
317 	"EXCEPTION",
318 	"EXEC",
319 	"EXECUTE",
320 	"EXISTS",
321 	"EXIT",
322 	"external",
323 	"EXTRACT",
324 	"FALSE",
325 	"FETCH",
326 	"FILTER",
327 	"FIRST",
328 	"FLOAT",
329 	"FOR",
330 	"FOREIGN",
331 	"FOUND",
332 	"FREE",
333 	"FROM",
334 	"FULL",
335 	"FUNCTION",
336 	"GENERAL",
337 	"GET",
338 	"GLOBAL",
339 	"GO",
340 	"GOTO",
341 	"GRANT",
342 	"GROUP",
343 	"GROUPING",
344 	"HANDLER",
345 	"HAVING",
346 	"HOLD",
347 	"HOUR",
348 	"IDENTITY",
349 	"IF",
350 	"IMMEDIATE",
351 	"IN",
352 	"INDICATOR",
353 	"INITIALLY",
354 	"INNER",
355 	"INOUT",
356 	"INPUT",
357 	"INSENSITIVE",
358 	"INSERT",
359 	"INT",
360 	"INTEGER",
361 	"INTERSECT",
362 	"INTERVAL",
363 	"INTO",
364 	"IS",
365 	"ISOLATION",
366 	"ITERATE",
367 	"JOIN",
368 	"KEY",
369 	"LANGUAGE",
370 	"LARGE",
371 	"LAST",
372 	"LATERAL",
373 	"LEADING",
374 	"LEAVE",
375 	"LEFT",
376 	"LEVEL",
377 	"LIKE",
378 	"LOCAL",
379 	"LOCALTIME",
380 	"LOCALTIMESTAMP",
381 	"LOCATOR",
382 	"LOOP",
383 	"LOWER",
384 	"MAP",
385 	"MATCH",
386 	"MAX",
387 	"MEMBER",
388 	"MERGE",
389 	"METHOD",
390 	"MIN",
391 	"MINUTE",
392 	"MODIFIES",
393 	"MODULE",
394 	"MONTH",
395 	"MULTISET",
396 	"NAMES",
397 	"NATIONAL",
398 	"NATURAL",
399 	"NCHAR",
400 	"NCLOB",
401 	"NEW",
402 	"NEXT",
403 	"NO",
404 	"NONE",
405 	"NOT",
406 	"NULL",
407 	"NULLIF",
408 	"NUMERIC",
409 	"OBJECT",
410 	"OCTET_LENGTH",
411 	"OF",
412 	"OLD",
413 	"ON",
414 	"ONLY",
415 	"OPEN",
416 	"OPTION",
417 	"OR",
418 	"ORDER",
419 	"ORDINALITY",
420 	"OUT",
421 	"OUTER",
422 	"OUTPUT",
423 	"OVER",
424 	"OVERLAPS",
425 	"PAD",
426 	"PARAMETER",
427 	"PARTIAL",
428 	"PARTITION",
429 	"PATH",
430 	"POSITION",
431 	"PRECISION",
432 	"PREPARE",
433 	"PRESERVE",
434 	"PRIMARY",
435 	"PRIOR",
436 	"PRIVILEGES",
437 	"PROCEDURE",
438 	"PUBLIC",
439 	"RANGE",
440 	"READ",
441 	"READS",
442 	"REAL",
443 	"RECURSIVE",
444 	"REF",
445 	"REFERENCES",
446 	"REFERENCING",
447 	"RELATIVE",
448 	"RELEASE",
449 	"REPEAT",
450 	"RESIGNAL",
451 	"RESTRICT",
452 	"RESULT",
453 	"RETURN",
454 	"RETURNS",
455 	"REVOKE",
456 	"RIGHT",
457 	"ROLE",
458 	"ROLLBACK",
459 	"ROLLUP",
460 	"ROUTINE",
461 	"ROW",
462 	"ROWS",
463 	"SAVEPOINT",
464 	"SCHEMA",
465 	"SCOPE",
466 	"SCROLL",
467 	"SEARCH",
468 	"SECOND",
469 	"SECTION",
470 	"SELECT",
471 	"SENSITIVE",
472 	"SESSION",
473 	"SESSION_USER",
474 	"SET",
475 	"SETS",
476 	"SIGNAL",
477 	"SIMILAR",
478 	"SIZE",
479 	"SMALLINT",
480 	"SOME",
481 	"SPACE",
482 	"SPECIFIC",
483 	"SPECIFICTYPE",
484 	"SQL",
485 	"SQLCODE",
486 	"SQLERROR",
487 	"SQLEXCEPTION",
488 	"SQLSTATE",
489 	"SQLWARNING",
490 	"START",
491 	"STATE",
492 	"STATIC",
493 	"SUBMULTISET",
494 	"SUBSTRING",
495 	"SUM",
496 	"SYMMETRIC",
497 	"SYSTEM",
498 	"SYSTEM_USER",
499 	"TABLE",
500 	"TABLESAMPLE",
501 	"TEMPORARY",
502 	"THEN",
503 	"TIME",
504 	"TIMESTAMP",
505 	"TIMEZONE_HOUR",
506 	"TIMEZONE_MINUTE",
507 	"TO",
508 	"TRAILING",
509 	"TRANSACTION",
510 	"TRANSLATE",
511 	"TRANSLATION",
512 	"TREAT",
513 	"TRIGGER",
514 	"TRIM",
515 	"TRUE",
516 	"UNDER",
517 	"UNDO",
518 	"UNION",
519 	"UNIQUE",
520 	"UNKNOWN",
521 	"UNNEST",
522 	"UNTIL",
523 	"UPDATE",
524 	"UPPER",
525 	"USAGE",
526 	"USER",
527 	"USING",
528 	"VALUE",
529 	"VALUES",
530 	"VARCHAR",
531 	"VARYING",
532 	"VIEW",
533 	"WHEN",
534 	"WHENEVER",
535 	"WHERE",
536 	"WHILE",
537 	"WINDOW",
538 	"WITH",
539 	"WITHIN",
540 	"WITHOUT",
541 	"WORK",
542 	"WRITE",
543 	"YEAR",
544 	"ZONE",
545 	NULL
546     };
547     char **pw = reserved;
548     while (*pw != NULL)
549       {
550 	  if (strcasecmp (name, *pw) == 0)
551 	      return 1;
552 	  pw++;
553       }
554     return 0;
555 }
556 
557 GAIAAUX_DECLARE char *
gaiaDequotedSql(const char * value)558 gaiaDequotedSql (const char *value)
559 {
560 /*
561 / returns a well formatted TEXT value from SQL
562 / 1] if the input string begins and ends with ' sigle quote will be the target
563 / 2] if the input string begins and ends with " double quote will be the target
564 / 3] in any othet case the string will simply be copied
565 */
566     const char *pi = value;
567     const char *start;
568     const char *end;
569     char *clean;
570     char *po;
571     int len;
572     char target;
573     int mark = 0;
574     if (value == NULL)
575 	return NULL;
576 
577     len = strlen (value);
578     clean = malloc (len + 1);
579     if (*(value + 0) == '"' && *(value + len - 1) == '"')
580 	target = '"';
581     else if (*(value + 0) == '\'' && *(value + len - 1) == '\'')
582 	target = '\'';
583     else
584       {
585 	  /* no dequoting; simply copying */
586 	  strcpy (clean, value);
587 	  return clean;
588       }
589     start = value;
590     end = value + len - 1;
591     po = clean;
592     while (*pi != '\0')
593       {
594 	  if (mark)
595 	    {
596 		if (*pi == target)
597 		  {
598 		      *po++ = *pi++;
599 		      mark = 0;
600 		      continue;
601 		  }
602 		else
603 		  {
604 		      /* error: mismatching quote */
605 		      free (clean);
606 		      return NULL;
607 		  }
608 	    }
609 	  if (*pi == target)
610 	    {
611 		if (pi == start || pi == end)
612 		  {
613 		      /* first or last char */
614 		      pi++;
615 		      continue;
616 		  }
617 		/* found a quote marker */
618 		mark = 1;
619 		pi++;
620 		continue;
621 	    }
622 	  *po++ = *pi++;
623       }
624     *po = '\0';
625     return clean;
626 }
627 
628 GAIAAUX_DECLARE char *
gaiaQuotedSql(const char * value,int quote)629 gaiaQuotedSql (const char *value, int quote)
630 {
631 /*
632 / returns a well formatted TEXT value for SQL
633 / 1] strips trailing spaces
634 / 2] masks any QUOTE inside the string, appending another QUOTE
635 / 3] works for both SINGLE- and DOUBLE-QUOTE
636 */
637     const char *p_in;
638     const char *p_end;
639     char qt;
640     char *out;
641     char *p_out;
642     int len = 0;
643     int i;
644 
645     if (!value)
646 	return NULL;
647     if (quote == GAIA_SQL_SINGLE_QUOTE)
648 	qt = '\'';
649     else if (quote == GAIA_SQL_DOUBLE_QUOTE)
650 	qt = '"';
651     else
652 	return NULL;
653 
654     p_end = value;
655     for (i = (strlen (value) - 1); i >= 0; i--)
656       {
657 	  /* stripping trailing spaces */
658 	  p_end = value + i;
659 	  if (value[i] != ' ')
660 	      break;
661       }
662 
663     p_in = value;
664     while (p_in <= p_end)
665       {
666 	  /* computing the output length */
667 	  len++;
668 	  if (*p_in == qt)
669 	      len++;
670 	  p_in++;
671       }
672     if (len == 1 && *value == ' ')
673       {
674 	  /* empty string */
675 	  len = 0;
676       }
677 
678     out = malloc (len + 1);
679     if (!out)
680 	return NULL;
681 
682     if (len == 0)
683       {
684 	  /* empty string */
685 	  *out = '\0';
686 	  return out;
687       }
688 
689     p_out = out;
690     p_in = value;
691     while (p_in <= p_end)
692       {
693 	  /* creating the output string */
694 	  if (*p_in == qt)
695 	      *p_out++ = qt;
696 	  *p_out++ = *p_in++;
697       }
698     *p_out = '\0';
699     return out;
700 }
701 
702 GAIAAUX_DECLARE char *
gaiaSingleQuotedSql(const char * value)703 gaiaSingleQuotedSql (const char *value)
704 {
705 /* convenience method supporting SINGLE-QUOTES */
706     return gaiaQuotedSql (value, GAIA_SQL_SINGLE_QUOTE);
707 }
708 
709 GAIAAUX_DECLARE char *
gaiaDoubleQuotedSql(const char * value)710 gaiaDoubleQuotedSql (const char *value)
711 {
712 /* convenience method supporting DOUBLE-QUOTES */
713     return gaiaQuotedSql (value, GAIA_SQL_DOUBLE_QUOTE);
714 }
715 
716 GAIAAUX_DECLARE void
gaiaCleanSqlString(char * value)717 gaiaCleanSqlString (char *value)
718 {
719 /*
720 / returns a well formatted TEXT value for SQL
721 / 1] strips trailing spaces
722 / 2] masks any ' inside the string, appending another '
723 */
724     char new_value[1024];
725     char *p;
726     int len;
727     int i;
728     len = strlen (value);
729     for (i = (len - 1); i >= 0; i--)
730       {
731 	  /* stripping trailing spaces */
732 	  if (value[i] == ' ')
733 	      value[i] = '\0';
734 	  else
735 	      break;
736       }
737     p = new_value;
738     for (i = 0; i < len; i++)
739       {
740 	  if (value[i] == '\'')
741 	      *(p++) = '\'';
742 	  *(p++) = value[i];
743       }
744     *p = '\0';
745     strcpy (value, new_value);
746 }
747 
748 GAIAAUX_DECLARE void
gaiaInsertIntoSqlLog(sqlite3 * sqlite,const char * user_agent,const char * utf8Sql,sqlite3_int64 * sqllog_pk)749 gaiaInsertIntoSqlLog (sqlite3 * sqlite, const char *user_agent,
750 		      const char *utf8Sql, sqlite3_int64 * sqllog_pk)
751 {
752 /* inserting an event into the SQL Log */
753     char *sql_statement;
754     int ret;
755 
756     *sqllog_pk = -1;
757     if (checkSpatialMetaData (sqlite) != 3)
758       {
759 /* CURRENT db-schema (>= 4.0.0) required */
760 	  return;
761       }
762 
763     sql_statement = sqlite3_mprintf ("INSERT INTO sql_statements_log "
764 				     "(id, time_start, user_agent, sql_statement) VALUES ("
765 				     "NULL, strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now'), %Q, %Q)",
766 				     user_agent, utf8Sql);
767     ret = sqlite3_exec (sqlite, sql_statement, NULL, 0, NULL);
768     sqlite3_free (sql_statement);
769     if (ret != SQLITE_OK)
770 	return;
771     *sqllog_pk = sqlite3_last_insert_rowid (sqlite);
772 }
773 
774 GAIAAUX_DECLARE void
gaiaUpdateSqlLog(sqlite3 * sqlite,sqlite3_int64 sqllog_pk,int success,const char * errMsg)775 gaiaUpdateSqlLog (sqlite3 * sqlite, sqlite3_int64 sqllog_pk, int success,
776 		  const char *errMsg)
777 {
778 /* completing an event already inserted into the SQL Log */
779     char *sql_statement;
780     char dummy[64];
781 
782     if (checkSpatialMetaData (sqlite) != 3)
783       {
784 /* CURRENT db-schema (>= 4.0.0) required */
785 	  return;
786       }
787     sprintf (dummy, FRMT64, sqllog_pk);
788     if (success)
789       {
790 	  sql_statement = sqlite3_mprintf ("UPDATE sql_statements_log SET "
791 					   "time_end = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now'), "
792 					   "success = 1, error_cause = 'success' WHERE id = %s",
793 					   dummy);
794       }
795     else
796       {
797 	  sql_statement = sqlite3_mprintf ("UPDATE sql_statements_log SET "
798 					   "time_end = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now'), "
799 					   "success = 0, error_cause = %Q WHERE id = %s",
800 					   (errMsg == NULL)
801 					   ? "UNKNOWN" : errMsg, dummy);
802       }
803     sqlite3_exec (sqlite, sql_statement, NULL, 0, NULL);
804     sqlite3_free (sql_statement);
805 }
806 
807 static void
consume_blank(const char * p_start,const char ** p_end)808 consume_blank (const char *p_start, const char **p_end)
809 {
810 /* consuming blanks */
811     const char *p = p_start;
812     while (1)
813       {
814 	  if (*p == ' ' || *p == '\t')
815 	    {
816 		p++;
817 		continue;
818 	    }
819 	  else
820 	    {
821 		*p_end = p;
822 		return;
823 	    }
824       }
825 }
826 
827 static int
check_deg_delimiter(const char * p_start,const char ** p_end)828 check_deg_delimiter (const char *p_start, const char **p_end)
829 {
830 /* testing a "degrees" delimiter/qualifier */
831     unsigned char ctrl1;
832     unsigned char ctrl2;
833     if (*p_start == 'd')
834       {
835 	  *p_end = p_start + 1;
836 	  return 1;
837       }
838     ctrl1 = *(p_start + 0);
839     ctrl2 = *(p_start + 1);
840     if (ctrl1 == 0xc2 && ctrl2 == 0xb0)
841       {
842 	  *p_end = p_start + 2;
843 	  return 1;
844       }
845     return 0;
846 }
847 
848 static int
check_min_delimiter(const char * p_start,const char ** p_end)849 check_min_delimiter (const char *p_start, const char **p_end)
850 {
851 /* testing a "minutes" delimiter/qualifier */
852     unsigned char ctrl1;
853     unsigned char ctrl2;
854     unsigned char ctrl3;
855     if (*p_start == '\'')
856       {
857 	  *p_end = p_start + 1;
858 	  return 1;
859       }
860     ctrl1 = *(p_start + 0);
861     ctrl2 = *(p_start + 1);
862     ctrl3 = *(p_start + 2);
863     if (ctrl1 == 0xe2 && ctrl2 == 0x80 && ctrl3 == 0xb2)
864       {
865 	  *p_end = p_start + 3;
866 	  return 1;
867       }
868     return 0;
869 }
870 
871 static int
check_sec_delimiter(const char * p_start,const char ** p_end)872 check_sec_delimiter (const char *p_start, const char **p_end)
873 {
874 /* testing a "seconds" delimiter/qualifier */
875     unsigned char ctrl1;
876     unsigned char ctrl2;
877     unsigned char ctrl3;
878     if (*p_start == '"')
879       {
880 	  *p_end = p_start + 1;
881 	  return 1;
882       }
883     ctrl1 = *(p_start + 0);
884     ctrl2 = *(p_start + 1);
885     ctrl3 = *(p_start + 2);
886     if (ctrl1 == 0xe2 && ctrl2 == 0x80 && ctrl3 == 0xb3)
887       {
888 	  *p_end = p_start + 3;
889 	  return 1;
890       }
891     return 0;
892 }
893 
894 static void
consume_int(const char * p_start,const char ** p_end,int * value)895 consume_int (const char *p_start, const char **p_end, int *value)
896 {
897 /* consuming an integer value */
898     char *buf;
899     int len = 0;
900     const char *p = p_start;
901     while (1)
902       {
903 	  if (*p >= '0' && *p <= '9')
904 	    {
905 		len++;
906 		p++;
907 		continue;
908 	    }
909 	  else
910 	    {
911 		*p_end = p;
912 		break;
913 	    }
914       }
915     if (len == 0)
916       {
917 	  *value = 181;
918 	  return;
919       }
920     buf = malloc (len + 1);
921     memcpy (buf, p_start, len);
922     *(buf + len) = '\0';
923     *value = atoi (buf);
924     free (buf);
925 }
926 
927 static void
consume_float(const char * p_start,const char ** p_end,double * value)928 consume_float (const char *p_start, const char **p_end, double *value)
929 {
930 /* consuming a double value */
931     char *buf;
932     int pt = 0;
933     int len = 0;
934     const char *p = p_start;
935     while (1)
936       {
937 	  if (*p >= '0' && *p <= '9')
938 	    {
939 		len++;
940 		p++;
941 		continue;
942 	    }
943 	  else if (*p == '.' || *p == ',')
944 	    {
945 		len++;
946 		pt++;
947 		p++;
948 		continue;
949 	    }
950 	  else
951 	    {
952 		*p_end = p;
953 		break;
954 	    }
955       }
956     if (len == 0 || pt > 1)
957       {
958 	  *value = 61.0;
959 	  return;
960       }
961     buf = malloc (len + 1);
962     memcpy (buf, p_start, len);
963     *(buf + len) = '\0';
964     *value = atof (buf);
965     free (buf);
966 }
967 
968 GAIAAUX_DECLARE int
gaiaParseDMS(const char * dms,double * longitude,double * latitude)969 gaiaParseDMS (const char *dms, double *longitude, double *latitude)
970 {
971 /* attempting to parse a DMS string */
972     double lg;
973     double lt;
974     int lat_d;
975     int lat_m;
976     double lat_s;
977     char lat_prefix = '\0';
978     int long_d;
979     int long_m;
980     double long_s;
981     char long_prefix = '\0';
982     const char *p = dms;
983     const char *p_end;
984     if (dms == NULL)
985 	return 0;
986 
987 /* attempting to parse the latitude */
988     consume_blank (p, &p_end);
989     p = p_end;
990     if (*p == 'S' || *p == 'N')
991       {
992 	  lat_prefix = *p;
993 	  p++;
994 	  consume_blank (p, &p_end);
995 	  p = p_end;
996       }
997     if (*p >= '0' && *p <= '9')
998       {
999 	  consume_int (p, &p_end, &lat_d);
1000 	  if (lat_d < 0 && lat_d > 90)
1001 	      return 0;
1002 	  p = p_end;
1003       }
1004     else
1005 	return 0;
1006     consume_blank (p, &p_end);
1007     p = p_end;
1008     if (check_deg_delimiter (p, &p_end))
1009 	p = p_end;
1010     else
1011 	return 0;
1012     consume_blank (p, &p_end);
1013     p = p_end;
1014     if (*p >= '0' && *p <= '9')
1015       {
1016 	  consume_int (p, &p_end, &lat_m);
1017 	  if (lat_m < 0 && lat_m >= 60)
1018 	      return 0;
1019 	  p = p_end;
1020       }
1021     else
1022 	return 0;
1023     consume_blank (p, &p_end);
1024     p = p_end;
1025     if (check_min_delimiter (p, &p_end))
1026 	p = p_end;
1027     else
1028 	return 0;
1029     consume_blank (p, &p_end);
1030     p = p_end;
1031     if (*p >= '0' && *p <= '9')
1032       {
1033 	  consume_float (p, &p_end, &lat_s);
1034 	  if (lat_s < 0.0 && lat_s >= 60.0)
1035 	      return 0;
1036 	  p = p_end;
1037       }
1038     else
1039 	return 0;
1040     consume_blank (p, &p_end);
1041     p = p_end;
1042     if (check_sec_delimiter (p, &p_end))
1043 	p = p_end;
1044     else
1045 	return 0;
1046     consume_blank (p, &p_end);
1047     p = p_end;
1048     if (lat_prefix == '\0')
1049       {
1050 	  /* attempting to retrieve the prefix */
1051 	  if (*p == 'S' || *p == 'N')
1052 	    {
1053 		lat_prefix = *p;
1054 		p++;
1055 	    }
1056 	  else
1057 	      return 0;
1058       }
1059     lt = (double) lat_d + ((double) lat_m / 60.0) + (lat_s / 3600.0);
1060     if (lat_prefix == 'S')
1061 	lt *= -1.0;
1062     if (lt < -90.0 || lt > 90.0)
1063 	return 0;
1064 
1065 /* attempting to parse the longitude */
1066     consume_blank (p, &p_end);
1067     p = p_end;
1068     if (*p == 'E' || *p == 'W')
1069       {
1070 	  long_prefix = *p;
1071 	  p++;
1072 	  consume_blank (p, &p_end);
1073 	  p = p_end;
1074       }
1075     if (*p >= '0' && *p <= '9')
1076       {
1077 	  consume_int (p, &p_end, &long_d);
1078 	  if (long_d < 0 && long_d > 90)
1079 	      return 0;
1080 	  p = p_end;
1081       }
1082     else
1083 	return 0;
1084     consume_blank (p, &p_end);
1085     p = p_end;
1086     if (check_deg_delimiter (p, &p_end))
1087 	p = p_end;
1088     else
1089 	return 0;
1090     consume_blank (p, &p_end);
1091     p = p_end;
1092     if (*p >= '0' && *p <= '9')
1093       {
1094 	  consume_int (p, &p_end, &long_m);
1095 	  if (long_m < 0 && long_m >= 60)
1096 	      return 0;
1097 	  p = p_end;
1098       }
1099     else
1100 	return 0;
1101     consume_blank (p, &p_end);
1102     p = p_end;
1103     if (check_min_delimiter (p, &p_end))
1104 	p = p_end;
1105     else
1106 	return 0;
1107     consume_blank (p, &p_end);
1108     p = p_end;
1109     if (*p >= '0' && *p <= '9')
1110       {
1111 	  consume_float (p, &p_end, &long_s);
1112 	  if (long_s < 0.0 && long_s >= 60.0)
1113 	      return 0;
1114 	  p = p_end;
1115       }
1116     else
1117 	return 0;
1118     consume_blank (p, &p_end);
1119     p = p_end;
1120     if (check_sec_delimiter (p, &p_end))
1121 	p = p_end;
1122     else
1123 	return 0;
1124     consume_blank (p, &p_end);
1125     p = p_end;
1126     if (long_prefix == '\0')
1127       {
1128 	  /* attempting to retrieve the prefix */
1129 	  if (*p == 'E' || *p == 'W')
1130 	    {
1131 		long_prefix = *p;
1132 		p++;
1133 	    }
1134 	  else
1135 	      return 0;
1136       }
1137     lg = (double) long_d + ((double) long_m / 60.0) + (long_s / 3600.0);
1138     if (long_prefix == 'W')
1139 	lg *= -1.0;
1140     if (lg < -180.0 || lg > 180.0)
1141 	return 0;
1142 
1143     *longitude = lg;
1144     *latitude = lt;
1145     return 1;
1146 }
1147 
1148 GAIAAUX_DECLARE char *
gaiaConvertToDMS(double longitude,double latitude)1149 gaiaConvertToDMS (double longitude, double latitude)
1150 {
1151 /* formatting a DMS string */
1152     return gaiaConvertToDMS (longitude, latitude);
1153 }
1154 
1155 GAIAAUX_DECLARE char *
gaiaConvertToDMSex(double longitude,double latitude,int decimal_digits)1156 gaiaConvertToDMSex (double longitude, double latitude, int decimal_digits)
1157 {
1158 /* formatting a DMS string */
1159     char *dms0;
1160     char *dms;
1161     char long_prefix = 'E';
1162     char lat_prefix = 'N';
1163     int long_d;
1164     int long_m;
1165     int long_s;
1166     double long_s_dbl;
1167     int lat_d;
1168     int lat_m;
1169     int lat_s;
1170     double lat_s_dbl;
1171     double val;
1172     int len;
1173     if (decimal_digits < 0)
1174 	decimal_digits = 0;
1175     if (decimal_digits > 8)
1176 	decimal_digits = 8;
1177     if (longitude < -180.0 || longitude > 180.0)
1178 	return NULL;
1179     if (latitude < -90.0 || latitude > 90.0)
1180 	return NULL;
1181     if (longitude < 0.0)
1182       {
1183 	  long_prefix = 'W';
1184 	  longitude *= -1.0;
1185       }
1186     if (latitude < 0.0)
1187       {
1188 	  lat_prefix = 'S';
1189 	  latitude *= -1.0;
1190       }
1191     long_d = (int) floor (longitude);
1192     val = 60.0 * (longitude - (double) long_d);
1193     long_m = (int) floor (val);
1194     val = 60.0 * (val - (double) long_m);
1195     long_s_dbl = val;
1196     long_s = (int) floor (val);
1197     if ((val - (double) long_s) > 0.5)
1198 	long_s++;
1199     lat_d = (int) floor (latitude);
1200     val = 60.0 * (latitude - (double) lat_d);
1201     lat_m = (int) floor (val);
1202     val = 60.0 * (val - (double) lat_m);
1203     lat_s = (int) floor (val);
1204     lat_s_dbl = val;
1205     if ((val - (double) lat_s) > 0.5)
1206 	lat_s++;
1207     if (decimal_digits == 0)
1208 	dms0 =
1209 	    sqlite3_mprintf ("%02d°%02d′%02d″%c %03d°%02d′%02d″%c",
1210 			     lat_d, lat_m, lat_s, lat_prefix, long_d, long_m,
1211 			     long_s, long_prefix);
1212     else
1213       {
1214 	  char format[256];
1215 	  sprintf (format,
1216 		   "%%02d°%%02d′%%0%d.%df″%%c %%03d°%%02d′%%0%d.%df″%%c",
1217 		   decimal_digits + 3, decimal_digits, decimal_digits + 3,
1218 		   decimal_digits);
1219 	  dms0 =
1220 	      sqlite3_mprintf (format, lat_d, lat_m, lat_s_dbl, lat_prefix,
1221 			       long_d, long_m, long_s_dbl, long_prefix);
1222       }
1223     len = strlen (dms0);
1224     dms = malloc (len + 1);
1225     strcpy (dms, dms0);
1226     sqlite3_free (dms0);
1227     return dms;
1228 }
1229 
1230 #if OMIT_ICONV == 0		/* ICONV is absolutely required */
1231 
1232 /*********************************************************************
1233 /
1234 / DISCLAIMER
1235 /
1236 / the following code implementation (URL percent-encoding/-decoding)
1237 / simply is a rearranged adaption of this original code released
1238 / into the Public Domain:
1239 /
1240 / http://www.geekhideout.com/urlcode.shtml
1241 /
1242 *********************************************************************/
1243 
1244 static char
url_to_hex(char code)1245 url_to_hex (char code)
1246 {
1247     static char hex[] = "0123456789abcdef";
1248     return hex[code & 15];
1249 }
1250 
1251 GAIAAUX_DECLARE char *
gaiaEncodeURL(const char * url,const char * out_charset)1252 gaiaEncodeURL (const char *url, const char *out_charset)
1253 {
1254 /* encoding some URL */
1255     char *encoded = NULL;
1256     const char *in;
1257     char *utf8_url = NULL;
1258     char *out;
1259     int len;
1260     if (url == NULL)
1261 	return NULL;
1262 
1263     utf8_url = url_fromUtf8 (url, out_charset);
1264     if (utf8_url == NULL)
1265 	return NULL;
1266     len = strlen (url);
1267     if (len == 0)
1268 	return NULL;
1269     in = utf8_url;
1270 
1271     encoded = malloc ((len * 3) + 1);
1272     out = encoded;
1273     while (*in != '\0')
1274       {
1275 	  if (isalnum (*in) || *in == '-' || *in == '_' || *in == '.'
1276 	      || *in == '~')
1277 	      *out++ = *in;
1278 	  else
1279 	    {
1280 		*out++ = '%';
1281 		*out++ = url_to_hex (*in >> 4);
1282 		*out++ = url_to_hex (*in & 15);
1283 	    }
1284 	  in++;
1285       }
1286     *out = '\0';
1287     free (utf8_url);
1288     return encoded;
1289 }
1290 
1291 static char
url_from_hex(char ch)1292 url_from_hex (char ch)
1293 {
1294     return isdigit (ch) ? ch - '0' : (char) (tolower (ch) - 'a' + 10);
1295 }
1296 
1297 GAIAAUX_DECLARE char *
gaiaDecodeURL(const char * encoded,const char * in_charset)1298 gaiaDecodeURL (const char *encoded, const char *in_charset)
1299 {
1300 /* decoding some URL */
1301     char *url = NULL;
1302     char *utf8_url = NULL;
1303     const char *in = encoded;
1304     char *out;
1305     int len;
1306     if (encoded == NULL)
1307 	return NULL;
1308     len = strlen (encoded);
1309     if (len == 0)
1310 	return NULL;
1311 
1312     url = malloc (len + 1);
1313     out = url;
1314     while (*in != '\0')
1315       {
1316 	  if (*in == '%')
1317 	    {
1318 		if (*(in + 1) && *(in + 2))
1319 		  {
1320 		      *out++ =
1321 			  url_from_hex (*(in + 1)) << 4 |
1322 			  url_from_hex (*(in + 2));
1323 		      in += 2;
1324 		  }
1325 	    }
1326 	  else if (*in == '+')
1327 	      *out++ = ' ';
1328 	  else
1329 	      *out++ = *in;
1330 	  in++;
1331       }
1332     *out = '\0';
1333     utf8_url = url_toUtf8 (url, in_charset);
1334     free (url);
1335     return utf8_url;
1336 }
1337 
1338 #endif /* ICONV enabled/disabled */
1339 
1340 GAIAAUX_DECLARE char *
gaiaDirNameFromPath(const char * path)1341 gaiaDirNameFromPath (const char *path)
1342 {
1343 /* extracting the DirName (if any) from a Path */
1344     const char *in = path;
1345     const char *last = NULL;
1346     int len = 0;
1347     int dirlen = len;
1348     char *name;
1349 
1350     if (path == NULL)
1351 	return NULL;
1352 
1353     while (*in != '\0')
1354       {
1355 	  /* parsing the Path */
1356 	  len++;
1357 	  if (*in == '/' || *in == '\\')
1358 	    {
1359 		last = in;
1360 		dirlen = len;
1361 	    }
1362 	  in++;
1363       }
1364     if (last == NULL)
1365 	return NULL;		/* there is no Dir component */
1366 
1367 /* allocating the DirName  to be returned */
1368     name = malloc (dirlen + 1);
1369     memcpy (name, path, dirlen);
1370     *(name + dirlen) = '\0';
1371     return name;
1372 }
1373 
1374 GAIAAUX_DECLARE char *
gaiaFullFileNameFromPath(const char * path)1375 gaiaFullFileNameFromPath (const char *path)
1376 {
1377 /* extracting the FullFileName (including Extension) from a Path */
1378     const char *in = path;
1379     const char *last = path - 1;
1380     int len;
1381     char *name;
1382 
1383     if (path == NULL)
1384 	return NULL;
1385 
1386     while (*in != '\0')
1387       {
1388 	  /* parsing the Path */
1389 	  if (*in == '/' || *in == '\\')
1390 	      last = in;
1391 	  in++;
1392       }
1393     len = strlen (last + 1);
1394     if (len == 0)
1395 	return NULL;
1396 
1397 /* allocating the FullFileName to be returned */
1398     name = malloc (len + 1);
1399     strcpy (name, last + 1);
1400     return name;
1401 }
1402 
1403 GAIAAUX_DECLARE char *
gaiaFileNameFromPath(const char * path)1404 gaiaFileNameFromPath (const char *path)
1405 {
1406 /* extracting the FileName (excluding Extension) from a Path */
1407     const char *in = path;
1408     const char *last = path - 1;
1409     int len;
1410     char *name;
1411     int i;
1412 
1413     if (path == NULL)
1414 	return NULL;
1415 
1416     while (*in != '\0')
1417       {
1418 	  /* parsing the Path */
1419 	  if (*in == '/' || *in == '\\')
1420 	      last = in;
1421 	  in++;
1422       }
1423     len = strlen (last + 1);
1424     if (len == 0)
1425 	return NULL;
1426 
1427 /* allocating the FullFileName to be returned */
1428     name = malloc (len + 1);
1429     strcpy (name, last + 1);
1430     for (i = len - 1; i > 0; i--)
1431       {
1432 	  if (*(name + i) == '.')
1433 	    {
1434 		/* stripping out the extension */
1435 		*(name + i) = '\0';
1436 		break;
1437 	    }
1438       }
1439     return name;
1440 }
1441 
1442 GAIAAUX_DECLARE char *
gaiaFileExtFromPath(const char * path)1443 gaiaFileExtFromPath (const char *path)
1444 {
1445 /* extracting the FileExtension (if any) from a Path */
1446     int len;
1447     char *name;
1448     int i;
1449     int pos = -1;
1450 
1451     if (path == NULL)
1452 	return NULL;
1453 
1454     len = strlen (path);
1455     for (i = len - 1; i > 0; i--)
1456       {
1457 	  if (*(path + i) == '/' || *(path + i) == '\\')
1458 	      break;
1459 	  if (*(path + i) == '.')
1460 	    {
1461 		/* found an extension */
1462 		pos = i;
1463 		break;
1464 	    }
1465       }
1466     if (pos <= 0)
1467 	return NULL;
1468 
1469 /* allocating the FileExtension to be returned */
1470     len = strlen (path + pos + 1);
1471     if (len == 0)
1472 	return NULL;
1473     name = malloc (len + 1);
1474     strcpy (name, path + pos + 1);
1475     return name;
1476 }
1477 
1478 GAIAAUX_DECLARE char *
gaiaRemoveExtraSpaces(const char * string)1479 gaiaRemoveExtraSpaces (const char *string)
1480 {
1481 /* removing all repeated whitespaces from a string */
1482     int len;
1483     char *clean;
1484     char *p;
1485     int i;
1486     int space = 0;
1487 
1488     if (string == NULL)
1489 	return NULL;
1490 
1491     len = strlen (string);
1492     clean = malloc (len + 1);
1493     p = clean;
1494     for (i = 0; i < len; i++)
1495       {
1496 	  int sp = 0;
1497 	  if (string[i] == ' ' || string[i] == '\t')
1498 	      sp = 1;
1499 	  else
1500 	      sp = 0;
1501 	  if (space && sp)
1502 	      continue;
1503 	  *p++ = string[i];
1504 	  if (sp)
1505 	      space = 1;
1506 	  else
1507 	      space = 0;
1508       }
1509     *p = '\0';
1510     return clean;
1511 }
1512