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