aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Evers <kristianevers@gmail.com>2019-01-20 16:16:10 +0100
committerGitHub <noreply@github.com>2019-01-20 16:16:10 +0100
commita4ec5e49bbcf3de1c779c3ed13389def99dd6a4e (patch)
tree179b4b60d3e387d3f8ba528db73c8e348819bd70
parentaba9c7747fc47bbc6f0ec6e6957449ea5090c5fc (diff)
parent53a8cbbec1a6c9c644b35da86bc26a33ff1279e0 (diff)
downloadPROJ-a4ec5e49bbcf3de1c779c3ed13389def99dd6a4e.tar.gz
PROJ-a4ec5e49bbcf3de1c779c3ed13389def99dd6a4e.zip
Merge pull request #1230 from rouault/space_in_grid_names
Add support for spaces in grid name parameters (fixes #1152)
-rw-r--r--src/4D_api.cpp10
-rw-r--r--src/init.cpp14
-rw-r--r--src/internal.cpp105
-rw-r--r--src/iso19111/io.cpp49
-rw-r--r--src/param.cpp50
-rw-r--r--src/proj_internal.h3
-rw-r--r--test/cli/Makefile.am2
-rwxr-xr-xtest/cli/testdatumfile8
8 files changed, 179 insertions, 62 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 37afd289..642fbb1f 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -473,10 +473,11 @@ Returns 1 on success, 0 on failure
p = pj_param_exists (P->params, "geoidgrids");
if (p && strlen (p->param) > strlen ("geoidgrids=")) {
char *gridnames = p->param + strlen ("geoidgrids=");
- char *def = static_cast<char*>(malloc (100+strlen(gridnames)));
+ char *def = static_cast<char*>(malloc (100+2*strlen(gridnames)));
if (nullptr==def)
return 0;
- sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames);
+ sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s",
+ pj_double_quote_string_param_if_needed(gridnames).c_str());
Q = pj_create_internal (P->ctx, def);
free (def);
if (nullptr==Q)
@@ -488,10 +489,11 @@ Returns 1 on success, 0 on failure
p = pj_param_exists (P->params, "nadgrids");
if (p && strlen (p->param) > strlen ("nadgrids=")) {
char *gridnames = p->param + strlen ("nadgrids=");
- char *def = static_cast<char*>(malloc (100+strlen(gridnames)));
+ char *def = static_cast<char*>(malloc (100+2*strlen(gridnames)));
if (nullptr==def)
return 0;
- sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames);
+ sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s",
+ pj_double_quote_string_param_if_needed(gridnames).c_str());
Q = pj_create_internal (P->ctx, def);
free (def);
if (nullptr==Q)
diff --git a/src/init.cpp b/src/init.cpp
index 8af3d26c..482e398d 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -48,27 +48,19 @@ static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) {
/***************************************************************************************
Convert a string (presumably originating from get_init_string) to a paralist.
***************************************************************************************/
- char *c = definition;
+ const char *c = definition;
paralist *first = nullptr, *next = nullptr;
while (*c) {
- /* Find start of next substring */
- while (isspace (*c))
- c++;
-
/* Keep a handle to the start of the list, so we have something to return */
if (nullptr==first)
- first = next = pj_mkparam_ws (c);
+ first = next = pj_mkparam_ws (c, &c);
else
- next = next->next = pj_mkparam_ws (c);
+ next = next->next = pj_mkparam_ws (c, &c);
if (nullptr==next) {
pj_dealloc_params (ctx, first, ENOMEM);
return nullptr;
}
-
- /* And skip to the end of the substring */
- while ((!isspace(*c)) && 0!=*c)
- c++;
}
if( next == nullptr )
diff --git a/src/internal.cpp b/src/internal.cpp
index 84db5d82..44246842 100644
--- a/src/internal.cpp
+++ b/src/internal.cpp
@@ -29,6 +29,8 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+#define FROM_PROJ_CPP
+
#include <ctype.h>
#include <errno.h>
#include <math.h>
@@ -39,8 +41,10 @@
#include "geodesic.h"
#include "proj_internal.h"
-#include "proj_internal.h"
+#include "proj/internal/internal.hpp"
+
+using namespace NS_PROJ::internal;
enum pj_io_units pj_left (PJ *P) {
enum pj_io_units u = P->inverted? P->right: P->left;
@@ -200,7 +204,7 @@ consuming their surrounding whitespace.
size_t i, j, n;
/* Flag showing that a whitespace (ws) has been written after last non-ws */
- size_t ws;
+ bool ws = false;
if (nullptr==c)
return nullptr;
@@ -212,9 +216,20 @@ consuming their surrounding whitespace.
/* First collapse repeated whitespace (including +/;) */
i = 0;
- ws = 0;
+ bool in_string = false;
for (j = 0; j < n; j++) {
+ if( in_string ) {
+ if( c[j] == '"' && c[j+1] == '"' ) {
+ c[i++] = c[j];
+ j++;
+ } else if( c[j] == '"' ) {
+ in_string = false;
+ }
+ c[i++] = c[j];
+ continue;
+ }
+
/* Eliminate prefix '+', only if preceded by whitespace */
/* (i.e. keep it in 1.23e+08) */
if ((i > 0) && ('+'==c[j]) && ws)
@@ -222,14 +237,22 @@ consuming their surrounding whitespace.
if ((i==0) && ('+'==c[j]))
c[j] = ' ';
+ // Detect a string beginning after '='
+ if( c[j] == '"' && i > 0 && c[i-1] == '=' ) {
+ in_string = true;
+ ws = false;
+ c[i++] = c[j];
+ continue;
+ }
+
if (isspace (c[j]) || ';'==c[j]) {
- if (0==ws && (i > 0))
+ if (false==ws && (i > 0))
c[i++] = ' ';
- ws = 1;
+ ws = true;
continue;
}
else {
- ws = 0;
+ ws = false;
c[i++] = c[j];
}
}
@@ -275,8 +298,20 @@ argument string, args, and count its number of elements.
n = strlen (args);
if (n==0)
return 0;
+ bool in_string = false;
for (i = m = 0; i < n; i++) {
- if (' '==args[i]) {
+ if (in_string ) {
+ if( args[i] == '"' && args[i+1] == '"' ) {
+ i++;
+ } else if( args[i] == '"' ) {
+ in_string = false;
+ }
+ }
+ else if (args[i] == '=' && args[i+1] == '"' ) {
+ i++;
+ in_string = true;
+ }
+ else if (' '==args[i]) {
args[i] = 0;
m++;
}
@@ -302,8 +337,6 @@ is allocated and returned.
It is the duty of the caller to free this array.
******************************************************************************/
- size_t i, j;
- char **argv;
if (nullptr==args)
return nullptr;
@@ -312,22 +345,27 @@ It is the duty of the caller to free this array.
/* turn the input string into an array of strings */
- argv = (char **) calloc (argc, sizeof (char *));
+ char** argv = (char **) calloc (argc, sizeof (char *));
if (nullptr==argv)
return nullptr;
- argv[0] = args;
- j = 1;
- for (i = 0; ; i++) {
- if (0==args[i]) {
- argv[j++] = args + (i + 1);
- }
- if (j==argc)
- break;
+ for(size_t i = 0, j = 0; j < argc; j++) {
+ argv[j] = args + i;
+ char* str = argv[j];
+ size_t nLen = strlen(str);
+ i += nLen + 1;
}
return argv;
}
+/*****************************************************************************/
+std::string pj_double_quote_string_param_if_needed(const std::string& str) {
+/*****************************************************************************/
+ if( str.find(' ') == std::string::npos ) {
+ return str;
+ }
+ return '"' + replaceAll(str, "\"", "\"\"") + '"';
+}
/*****************************************************************************/
char *pj_make_args (size_t argc, char **argv) {
@@ -339,23 +377,28 @@ Allocates, and returns, an array of char, large enough to hold a whitespace
separated copy of the args in argv. It is the duty of the caller to free this
array.
******************************************************************************/
- size_t i, n;
- char *p;
- for (i = n = 0; i < argc; i++)
- n += strlen (argv[i]);
+ try
+ {
+ std::string s;
+ for( size_t i = 0; i < argc; i++ )
+ {
+ const char* equal = strchr(argv[i], '=');
+ if( equal ) {
+ s += std::string(argv[i], equal - argv[i] + 1);
+ s += pj_double_quote_string_param_if_needed(equal + 1);
+ } else {
+ s += argv[i];
+ }
+ s += ' ';
+ }
- p = static_cast<char*>(pj_calloc (n + argc + 1, sizeof (char)));
- if (nullptr==p)
+ char* p = pj_strdup(s.c_str());
+ return pj_shrink (p);
+ }
+ catch( const std::exception& ) {
return nullptr;
- if (0==argc)
- return p;
-
- for (i = 0; i < argc; i++) {
- strcat (p, argv[i]);
- strcat (p, " ");
}
- return pj_shrink (p);
}
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index bac42d5b..4c417585 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -5208,8 +5208,9 @@ const std::string &PROJStringFormatter::toString() const {
d->appendToResult("+");
d->result_ += paramValue.key;
if (!paramValue.value.empty()) {
- d->result_ += "=";
- d->result_ += paramValue.value;
+ d->result_ += '=';
+ d->result_ +=
+ pj_double_quote_string_param_if_needed(paramValue.value);
}
}
}
@@ -5229,8 +5230,9 @@ const std::string &PROJStringFormatter::toString() const {
d->appendToResult("+");
d->result_ += paramValue.key;
if (!paramValue.value.empty()) {
- d->result_ += "=";
- d->result_ += paramValue.value;
+ d->result_ += '=';
+ d->result_ +=
+ pj_double_quote_string_param_if_needed(paramValue.value);
}
}
}
@@ -5273,8 +5275,39 @@ static void
PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps,
std::vector<Step::KeyValue> &globalParamValues,
std::string &title) {
- std::string word;
- std::istringstream iss(projString, std::istringstream::in);
+ const char *c_str = projString.c_str();
+ std::vector<std::string> tokens;
+
+ size_t i = 0;
+ while (true) {
+ for (; isspace(c_str[i]); i++) {
+ }
+ std::string token;
+ bool in_string = false;
+ for (; c_str[i]; i++) {
+ if (in_string) {
+ if (c_str[i] == '"' && c_str[i + 1] == '"') {
+ i++;
+ } else if (c_str[i] == '"') {
+ in_string = false;
+ continue;
+ }
+ } else if (c_str[i] == '=' && c_str[i + 1] == '"') {
+ in_string = true;
+ token += c_str[i];
+ i++;
+ continue;
+ } else if (isspace(c_str[i])) {
+ break;
+ }
+ token += c_str[i];
+ }
+ if (token.empty()) {
+ break;
+ }
+ tokens.emplace_back(token);
+ }
+
bool prevWasTitle = false;
if (projString.find("proj=pipeline") == std::string::npos) {
@@ -5290,7 +5323,7 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps,
steps.push_back(Step());
}
- while (iss >> word) {
+ for (auto &word : tokens) {
if (word[0] == '+') {
word = word.substr(1);
} else if (prevWasTitle && word.find('=') == std::string::npos) {
@@ -5334,7 +5367,7 @@ PROJStringSyntaxParser(const std::string &projString, std::vector<Step> &steps,
bool inPipeline = false;
bool invGlobal = false;
- while (iss >> word) {
+ for (auto &word : tokens) {
if (word[0] == '+') {
word = word.substr(1);
} else if (prevWasTitle && word.find('=') == std::string::npos) {
diff --git a/src/param.cpp b/src/param.cpp
index 8125d62f..182c40b0 100644
--- a/src/param.cpp
+++ b/src/param.cpp
@@ -9,6 +9,31 @@
#include "proj.h"
#include "proj_internal.h"
+static void unquote_string(char* param_str) {
+
+ size_t len = strlen(param_str);
+ // Remove leading and terminating spaces after equal sign
+ const char* equal = strstr(param_str, "=\"");
+ if( equal && equal - param_str + 1 > 2 && param_str[len-1] == '"' ) {
+ size_t dst = equal + 1 - param_str;
+ size_t src = dst + 1;
+ for( ; param_str[src]; dst++, src++)
+ {
+ if( param_str[src] == '"' ) {
+ if( param_str[src+1] == '"' ) {
+ src++;
+ } else {
+ break;
+ }
+ }
+ param_str[dst] = param_str[src];
+ }
+ param_str[dst] = '\0';
+ }
+
+}
+
+
/* create parameter list entry */
paralist *pj_mkparam(const char *str) {
paralist *newitem;
@@ -19,13 +44,14 @@ paralist *pj_mkparam(const char *str) {
if (*str == '+')
++str;
(void)strcpy(newitem->param, str);
+ unquote_string(newitem->param);
}
return newitem;
}
/* As pj_mkparam, but payload ends at first whitespace, rather than at end of <str> */
-paralist *pj_mkparam_ws (const char *str) {
+paralist *pj_mkparam_ws (const char *str, const char **next_str) {
paralist *newitem;
size_t len = 0;
@@ -35,18 +61,32 @@ paralist *pj_mkparam_ws (const char *str) {
/* Find start and length of string */
while (isspace (*str))
str++;
- while ((!isspace(str[len])) && 0!=str[len])
- len++;
- if (*str == '+') {
+ if (*str == '+')
str++;
- len--;
+ bool in_string = false;
+ for( ; str[len] != '\0'; len++ ) {
+ if( in_string ) {
+ if( str[len] == '"' && str[len+1] == '"' ) {
+ len++;
+ } else if( str[len] == '"' ) {
+ in_string = false;
+ }
+ } else if( str[len] == '=' && str[len+1] == '"' ) {
+ in_string = true;
+ } else if( isspace(str[len]) ) {
+ break;
+ }
}
+ if( next_str )
+ *next_str = str + len;
+
/* Use calloc to automagically 0-terminate the copy */
newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len + 1);
if (nullptr==newitem)
return nullptr;
memmove(newitem->param, str, len);
+ unquote_string(newitem->param);
newitem->used = 0;
newitem->next = nullptr;
diff --git a/src/proj_internal.h b/src/proj_internal.h
index dcfd6797..b97afdec 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -775,7 +775,7 @@ double aatan2(double, double);
PROJVALUE PROJ_DLL pj_param(projCtx_t *ctx, paralist *, const char *);
paralist PROJ_DLL *pj_param_exists (paralist *list, const char *parameter);
paralist PROJ_DLL *pj_mkparam(const char *);
-paralist *pj_mkparam_ws (const char *str);
+paralist *pj_mkparam_ws (const char *str, const char **next_str);
int PROJ_DLL pj_ell_set(projCtx_t *ctx, paralist *, double *, double *);
@@ -900,6 +900,7 @@ void pj_freeup_plain (PJ *P);
PJ* pj_init_ctx_with_allow_init_epsg( projCtx_t *ctx, int argc, char **argv, int allow_init_epsg );
std::string PROJ_DLL pj_add_type_crs_if_needed(const std::string& str);
+std::string pj_double_quote_string_param_if_needed(const std::string& str);
PJ *pj_create_internal (PJ_CONTEXT *ctx, const char *definition);
PJ *pj_create_argv_internal (PJ_CONTEXT *ctx, int argc, char **argv);
diff --git a/test/cli/Makefile.am b/test/cli/Makefile.am
index 55667ee8..50654968 100644
--- a/test/cli/Makefile.am
+++ b/test/cli/Makefile.am
@@ -36,7 +36,7 @@ testvarious-check:
PROJ_LIB=$(DATAPATH) $(TESTVARIOUS) $(CS2CSEXE)
testdatumfile-check:
- @if [ -f $(DATAPATH)/conus -a -f $(DATAPATH)/ntv1_can.dat -a -f $(DATAPATH)/MD -a -f $(DATAPATH)/ntf_r93.dat ]; then \
+ @if [ -f $(DATAPATH)/conus -a -f $(DATAPATH)/ntv1_can.dat -a -f $(DATAPATH)/MD -a -f $(DATAPATH)/ntf_r93.gsb ]; then \
PROJ_LIB=$(DATAPATH) $(TESTDATUMFILE) $(CS2CSEXE) ; \
fi
diff --git a/test/cli/testdatumfile b/test/cli/testdatumfile
index 55900427..d048d8e6 100755
--- a/test/cli/testdatumfile
+++ b/test/cli/testdatumfile
@@ -27,6 +27,9 @@ echo "============================================"
echo "Running ${0} using ${EXE}:"
echo "============================================"
+mkdir "dir with \" space"
+cp ${PROJ_LIB}/conus "dir with \" space/myconus"
+
OUT=td_out
#EXE=../src/cs2cs
#
@@ -46,7 +49,7 @@ EOF
echo "##############################################################" >> ${OUT}
echo As above, but without ntv1 everything goes through conus file. >> ${OUT}
#
-$EXE +proj=latlong +ellps=clrk66 +nadgrids=conus \
+$EXE +proj=latlong +ellps=clrk66 '+nadgrids="./dir with "" space/myconus"' \
+to +proj=latlong +datum=NAD83 \
-E >>${OUT} <<EOF
111d00'00.000"W 44d00'00.000"N 0.0
@@ -94,6 +97,9 @@ $EXE +proj=latlong +datum=WGS84 \
-5.4999 51.9999
-5.5001 52.0
EOF
+
+rm -rf "dir with \" space"
+
#
##############################################################################
# Done!