aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/PJ_pipeline.c9
-rw-r--r--src/cct.c15
-rw-r--r--src/gie.c676
-rw-r--r--src/makefile.vc3
-rw-r--r--src/optargpm.h33
-rw-r--r--src/pj_factors.c15
-rw-r--r--src/pj_init.c636
-rw-r--r--src/pj_internal.c288
-rw-r--r--src/pj_malloc.c3
-rw-r--r--src/pj_param.c217
-rw-r--r--src/proj.def2
-rw-r--r--src/proj_4D_api.c102
-rw-r--r--src/proj_internal.h7
-rw-r--r--src/projects.h6
14 files changed, 1274 insertions, 738 deletions
diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c
index c8ce8582..a0ef7c1e 100644
--- a/src/PJ_pipeline.c
+++ b/src/PJ_pipeline.c
@@ -51,12 +51,9 @@
pj_init code, implementing support for multilevel, embedded pipelines.
Syntactically, the pipeline system introduces the "+step" keyword (which
- indicates the start of each transformation step), the "+omit_fwd" and
- "+omit_inv" keywords (which indicate that a given transformation step
- should be omitted when the pipeline is executed in forward, resp. inverse
- direction), and reintroduces the +inv keyword (indicating that a given
- transformation step should run in reverse, i.e. forward, when the pipeline
- is executed in inverse direction, and vice versa).
+ indicates the start of each transformation step), and reintroduces the +inv
+ keyword (indicating that a given transformation step should run in reverse, i.e.
+ forward, when the pipeline is executed in inverse direction, and vice versa).
Hence, the first transformation example above, can be implemented as:
diff --git a/src/cct.c b/src/cct.c
index a1c275b5..eae815e9 100644
--- a/src/cct.c
+++ b/src/cct.c
@@ -71,20 +71,23 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26
***********************************************************************/
-#include "optargpm.h"
-#include "proj_internal.h"
-#include <proj.h>
-#include "projects.h"
+#include <ctype.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
#include <string.h>
-#include <math.h>
+
+#include <proj.h>
+#include "proj_internal.h"
+#include "projects.h"
+#include "optargpm.h"
+/* Prototypes for functions in proj_strtod.c */
double proj_strtod(const char *str, char **endptr);
double proj_atof(const char *str);
+/* Prototypes from functions in this file */
char *column (char *buf, int n);
PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time);
diff --git a/src/gie.c b/src/gie.c
index c768c765..e23ca92b 100644
--- a/src/gie.c
+++ b/src/gie.c
@@ -15,9 +15,9 @@ C compiler already employed for compiling the library.
The basic functionality of the gie command language is implemented
through just 3 command verbs:
-OPERATION, which defines the PROJ.4 operation to test,
-ACCEPT, which defines the input coordinate to read, and
-EXPECT, which defines the result to expect.
+operation, which defines the PROJ.4 operation to test,
+accept, which defines the input coordinate to read, and
+expect, which defines the result to expect.
E.g:
@@ -32,7 +32,7 @@ long strings of numbers typically required in projected coordinates.
By default, gie considers the EXPECTation met, if the result comes to
within 0.5 mm of the expected. This default can be changed using the
-TOLERANCE command verb (and yes, I know, linguistically speaking, both
+'tolerance' command verb (and yes, I know, linguistically speaking, both
"operation" and "tolerance" are nouns, not verbs). See the first
few hundred lines of the "builtins.gie" test file for more details of
the command verbs available (verbs of both the VERBal and NOUNistic
@@ -64,15 +64,16 @@ hence making a map projection function call, pj_fwd(PJ, point), as easy
as a traditional function call like hypot(x,y).
While today, we may have more formally well defined metadata systems
-(most prominent the OGC WKT representation), nothing comes close being
+(most prominent the OGC WKT2 representation), nothing comes close being
as easily readable ("human compatible") as Gerald's key-value system.
This system in particular, and the PROJ.4 system in general, was
Gerald's great gift to anyone using and/or communicating about geodata.
-It is only reasonable to name a program keeping an eye on the integrity
-of the PROJ.4 system in honour of Gerald. So in honour, and hopefully
-also in the spirit, of Gerald Ian Evenden (1935--2016), this is the
-Geospatial Integrity Investigation Environment.
+It is only reasonable to name a program, keeping an eye on the integrity
+of the PROJ.4 system, in honour of Gerald.
+
+So in honour, and hopefully also in the spirit, of Gerald Ian Evenden
+(1935--2016), this is the Geospatial Integrity Investigation Environment.
************************************************************************
@@ -103,22 +104,54 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08
***********************************************************************/
-#include "optargpm.h"
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include <string.h>
-#include <ctype.h>
+#include "optargpm.h"
-#include <math.h>
-#include <errno.h>
+/* Package for flexible format I/O - ffio */
+typedef struct ffio {
+ FILE *f;
+ const char **tags;
+ const char *tag;
+ char *args;
+ char *next_args;
+ size_t n_tags;
+ size_t args_size;
+ size_t next_args_size;
+ size_t argc;
+ size_t lineno, next_lineno;
+ size_t level;
+} ffio;
+
+FILE *test = 0;
+
+static int get_inp (ffio *F);
+static int skip_to_next_tag (ffio *F);
+static int step_into_gie_block (ffio *F);
+static int locate_tag (ffio *F, const char *tag);
+static int nextline (ffio *F);
+static int at_end_delimiter (ffio *F);
+static const char *at_tag (ffio *F);
+static int at_decorative_element (ffio *F);
+static ffio *ffio_destroy (ffio *F);
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
+
+static const char *gie_tags[] = {
+ "<gie>", "operation", "accept", "expect", "roundtrip", "banner", "verbose",
+ "direction", "tolerance", "builtins", "echo", "</gie>"
+};
+static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0];
/* from proj_strtod.c */
@@ -127,16 +160,14 @@ double proj_atof(const char *str);
int main(int argc, char **argv);
-static int process_file (const char *fname);
-static int errmsg (int errlev, const char *msg, ...);
-static int get_inp (FILE *f, char *inp, int size);
-static int get_cmnd (const char *inp, char *cmnd, int len);
-static const char *get_args (const char *inp);
static int dispatch (const char *cmnd, const char *args);
+static int errmsg (int errlev, const char *msg, ...);
+static int errno_from_err_const (const char *err_const);
+static int list_err_codes (void);
+static int process_file (const char *fname);
+
static const char *column (const char *buf, int n);
-static int errno_from_err_const (const char *err_const);
static const char *err_const_from_errno (int err);
-static void list_err_codes (void);
@@ -148,7 +179,6 @@ typedef struct {
PJ_COORD a, b, c, e;
PJ_DIRECTION dir;
int verbosity;
- int nargs;
int op_id;
int op_ok, op_ko;
int total_ok, total_ko;
@@ -159,20 +189,15 @@ typedef struct {
FILE *fout;
} gie_ctx;
-gie_ctx T = {{""}, 0, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, PJ_FWD, 1, 0, 0,0,0,0,0,0,0,0, 0.0005, 0, 0};
+ffio *F = 0;
-OPTARGS *o;
+static gie_ctx T;
+static const char delim[] = {"-------------------------------------------------------------------------------\n"};
-size_t tol_lineno = 0;
-size_t lineno = 0;
-size_t level = 0;
-char delim[] = {"-------------------------------------------------------------------------------\n"};
-char DELIM[] = {"===============================================================================\n"};
-
-#define CMDLEN 25000
+#define CMDLEN 250000
int nfiles = 0;
@@ -212,7 +237,14 @@ int main (int argc, char **argv) {
int i;
const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", 0};
const char *longkeys[] = {"o=output", 0};
+ OPTARGS *o;
+
+ memset (&T, 0, sizeof (T));
+ T.dir = PJ_FWD;
+ T.verbosity = 1;
+ T.tolerance = 5e-4;
+ test = fopen ("test.c", "wt");
o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys);
if (0==o)
return 0;
@@ -223,7 +255,7 @@ int main (int argc, char **argv) {
}
if (opt_given (o, "l"))
- list_err_codes ();
+ return list_err_codes ();
T.verbosity = opt_given (o, "q");
@@ -235,6 +267,7 @@ int main (int argc, char **argv) {
T.fout = stdout;
if (opt_given (o, "o"))
T.fout = fopen (opt_arg (o, "output"), "rt");
+
if (0==T.fout) {
fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output"));
free (o);
@@ -245,10 +278,18 @@ int main (int argc, char **argv) {
if (T.verbosity==-1)
return -1;
fprintf (T.fout, "Nothing to do\n");
+ free (o);
return 0;
}
- for (i = 0; i < o->fargc; i++)
+ F = ffio_create (gie_tags, n_gie_tags, 1000);
+ if (0==F) {
+ fprintf (stderr, "%s: No memory\n", o->progname);
+ free (o);
+ return 1;
+ }
+
+ for (i = 0; i < o->fargc; i++)
process_file (o->fargv[i]);
if (T.verbosity > 0) {
@@ -264,6 +305,7 @@ int main (int argc, char **argv) {
fclose (T.fout);
free (o);
+ ffio_destroy (F);
return T.grand_ko;
}
@@ -282,11 +324,8 @@ static int another_success (void) {
static int process_file (const char *fname) {
FILE *f;
- char inp[CMDLEN];
- char cmnd[1000];
- const char *args;
- lineno = level = 0;
+ F->lineno = F->next_lineno = F->level = 0;
T.op_ok = T.total_ok = 0;
T.op_ko = T.total_ko = 0;
@@ -298,37 +337,29 @@ static int process_file (const char *fname) {
}
errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname);
}
+ F->f = f;
+
if (T.verbosity > 0)
fprintf (T.fout, "%sReading file '%s'\n", delim, fname);
T.curr_file = fname;
- while (get_inp(f, inp, CMDLEN)) {
- int len;
-
- if (feof(f))
- break;
- len = get_cmnd (inp, cmnd, 1000);
- if (len>=999) {
- errmsg (2, "Command verb too long: '%s' - bye!\n", cmnd);
- proj_destroy (T.P);
- T.P = 0;
- return 0;
- }
- args = get_args (inp);
- if (SKIP==dispatch (cmnd, args))
+ while (get_inp(F)) {
+ if (SKIP==dispatch (F->tag, F->args))
return proj_destroy (T.P), T.P = 0, 0;
}
+
fclose (f);
+ F->lineno = F->next_lineno = 0;
T.grand_ok += T.total_ok;
T.grand_ko += T.total_ko;
if (T.verbosity > 0)
fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests %s\n", delim, T.total_ok, T.total_ko, T.total_ko? "FAILED!": "failed.");
- if (level==0)
- return errmsg (-3, "File '%s':Missing 'BEGIN' cmnd - bye!\n", fname);
- if (level && level%2)
- return errmsg (-4, "File '%s':Missing 'END' cmnd - bye!\n", fname);
+ if (F->level==0)
+ return errmsg (-3, "File '%s':Missing '<gie>' cmnd - bye!\n", fname);
+ if (F->level && F->level%2)
+ return errmsg (-4, "File '%s':Missing '</gie>' cmnd - bye!\n", fname);
return 0;
}
@@ -336,7 +367,7 @@ static int process_file (const char *fname) {
/*****************************************************************************/
const char *column (const char *buf, int n) {
/*****************************************************************************
- Return a pointer to the n'th column of buf. Coulmn numbers start at 0.
+Return a pointer to the n'th column of buf. Column numbers start at 0.
******************************************************************************/
int i;
if (n <= 0)
@@ -356,8 +387,8 @@ const char *column (const char *buf, int n) {
/*****************************************************************************/
static double strtod_scaled (const char *args, double default_scale) {
/*****************************************************************************
- Interpret <args> as a numeric followed by a linear decadal prefix.
- Return the properly scaled numeric
+Interpret <args> as a numeric followed by a linear decadal prefix.
+Return the properly scaled numeric
******************************************************************************/
double s;
const char *endp = args;
@@ -424,6 +455,7 @@ static int direction (const char *args) {
default:
return 1;
}
+
return 0;
}
@@ -439,46 +471,38 @@ static void finish_previous_operation (const char *args) {
/*****************************************************************************/
static int operation (char *args) {
/*****************************************************************************
- Define the operation to apply to the input data (in ISO 19100 lingo,
- an operation is the general term describing something that can be
- either a conversion or a transformation)
+Define the operation to apply to the input data (in ISO 19100 lingo,
+an operation is the general term describing something that can be
+either a conversion or a transformation)
******************************************************************************/
- int i, j, n;
T.op_id++;
- T.operation_lineno = lineno;
-
- /* compactify the args, so we can fit more info on a line in verbose mode */
- n = (int) strlen (args);
- for (i = j = 0; i < n; ) {
- /* skip prefix whitespace */
- while (isspace (args[i]))
- i++;
- /* move a whitespace delimited text string to the left, skipping over superfluous whitespace */
- while ((0!=args[i]) && (!isspace (args[i])))
- args[j++] = args[i++];
- if (args[j+1]!=0)
- args[j++] = ' ';
- i++;
- }
- args[j++] = 0;
- strcpy (&(T.operation[0]), args);
+ T.operation_lineno = F->lineno;
+
+ strcpy (&(T.operation[0]), F->args);
if (T.verbosity > 1) {
- finish_previous_operation (args);
+ finish_previous_operation (F->args);
banner (args);
}
+
T.op_ok = 0;
T.op_ko = 0;
direction ("forward");
tolerance ("0.5 mm");
+ proj_errno_reset (T.P);
if (T.P)
proj_destroy (T.P);
- T.P = proj_create (0, args);
+ proj_errno_reset (0);
+
+ T.P = proj_create (0, F->args);
+
+ /* Checking that proj_create succeeds is first done at "expect" time, */
+ /* since we want to support "expect"ing specific error codes */
return 0;
}
@@ -493,9 +517,9 @@ static int pj_horner_selftest (void);
/*****************************************************************************/
static int builtins (const char *args) {
/*****************************************************************************
- There are still a few tests that cannot be described using gie
- primitives. Instead, they are implemented as builtins, and invoked
- using the "builtins" command verb.
+There are still a few tests that cannot be described using gie
+primitives. Instead, they are implemented as builtins, and invoked
+using the "builtins" command verb.
******************************************************************************/
int i;
if (T.verbosity > 1) {
@@ -504,10 +528,9 @@ static int builtins (const char *args) {
}
T.op_ok = 0;
T.op_ko = 0;
-
i = pj_unitconvert_selftest ();
if (i!=0) {
- printf ("pj_unitconvert_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_unitconvert_selftest fails with %d\n", i);
another_failure();
}
else
@@ -516,7 +539,7 @@ static int builtins (const char *args) {
i = pj_cart_selftest ();
if (i!=0) {
- printf ("pj_cart_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_cart_selftest fails with %d\n", i);
another_failure();
}
else
@@ -524,7 +547,7 @@ static int builtins (const char *args) {
i = pj_horner_selftest ();
if (i!=0) {
- printf ("pj_horner_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_horner_selftest fails with %d\n", i);
another_failure();
}
else
@@ -554,7 +577,7 @@ static PJ_COORD todeg_coord (PJ_COORD a) {
/*****************************************************************************/
static PJ_COORD parse_coord (const char *args) {
/*****************************************************************************
- Attempt to interpret args as a PJ_COORD.
+Attempt to interpret args as a PJ_COORD.
******************************************************************************/
int i;
const char *endp, *prev = args;
@@ -575,7 +598,7 @@ static PJ_COORD parse_coord (const char *args) {
/*****************************************************************************/
static int accept (const char *args) {
/*****************************************************************************
- Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate.
+Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate.
******************************************************************************/
T.a = parse_coord (args);
if (T.verbosity > 3)
@@ -587,8 +610,8 @@ static int accept (const char *args) {
/*****************************************************************************/
static int roundtrip (const char *args) {
/*****************************************************************************
- Check how far we go from the ACCEPTed point when doing successive
- back/forward transformation pairs.
+Check how far we go from the ACCEPTed point when doing successive
+back/forward transformation pairs.
******************************************************************************/
int ntrips;
double d, r, ans;
@@ -602,7 +625,6 @@ static int roundtrip (const char *args) {
ntrips = (int) (endp==args? 100: fabs(ans));
d = strtod_scaled (endp, 1);
d = d==HUGE_VAL? T.tolerance: d;
- coo = T.a;
/* input ("accepted") values - probably in degrees */
coo = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a;
@@ -615,7 +637,7 @@ static int roundtrip (const char *args) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " roundtrip deviation: %.3f mm, expected: %.3f mm\n", 1000*r, 1000*d);
}
return another_failure ();
@@ -633,7 +655,7 @@ static int expect_message (double d, const char *args) {
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " expected: %s\n", args);
fprintf (T.fout, " got: %.9f %.9f", T.b.xy.x, T.b.xy.y);
if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0)
@@ -652,7 +674,7 @@ static int expect_message_cannot_parse (const char *args) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) lineno, args);
+ fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args);
}
return 1;
}
@@ -665,7 +687,7 @@ static int expect_failure_with_errno_message (int expected, int got) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, pj_strerrno (got));
fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, pj_strerrno (expected));
fprintf (T.fout, "\n");
@@ -676,7 +698,7 @@ static int expect_failure_with_errno_message (int expected, int got) {
/*****************************************************************************/
static int expect (const char *args) {
/*****************************************************************************
- Tell GIE what to expect, when transforming the ACCEPTed input
+Tell GIE what to expect, when transforming the ACCEPTed input
******************************************************************************/
PJ_COORD ci, co, ce;
double d;
@@ -737,6 +759,7 @@ static int expect (const char *args) {
puts (T.dir== 1? "forward": "reverse");
puts (proj_angular_input (T.P, T.dir)? "angular in": "linear in");
puts (proj_angular_output (T.P, T.dir)? "angular out": "linear out");
+ printf ("left: %d right: %d\n", T.P->left, T.P->right);
}
T.e = parse_coord (args);
@@ -753,7 +776,6 @@ static int expect (const char *args) {
if (T.verbosity > 3)
printf ("ACCEPTS %.4f %.4f %.4f %.4f\n", ci.v[0],ci.v[1],ci.v[2],ci.v[3]);
-
/* angular output from proj_trans comes in radians */
co = proj_trans (T.P, T.dir, ci);
T.b = proj_angular_output (T.P, T.dir)? todeg_coord (co): co;
@@ -791,7 +813,7 @@ static int expect (const char *args) {
/*****************************************************************************/
static int verbose (const char *args) {
/*****************************************************************************
- Tell the system how noisy it should be
+Tell the system how noisy it should be
******************************************************************************/
int i = (int) proj_atof (args);
@@ -806,20 +828,11 @@ static int verbose (const char *args) {
return 0;
}
-/*****************************************************************************/
-static int comment (const char *args) {
-/*****************************************************************************
- in line comment. Equivalent to #
-******************************************************************************/
- (void) args;
- return 0;
-}
-
/*****************************************************************************/
static int echo (const char *args) {
/*****************************************************************************
- Add user defined noise to the output stream
+Add user defined noise to the output stream
******************************************************************************/
fprintf (T.fout, "%s\n", args);
return 0;
@@ -828,46 +841,17 @@ fprintf (T.fout, "%s\n", args);
static int dispatch (const char *cmnd, const char *args) {
-#if 0
- int last_errno = proj_errno_reset (T.P);
-#endif
-
- if (0==level%2) {
- if (0==strcmp (cmnd, "BEGIN") || 0==strcmp (cmnd, "<begin>") || 0==strcmp (cmnd, "<gie>"))
- level++;
- return 0;
- }
-
- if (0==strcmp (cmnd, "OPERATION")) return operation ((char *) args);
if (0==strcmp (cmnd, "operation")) return operation ((char *) args);
- if (0==strcmp (cmnd, "ACCEPT")) return accept (args);
if (0==strcmp (cmnd, "accept")) return accept (args);
- if (0==strcmp (cmnd, "EXPECT")) return expect (args);
if (0==strcmp (cmnd, "expect")) return expect (args);
- if (0==strcmp (cmnd, "ROUNDTRIP")) return roundtrip (args);
if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args);
- if (0==strcmp (cmnd, "BANNER")) return banner (args);
if (0==strcmp (cmnd, "banner")) return banner (args);
- if (0==strcmp (cmnd, "VERBOSE")) return verbose (args);
if (0==strcmp (cmnd, "verbose")) return verbose (args);
- if (0==strcmp (cmnd, "DIRECTION")) return direction (args);
if (0==strcmp (cmnd, "direction")) return direction (args);
- if (0==strcmp (cmnd, "TOLERANCE")) return tolerance (args);
if (0==strcmp (cmnd, "tolerance")) return tolerance (args);
- if (0==strcmp (cmnd, "BUILTINS")) return builtins (args);
if (0==strcmp (cmnd, "builtins")) return builtins (args);
- if (0==strcmp (cmnd, "ECHO")) return echo (args);
if (0==strcmp (cmnd, "echo")) return echo (args);
- if (0==strcmp (cmnd, "END")) return finish_previous_operation (args), level++, 0;
- if (0==strcmp (cmnd, "<end>")) return finish_previous_operation (args), level++, 0;
- if (0==strcmp (cmnd, "</gie>")) return finish_previous_operation (args), level++, 0;
- if ('#'==cmnd[0]) return comment (args);
-
-#if 0
- if (proj_errno(T.P))
- printf ("#####***** ERRNO=%d\n", proj_errno(T.P));
- proj_errno_restore (T.P, last_errno);
-#endif
+
return 0;
}
@@ -941,7 +925,7 @@ static const struct errno_vs_err_const lookup[] = {
static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999};
-static void list_err_codes (void) {
+static int list_err_codes (void) {
int i;
const int n = sizeof lookup / sizeof lookup[0];
@@ -950,7 +934,7 @@ static void list_err_codes (void) {
break;
printf ("%25s (%2.2d): %s\n", lookup[i].the_err_const + 8, lookup[i].the_errno, pj_strerrno(lookup[i].the_errno));
}
- exit (0);
+ return 0;
}
@@ -966,7 +950,6 @@ static const char *err_const_from_errno (int err) {
}
-
static int errno_from_err_const (const char *err_const) {
const size_t n = sizeof lookup / sizeof lookup[0];
size_t i, len;
@@ -1006,12 +989,6 @@ static int errno_from_err_const (const char *err_const) {
}
-
-
-
-
-
-
static int errmsg (int errlev, const char *msg, ...) {
va_list args;
va_start(args, msg);
@@ -1022,105 +999,318 @@ static int errmsg (int errlev, const char *msg, ...) {
return errlev;
}
-#define skipspace(f, c) \
- do { \
- while (isspace (c=fgetc(f)) && !feof(f)){ \
- if ('\n'==c) lineno++; \
- } \
- if (feof(f)) \
- break; \
- } while (ungetc(c, f), 0)
-
-#define skipline(f, c) \
- do { \
- while ((c=fgetc(f)) && !feof(f)) { \
- if ((c=='\r') || (c=='\n')) \
- break; \
- } \
- skipspace (f, c); \
- } while (0)
-
-
-/* skip whitespace at continuation line */
-#define continuation(f, buf, c) \
- if ((c=='\r')||(c=='\n')) { \
- if (c=='\n') lineno++; \
- next--; \
- while (isspace (c=fgetc(f)) && !feof(f)); \
- }
-static int get_inp (FILE *f, char *inp, int size) {
- char *next;
- int c = 0, esc;
- char *endp = inp + size - 2;
-
- skipspace (f, c);
-
- for (c = esc = 0, next = inp; !feof(f); ) {
- c = fgetc(f);
- if (esc) {
- continuation (f, next, c);
- esc = 0;
- /* handle escape sequences here */
- switch (c) {
- case '\\': c = '\\'; break;
- default: (void) c;
- }
- }
- if (c=='\r')
- break;
- if (c=='\n') {
- lineno++;
- break;
- }
- *next++ = (char) c;
- if ('\\'==c)
- esc = 1;
- if (feof(f) || (next==endp))
- break;
+
+
+
+
+
+/****************************************************************************************
+
+FFIO - Flexible format I/O
+
+FFIO provides functionality for reading proj style instruction strings written
+in a less strict format than usual:
+
+* Whitespace is generally allowed everywhere
+* Comments can be written inline, '#' style
+* ... or as free format blocks
+
+The overall mission of FFIO is to facilitate communications of geodetic
+parameters and test material in a format that is highly human readable,
+and provides ample room for comment, documentation, and test material.
+
+See the PROJ ".gie" test suites for examples of supported formatting.
+
+****************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <math.h>
+#include <errno.h>
+
+
+
+static int get_inp (ffio *F);
+static int skip_to_next_tag (ffio *F);
+static int step_into_gie_block (ffio *F);
+static int locate_tag (ffio *F, const char *tag);
+static int nextline (ffio *F);
+static int at_end_delimiter (ffio *F);
+static const char *at_tag (ffio *F);
+static int at_decorative_element (ffio *F);
+static ffio *ffio_destroy (ffio *F);
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
+
+
+
+/***************************************************************************************/
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) {
+/****************************************************************************************
+Constructor for the ffio object.
+****************************************************************************************/
+ ffio *G = calloc (1, sizeof (ffio));
+ if (0==G)
+ return 0;
+
+ if (0==max_record_size)
+ max_record_size = 1000;
+
+ G->args = calloc (1, 5*max_record_size);
+ if (0==G->args) {
+ free (G);
+ return 0;
}
- *(next) = 0;
- return (int) strlen(inp);
+
+ G->next_args = calloc (1, max_record_size);
+ if (0==G->args) {
+ free (G->args);
+ free (G);
+ return 0;
+ }
+
+ G->args_size = 5*max_record_size;
+ G->next_args_size = max_record_size;
+
+ G->tags = tags;
+ G->n_tags = n_tags;
+ return G;
}
-static int get_cmnd (const char *inp, char *cmnd, int len) {
- cmnd[0] = 0;
- while (isspace(*inp++));
- inp--;
- while (len-- && !isspace(*inp) && *inp)
- *cmnd++ = *inp++;
- *cmnd = 0;
- return len;
+
+
+/***************************************************************************************/
+static ffio *ffio_destroy (ffio *G) {
+/****************************************************************************************
+Free all allocated associated memory, then free G itself. For extra RAII compliancy,
+the file object should also be closed if still open, but this will require additional
+control logic, and ffio is a gie tool specific package, so we fall back to asserting that
+fclose has been called prior to ffio_destroy.
+****************************************************************************************/
+ free (G->args);
+ free (G->next_args);
+ free (G);
+ return 0;
}
-static const char *get_args (const char *inp) {
- const char *args = inp;
- while (isspace(*args++))
- if (0==*args)
- return args;
- while (!isspace(*++args))
- if (0==*args)
- return args;
- while (isspace(*args++))
- if (0==*args)
- return args;
- return --args;
+
+
+/***************************************************************************************/
+static int at_decorative_element (ffio *G) {
+/****************************************************************************************
+A decorative element consists of a line of at least 5 consecutive identical chars,
+starting at buffer position 0:
+"-----", "=====", "*****", etc.
+
+A decorative element serves as a end delimiter for the current element, and
+continues until a gie command verb is found at the start of a line
+****************************************************************************************/
+ int i;
+ char *c;
+ if (0==G)
+ return 0;
+ c = G->next_args;
+ if (0==c)
+ return 0;
+ if (0==c[0])
+ return 0;
+ for (i = 1; i < 5; i++)
+ if (c[i]!=c[0])
+ return 0;
+ return 1;
+}
+
+
+
+/***************************************************************************************/
+static const char *at_tag (ffio *G) {
+/****************************************************************************************
+A start of a new command serves as an end delimiter for the current command
+****************************************************************************************/
+ size_t j;
+ for (j = 0; j < G->n_tags; j++)
+ if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0)
+ return G->tags[j];
+ return 0;
}
+/***************************************************************************************/
+static int at_end_delimiter (ffio *G) {
+/****************************************************************************************
+An instruction consists of everything from its introductory tag to its end
+delimiter. An end delimiter can be either the introductory tag of the next
+instruction, or a "decorative element", i.e. one of the "ascii art" style
+block delimiters typically used to mark up block comments in a free format
+file.
+****************************************************************************************/
+ if (G==0)
+ return 0;
+ if (at_decorative_element (G))
+ return 1;
+ if (at_tag (G))
+ return 1;
+ return 0;
+}
+/***************************************************************************************/
+static int nextline (ffio *G) {
+/****************************************************************************************
+Read next line of input file. Returns 1 on success, 0 on failure.
+****************************************************************************************/
+ G->next_args[0] = 0;
+ if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f))
+ return 0;
+ if (feof (G->f))
+ return 0;
+ pj_chomp (G->next_args);
+ G->next_lineno++;
+ return 1;
+}
+/***************************************************************************************/
+static int locate_tag (ffio *G, const char *tag) {
+/****************************************************************************************
+Find start-of-line tag (currently only used to search for for <gie>, but any tag
+valid).
+
+Returns 1 on success, 0 on failure.
+****************************************************************************************/
+ size_t n = strlen (tag);
+ while (0!=strncmp (tag, G->next_args, n))
+ if (0==nextline (G))
+ return 0;
+ return 1;
+}
+
+/***************************************************************************************/
+static int step_into_gie_block (ffio *G) {
+/****************************************************************************************
+Make sure we're inside a <gie>-block. Return 1 on success, 0 otherwise.
+****************************************************************************************/
+ /* Already inside */
+ if (G->level % 2)
+ return 1;
+ if (0==locate_tag (G, "<gie>"))
+ return 0;
+ while (0!=strncmp ("<gie>", G->next_args, 5)) {
+ G->next_args[0] = 0;
+ if (feof (G->f))
+ return 0;
+ if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f))
+ return 0;
+ pj_chomp (G->next_args);
+ G->next_lineno++;
+ }
+ G->level++;
+ /* We're ready at the start - now step into the block */
+ return nextline (G);
+}
+
+
+
+/***************************************************************************************/
+static int skip_to_next_tag (ffio *G) {
+/****************************************************************************************
+Skip forward to the next command tag. Return 1 on success, 0 otherwise.
+****************************************************************************************/
+ const char *c;
+ if (0==step_into_gie_block (G))
+ return 0;
+
+ c = at_tag (G);
+
+ /* If not already there - get there */
+ while (!c) {
+ if (0==nextline (G))
+ return 0;
+ c = at_tag (G);
+ }
+
+ /* If we reached the end of a <gie> block, locate the next and retry */
+ if (0==strcmp (c, "</gie>")) {
+ G->level++;
+ if (feof (G->f))
+ return 0;
+ if (0==step_into_gie_block (G))
+ return 0;
+ G->args[0] = 0;
+ return skip_to_next_tag (G);
+ }
+ G->lineno = G->next_lineno;
+
+ return 1;
+}
+
+/* Add the most recently read line of input to the block already stored. */
+static int append_args (ffio *G) {
+ size_t skip_chars = 0;
+ size_t next_len = strlen (G->next_args);
+ size_t args_len = strlen (G->args);
+ const char *tag = at_tag (G);
+
+ if (tag)
+ skip_chars = strlen (tag);
+
+ if (G->args_size < args_len + next_len - skip_chars + 1) {
+ void *p = realloc (G->args, 2 * G->args_size);
+ if (0==p)
+ return 0;
+ G->args = p;
+ G->args_size = 2 * G->args_size;
+ }
+
+ G->args[args_len] = ' ';
+ strcpy (G->args + args_len + 1, G->next_args + skip_chars);
+
+ G->next_args[0] = 0;
+ return 1;
+}
+
+
+
+
+
+/***************************************************************************************/
+static int get_inp (ffio *G) {
+/****************************************************************************************
+The primary command reader for gie. Reads a block of gie input, cleans up repeated
+whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise.
+****************************************************************************************/
+ G->args[0] = 0;
+
+ if (0==skip_to_next_tag (G))
+ return 0;
+ G->tag = at_tag (G);
+
+ if (0==G->tag)
+ return 0;
+
+ do {
+ append_args (G);
+ if (0==nextline (G))
+ return 0;
+ } while (!at_end_delimiter (G));
+
+ pj_shrink (G->args);
+ return 1;
+}
@@ -1171,6 +1361,7 @@ static int pj_horner_selftest (void) {
dist = proj_roundtrip (P, PJ_FWD, 1, &c);
if (dist > 0.01)
return 1;
+ proj_destroy(P);
/* The complex polynomial transformation between the "System Storebaelt" and utm32/ed50 */
P = proj_create (PJ_DEFAULT_CTX, sb_utm32);
@@ -1239,7 +1430,7 @@ static int pj_cart_selftest (void) {
size_t n, sz;
double dist, h, t;
char *args[3] = {"proj=utm", "zone=32", "ellps=GRS80"};
- char *arg = {"+proj=utm +zone=32 +ellps=GRS80"};
+ const char *arg = {"+proj=utm +zone=32 +ellps=GRS80"};
char buf[40];
/* An utm projection on the GRS80 ellipsoid */
@@ -1279,7 +1470,7 @@ static int pj_cart_selftest (void) {
return 3;
/* Clear any previous error */
- proj_errno_set (P, 0);
+ proj_errno_reset (P);
/* Invalid projection */
a = proj_trans (P, 42, a);
@@ -1290,7 +1481,7 @@ static int pj_cart_selftest (void) {
return 5;
/* Clear error again */
- proj_errno_set (P, 0);
+ proj_errno_reset (P);
/* Clean up */
proj_destroy (P);
@@ -1310,9 +1501,9 @@ static int pj_cart_selftest (void) {
b = proj_trans (P, PJ_FWD, a);
/* Check roundtrip precision for 10000 iterations each way */
- dist = proj_roundtrip (P, PJ_FWD, 10000, &a);
- dist = proj_roundtrip (P, PJ_INV, 10000, &b);
- if (dist > 2e-9)
+ dist = proj_roundtrip (P, PJ_FWD, 10000, &a);
+ dist += proj_roundtrip (P, PJ_INV, 10000, &b);
+ if (dist > 4e-9)
return 7;
@@ -1463,6 +1654,7 @@ static int pj_cart_selftest (void) {
/* proj_info() */
/* this one is difficult to test, since the output changes with the setup */
info = proj_info();
+
if (info.version[0] != '\0' ) {
char tmpstr[64];
sprintf(tmpstr, "%d.%d.%d", info.major, info.minor, info.patch);
@@ -1481,6 +1673,7 @@ static int pj_cart_selftest (void) {
if ( !pj_info.has_inverse ) { proj_destroy(P); return 61; }
if ( strcmp(pj_info.definition, arg) ) { proj_destroy(P); return 62; }
if ( strcmp(pj_info.id, "utm") ) { proj_destroy(P); return 63; }
+
proj_destroy(P);
/* proj_grid_info() */
@@ -1500,7 +1693,6 @@ static int pj_cart_selftest (void) {
if ( strcmp(init_info.name, "epsg") ) return 68;
-
/* test proj_rtodms() and proj_dmstor() */
if (strcmp("180dN", proj_rtodms(buf, M_PI, 'N', 'S')))
return 70;
@@ -1616,14 +1808,6 @@ static int pj_cart_selftest (void) {
-
-
-
-
-
-
-
-
static int test_time(const char* args, double tol, double t_in, double t_exp) {
PJ_COORD in, out;
PJ *P = proj_create(PJ_DEFAULT_CTX, args);
@@ -1706,10 +1890,10 @@ static int pj_unitconvert_selftest (void) {
ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30;
ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40;
ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50;
+ ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 50;
ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60;
ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70;
return 0;
}
-
diff --git a/src/makefile.vc b/src/makefile.vc
index ef460719..1155035e 100644
--- a/src/makefile.vc
+++ b/src/makefile.vc
@@ -159,6 +159,8 @@ install: all
-mkdir $(INSTDIR)\share
-mkdir $(INSTDIR)\lib
-mkdir $(INSTDIR)\include
+ -mkdir $(INSTDIR)\test
+ -mkdir $(INSTDIR)\test\gie
copy *.exe $(INSTDIR)\bin
copy *.dll $(INSTDIR)\bin
copy *.lib $(INSTDIR)\lib
@@ -166,3 +168,4 @@ install: all
copy proj_api.h $(INSTDIR)\include
copy projects.h $(INSTDIR)\include
copy geodesic.h $(INSTDIR)\include
+ copy ..\test\gie\*.gie $(INSTDIR)\test\gie
diff --git a/src/optargpm.h b/src/optargpm.h
index acb96583..97755cdb 100644
--- a/src/optargpm.h
+++ b/src/optargpm.h
@@ -183,15 +183,12 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-09-10
* DEALINGS IN THE SOFTWARE.
***********************************************************************/
-
-#define PJ_LIB__
-#include <proj.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
#include <string.h>
-#include <math.h>
-#include <errno.h>
/**************************************************************************************************/
struct OPTARGS;
@@ -219,6 +216,7 @@ struct OPTARGS {
FILE *input;
int input_index;
int record_index;
+ int free_format; /* plus-style specs replaced by free format */
const char *progname; /* argv[0], stripped from /path/to, if present */
char flaglevel[21]; /* if flag -f is specified n times, its optarg pointer is set to flaglevel + n */
char *optarg[256]; /* optarg[(int) 'f'] holds a pointer to the argument of option "-f" */
@@ -426,6 +424,12 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
o->argc = argc;
o->argv = argv;
o->progname = opt_strip_path (argv[0]);
+ o->free_format = 0;
+
+ /* Is free format in use, instead of plus-style? */
+ for (i = 1; i < argc; i++)
+ if (0==strcmp ("--", argv[i]))
+ o->free_format = i;
/* Reset all flags */
for (i = 0; i < (int) strlen (flags); i++)
@@ -442,7 +446,8 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
o->longkeys = longkeys;
- /* check aliases, An end user should never experience this, but the developer should make sure that aliases are valid */
+ /* check aliases, An end user should never experience this, but */
+ /* the developer should make sure that aliases are valid */
for (i = 0; longflags && longflags[i]; i++) {
/* Go on if it does not look like an alias */
if (strlen (longflags[i]) < 3)
@@ -500,6 +505,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
if ('-' != argv[i][0])
break;
+
if (0==o->margv)
o->margv = argv + i;
o->margc++;
@@ -516,7 +522,10 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
char *equals;
crepr = argv[i] + 2;
- /* need to maniplulate a bit to support gnu style --pap=pop syntax */
+ /* We need to manipulate a bit to support gnu style --foo=bar syntax. */
+ /* NOTE: This will segfault for read-only (const char * style) storage, */
+ /* but since the canonical use case, int main (int argc, char **argv), */
+ /* is non-const, we ignore this for now */
equals = strchr (crepr, '=');
if (equals)
*equals = 0;
@@ -579,6 +588,14 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
/* Process all '+'-style options, starting from where '-'-style processing ended */
o->pargv = argv + i;
+ if (o->free_format) {
+ o->pargc = o->free_format - (o->margc + 1);
+ o->fargc = argc - (o->free_format + 1);
+ if (0 != o->fargc)
+ o->fargv = argv + o->free_format + 1;
+ return o;
+ }
+
for (/* empty */; i < argc; i++) {
if ('-' == argv[i][0]) {
free (o);
diff --git a/src/pj_factors.c b/src/pj_factors.c
index 31c0e539..6ba993b9 100644
--- a/src/pj_factors.c
+++ b/src/pj_factors.c
@@ -17,12 +17,19 @@ int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) {
PJ_COORD coo = {{0, 0, 0, 0}};
coo.lp = lp;
- err = proj_errno_reset (P);
+ /* Failing the 3 initial checks will most likely be due to */
+ /* earlier errors, so we leave errno alone */
+ if (0==fac)
+ return 1;
- if (0==fac) {
- proj_errno_set (P, ENOMEM);
+ if (0==P)
return 1;
- }
+
+ if (HUGE_VAL==lp.lam)
+ return 1;
+
+ /* But from here, we're ready to make our own mistakes */
+ err = proj_errno_reset (P);
/* Indicate that all factors are numerical approximations */
fac->code = 0;
diff --git a/src/pj_init.c b/src/pj_init.c
index 2d588ee4..8f4eb477 100644
--- a/src/pj_init.c
+++ b/src/pj_init.c
@@ -27,320 +27,352 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+
+
#define PJ_LIB__
#include <geodesic.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
+#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
-/* Maximum size of files using the "escape carriage return" feature */
-#define MAX_CR_ESCAPE 65537
-typedef struct {
- projCtx ctx;
- PAFile fid;
- char buffer[MAX_CR_ESCAPE];
- int buffer_filled;
- int at_eof;
-} pj_read_state;
-/************************************************************************/
-/* fill_buffer() */
-/************************************************************************/
-static const char *fill_buffer(pj_read_state *state, const char *last_char)
-{
- size_t bytes_read;
- size_t char_remaining, char_requested;
- char *r, *w;
-
-/* -------------------------------------------------------------------- */
-/* Don't bother trying to read more if we are at eof, or if the */
-/* buffer is still over half full. */
-/* -------------------------------------------------------------------- */
- if (last_char == NULL)
- last_char = state->buffer;
-
- if (state->at_eof)
- return last_char;
-
- char_remaining = state->buffer_filled - (last_char - state->buffer);
- if (char_remaining >= sizeof(state->buffer) / 2)
- return last_char;
-
-/* -------------------------------------------------------------------- */
-/* Move the existing data to the start of the buffer. */
-/* -------------------------------------------------------------------- */
- memmove(state->buffer, last_char, char_remaining);
- state->buffer_filled = (int)char_remaining;
- last_char = state->buffer;
-
-/* -------------------------------------------------------------------- */
-/* Refill. */
-/* -------------------------------------------------------------------- */
- char_requested = sizeof(state->buffer) - state->buffer_filled - 1;
- bytes_read = pj_ctx_fread( state->ctx, state->buffer + state->buffer_filled,
- 1, char_requested, state->fid );
- if (bytes_read < char_requested)
- {
- state->at_eof = 1;
- state->buffer[state->buffer_filled + bytes_read] = '\0';
- }
+/**************************************************************************************/
+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;
+ paralist *first = 0, *next = 0;
-/* -------------------------------------------------------------------- */
-/* Line continuations: skip whitespace after escaped newlines */
-/* -------------------------------------------------------------------- */
- r = state->buffer;
- w = state->buffer;
- while (*r) {
- /* Escaped newline? */
- while ((r[0]=='\\') && ((r[1]=='\n') || (r[1]=='\r'))) {
- r += 2;
- while (isspace (*r))
- r++;
- /* we also skip comments immediately after an escaped newline */
- while (*r=='#') {
- while( *r && (*r != '\n') )
- r++;
- while (isspace (*r))
- r++;
- /* Reaching end of buffer while skipping continuation comment is currently an error */
- if (0==*r) {
- pj_ctx_set_errno (state->ctx, -2);
- pj_log (state->ctx, PJ_LOG_ERROR, "init file too big");
- return 0;
- }
- }
- }
- *w++ = *r++;
+ 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 (0==first)
+ first = next = pj_mkparam_ws (c);
+ else
+ next = next->next = pj_mkparam_ws (c);
+ if (0==next)
+ return pj_dealloc_params (ctx, first, ENOMEM);
+
+ /* And skip to the end of the substring */
+ while ((!isspace(*c)) && 0!=*c)
+ c++;
}
- *w = 0;
- state->buffer_filled += (int)(bytes_read - (r-w));
- return last_char;
+ /* Terminate list and return */
+ next->next = 0;
+ return first;
}
-/************************************************************************/
-/* get_opt() */
-/************************************************************************/
-static paralist *
-get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next,
- int *found_def) {
- pj_read_state *state = (pj_read_state*) calloc(1,sizeof(pj_read_state));
- char sword[MAX_CR_ESCAPE];
- int len;
- int in_target = 0;
- const char *next_char = NULL;
- state->fid = fid;
- state->ctx = ctx;
- next_char = fill_buffer(state, NULL);
- if(found_def)
- *found_def = 0;
-
- len = (int)strlen(name);
- *sword = 't';
-
- if (0==next_char)
+
+
+/**************************************************************************************/
+static char *get_init_string (PJ_CONTEXT *ctx, char *name) {
+/***************************************************************************************
+ Read a section of an init file. Return its contents as a plain character string.
+ It is the duty of the caller to free the memory allocated for the string.
+***************************************************************************************/
+#define MAX_LINE_LENGTH 1000
+ size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1);
+ char *fname, *section, *key;
+ char *buffer = 0;
+ char *line = 0;
+ PAFile fid;
+ size_t n;
+
+
+ line = pj_malloc (MAX_LINE_LENGTH + 1);
+ if (0==line)
return 0;
- /* loop till we find our target keyword */
- while (*next_char)
- {
- next_char = fill_buffer(state, next_char);
+ fname = pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3);
+ if (0==fname) {
+ pj_dealloc (line);
+ return 0;
+ }
+
+ /* Support "init=file:section", "+init=file:section", and "file:section" format */
+ key = strstr (name, "init=");
+ if (0==key)
+ key = name;
+ else
+ key += 5;
+ if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return 0;
+ }
+ memmove (fname, key, strlen (key) + 1);
+
+ /* Locate the name of the section we search for */
+ section = strrchr(fname, ':');
+ if (0==section) {
+ proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return 0;
+ }
+ *section = 0;
+ section++;
+ n = strlen (section);
+ pj_log (ctx, 3, "get_init_string: searching for section [%s] in init file [%s]\n", section, fname);
+
+ fid = pj_open_lib (ctx, fname, "rt");
+ if (0==fid) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
+ return 0;
+ }
- /* Skip white space. */
- while( isspace(*next_char) )
- next_char++;
+ /* Search for section in init file */
+ for (;;) {
- next_char = fill_buffer(state, next_char);
- if (0==next_char)
+ /* End of file? */
+ if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) {
+ pj_dealloc (buffer);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
return 0;
+ }
- /* for comments, skip past end of line. */
- if( *next_char == '#' )
- {
- while( *next_char && *next_char != '\n' )
- next_char++;
+ /* At start of right section? */
+ pj_chomp (line);
+ if ('<'!=line[0])
+ continue;
+ if (strlen (line) < n + 2)
+ continue;
+ if (line[n + 1] != '>')
+ continue;
+ if (0==strncmp (line + 1, section, n))
+ break;
+ }
- next_char = fill_buffer(state, next_char);
- if (0==next_char)
- return 0;
- if (*next_char == '\n')
- next_char++;
- if (*next_char == '\r')
- next_char++;
+ /* We're at the first line of the right section - copy line to buffer */
+ buffer = pj_malloc (current_buffer_size);
+ if (0==buffer) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ return 0;
+ }
- }
+ /* Skip the "<section>" indicator, and copy the rest of the line over */
+ strcpy (buffer, line + strlen (section) + 2);
- /* Is this our target? */
- else if( *next_char == '<' )
- {
- /* terminate processing target on the next block definition */
- if (in_target)
- break;
+ /* Copy the remaining lines of the section to buffer */
+ for (;;) {
+ char *end_i_cator;
+ size_t next_length, buffer_length;
- next_char++;
- if (strncmp(name, next_char, len) == 0
- && next_char[len] == '>')
- {
- /* skip past target word */
- next_char += len + 1;
- in_target = 1;
- if(found_def)
- *found_def = 1;
- }
- else
- {
- /* skip past end of line */
- while( *next_char && *next_char != '\n' )
- next_char++;
- }
+ /* Did the section end somewhere in the most recently read line? */
+ end_i_cator = strchr (buffer, '<');
+ if (end_i_cator) {
+ *end_i_cator = 0;
+ break;
}
- else if (in_target)
- {
- const char *start_of_word = next_char;
- int word_len = 0;
- if (*start_of_word == '+')
- {
- start_of_word++;
- next_char++;
- }
+ /* End of file? - done! */
+ if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid))
+ break;
- /* capture parameter */
- while ( *next_char && !isspace(*next_char) )
- {
- next_char++;
- word_len++;
+ /* Otherwise, handle the line. It MAY be the start of the next section, */
+ /* but that will be handled at the start of next trip through the loop */
+ buffer_length = strlen (buffer);
+ next_length = strlen (line) + buffer_length + 2;
+ if (next_length > current_buffer_size) {
+ char *b = pj_malloc (2 * current_buffer_size);
+ if (0==b) {
+ pj_dealloc (buffer);
+ buffer = 0;
+ break;
}
+ strcpy (b, buffer);
+ current_buffer_size *= 2;
+ pj_dealloc (buffer);
+ buffer = b;
+ }
+ buffer[buffer_length] = ' ';
+ strcpy (buffer + buffer_length + 1, line);
+ }
- strncpy(sword+1, start_of_word, word_len);
- sword[word_len+1] = '\0';
+ pj_ctx_fclose (ctx, fid);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ if (0==buffer)
+ return 0;
+ pj_shrink (buffer);
+ pj_log (ctx, 3, "key=%s, value: [%s]\n", key, buffer);
+ return buffer;
+}
- /* do not override existing parameter value of same name */
- if (!pj_param(ctx, *start, sword).i) {
- /* don't default ellipse if datum, ellps or any earth model information is set */
- if (0==strncmp(sword,"tellps=", 7)) {
- int n = 0;
- n += pj_param(ctx, *start, "tdatum").i;
- n += pj_param(ctx, *start, "tellps").i;
- n += pj_param(ctx, *start, "ta").i;
- n += pj_param(ctx, *start, "tb").i;
- n += pj_param(ctx, *start, "trf").i;
- n += pj_param(ctx, *start, "tf").i;
- n += pj_param(ctx, *start, "te").i;
- n += pj_param(ctx, *start, "tes").i;
+/************************************************************************/
+static paralist *get_init(PJ_CONTEXT *ctx, char *key) {
+/*************************************************************************
+Expand key from buffer or (if not in buffer) from init file
+*************************************************************************/
+ char *xkey, *definition;
+ paralist *init_items = 0;
+
+ /* support "init=file:section", "+init=file:section", and "file:section" format */
+ xkey = strstr (key, "init=");
+ if (0==xkey)
+ xkey = key;
+ else
+ xkey += 5;
+ pj_log (ctx, 3, "get_init: searching cache for key: [%s]\n", xkey);
+
+ /* Is file/key pair already in cache? */
+ init_items = pj_search_initcache (xkey);
+ if (init_items)
+ return init_items;
+
+ /* If not, we must read it from file */
+ pj_log (ctx, 3, "get_init: searching on in init files for [%s]\n", xkey);
+ definition = get_init_string (ctx, xkey);
+ if (0==definition)
+ return 0;
+ init_items = string_to_paralist (ctx, definition);
+ pj_log (ctx, 3, "get_init: got [%s], paralist[0,1]: [%s,%s]\n", definition, init_items->param, init_items->next? init_items->next->param: "(empty)");
+ pj_dealloc (definition);
+ if (0==init_items)
+ return 0;
- if (0==n)
- next = next->next = pj_mkparam(sword+1);
- }
- else
- next = next->next = pj_mkparam(sword+1);
- }
+ /* We found it in file - now insert into the cache, before returning */
+ pj_insert_initcache (xkey, init_items);
+ return init_items;
+}
- }
- else
- {
- /* skip past word */
- while( *next_char && !isspace(*next_char) ) {
- next_char++;
- }
- }
- }
+static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, char *key) {
+ paralist *defaults, *last = 0;
+ char keystring[ID_TAG_MAX + 20];
+ paralist *next, *proj;
+ int err;
- if (errno == 25)
- errno = 0;
+ if (0==start)
+ return 0;
- free(state);
- return next;
-}
+ if (strlen(key) > ID_TAG_MAX)
+ return 0;
-/************************************************************************/
-/* get_defaults() */
-/************************************************************************/
-static paralist *get_defaults(projCtx ctx, paralist **start, paralist *next, char *name) {
- PAFile fid;
+ /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */
+ /* or implicitly, because we are initializing a pipeline) */
+ if (pj_param_exists (start, "no_defs"))
+ return start;
+ proj = pj_param_exists (start, "proj");
+ if (0==proj)
+ return start;
+ if (strlen (proj->param) < 6)
+ return start;
+ if (0==strcmp ("pipeline", proj->param + 5))
+ return start;
+
+ err = pj_ctx_get_errno (ctx);
+ pj_ctx_set_errno (ctx, 0);
+
+ /* Locate end of start-list */
+ for (last = start; last->next; last = last->next);
+
+ strcpy (keystring, "proj_def.dat:");
+ strcat (keystring, key);
+ defaults = get_init (ctx, keystring);
+
+ /* Defaults are optional - so we don't care if we cannot open the file */
+ pj_ctx_set_errno (ctx, err);
+
+ if (!defaults)
+ return last;
+
+ /* Loop over all default items */
+ for (next = defaults; next; next = next->next) {
+
+ /* Don't override existing parameter value of same name */
+ if (pj_param_exists (start, next->param))
+ continue;
+
+ /* Don't default ellipse if datum, ellps or any ellipsoid information is set */
+ if (0==strncmp(next->param,"ellps=", 6)) {
+ if (pj_param_exists (start, "datum")) continue;
+ if (pj_param_exists (start, "ellps")) continue;
+ if (pj_param_exists (start, "a")) continue;
+ if (pj_param_exists (start, "b")) continue;
+ if (pj_param_exists (start, "rf")) continue;
+ if (pj_param_exists (start, "f")) continue;
+ if (pj_param_exists (start, "e")) continue;
+ if (pj_param_exists (start, "es")) continue;
+ }
- if ( (fid = pj_open_lib(ctx,"proj_def.dat", "rt")) != NULL) {
- next = get_opt(ctx, start, fid, "general", next, NULL);
- pj_ctx_fseek(ctx, fid, 0, SEEK_SET);
- next = get_opt(ctx, start, fid, name, next, NULL);
- pj_ctx_fclose(ctx, fid);
+ /* If we're here, it's OK to append the current default item */
+ last = last->next = pj_mkparam(next->param);
}
- if (errno)
- errno = 0; /* don't care if can't open file */
- ctx->last_errno = 0;
+ last->next = 0;
- return next;
+ pj_dealloc_params (ctx, defaults, 0);
+ return last;
}
-/************************************************************************/
-/* get_init() */
-/************************************************************************/
-static paralist *get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) {
- char fname[MAX_PATH_FILENAME+ID_TAG_MAX+3], *opt;
- PAFile fid;
- paralist *init_items = NULL;
- const paralist *orig_next = next;
+/*****************************************************************************/
+paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) {
+/******************************************************************************
+Append expansion of <key> to the paralist <init>. The expansion is appended,
+rather than inserted at <init>'s place, since <init> may contain
+overrides to the expansion. These must take precedence, and hence come first
+in the expanded list.
- (void)strncpy(fname, name, sizeof(fname)-2);
- fname[sizeof(fname)-2] = '\0';
+Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm
+zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid.
- /*
- ** Search for file/key pair in cache
- */
+The expression 'init=foo:bar ellps=intl' will then expand to:
- init_items = pj_search_initcache( name );
- if( init_items != NULL )
- {
- next->next = init_items;
- while( next->next != NULL )
- next = next->next;
- *found_def = 1;
- return next;
- }
+ 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80',
- /*
- ** Otherwise we try to open the file and search for it.
- */
- if ((opt = strrchr(fname, ':')) != NULL)
- *opt++ = '\0';
- else { pj_ctx_set_errno(ctx,-3); return NULL; }
+where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence,
+turning the expansion into an UTM projection on the Hayford ellipsoid.
- if ( (fid = pj_open_lib(ctx,fname, "rt")) != NULL)
- next = get_opt(ctx, start, fid, opt, next, found_def);
- else
- return NULL;
+Note that 'init=foo:bar' stays in the list. It is ignored after expansion.
+******************************************************************************/
+ paralist *last;
+ paralist *expn;
- pj_ctx_fclose(ctx, fid);
- if (errno == 25)
- errno = 0; /* unknown problem with some sys errno<-25 */
+ /* Nowhere to start? */
+ if (0==init)
+ return 0;
- /*
- ** If we seem to have gotten a result, insert it into the
- ** init file cache.
- */
- if( next != NULL && next != orig_next )
- pj_insert_initcache( name, orig_next->next );
+ expn = get_init(ctx, init->param);
- return next;
-}
+ /* Nothing in expansion? */
+ if (0==expn)
+ return 0;
-paralist * pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) {
- return get_init(ctx, start, next, name, found_def);
+ /* Locate the end of the list */
+ for (last = init; last && last->next; last = last->next);
+
+ /* Then append and return */
+ last->next = expn;
+ return init;
}
+
+
/************************************************************************/
/* pj_init_plus() */
/* */
/* Same as pj_init() except it takes one argument string with */
-/* individual arguments preceded by '+', such as "+proj=utm */
+/* individual arguments preceded by '+', such as "+proj=utm */
/* +zone=11 +ellps=WGS84". */
/************************************************************************/
@@ -434,15 +466,27 @@ pj_init(int argc, char **argv) {
return pj_init_ctx( pj_get_default_ctx(), argc, argv );
}
+
+typedef PJ *(constructor)(PJ *);
+typedef constructor *(*function_returning_constructor)(const char *);
+
+static constructor *pj_constructor (const char *name) {
+ int i;
+ char *s;
+ for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ;
+ if (0==s)
+ return 0;
+ return (constructor *) pj_list[i].proj;
+}
+
+
PJ *
pj_init_ctx(projCtx ctx, int argc, char **argv) {
char *s, *name;
- paralist *start = NULL;
- PJ *(*proj)(PJ *);
- paralist *curr;
+ constructor *proj;
+ paralist *curr, *init, *start;
int i;
int err;
- int found_def = 0;
PJ *PIN = 0;
int n_pipelines = 0;
int n_inits = 0;
@@ -451,7 +495,6 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
ctx = pj_get_default_ctx ();
ctx->last_errno = 0;
- start = NULL;
if (argc <= 0) {
pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS);
@@ -478,6 +521,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return 0;
}
+
/* put arguments into internal linked list */
start = curr = pj_mkparam(argv[0]);
if (!curr)
@@ -490,41 +534,45 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
curr = curr->next;
}
- /* Only expand +init's in non-pipeline operations. +init's in pipelines are */
- /* expanded in the individual pipeline steps during pipeline initialization. */
- /* Potentially this leads to many nested pipelines, which shouldn't be a */
- /* problem when +inits are expanded as late as possible. */
- if (pj_param(ctx, start, "tinit").i && n_pipelines == 0) {
- found_def = 0;
- curr = get_init(ctx, &start, curr, pj_param(ctx, start, "sinit").s, &found_def);
- if (!curr)
+
+ /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */
+ /* expanded in the individual pipeline steps during pipeline initialization. */
+ /* Potentially this leads to many nested pipelines, which shouldn't be a */
+ /* problem when '+init's are expanded as late as possible. */
+ init = pj_param_exists (start, "init");
+ if (init && n_pipelines == 0) {
+ init = pj_expand_init (ctx, init);
+ if (!init)
return pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS);
- if (!found_def)
- return pj_dealloc_params (ctx, start, PJD_ERR_NO_OPTION_IN_INIT_FILE);
}
-
if (ctx->last_errno)
return pj_dealloc_params (ctx, start, ctx->last_errno);
- /* find projection selection */
- if (!(name = pj_param(ctx, start, "sproj").s))
+ /* Find projection selection */
+ curr = pj_param_exists (start, "proj");
+ if (0==curr)
return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
- for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ;
+ name = curr->param;
+ if (strlen (name) < 6)
+ return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
+ name += 5;
- if (!s)
+ proj = pj_constructor (name);
+ if (0==proj)
return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID);
- /* set defaults, unless inhibited or we are initializing a pipeline */
- if (!(pj_param(ctx, start, "bno_defs").i) && n_pipelines == 0)
- curr = get_defaults(ctx,&start, curr, name);
- proj = (PJ *(*)(PJ *)) pj_list[i].proj;
+ /* Append general and projection specific defaults to the definition list */
+ append_defaults_to_paralist (ctx, start, "general");
+ append_defaults_to_paralist (ctx, start, name);
+
- /* allocate projection structure */
+ /* Allocate projection structure */
PIN = proj(0);
if (0==PIN)
return pj_dealloc_params (ctx, start, ENOMEM);
+
PIN->ctx = ctx;
PIN->params = start;
PIN->is_latlong = 0;
@@ -539,7 +587,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->vgridlist_geoid = NULL;
PIN->vgridlist_geoid_count = 0;
- /* set datum parameters */
+ /* Set datum parameters */
if (pj_datum_set(ctx, start, PIN))
return pj_default_destructor (PIN, proj_errno(PIN));
@@ -566,18 +614,18 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->datum_type = PJD_WGS84;
}
- /* set PIN->geoc coordinate system */
+ /* Set PIN->geoc coordinate system */
PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i);
- /* over-ranging flag */
+ /* Over-ranging flag */
PIN->over = pj_param(ctx, start, "bover").i;
- /* vertical datum geoid grids */
+ /* Vertical datum geoid grids */
PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i;
if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */
pj_param(ctx, start, "sgeoidgrids");
- /* longitude center for wrapping */
+ /* Longitude center for wrapping */
PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i;
if (PIN->is_long_wrap_set) {
PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f;
@@ -588,7 +636,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT);
}
- /* axis orientation */
+ /* Axis orientation */
if( (pj_param(ctx, start,"saxis").s) != NULL )
{
static const char *axis_legal = "ewnsud";
@@ -601,23 +649,23 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
|| strchr( axis_legal, axis_arg[2] ) == NULL)
return pj_default_destructor (PIN, PJD_ERR_AXIS);
- /* it would be nice to validate we don't have on axis repeated */
+ /* TODO: it would be nice to validate we don't have on axis repeated */
strcpy( PIN->axis, axis_arg );
}
- /* central meridian */
+ /* Central meridian */
PIN->lam0=pj_param(ctx, start, "rlon_0").f;
- /* central latitude */
+ /* Central latitude */
PIN->phi0 = pj_param(ctx, start, "rlat_0").f;
- /* false easting and northing */
+ /* False easting and northing */
PIN->x0 = pj_param(ctx, start, "dx_0").f;
PIN->y0 = pj_param(ctx, start, "dy_0").f;
PIN->z0 = pj_param(ctx, start, "dz_0").f;
PIN->t0 = pj_param(ctx, start, "dt_0").f;
- /* general scaling factor */
+ /* General scaling factor */
if (pj_param(ctx, start, "tk_0").i)
PIN->k0 = pj_param(ctx, start, "dk_0").f;
else if (pj_param(ctx, start, "tk").i)
@@ -627,7 +675,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
if (PIN->k0 <= 0.)
return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO);
- /* set units */
+ /* Set units */
s = 0;
if ((name = pj_param(ctx, start, "sunits").s) != NULL) {
for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ;
@@ -655,7 +703,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
} else
PIN->to_meter = PIN->fr_meter = 1.;
- /* set vertical units */
+ /* Set vertical units */
s = 0;
if ((name = pj_param(ctx, start, "svunits").s) != NULL) {
for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ;
@@ -675,7 +723,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->vfr_meter = PIN->fr_meter;
}
- /* prime meridian */
+ /* Prime meridian */
s = 0;
if ((name = pj_param(ctx, start, "spm").s) != NULL) {
const char *value = NULL;
@@ -708,7 +756,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return pj_default_destructor (PIN, ENOMEM);
geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es)));
- /* projection specific initialization */
+ /* Projection specific initialization */
err = proj_errno_reset (PIN);
PIN = proj(PIN);
if (proj_errno (PIN)) {
@@ -719,6 +767,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return PIN;
}
+
+
/************************************************************************/
/* pj_free() */
/* */
diff --git a/src/pj_internal.c b/src/pj_internal.c
index 8a5d2d15..7bfd192b 100644
--- a/src/pj_internal.c
+++ b/src/pj_internal.c
@@ -33,6 +33,7 @@
#include "projects.h"
#include <geodesic.h>
+#include <ctype.h>
#include <stddef.h>
#include <stdarg.h>
#include <errno.h>
@@ -114,14 +115,15 @@ void proj_context_inherit (PJ *parent, PJ *child) {
-size_t pj_strlcpy(char *dst, const char *src, size_t siz) {
-/*******************************************************************
+/**************************************************************************************/
+size_t pj_strlcpy(char *dst, const char *src, size_t dsize) {
+/***************************************************************************************
Copy src to string dst of size siz. At most siz-1 characters
will be copied. Always NUL terminates (unless siz == 0).
Returns strlen(src); if retval >= siz, truncation occurred.
-
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -134,40 +136,243 @@ size_t pj_strlcpy(char *dst, const char *src, size_t siz) {
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
- Source: http://www.i-pi.com/Training/EthicalHacking/Solutions/strlcpy.c
+ Source: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c
-********************************************************************/
- register char *d = dst;
- register const char *s = src;
- register size_t n = siz;
+***************************************************************************************/
+ const char *osrc = src;
+ size_t nleft = dsize;
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0) {
- do {
- if ((*d++ = *s++) == 0)
+ /* Copy as many bytes as will fit. */
+ if (nleft != 0) {
+ while (--nleft != 0) {
+ if ((*dst++ = *src++) == '\0')
break;
- } while (--n != 0);
+ }
}
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
+ /* Not enough room in dst, add NUL and traverse rest of src. */
+ if (nleft == 0) {
+ if (dsize != 0)
+ *dst = '\0'; /* NUL-terminate dst */
+ while (*src++)
;
}
- return(s - src - 1); /* count does not include NUL */
+ return(src - osrc - 1); /* count does not include NUL */
}
-/* stuff below is *not* considered API, and will be moved to an "internal plumbing toolset" */
+/*****************************************************************************/
+char *pj_chomp (char *c) {
+/******************************************************************************
+Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
+considered whitespace.
+******************************************************************************/
+ size_t i, n;
+ char *comment;
+ char *start = c;
+
+ if (0==c)
+ return 0;
+
+ comment = strchr (c, '#');
+ if (comment)
+ *comment = 0;
+
+ n = strlen (c);
+ if (0==n)
+ return c;
+
+ /* Eliminate postfix whitespace */
+ for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--)
+ c[i] = 0;
+
+ /* Find start of non-whitespace */
+ while (0 != *start && (';'==*start || isspace (*start)))
+ start++;
+
+ n = strlen (start);
+ if (0==n) {
+ c[0] = 0;
+ return c;
+ }
+
+ memmove (c, start, n + 1);
+ return c;
+}
+/*****************************************************************************/
+char *pj_shrink (char *c) {
+/******************************************************************************
+Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
+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;
+
+ if (0==c)
+ return 0;
+
+ pj_chomp (c);
+ n = strlen (c);
+
+ /* First collapse repeated whitespace (including +/;) */
+ for (i = j = 0, ws = 0; j < n; j++) {
+
+ /* Eliminate prefix '+', only if preceeded by whitespace */
+ /* (i.e. keep it in 1.23e+08) */
+ if ((i > 0) && ('+'==c[j]) && isspace (c[i]))
+ c[j] = ' ';
+
+ if (isspace (c[j]) || ';'==c[j]) {
+ if (0==ws && (i > 0))
+ c[i++] = ' ';
+ ws = 1;
+ continue;
+ }
+ else {
+ ws = 0;
+ c[i++] = c[j];
+ }
+ }
+ c[i] = 0;
+ n = strlen(c);
+
+ /* Then make ',' and '=' greedy */
+ for (i = j = 0; j < n; j++) {
+ if (i==0) {
+ c[i++] = c[j];
+ continue;
+ }
+
+ /* Skip space before '='/',' */
+ if ('='==c[j] || ','==c[j]) {
+ if (c[i - 1]==' ')
+ c[i - 1] = c[j];
+ else
+ c[i++] = c[j];
+ continue;
+ }
+
+ if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) )
+ continue;
+
+ c[i++] = c[j];
+ }
+ c[i] = 0;
+ return c;
+}
+
+
+
+/*****************************************************************************/
+size_t pj_trim_argc (char *args) {
+/******************************************************************************
+Trim all unnecessary whitespace (and non-essential syntactic tokens) from the
+argument string, args, and count its number of elements.
+******************************************************************************/
+ size_t i, m, n;
+ pj_shrink (args);
+ n = strlen (args);
+ if (n==0)
+ return 0;
+ for (i = m = 0; i < n; i++) {
+ if (' '==args[i]) {
+ args[i] = 0;
+ m++;
+ }
+ }
+ return m + 1;
+}
+
+
+
+/*****************************************************************************/
+char **pj_trim_argv (size_t argc, char *args) {
+/******************************************************************************
+Create an argv-style array from elements placed in the argument string, args.
+
+args is a trimmed string as returned by pj_trim_argc(), and argc is the number
+of trimmed strings found (i.e. the return value of pj_trim_args()). Hence,
+ int argc = pj_trim_argc (args);
+ char **argv = pj_trim_argv (argc, args);
+will produce a classic style (argc, argv) pair from a string of whitespace
+separated args. No new memory is allocated for storing the individual args
+(they stay in the args string), but for the pointers to the args a new array
+is allocated and returned.
+
+It is the duty of the caller to free this array.
+******************************************************************************/
+ size_t i, j;
+ char **argv;
+
+ if (0==args)
+ return 0;
+ if (0==argc)
+ return 0;
+
+
+ /* turn the input string into an array of strings */
+ argv = (char **) calloc (argc, sizeof (char *));
+ if (0==argv)
+ return 0;
+ argv[0] = args;
+ for (i = 0, j = 1; ; i++) {
+ if (0==args[i]) {
+ argv[j++] = args + (i + 1);
+ }
+ if (j==argc)
+ break;
+ }
+ return argv;
+}
+
+
+
+/*****************************************************************************/
+char *pj_make_args (size_t argc, char **argv) {
+/******************************************************************************
+pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It
+converts free format command line input to something proj_create can consume.
+
+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]);
+
+ p = pj_calloc (n + argc + 1, sizeof (char));
+ if (0==p)
+ return 0;
+ if (0==argc)
+ return p;
+
+ for (i = n = 0; i < argc; i++) {
+ strcat (p, argv[i]);
+ strcat (p, " ");
+ }
+ return pj_shrink (p);
+}
+
+
+
+/*****************************************************************************/
void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
+/******************************************************************************
+Raise an error directly on a context, without going through a PJ belonging
+to that context.
+******************************************************************************/
if (0==ctx)
ctx = pj_get_default_ctx();
pj_ctx_set_errno (ctx, err);
@@ -176,8 +381,20 @@ void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
-/* Set logging level 0-3. Higher number means more debug info. 0 turns it off */
+
+
+
+/* logging */
+
+/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
+void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
+
+
+/***************************************************************************************/
enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_level) {
+/****************************************************************************************
+ Set logging level 0-3. Higher number means more debug info. 0 turns it off
+****************************************************************************************/
enum proj_log_level previous;
if (0==ctx)
ctx = pj_get_default_ctx();
@@ -191,35 +408,48 @@ enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_lev
}
-
-/* logging */
-
-/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
-void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
-
+/*****************************************************************************/
void proj_log_error (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting the most severe events.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args);
va_end( args );
}
+
+/*****************************************************************************/
void proj_log_debug (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting debugging information.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args);
va_end( args );
}
+
+/*****************************************************************************/
void proj_log_trace (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting embarrasingly detailed debugging information.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args);
va_end( args );
}
-/* Put a new logging function into P's context. The opaque object app_data is passed as first arg at each call to the logger */
+
+/*****************************************************************************/
void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION log) {
+/******************************************************************************
+ Put a new logging function into P's context. The opaque object app_data is
+ passed as first arg at each call to the logger
+******************************************************************************/
if (0==ctx)
pj_get_default_ctx ();
if (0==ctx)
diff --git a/src/pj_malloc.c b/src/pj_malloc.c
index c9275074..63278a56 100644
--- a/src/pj_malloc.c
+++ b/src/pj_malloc.c
@@ -40,6 +40,7 @@
** projection system memory allocation/deallocation call with custom
** application procedures. */
+#include <proj.h>
#include "projects.h"
#include <errno.h>
@@ -143,7 +144,7 @@ char *pj_strdup(const char *str)
/*****************************************************************************/
-void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev) {
+void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) {
/*****************************************************************************
Companion to pj_default_destructor (below). Deallocates a linked list
of "+proj=xxx" initialization parameters.
diff --git a/src/pj_param.c b/src/pj_param.c
index ee952eca..5024346d 100644
--- a/src/pj_param.c
+++ b/src/pj_param.c
@@ -1,20 +1,79 @@
/* put parameters in linked list and retrieve */
-#include <projects.h>
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
+#include "projects.h"
+
/* create parameter list entry */
paralist *pj_mkparam(char *str) {
- paralist *newitem;
-
- if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) {
- newitem->used = 0;
- newitem->next = 0;
- if (*str == '+')
- ++str;
- (void)strcpy(newitem->param, str);
- }
- return newitem;
+ paralist *newitem;
+
+ if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) {
+ newitem->used = 0;
+ newitem->next = 0;
+ if (*str == '+')
+ ++str;
+ (void)strcpy(newitem->param, str);
+ }
+ return newitem;
+}
+
+
+/* As pj_mkparam, but payload ends at first whitespace, rather than at end of <str> */
+paralist *pj_mkparam_ws (char *str) {
+ paralist *newitem;
+ size_t len = 0;
+
+ if (0==str)
+ return 0;
+
+ /* Find start and length of string */
+ while (isspace (*str))
+ str++;
+ while ((!isspace(str[len])) && 0!=str[len])
+ len++;
+ if (*str == '+') {
+ str++;
+ len--;
+ }
+
+ /* Use calloc to automagically 0-terminate the copy */
+ newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len);
+ if (0==newitem)
+ return 0;
+ memmove(newitem->param, str, len);
+
+ newitem->used = 0;
+ newitem->next = 0;
+
+ return newitem;
+}
+
+/**************************************************************************************/
+paralist *pj_param_exists (paralist *list, const char *parameter) {
+/***************************************************************************************
+ Determine whether a given parameter exists in a paralist. If it does, return
+ a pointer to the corresponding list element - otherwise return 0.
+
+ This function is equivalent to the pj_param (...) call with the "opt" argument
+ set to the parameter name preceeeded by a 't'. But by using this one, one avoids
+ writing the code allocating memory for a new copy of parameter name, and prepending
+ the t (for compile time known names, this is obviously not an issue).
+***************************************************************************************/
+ paralist *next = list;
+ char *c = strchr (parameter, '=');
+ size_t len = strlen (parameter);
+ if (c)
+ len = c - parameter;
+ if (list==0)
+ return 0;
+
+ for (next = list; next; next = next->next)
+ if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0))
+ return next;
+
+ return 0;
}
@@ -35,76 +94,76 @@ paralist *pj_mkparam(char *str) {
/* */
/************************************************************************/
- PROJVALUE /* test for presence or get parameter value */
+ PROJVALUE /* test for presence or get parameter value */
pj_param(projCtx ctx, paralist *pl, const char *opt) {
- int type;
- unsigned l;
- PROJVALUE value;
-
- if( ctx == NULL )
- ctx = pj_get_default_ctx();
-
- type = *opt++;
- /* simple linear lookup */
- l = (int)strlen(opt);
- while (pl && !(!strncmp(pl->param, opt, l) &&
- (!pl->param[l] || pl->param[l] == '=')))
- pl = pl->next;
- if (type == 't')
- value.i = pl != 0;
- else if (pl) {
- pl->used |= 1;
- opt = pl->param + l;
- if (*opt == '=')
- ++opt;
- switch (type) {
- case 'i': /* integer input */
- value.i = atoi(opt);
- break;
- case 'd': /* simple real input */
- value.f = pj_atof(opt);
- break;
- case 'r': /* degrees input */
- value.f = dmstor_ctx(ctx, opt, 0);
- break;
- case 's': /* char string */
+ int type;
+ unsigned l;
+ PROJVALUE value;
+
+ if( ctx == NULL )
+ ctx = pj_get_default_ctx();
+
+ type = *opt++;
+ /* simple linear lookup */
+ l = (int)strlen(opt);
+ while (pl && !(!strncmp(pl->param, opt, l) &&
+ (!pl->param[l] || pl->param[l] == '=')))
+ pl = pl->next;
+ if (type == 't')
+ value.i = pl != 0;
+ else if (pl) {
+ pl->used |= 1;
+ opt = pl->param + l;
+ if (*opt == '=')
+ ++opt;
+ switch (type) {
+ case 'i': /* integer input */
+ value.i = atoi(opt);
+ break;
+ case 'd': /* simple real input */
+ value.f = pj_atof(opt);
+ break;
+ case 'r': /* degrees input */
+ value.f = dmstor_ctx(ctx, opt, 0);
+ break;
+ case 's': /* char string */
value.s = (char *) opt;
- break;
- case 'b': /* boolean */
- switch (*opt) {
- case 'F': case 'f':
- value.i = 0;
- break;
- case '\0': case 'T': case 't':
- value.i = 1;
- break;
- default:
- pj_ctx_set_errno(ctx, -8);
- value.i = 0;
- break;
- }
- break;
- default:
+ break;
+ case 'b': /* boolean */
+ switch (*opt) {
+ case 'F': case 'f':
+ value.i = 0;
+ break;
+ case '\0': case 'T': case 't':
+ value.i = 1;
+ break;
+ default:
+ pj_ctx_set_errno(ctx, -8);
+ value.i = 0;
+ break;
+ }
+ break;
+ default:
bum_type: /* note: this is an error in parameter, not a user error */
- fprintf(stderr, "invalid request to pj_param, fatal\n");
- exit(1);
- }
- } else /* not given */
- switch (type) {
- case 'b':
- case 'i':
- value.i = 0;
- break;
- case 'd':
- case 'r':
- value.f = 0.;
- break;
- case 's':
- value.s = 0;
- break;
- default:
- goto bum_type;
- }
- return value;
+ fprintf(stderr, "invalid request to pj_param, fatal\n");
+ exit(1);
+ }
+ } else /* not given */
+ switch (type) {
+ case 'b':
+ case 'i':
+ value.i = 0;
+ break;
+ case 'd':
+ case 'r':
+ value.f = 0.;
+ break;
+ case 's':
+ value.s = 0;
+ break;
+ default:
+ goto bum_type;
+ }
+ return value;
}
diff --git a/src/proj.def b/src/proj.def
index 887f4719..51b79558 100644
--- a/src/proj.def
+++ b/src/proj.def
@@ -149,3 +149,5 @@ EXPORTS
pj_ellipsoid @134
pj_calc_ellipsoid_params @135
+ pj_chomp @136
+ pj_shrink @137
diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c
index 5f4bf334..0fba5097 100644
--- a/src/proj_4D_api.c
+++ b/src/proj_4D_api.c
@@ -28,7 +28,6 @@
*****************************************************************************/
#include <stddef.h>
#include <errno.h>
-#include <ctype.h>
#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
@@ -81,6 +80,8 @@ double proj_lpz_dist (const PJ *P, LPZ a, LPZ b) {
PJ_COORD aa, bb;
aa.lpz = a;
bb.lpz = b;
+ if (HUGE_VAL==a.lam || HUGE_VAL==b.lam)
+ return HUGE_VAL;
return hypot (proj_lp_dist (P, aa.lp, bb.lp), a.z - b.z);
}
@@ -340,7 +341,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
direction = PJ_INV)
The conversion involves a call to the tangent function, which goes through the
- roof at the poles, so very close (the last few micrometers) to the poles no
+ roof at the poles, so very close (the last centimeter) to the poles no
conversion takes place and the input latitude is copied directly to the output.
Fortunately, the geocentric latitude converges to the geographical at the
@@ -349,7 +350,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
For the spherical case, the geographical latitude equals the geocentric, and
consequently, the input is copied directly to the output.
**************************************************************************************/
- const double limit = M_HALFPI - 1e-12;
+ const double limit = M_HALFPI - 1e-9;
PJ_COORD res = coo;
if ((coo.lp.phi > limit) || (coo.lp.phi < -limit) || (P->es==0))
return res;
@@ -388,76 +389,29 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) {
**************************************************************************************/
PJ *P;
char *args, **argv;
- int argc, i, j, last, n;
+ size_t argc, n;
if (0==ctx)
ctx = pj_get_default_ctx ();
/* Make a copy that we can manipulate */
- n = (int) strlen (definition);
+ n = strlen (definition);
args = (char *) malloc (n + 1);
if (0==args)
return 0;
strcpy (args, definition);
- /* All-in-one: count args, eliminate superfluous whitespace, 0-terminate substrings */
- for (i = j = argc = last = 0; i < n; ) {
-
- /* Skip prefix whitespace */
- while (isspace (args[i]))
- i++;
-
- /* Skip at most one prefix '+' */
- if ('+'==args[i])
- i++;
-
- /* Whitespace after a '+' is a syntax error - but by Postel's prescription, we ignore and go on */
- if (isspace (args[i]))
- continue;
-
- /* Move a whitespace delimited text string to the left, skipping over superfluous whitespace */
- while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i]))
- args[j++] = args[i++];
-
- /* Skip postfix whitespace */
- while (isspace (args[i]) || ';'==args[i])
- i++;
-
- /* Greedy assignment operator: turn "a = b" into "a=b" */
- if ('='==args[i]) {
- args[j++] = '=';
- i++;
- while (isspace (args[i]))
- i++;
- while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i]))
- args[j++] = args[i++];
- while (isspace (args[i]) || ';'==args[i])
- i++;
- }
-
- /* terminate string - if that makes j pass i (often the case for first arg), let i catch up */
- args[j++] = 0;
- if (i < j)
- i = j;
-
- /* we finished another arg */
- argc++;
+ argc = pj_trim_argc (args);
+ if (argc==0) {
+ pj_dealloc (args);
+ return 0;
}
- /* turn the massaged input into an array of strings */
- argv = (char **) calloc (argc, sizeof (char *));
- if (0==argv)
- return pj_dealloc (args);
- argv[0] = args;
- for (i = 0, j = 1; i < n; i++) {
- if (0==args[i])
- argv[j++] = args + (i + 1);
- if (j==argc)
- break;
- }
+ argv = pj_trim_argv (argc, args);
/* ...and let pj_init_ctx do the hard work */
- P = pj_init_ctx (ctx, argc, argv);
+ P = pj_init_ctx (ctx, (int) argc, argv);
+
pj_dealloc (argv);
pj_dealloc (args);
return P;
@@ -474,11 +428,22 @@ a null-pointer is returned. The definition arguments may use '+' as argument sta
indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm",
"zone=32"}.
**************************************************************************************/
+ PJ *P;
+ const char *c;
+
if (0==argv)
return 0;
if (0==ctx)
ctx = pj_get_default_ctx ();
- return pj_init_ctx (ctx, argc, argv);
+
+ /* We assume that free format is used, and build a full proj_create compatible string */
+ c = pj_make_args (argc, argv);
+ if (0==c)
+ return 0;
+
+ P = proj_create (ctx, c);
+ pj_dealloc ((char *) c);
+ return P;
}
@@ -781,6 +746,8 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) {
return info;
}
+
+
/*****************************************************************************/
PJ_INIT_INFO proj_init_info(const char *initname){
/******************************************************************************
@@ -790,12 +757,14 @@ PJ_INIT_INFO proj_init_info(const char *initname){
Returns PJ_INIT_INFO struct.
- If the init file is not found all members of
- the return struct are set to 0. If the init file is found, but it the
- metadata is missing, the value is set to "Unknown".
+ If the init file is not found all members of the return struct are set
+ to the empty string.
+
+ If the init file is found, but the metadata is missing, the value is
+ set to "Unknown".
******************************************************************************/
- int file_found, def_found=0;
+ int file_found;
char param[80], key[74];
paralist *start, *next;
PJ_INIT_INFO info;
@@ -819,7 +788,7 @@ PJ_INIT_INFO proj_init_info(const char *initname){
strncat(param, key, 73);
start = pj_mkparam(param);
- next = pj_get_init(ctx, &start, start, key, &def_found);
+ pj_expand_init(ctx, start);
if (pj_param(ctx, start, "tversion").i) {
pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version));
@@ -858,6 +827,9 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) {
PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0};
struct FACTORS f;
+ if (0==P)
+ return factors;
+
if (pj_factors(lp, P, 0.0, &f))
return factors;
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 4e70e690..6571bce9 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -57,6 +57,7 @@ extern "C" {
#define PJ_TORAD(deg) ((deg)*M_PI/180.0)
#endif
+
/* This enum is also conditionally defined in projects.h - but we need it here */
/* for the pj_left/right prototypes, and enums cannot be forward declared */
enum pj_io_units {
@@ -109,6 +110,12 @@ void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst);
void pj_erase_ellipsoid_def (PJ *P);
int pj_calc_ellipsoid_params (PJ *P, double a, double es);
+char *pj_chomp (char *c);
+char *pj_shrink (char *c);
+size_t pj_trim_argc (char *args);
+char **pj_trim_argv (size_t argc, char *args);
+char *pj_make_args (size_t argc, char **argv);
+
/* Lowest level: Minimum support for fileapi */
void proj_fileapi_set (PJ *P, void *fileapi);
diff --git a/src/projects.h b/src/projects.h
index 8c0f81fa..ca2cdbe3 100644
--- a/src/projects.h
+++ b/src/projects.h
@@ -683,7 +683,10 @@ double adjlon(double);
double aacos(projCtx,double), aasin(projCtx,double), asqrt(double), aatan2(double, double);
PROJVALUE pj_param(projCtx ctx, paralist *, const char *);
+paralist *pj_param_exists (paralist *list, const char *parameter);
paralist *pj_mkparam(char *);
+paralist *pj_mkparam_ws (char *str);
+
int pj_ellipsoid (PJ *);
int pj_ell_set(projCtx ctx, paralist *, double *, double *);
@@ -694,7 +697,8 @@ int pj_angular_units_set(paralist *, PJ *);
paralist *pj_clone_paralist( const paralist* );
paralist *pj_search_initcache( const char *filekey );
void pj_insert_initcache( const char *filekey, const paralist *list);
-paralist *pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def);
+paralist *pj_expand_init(projCtx ctx, paralist *init);
+
void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev);