aboutsummaryrefslogtreecommitdiff
path: root/src/gie.cpp
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2018-12-18 20:24:11 +0100
committerEven Rouault <even.rouault@spatialys.com>2018-12-26 10:08:53 +0100
commit610957f7035242f15743c399ffd429b92bc36206 (patch)
tree73f0d51147e2f4860c4bfc875f7a4bf9359386d4 /src/gie.cpp
parent355d681ed88019e97742344bd642c2fd97e700a1 (diff)
downloadPROJ-610957f7035242f15743c399ffd429b92bc36206.tar.gz
PROJ-610957f7035242f15743c399ffd429b92bc36206.zip
cpp conversion: minimal steps to fix compilation errors, not warnings
Diffstat (limited to 'src/gie.cpp')
-rw-r--r--src/gie.cpp1456
1 files changed, 1456 insertions, 0 deletions
diff --git a/src/gie.cpp b/src/gie.cpp
new file mode 100644
index 00000000..74d78db5
--- /dev/null
+++ b/src/gie.cpp
@@ -0,0 +1,1456 @@
+/***********************************************************************
+
+ gie - The Geospatial Integrity Investigation Environment
+
+************************************************************************
+
+The Geospatial Integrity Investigation Environment "gie" is a modest
+regression testing environment for the PROJ.4 transformation library.
+
+Its primary design goal was to be able to replace those thousands of
+lines of regression testing code that are (at time of writing) part
+of PROJ.4, while not requiring any other kind of tooling than the same
+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.
+
+E.g:
+
+operation +proj=utm +zone=32 +ellps=GRS80
+accept 12 55
+expect 691_875.632_14 6_098_907.825_05
+
+Note that gie accepts the underscore ("_") as a thousands separator.
+It is not required (in fact, it is entirely ignored by the input
+routine), but it significantly improves the readability of the very
+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
+"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
+persuation).
+
+--
+
+But more importantly than being an acronym for "Geospatial Integrity
+Investigation Environment", gie were also the initials, user id, and
+USGS email address of Gerald Ian Evenden (1935--2016), the geospatial
+visionary, who, already in the 1980s, started what was to become the
+PROJ.4 of today.
+
+Gerald's clear vision was that map projections are *just special
+functions*. Some of them rather complex, most of them of two variables,
+but all of them *just special functions*, and not particularly more
+special than the sin(), cos(), tan(), and hypot() already available in
+the C standard library.
+
+And hence, according to Gerald, *they should not be particularly much
+harder to use*, for a programmer, than the sin()s, tan()s and hypot()s
+so readily available.
+
+Gerald's ingenuity also showed in the implementation of the vision,
+where he devised a comprehensive, yet simple, system of key-value
+pairs for parameterising a map projection, and the highly flexible
+PJ struct, storing run-time compiled versions of those key-value pairs,
+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 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.
+
+************************************************************************
+
+Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08
+
+************************************************************************
+
+* Copyright (c) 2017 Thomas Knudsen
+* Copyright (c) 2017, SDFE
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+
+***********************************************************************/
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "proj.h"
+#include "proj_internal.h"
+#include "proj_math.h"
+#include "proj_strtod.h"
+#include "projects.h"
+
+#include "optargpm.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;
+
+static int get_inp (ffio *G);
+static int skip_to_next_tag (ffio *G);
+static int step_into_gie_block (ffio *G);
+static int locate_tag (ffio *G, const char *tag);
+static int nextline (ffio *G);
+static int at_end_delimiter (ffio *G);
+static const char *at_tag (ffio *G);
+static int at_decorative_element (ffio *G);
+static ffio *ffio_destroy (ffio *G);
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
+
+static const char *gie_tags[] = {
+ "<gie>", "operation", "use_proj4_init_rules",
+ "accept", "expect", "roundtrip", "banner", "verbose",
+ "direction", "tolerance", "ignore", "require_grid", "echo", "skip", "</gie>"
+};
+
+static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0];
+
+
+int main(int argc, char **argv);
+
+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 const char *err_const_from_errno (int err);
+
+
+
+#define SKIP -1
+
+#define MAX_OPERATION 10000
+
+typedef struct {
+ char operation[MAX_OPERATION+1];
+ PJ *P;
+ PJ_COORD a, b, c, e;
+ PJ_DIRECTION dir;
+ int verbosity;
+ int skip;
+ int op_id;
+ int op_ok, op_ko, op_skip;
+ int total_ok, total_ko, total_skip;
+ int grand_ok, grand_ko, grand_skip;
+ size_t operation_lineno;
+ size_t dimensions_given, dimensions_given_at_last_accept;
+ double tolerance;
+ int use_proj4_init_rules;
+ int ignore;
+ int skip_test;
+ const char *curr_file;
+ FILE *fout;
+} gie_ctx;
+
+ffio *F = 0;
+
+static gie_ctx T;
+int tests=0, succs=0, succ_fails=0, fail_fails=0, succ_rtps=0, fail_rtps=0;
+
+static const char delim[] = {"-------------------------------------------------------------------------------\n"};
+
+
+static const char usage[] = {
+ "--------------------------------------------------------------------------------\n"
+ "Usage: %s [-options]... infile...\n"
+ "--------------------------------------------------------------------------------\n"
+ "Options:\n"
+ "--------------------------------------------------------------------------------\n"
+ " -h Help: print this usage information\n"
+ " -o /path/to/file Specify output file name\n"
+ " -v Verbose: Provide non-essential informational output.\n"
+ " Repeat -v for more verbosity (e.g. -vv)\n"
+ " -q Quiet: Opposite of verbose. In quiet mode not even errors\n"
+ " are reported. Only interaction is through the return code\n"
+ " (0 on success, non-zero indicates number of FAILED tests)\n"
+ " -l List the PROJ internal system error codes\n"
+ "--------------------------------------------------------------------------------\n"
+ "Long Options:\n"
+ "--------------------------------------------------------------------------------\n"
+ " --output Alias for -o\n"
+ " --verbose Alias for -v\n"
+ " --help Alias for -h\n"
+ " --list Alias for -l\n"
+ " --version Print version number\n"
+ "--------------------------------------------------------------------------------\n"
+ "Examples:\n"
+ "--------------------------------------------------------------------------------\n"
+ "1. Run all tests in file \"corner-cases.gie\", providing much extra information\n"
+ " gie -vvvv corner-cases.gie\n"
+ "2. Run all tests in files \"foo\" and \"bar\", providing info on failures only\n"
+ " gie foo bar\n"
+ "--------------------------------------------------------------------------------\n"
+};
+
+int main (int argc, char **argv) {
+ int i;
+ const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", "version", 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;
+ T.ignore = 5555; /* Error code that will not be issued by proj_create() */
+ T.use_proj4_init_rules = FALSE;
+
+ o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys);
+ if (0==o)
+ return 0;
+
+ if (opt_given (o, "h") || argc==1) {
+ printf (usage, o->progname);
+ free (o);
+ return 0;
+ }
+
+
+ if (opt_given (o, "version")) {
+ fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ());
+ free (o);
+ return 0;
+ }
+
+ T.verbosity = opt_given (o, "q");
+ if (T.verbosity)
+ T.verbosity = -1;
+ if (T.verbosity != -1)
+ T.verbosity = opt_given (o, "v") + 1;
+
+ 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);
+ return 1;
+ }
+
+ if (opt_given (o, "l")) {
+ free (o);
+ return list_err_codes ();
+ }
+
+ if (0==o->fargc) {
+ if (T.verbosity==-1)
+ return -1;
+ fprintf (T.fout, "Nothing to do\n");
+ free (o);
+ return 0;
+ }
+
+ 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) {
+ if (o->fargc > 1) {
+ fprintf (T.fout, "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n",
+ delim, T.grand_ok+T.grand_ko+T.grand_skip, T.grand_ok, T.grand_skip,
+ T.grand_ko);
+ }
+ fprintf (T.fout, "%s", delim);
+ if (T.verbosity > 1) {
+ fprintf (T.fout, "Failing roundtrips: %4d, Succeeding roundtrips: %4d\n", fail_rtps, succ_rtps);
+ fprintf (T.fout, "Failing failures: %4d, Succeeding failures: %4d\n", fail_fails, succ_fails);
+ fprintf (T.fout, "Internal counters: %4.4d(%4.4d)\n", tests, succs);
+ fprintf (T.fout, "%s", delim);
+ }
+ }
+ else
+ if (T.grand_ko)
+ fprintf (T.fout, "Failures: %d", T.grand_ko);
+
+ if (stdout != T.fout)
+ fclose (T.fout);
+
+ free (o);
+ ffio_destroy (F);
+ return T.grand_ko;
+}
+
+static int another_failure (void) {
+ T.op_ko++;
+ T.total_ko++;
+ proj_errno_reset (T.P);
+ return 0;
+}
+
+static int another_skip (void) {
+ T.op_skip++;
+ T.total_skip++;
+ return 0;
+}
+
+static int another_success (void) {
+ T.op_ok++;
+ T.total_ok++;
+ proj_errno_reset (T.P);
+ return 0;
+}
+
+static int another_succeeding_failure (void) {
+ succ_fails++;
+ return another_success ();
+}
+
+static int another_failing_failure (void) {
+ fail_fails++;
+ return another_failure ();
+}
+
+static int another_succeeding_roundtrip (void) {
+ succ_rtps++;
+ return another_success ();
+}
+
+static int another_failing_roundtrip (void) {
+ fail_rtps++;
+ return another_failure ();
+}
+
+static int process_file (const char *fname) {
+ FILE *f;
+
+ F->lineno = F->next_lineno = F->level = 0;
+ T.op_ok = T.total_ok = 0;
+ T.op_ko = T.total_ko = 0;
+ T.op_skip = T.total_skip = 0;
+
+ if (T.skip) {
+ proj_destroy (T.P);
+ T.P = 0;
+ return 0;
+ }
+
+ f = fopen (fname, "rt");
+ if (0==f) {
+ if (T.verbosity > 0) {
+ fprintf (T.fout, "%sCannot open spec'd input file '%s' - bye!\n", delim, fname);
+ return 2;
+ }
+ 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)) {
+ if (SKIP==dispatch (F->tag, F->args)) {
+ proj_destroy (T.P);
+ T.P = 0;
+ return 0;
+ }
+ }
+
+ fclose (f);
+ F->lineno = F->next_lineno = 0;
+
+ T.grand_ok += T.total_ok;
+ T.grand_ko += T.total_ko;
+ T.grand_skip += T.grand_skip;
+ if (T.verbosity > 0) {
+ fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n",
+ delim, T.total_ok, T.total_skip, T.total_ko,
+ T.total_ko? "FAILED!": "failed.");
+ }
+ 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;
+}
+
+
+/*****************************************************************************/
+const char *column (const char *buf, int n) {
+/*****************************************************************************
+Return a pointer to the n'th column of buf. Column numbers start at 0.
+******************************************************************************/
+ int i;
+ if (n <= 0)
+ return buf;
+ for (i = 0; i < n; i++) {
+ while (isspace(*buf))
+ buf++;
+ if (i == n - 1)
+ break;
+ while ((0 != *buf) && !isspace(*buf))
+ buf++;
+ }
+ return buf;
+}
+
+
+/*****************************************************************************/
+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
+******************************************************************************/
+ double s;
+ const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */
+ const char *endp = args;
+ s = proj_strtod (args, (char **) &endp);
+ if (args==endp)
+ return HUGE_VAL;
+
+ endp = column (args, 2);
+
+ if (0==strcmp(endp, "km"))
+ s *= 1000;
+ else if (0==strcmp(endp, "m"))
+ s *= 1;
+ else if (0==strcmp(endp, "dm"))
+ s /= 10;
+ else if (0==strcmp(endp, "cm"))
+ s /= 100;
+ else if (0==strcmp(endp, "mm"))
+ s /= 1000;
+ else if (0==strcmp(endp, "um"))
+ s /= 1e6;
+ else if (0==strcmp(endp, "nm"))
+ s /= 1e9;
+ else if (0==strcmp(endp, "rad"))
+ s = GRS80_DEG * proj_todeg (s);
+ else if (0==strcmp(endp, "deg"))
+ s = GRS80_DEG * s;
+ else
+ s *= default_scale;
+ return s;
+}
+
+
+static int banner (const char *args) {
+ char dots[] = {"..."}, nodots[] = {""}, *thedots = nodots;
+ if (strlen(args) > 70)
+ thedots = dots;
+ fprintf (T.fout, "%s%-70.70s%s\n", delim, args, thedots);
+ return 0;
+}
+
+
+static int tolerance (const char *args) {
+ T.tolerance = strtod_scaled (args, 1);
+ if (HUGE_VAL==T.tolerance) {
+ T.tolerance = 0.0005;
+ return 1;
+ }
+ return 0;
+}
+
+
+static int use_proj4_init_rules (const char *args) {
+ T.use_proj4_init_rules = strcmp(args, "true") == 0;
+ return 0;
+}
+
+static int ignore (const char *args) {
+ T.ignore = errno_from_err_const (column (args, 1));
+ return 0;
+}
+
+static int require_grid (const char *args) {
+ PJ_GRID_INFO grid_info;
+ const char* grid_filename = column (args, 1);
+ grid_info = proj_grid_info(grid_filename);
+ if( strlen(grid_info.filename) == 0 ) {
+ if (T.verbosity > 1) {
+ fprintf (T.fout, "Test skipped because of missing grid %s\n",
+ grid_filename);
+ }
+ T.skip_test = 1;
+ }
+ return 0;
+}
+
+static int direction (const char *args) {
+ const char *endp = args;
+ while (isspace (*endp))
+ endp++;
+ switch (*endp) {
+ case 'F':
+ case 'f':
+ T.dir = PJ_FWD;
+ break;
+ case 'I':
+ case 'i':
+ case 'R':
+ case 'r':
+ T.dir = PJ_INV;
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void finish_previous_operation (const char *args) {
+ if (T.verbosity > 1 && T.op_id > 1 && T.op_ok+T.op_ko)
+ fprintf (T.fout, "%s %d tests succeeded, %d tests skipped, %d tests %s\n",
+ delim, T.op_ok, T.op_skip, T.op_ko, T.op_ko? "FAILED!": "failed.");
+ (void) 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)
+******************************************************************************/
+ T.op_id++;
+
+ T.operation_lineno = F->lineno;
+
+ strncpy (&(T.operation[0]), F->args, MAX_OPERATION);
+ T.operation[MAX_OPERATION] = '\0';
+
+ if (T.verbosity > 1) {
+ finish_previous_operation (F->args);
+ banner (args);
+ }
+
+
+ T.op_ok = 0;
+ T.op_ko = 0;
+ T.op_skip = 0;
+ T.skip_test = 0;
+
+ direction ("forward");
+ tolerance ("0.5 mm");
+ ignore ("pjd_err_dont_skip");
+
+ proj_errno_reset (T.P);
+
+ if (T.P)
+ proj_destroy (T.P);
+ proj_errno_reset (0);
+ proj_context_use_proj4_init_rules(0, T.use_proj4_init_rules);
+
+ 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;
+}
+
+static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) {
+ size_t i, n;
+ char *axis = "enut";
+ paralist *l = pj_param_exists (P->params, "axis");
+ if (l && dir==PJ_INV)
+ axis = l->param + strlen ("axis=");
+ n = strlen (axis);
+ for (i = 0; i < n; i++)
+ if (strchr ("news", axis[i]))
+ a.v[i] = proj_torad (a.v[i]);
+ return a;
+}
+
+
+static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) {
+ size_t i, n;
+ char *axis = "enut";
+ paralist *l = pj_param_exists (P->params, "axis");
+ if (l && dir==PJ_FWD)
+ axis = l->param + strlen ("axis=");
+ n = strlen (axis);
+ for (i = 0; i < n; i++)
+ if (strchr ("news", axis[i]))
+ a.v[i] = proj_todeg (a.v[i]);
+ return a;
+}
+
+
+
+/*****************************************************************************/
+static PJ_COORD parse_coord (const char *args) {
+/*****************************************************************************
+Attempt to interpret args as a PJ_COORD.
+******************************************************************************/
+ int i;
+ const char *endp;
+ const char *dmsendp;
+ const char *prev = args;
+ PJ_COORD a = proj_coord (0,0,0,0);
+
+ T.dimensions_given = 0;
+ for (i = 0; i < 4; i++) {
+ /* proj_strtod doesn't read values like 123d45'678W so we need a bit */
+ /* of help from proj_dmstor. proj_strtod effectively ignores what */
+ /* comes after "d", so we use that fact that when dms is larger than */
+ /* d the value was stated in "dms" form. */
+ /* This could be avoided if proj_dmstor used the same proj_strtod() */
+ /* as gie, but that is not the case (yet). When we remove projects.h */
+ /* from the public API we can change that. */
+ double d = proj_strtod(prev, (char **) &endp);
+ double dms = PJ_TODEG(proj_dmstor (prev, (char **) &dmsendp));
+ /* TODO: When projects.h is removed, call proj_dmstor() in all cases */
+ if (d != dms && fabs(d) < fabs(dms) && fabs(dms) < fabs(d) + 1) {
+ d = dms;
+ endp = dmsendp;
+ }
+ /* A number like -81d00'00.000 will be parsed correctly by both */
+ /* proj_strtod and proj_dmstor but only the latter will return */
+ /* the correct end-pointer. */
+ if (d == dms && endp != dmsendp)
+ endp = dmsendp;
+
+ /* Break out if there were no more numerals */
+ if (prev==endp)
+ return i > 1? a: proj_coord_error ();
+
+ a.v[i] = d;
+ prev = endp;
+ T.dimensions_given++;
+ }
+
+ return a;
+}
+
+
+/*****************************************************************************/
+static int accept (const char *args) {
+/*****************************************************************************
+Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate.
+******************************************************************************/
+ T.a = parse_coord (args);
+ if (T.verbosity > 3)
+ fprintf (T.fout, "# %s\n", args);
+ T.dimensions_given_at_last_accept = T.dimensions_given;
+ return 0;
+}
+
+
+/*****************************************************************************/
+static int roundtrip (const char *args) {
+/*****************************************************************************
+Check how far we go from the ACCEPTed point when doing successive
+back/forward transformation pairs.
+
+Without args, roundtrip defaults to 100 iterations:
+
+ roundtrip
+
+With one arg, roundtrip will default to a tolerance of T.tolerance:
+
+ roundtrip ntrips
+
+With two args:
+
+ roundtrip ntrips tolerance
+
+Always returns 0.
+******************************************************************************/
+ int ntrips;
+ double d, r, ans;
+ char *endp;
+ PJ_COORD coo;
+
+ if (0==T.P) {
+ if (T.ignore == proj_errno(T.P))
+ return another_skip();
+
+ return another_failure ();
+ }
+
+ ans = proj_strtod (args, &endp);
+ if (endp==args) {
+ /* Default to 100 iterations if not args. */
+ ntrips = 100;
+ } else {
+ if (ans < 1.0 || ans > 1000000.0) {
+ errmsg (2, "Invalid number of roundtrips: %lf\n", ans);
+ return another_failing_roundtrip ();
+ }
+ ntrips = (int)ans;
+ }
+
+ d = strtod_scaled (endp, 1);
+ d = d==HUGE_VAL? T.tolerance: d;
+
+ /* input ("accepted") values - probably in degrees */
+ coo = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a;
+
+ r = proj_roundtrip (T.P, T.dir, ntrips, &coo);
+ if (r <= d)
+ return another_succeeding_roundtrip ();
+
+ if (T.verbosity > -1) {
+ 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) F->lineno);
+ fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d);
+ }
+ return another_failing_roundtrip ();
+}
+
+
+static int expect_message (double d, const char *args) {
+ another_failure ();
+
+ if (T.verbosity < 0)
+ return 1;
+ if (d > 1e6)
+ d = 999999.999999;
+ 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) F->lineno);
+ fprintf (T.fout, " expected: %s\n", args);
+ fprintf (T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y);
+ if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0)
+ fprintf (T.fout, " %.9f", T.b.xyz.z);
+ if (T.b.xyzt.t!=0)
+ fprintf (T.fout, " %.9f", T.b.xyzt.t);
+ fprintf (T.fout, "\n");
+ fprintf (T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000*d, 1000*T.tolerance);
+ return 1;
+}
+
+
+static int expect_message_cannot_parse (const char *args) {
+ another_failure ();
+ if (T.verbosity > -1) {
+ 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) F->lineno, args);
+ }
+ return 1;
+}
+
+static int expect_failure_with_errno_message (int expected, int got) {
+ another_failing_failure ();
+
+ if (T.verbosity < 0)
+ return 1;
+ 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) 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");
+ return 1;
+}
+
+
+/* For test purposes, we want to call a transformation of the same */
+/* dimensionality as the number of dimensions given in accept */
+static PJ_COORD expect_trans_n_dim (PJ_COORD ci) {
+ if (4==T.dimensions_given_at_last_accept)
+ return proj_trans (T.P, T.dir, ci);
+
+ if (3==T.dimensions_given_at_last_accept)
+ return pj_approx_3D_trans (T.P, T.dir, ci);
+
+ return pj_approx_2D_trans (T.P, T.dir, ci);
+}
+
+
+/*****************************************************************************/
+static int expect (const char *args) {
+/*****************************************************************************
+Tell GIE what to expect, when transforming the ACCEPTed input
+******************************************************************************/
+ PJ_COORD ci, co, ce;
+ double d;
+ int expect_failure = 0;
+ int expect_failure_with_errno = 0;
+
+ if (0==strncmp (args, "failure", 7)) {
+ expect_failure = 1;
+
+ /* Option: Fail with an expected errno (syntax: expect failure errno -33) */
+ if (0==strncmp (column (args, 2), "errno", 5))
+ expect_failure_with_errno = errno_from_err_const (column (args, 3));
+ }
+
+ if (T.ignore==proj_errno(T.P))
+ return another_skip ();
+
+ if (0==T.P) {
+ /* If we expect failure, and fail, then it's a success... */
+ if (expect_failure) {
+ /* Failed to fail correctly? */
+ if (expect_failure_with_errno && proj_errno (T.P)!=expect_failure_with_errno)
+ return expect_failure_with_errno_message (expect_failure_with_errno, proj_errno(T.P));
+
+ return another_succeeding_failure ();
+ }
+
+ /* Otherwise, it's a true failure */
+ banner (T.operation);
+ errmsg (3, "%sInvalid operation definition in line no. %d:\n %s (errno=%s/%d)\n",
+ delim, (int) T.operation_lineno, pj_strerrno(proj_errno(T.P)),
+ err_const_from_errno (proj_errno(T.P)), proj_errno(T.P)
+ );
+ return another_failing_failure ();
+ }
+
+ /* We may still successfully fail even if the proj_create succeeded */
+ if (expect_failure) {
+ proj_errno_reset (T.P);
+
+ /* Try to carry out the operation - and expect failure */
+ ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a;
+ co = expect_trans_n_dim (ci);
+
+ if (expect_failure_with_errno) {
+ if (proj_errno (T.P)==expect_failure_with_errno)
+ return another_succeeding_failure ();
+ fprintf (T.fout, "errno=%d, expected=%d\n", proj_errno (T.P), expect_failure_with_errno);
+ return another_failing_failure ();
+ }
+
+
+ /* Succeeded in failing? - that's a success */
+ if (co.xyz.x==HUGE_VAL)
+ return another_succeeding_failure ();
+
+ /* Failed to fail? - that's a failure */
+ banner (T.operation);
+ errmsg (3, "%sFailed to fail. Operation definition in line no. %d\n",
+ delim, (int) T.operation_lineno
+ );
+ return another_failing_failure ();
+ }
+
+
+ if (T.verbosity > 3) {
+ fprintf (T.fout, "%s\n", T.P->inverted? "INVERTED": "NOT INVERTED");
+ fprintf (T.fout, "%s\n", T.dir== 1? "forward": "reverse");
+ fprintf (T.fout, "%s\n", proj_angular_input (T.P, T.dir)? "angular in": "linear in");
+ fprintf (T.fout, "%s\n", proj_angular_output (T.P, T.dir)? "angular out": "linear out");
+ fprintf (T.fout, "left: %d right: %d\n", T.P->left, T.P->right);
+ }
+
+ tests++;
+ T.e = parse_coord (args);
+ if (HUGE_VAL==T.e.v[0])
+ return expect_message_cannot_parse (args);
+
+
+ /* expected angular values, probably in degrees */
+ ce = proj_angular_output (T.P, T.dir)? torad_coord (T.P, T.dir, T.e): T.e;
+ if (T.verbosity > 3)
+ fprintf (T.fout, "EXPECTS %.12f %.12f %.12f %.12f\n",
+ ce.v[0],ce.v[1],ce.v[2],ce.v[3]);
+
+ /* input ("accepted") values, also probably in degrees */
+ ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a;
+ if (T.verbosity > 3)
+ fprintf (T.fout, "ACCEPTS %.12f %.12f %.12f %.12f\n",
+ ci.v[0],ci.v[1],ci.v[2],ci.v[3]);
+
+ /* do the transformation, but mask off dimensions not given in expect-ation */
+ co = expect_trans_n_dim (ci);
+ if (T.dimensions_given < 4)
+ co.v[3] = 0;
+ if (T.dimensions_given < 3)
+ co.v[2] = 0;
+
+ /* angular output from proj_trans comes in radians */
+ T.b = proj_angular_output (T.P, T.dir)? todeg_coord (T.P, T.dir, co): co;
+ if (T.verbosity > 3)
+ fprintf (T.fout, "GOT %.12f %.12f %.12f %.12f\n",
+ co.v[0],co.v[1],co.v[2],co.v[3]);
+
+#if 0
+ /* We need to handle unusual axis orders - that'll be an item for version 5.1 */
+ if (T.P->axisswap) {
+ ce = proj_trans (T.P->axisswap, T.dir, ce);
+ co = proj_trans (T.P->axisswap, T.dir, co);
+ }
+#endif
+ if (proj_angular_output (T.P, T.dir))
+ d = proj_lpz_dist (T.P, ce, co);
+ else
+ d = proj_xyz_dist (co, ce);
+
+ if (d > T.tolerance)
+ return expect_message (d, args);
+ succs++;
+
+ another_success ();
+ return 0;
+}
+
+
+
+/*****************************************************************************/
+static int verbose (const char *args) {
+/*****************************************************************************
+Tell the system how noisy it should be
+******************************************************************************/
+ int i = (int) proj_atof (args);
+
+ /* if -q/--quiet flag has been given, we do nothing */
+ if (T.verbosity < 0)
+ return 0;
+
+ if (strlen (args))
+ T.verbosity = i;
+ else
+ T.verbosity++;
+ return 0;
+}
+
+
+/*****************************************************************************/
+static int echo (const char *args) {
+/*****************************************************************************
+Add user defined noise to the output stream
+******************************************************************************/
+fprintf (T.fout, "%s\n", args);
+ return 0;
+}
+
+
+
+/*****************************************************************************/
+static int skip (const char *args) {
+/*****************************************************************************
+Indicate that the remaining material should be skipped. Mostly for debugging.
+******************************************************************************/
+ T.skip = 1;
+ (void) args;
+ F->level = 2; /* Silence complaints about missing </gie> element */
+ return 0;
+}
+
+
+static int dispatch (const char *cmnd, const char *args) {
+ if (T.skip)
+ return SKIP;
+ if (0==strcmp (cmnd, "operation")) return operation ((char *) args);
+ if (T.skip_test)
+ {
+ if (0==strcmp (cmnd, "expect")) return another_skip();
+ return 0;
+ }
+ if (0==strcmp (cmnd, "accept")) return accept (args);
+ if (0==strcmp (cmnd, "expect")) return expect (args);
+ if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args);
+ if (0==strcmp (cmnd, "banner")) return banner (args);
+ if (0==strcmp (cmnd, "verbose")) return verbose (args);
+ if (0==strcmp (cmnd, "direction")) return direction (args);
+ if (0==strcmp (cmnd, "tolerance")) return tolerance (args);
+ if (0==strcmp (cmnd, "ignore")) return ignore (args);
+ if (0==strcmp (cmnd, "require_grid")) return require_grid (args);
+ if (0==strcmp (cmnd, "echo")) return echo (args);
+ if (0==strcmp (cmnd, "skip")) return skip (args);
+ if (0==strcmp (cmnd, "use_proj4_init_rules"))
+ return use_proj4_init_rules (args);
+
+ return 0;
+}
+
+
+
+
+struct errno_vs_err_const {const char *the_err_const; int the_errno;};
+static const struct errno_vs_err_const lookup[] = {
+ {"pjd_err_no_args" , -1},
+ {"pjd_err_no_option_in_init_file" , -2},
+ {"pjd_err_no_colon_in_init_string" , -3},
+ {"pjd_err_proj_not_named" , -4},
+ {"pjd_err_unknown_projection_id" , -5},
+ {"pjd_err_eccentricity_is_one" , -6},
+ {"pjd_err_unknown_unit_id" , -7},
+ {"pjd_err_invalid_boolean_param" , -8},
+ {"pjd_err_unknown_ellp_param" , -9},
+ {"pjd_err_rev_flattening_is_zero" , -10},
+ {"pjd_err_ref_rad_larger_than_90" , -11},
+ {"pjd_err_es_less_than_zero" , -12},
+ {"pjd_err_major_axis_not_given" , -13},
+ {"pjd_err_lat_or_lon_exceed_limit" , -14},
+ {"pjd_err_invalid_x_or_y" , -15},
+ {"pjd_err_wrong_format_dms_value" , -16},
+ {"pjd_err_non_conv_inv_meri_dist" , -17},
+ {"pjd_err_non_con_inv_phi2" , -18},
+ {"pjd_err_acos_asin_arg_too_large" , -19},
+ {"pjd_err_tolerance_condition" , -20},
+ {"pjd_err_conic_lat_equal" , -21},
+ {"pjd_err_lat_larger_than_90" , -22},
+ {"pjd_err_lat1_is_zero" , -23},
+ {"pjd_err_lat_ts_larger_than_90" , -24},
+ {"pjd_err_control_point_no_dist" , -25},
+ {"pjd_err_no_rotation_proj" , -26},
+ {"pjd_err_w_or_m_zero_or_less" , -27},
+ {"pjd_err_lsat_not_in_range" , -28},
+ {"pjd_err_path_not_in_range" , -29},
+ {"pjd_err_h_less_than_zero" , -30},
+ {"pjd_err_k_less_than_zero" , -31},
+ {"pjd_err_lat_1_or_2_zero_or_90" , -32},
+ {"pjd_err_lat_0_or_alpha_eq_90" , -33},
+ {"pjd_err_ellipsoid_use_required" , -34},
+ {"pjd_err_invalid_utm_zone" , -35},
+ {"pjd_err_tcheby_val_out_of_range" , -36},
+ {"pjd_err_failed_to_find_proj" , -37},
+ {"pjd_err_failed_to_load_grid" , -38},
+ {"pjd_err_invalid_m_or_n" , -39},
+ {"pjd_err_n_out_of_range" , -40},
+ {"pjd_err_lat_1_2_unspecified" , -41},
+ {"pjd_err_abs_lat1_eq_abs_lat2" , -42},
+ {"pjd_err_lat_0_half_pi_from_mean" , -43},
+ {"pjd_err_unparseable_cs_def" , -44},
+ {"pjd_err_geocentric" , -45},
+ {"pjd_err_unknown_prime_meridian" , -46},
+ {"pjd_err_axis" , -47},
+ {"pjd_err_grid_area" , -48},
+ {"pjd_err_invalid_sweep_axis" , -49},
+ {"pjd_err_malformed_pipeline" , -50},
+ {"pjd_err_unit_factor_less_than_0" , -51},
+ {"pjd_err_invalid_scale" , -52},
+ {"pjd_err_non_convergent" , -53},
+ {"pjd_err_missing_args" , -54},
+ {"pjd_err_lat_0_is_zero" , -55},
+ {"pjd_err_ellipsoidal_unsupported" , -56},
+ {"pjd_err_too_many_inits" , -57},
+ {"pjd_err_invalid_arg" , -58},
+ {"pjd_err_dont_skip" , 5555},
+ {"pjd_err_unknown" , 9999},
+ {"pjd_err_enomem" , ENOMEM},
+};
+
+static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999};
+
+
+static int list_err_codes (void) {
+ int i;
+ const int n = sizeof lookup / sizeof lookup[0];
+
+ for (i = 0; i < n; i++) {
+ if (9999==lookup[i].the_errno)
+ break;
+ fprintf (T.fout, "%25s (%2.2d): %s\n", lookup[i].the_err_const + 8,
+ lookup[i].the_errno, pj_strerrno(lookup[i].the_errno));
+ }
+ return 0;
+}
+
+
+static const char *err_const_from_errno (int err) {
+ size_t i;
+ const size_t n = sizeof lookup / sizeof lookup[0];
+
+ for (i = 0; i < n; i++) {
+ if (err==lookup[i].the_errno)
+ return lookup[i].the_err_const + 8;
+ }
+ return unknown.the_err_const;
+}
+
+
+static int errno_from_err_const (const char *err_const) {
+ const size_t n = sizeof lookup / sizeof lookup[0];
+ size_t i, len;
+ int ret;
+ char tolower_err_const[100];
+
+ /* Make a lower case copy for matching */
+ for (i = 0; i < 99; i++) {
+ if (0==err_const[i] || isspace (err_const[i]))
+ break;
+ tolower_err_const[i] = (char) tolower (err_const[i]);
+ }
+ tolower_err_const[i] = 0;
+
+ /* If it looks numeric, return that numeric */
+ ret = (int) pj_atof (err_const);
+ if (0!=ret)
+ return ret;
+
+ /* Else try to find a matching identifier */
+ len = strlen (tolower_err_const);
+
+ /* First try to find a match excluding the PJD_ERR_ prefix */
+ for (i = 0; i < n; i++) {
+ if (0==strncmp (lookup[i].the_err_const + 8, err_const, len))
+ return lookup[i].the_errno;
+ }
+
+ /* If that did not work, try with the full name */
+ for (i = 0; i < n; i++) {
+ if (0==strncmp (lookup[i].the_err_const, err_const, len))
+ return lookup[i].the_errno;
+ }
+
+ /* On failure, return something unlikely */
+ return 9999;
+}
+
+
+static int errmsg (int errlev, const char *msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stdout, msg, args);
+ va_end(args);
+ if (errlev)
+ errno = errlev;
+ return errlev;
+}
+
+
+
+
+
+
+
+
+/****************************************************************************************
+
+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.
+
+****************************************************************************************/
+
+
+/***************************************************************************************/
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) {
+/****************************************************************************************
+Constructor for the ffio object.
+****************************************************************************************/
+ ffio *G = static_cast<ffio*>(calloc (1, sizeof (ffio)));
+ if (0==G)
+ return 0;
+
+ if (0==max_record_size)
+ max_record_size = 1000;
+
+ G->args = static_cast<char*>(calloc (1, 5*max_record_size));
+ if (0==G->args) {
+ free (G);
+ return 0;
+ }
+
+ G->next_args = static_cast<char*>(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 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 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 (T.skip)
+ return 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);
+
+ /* +2: 1 for the space separator and 1 for the NUL termination. */
+ if (G->args_size < args_len + next_len - skip_chars + 2) {
+ char *p = static_cast<char*>(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;
+}