diff options
| author | Kristian Evers <kristianevers@gmail.com> | 2019-01-20 16:16:10 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-20 16:16:10 +0100 |
| commit | a4ec5e49bbcf3de1c779c3ed13389def99dd6a4e (patch) | |
| tree | 179b4b60d3e387d3f8ba528db73c8e348819bd70 | |
| parent | aba9c7747fc47bbc6f0ec6e6957449ea5090c5fc (diff) | |
| parent | 53a8cbbec1a6c9c644b35da86bc26a33ff1279e0 (diff) | |
| download | PROJ-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.cpp | 10 | ||||
| -rw-r--r-- | src/init.cpp | 14 | ||||
| -rw-r--r-- | src/internal.cpp | 105 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 49 | ||||
| -rw-r--r-- | src/param.cpp | 50 | ||||
| -rw-r--r-- | src/proj_internal.h | 3 | ||||
| -rw-r--r-- | test/cli/Makefile.am | 2 | ||||
| -rwxr-xr-x | test/cli/testdatumfile | 8 |
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! |
