1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  * Public functions for the GMT C/C++ API.  The API consist of functions
19  * in gmt_api.c, gmt_parse.c, and all the GMT modules; see gmt.h for list.
20  *
21  * Author: 	Paul Wessel
22  * Date:	1-JUN-2013
23  * Version:	5
24  *
25  * The API presently consists of 69 documented functions.  For a full
26  * description of the API, see the GMT_API documentation.
27  * These functions have Fortran bindings as well, provided you add
28  * -DFORTRAN_API to the C preprocessor flags [in ConfigUserAdvanced.cmake].
29  *
30  * There are 2 public functions used for GMT API session handling.
31  * This part of the API helps the developer create and delete GMT sessions:
32  *
33  * GMT_Create_Session	   : Initialize a new GMT session
34  * GMT_Destroy_Session	   : Destroy a GMT session
35  *
36  * There is 2 public functions for common error reporting.
37  * Errors will be reported to stderr or selected log file:
38  *
39  * GMT_Message		       : Report an message given a verbosity level
40  * GMT_Report		       : Report an error given an error code
41  *
42  * There are 33 further public functions used for GMT i/o activities:
43  *
44  * GMT_Alloc_Segment       : Allocate a single DATASET segment
45  * GMT_Begin_IO	           : Allow i/o to take place for rec-by-rec operations
46  * GMT_Convert_Data        : Convert between different data sets, if possible
47  * GMT_Create_Data         : Return an empty container for a new data set
48  * GMT_Destroy_Data        : Destroy a data set and its container
49  * GMT_Duplicate_Data      : Make an exact duplicate of a dataset
50  * GMT_Duplicate_String    : Allocates a copy of a string to be freed by API
51  * GMT_Get_FilePath        : Check existence of file and replace with full path
52  * GMT_End_IO              : Disallow further rec-by-rec i/o
53  * GMT_Get_Info            : Get meta-data from the object passed
54  * GMT_Get_Record          : Get the next single data record from the source(s)
55  * GMT_Get_Row             : Read one row from a grid
56  * GMT_Get_Status          : Examine current status of record-by-record i/o
57  * GMT_Get_Matrix          : Get user matrix from GMT_MATRIX array
58  * GMT_Get_Vector          : Get user vector from GMT_VECTOR column
59  * GMT_Get_Strings         : Get user strings from GMT_VECTOR or MATRIX container
60  * GMT_Init_IO             : Initialize rec-by-rec i/o machinery before program use
61  * GMT_Init_VirtualFile    : Reset a virtual file for reuse
62  * GMT_Inquire_VirtualFile : Determine family of a virtual file
63  * GMT_Open_VirtualFile    : Open a memory location for reading or writing by a module
64  * GMT_Put_Levels          : Place an array with 3rd dimension coordinates for a cube
65  * GMT_Put_Record          : Send the next output record to its destination
66  * GMT_Put_Row             : Write one row to a grid
67  * GMT_Put_Matrix          : Hook user matrix to GMT_MATRIX array
68  * GMT_Put_Vector          : Hook user vector to GMT_VECTOR column
69  * GMT_Put_Strings         : Hook user strings to GMT_VECTOR or MATRIX container
70  * GMT_Read_Data           : Load data into program memory from selected source
71  * GMT_Read_Group          : Read numerous files into an array of objects
72  * GMT_Read_VirtualFile    : Obtain the memory resource that a module wrote to.
73  * GMT_Register_IO         : Register a source (or destination) for i/o use
74  * GMT_Set_Comment         : Update a comment for a data set
75  * GMT_Write_Data          : Place data set from program memory to selected destination
76  * GMT_Encode_Options      : Used by external APIs to fill out options from implicit rules
77 
78  * The above functions deal with registration of input sources (files,
79  * streams, file handles, or memory locations) and output destinations
80  * (same flavors as input), the setup of the i/o, and generic functions
81  * to access the data either in one go (GMT_Get|Put_Data) or on a
82  * record-by-record basis (GMT_Get|Put_Record).  Finally, data sets that
83  * are allocated can then be destroyed when no longer needed.
84  *
85  * There are 6 functions that deal with options, defaults and arguments:
86  *
87  * GMT_Get_Common          : Checks for and returns values for GMT common options
88  * GMT_Get_Default         : Return the value of a GMT parameter as a string
89  * GMT_Get_Enum            : Return the integer constant of a GMT API enum.
90  * GMT_Get_Values          : Convert string to one or more coordinates or dimensions
91  * GMT_Set_Default         : Set a GMT parameter via a strings
92  * GMT_Option              : Display syntax for one or more GMT common options
93  *
94  * One function handles the listing of modules and the calling of any GMT module:
95  *
96  * GMT_Call_Module         : Call the specified GMT module
97  *
98  * Five functions are used to get grid index from row, col, and to obtain coordinates
99  *
100  * GMT_Get_Coord           : Return array of coordinates for one dimension
101  * GMT_Get_Index           : Return 1-D grid index given row, col
102  * GMT_Get_Index3          : Return 1-D cube index given row, col, layer
103  * GMT_Get_Pixel           : Return 1-D image index given row, col, layer
104  * GMT_Set_Columns         : Specify number of output columns for rec-by-rec writing
105  *
106  * For FFT operations there are 8 additional API functions:
107  *
108  * GMT_FFT                 : Call the forward or inverse FFT
109  * GMT_FFT_1D              : Lower-level 1-D FFT call
110  * GMT_FFT_2D              : Lower-level 2-D FFT call
111  * GMT_FFT_Create          : Initialize the FFT machinery for given dimension
112  * GMT_FFT_Destroy         : Destroy FFT machinery
113  * GMT_FFT_Option          : Display the syntax of the GMT FFT option settings
114  * GMT_FFT_Parse           : Parse the GMT FFT option
115  * GMT_FFT_Wavenumber      : Return selected wavenumber given its type
116  *
117  * There are also 13 functions for argument and option parsing.  See gmt_parse.c for these.
118  *
119  * Finally, three low-level F77-callable functions for grid i/o are given:
120  *
121  * gmt_f77_readgrdinfo_    : Read the header of a GMT grid
122  * gmt_f77_readgrd_        : Read a GMT grid from file
123  * gmt_f77_writegrd_       : Write a GMT grid to file
124  *
125  * --------------------------------------------------------------------------------------------
126  * Guru notes on memory management: Paul Wessel, June 2013.
127  *
128  * GMT maintains control over allocating, reallocating, and freeing of GMT objects created by GMT.
129  * Because GMT_modules may be given files, memory locations, streams, etc., as input and output we
130  * have to impose some structure as how this will work seamlessly.  Here, "GMT object" refers to
131  * any of the 5 GMT resources: grids, images, datasets, palettes, and PostScript.
132  *
133  * 1. When GMT allocates memory for a GMT object it sets its alloc_mode to GMT_ALLOC_INTERNALLY (1)
134  *    and its alloc_level to <module level>.  This is 0 for the gmt.c UNIX application as well as
135  *    for any external API (MEX, Python, Julia), 1 for any GMT module called, 2 for modules called
136  *    by top-level modules, etc., as far down as the thread goes.
137  * 2. Memory not allocated by GMT will have an implicit alloc_mode = GMT_ALLOC_EXTERNALLY [0]
138  *    and alloc_level = 0 (i.e., gmt executable or API level) but it does not matter since such memory is
139  *    only used for reading and we may never free it or reallocate it within GMT. This alloc_mode
140  *    only applies to data arrays inside objects (e.g., G->data), not the GMT objects themselves.
141  *    The GMT objects (the "containers") are freed at the end of their level, if not before.
142  * 3. Memory passed into modules as "input file" requires no special treatment since its level
143  *    will be lower than that of the module it is used in, and when that module tries to free it
144  *    (directly with GMT_Destroy_Data or via end-of-module gmtlib_garbage_collection) it will skip
145  *    it as its level does not match the current module level.  A module can only free memory that
146  *    it allocated; the exception is the top-level gmt application.
147  * 4. Passing memory out of a module (i.e., "writing to memory") requires that the calling function
148  *    first create an output object and use the ID to encode the memory filename (@GMTAPI@-######).
149  *    The object stores the level it was called at.  Pass the encoded filename as the output file.
150  *    When GMT_Create_Data is called with no dimensions then the direction is set to GMT_OUT and
151  *    we set the object's messenger flag to true.  This is used so that when the dataset we wish to
152  *    return out of a module is built it replaces the empty initial dataset but inherits that dataset's
153  *    alloc_level so it may survive the life of the module process.
154  *    Internally, the memory that the module allocates (e.g., grid, dataset, etc.) will initially
155  *    have an alloc_level matching the module level (and would be freed if written to a regular
156  *    file).  However, when GMT_Write_Data is called and we branch into the GMT_REFERENCE case we
157  *    instead take the following steps:
158  *	a) The registered output API->object's resource pointer is set to the GMT object that the
159  *         module allocated (this is how we pass the data out of a module).
160  *	b) The GMT object's alloc_level is changed to equal the output API->object's level (this
161  *         is how it will survive beyond the end of the module).
162  *	c) The API object originally pointing to the GMT object is flagged by having its variable
163  *         no_longer_owner set to true (this is how we avoid freeing something twice).
164  *    When the module ends there are two API objects with references to the GMT object: the internal
165  *    module object and the output object.  The first is set to NULL by gmtlib_garbage_collection because
166  *    the object is no longer the owner of the data. The second is ignored because its level is too low.
167  *    After that any empty API objects are removed (so the no_longer_owner one is removed), while
168  *    the second survives the life of the module, as we require.
169  *
170  * Thus, at the session (gmt) level all GMT objects have alloc_level = 0 since anything higher will
171  * have been freed by a module.  GMT_Destroy_Session finally calls gmtlib_garbage_collection a final
172  * time and he frees any remaining GMT objects.
173  *
174  * Notes on family vs actual_family:
175  * The S->actual_family contains the object type that we allocated.  However, we allow modules
176  * that expect a DATASET to instead be passed a GMT_VECTOR or GMT_MATRIX.  If so then S->family
177  * will be GMT_IS_DATASET while the actual_family remains GMT_VECTOR|GMT_MATRIX.  The i/o functions
178  * GMT_Read_Data, GMT_Put_Record, etc knows how to deal with this.
179  */
180 
181 /*!
182  * \file gmt_api.c
183  * \brief Public functions for the GMT C/C++ API.
184  */
185 
186 #include "gmt_dev.h"
187 #include "gmt_internals.h"
188 #include "gmt_sharedlibs.h" 	/* Common shared libs structures */
189 #include <stdarg.h>
190 
191 #ifdef HAVE_DIRENT_H_
192 #	include <dirent.h>
193 #endif
194 
195 #ifdef HAVE_SYS_DIR_H_
196 #	include <sys/dir.h>
197 #endif
198 
199 /* Possibly define missing shared library constants */
200 #ifndef DT_DIR
201 #	define DT_DIR 4
202 #endif
203 
204 #ifndef RTLD_LAZY
205 #	define RTLD_LAZY 1
206 #endif
207 
208 #ifdef WIN32	/* Special for Windows */
209 #	include <windows.h>
210 #	include <process.h>
211 #	define getpid _getpid
212 #else
213 #	include <sys/ioctl.h>
214 #endif
215 
216 #define GMTAPI_MAX_ID 999999	/* Largest integer that will fit in the %06d format */
217 #define GMTAPI_UNLIMITED	0	/* Using 0 to mean we may allow 1 or more data objects of this family */
218 
219 #ifdef FORTRAN_API
220 /* Global structure pointer needed for FORTRAN-77 [PW: not tested yet - is it even needed?] */
221 static struct GMTAPI_CTRL *GMT_FORTRAN = NULL;
222 #endif
223 
224 static int GMTAPI_session_counter = 0;	/* Keeps track of the ID of new sessions for multi-session programs */
225 
226 /* Grid node lookup */
227 static uint64_t (*GMTAPI_index_function) (struct GMT_GRID_HEADER *, uint64_t, uint64_t, uint64_t);	/* Pointer to index function (for images only) */
228 
229 /*! Macros that report error, then return a NULL pointer, the error, or a value, respectively */
230 #define return_null(API,err) { gmtlib_report_error(API,err); return (NULL);}
231 #define return_error(API,err) { gmtlib_report_error(API,err); return (err);}
232 #define return_value(API,err,val) { gmtlib_report_error(API,err); return (val);}
233 
234 /* We asked for subset of grid if the wesn pointer is not NULL and indicates a nonzero region */
235 #define full_region(wesn) (!wesn || (wesn[XLO] == wesn[XHI] && wesn[YLO] == wesn[YHI]))
236 
237 /* DATASET can be given via many individual files. */
238 #define multiple_files_ok(family) (family == GMT_IS_DATASET)
239 /* GRID and IMAGE can be read it two steps (header, then data). */
240 #define a_grid_or_image(family) (family == GMT_IS_GRID || family == GMT_IS_IMAGE)
241 /* GRID, IMAGE, and DATACUBE can be read it two steps (header, then data). */
242 #define a_grid_or_image_or_cube(family) (family == GMT_IS_GRID || family == GMT_IS_IMAGE || family == GMT_IS_CUBE)
243 /* A MATRIX read as a SURFACE will read a grid */
244 #define a_matrix_surface(family,geometry) (family == GMT_IS_MATRIX && geometry == GMT_IS_SURFACE)
245 
246 /* Misc. local text strings needed in this file only, used when debug verbose is on (-Vd).
247  * NOTE: The order of these MUST MATCH the order in the enums in gmt_resources.h! */
248 
249 static const char *GMT_method[] = {"File", "Stream", "File Descriptor", "Memory Copy", "Memory Reference"};
250 static const char *GMT_family[] = {"Data Table", "Grid", "Image", "CPT", "PostScript", "Matrix", "Vector", "Cube", "Coord"};
251 static const char *GMT_direction[] = {"Input", "Output"};
252 static const char *GMT_stream[] = {"Standard", "User-supplied"};
253 static const char *GMT_status[] = {"Unused", "In-use", "Used"};
254 static const char *GMT_geometry[] = {"Not Set", "Point", "Line", "Polygon", "Point|Line|Poly", "Line|Poly", "Surface", "Volume", "Non-Geographical", "Text"};
255 static const char *GMT_class[] = {"QUIET", "NOTICE", "ERROR", "WARNING", "TIMING", "INFORMATION", "COMPATIBILITY", "DEBUG"};
256 static unsigned int GMT_no_pad[4] = {0, 0, 0, 0};
257 static const char *GMT_family_abbrev[] = {"D", "G", "I", "C", "X", "M", "V", "U", "-"};
258 static const char *GMT_type[GMT_N_TYPES] = {"byte", "byte", "integer", "integer", "integer", "integer",
259                                             "integer", "integer", "double", "double", "string", "datetime"};
260 
261 /*! Two different i/o mode: GMT_Put|Get_Data vs GMT_Put|Get_Record */
262 enum GMT_enum_iomode {
263 	GMTAPI_BY_SET 	= 0,	/* Default is to read the entire dataset */
264 	GMTAPI_BY_REC	= 1};	/* Means we will access the registered files on a record-by-record basis */
265 
266 /*! Entries into dim[] for matrix or vector */
267 enum GMT_dim {
268 	GMTAPI_HDR_POS = 3,	/* Used with curr_pos to keep track of table headers only */
269 	GMTAPI_DIM_COL	= 0,	/* Holds the number of columns for vectors and x-nodes for matrix */
270 	GMTAPI_DIM_ROW = 1};	/* Holds the number of rows for vectors and y-nodes for matrix */
271 
272 enum GMTAPI_enum_input {
273 	GMTAPI_OPTION_INPUT 	= 0,	/* Input resource specified via an option (e.g., -G<file>) */
274 	GMTAPI_MODULE_INPUT 	= 1};	/* Input resource specified via the module command line */
275 
276 enum GMTAPI_enum_status {
277 	GMTAPI_GOT_SEGHEADER 	= -1,	/* Read a segment header */
278 	GMTAPI_GOT_SEGGAP 	= -2};	/* Detected a gap and insertion of new segment header */
279 
280 /*==================================================================================================
281  *		PRIVATE FUNCTIONS ONLY USED BY THIS LIBRARY FILE
282  *==================================================================================================
283  *
284  * gmtapi_* functions are static and only used in gmt_api.c
285  * gmtlib_* functions are exported and may be used in other gmt_*.c files
286  */
287 
gmtapi_method(unsigned int M)288 GMT_LOCAL const char *gmtapi_method (unsigned int M) {
289 	if (M < GMT_IS_DUPLICATE) return (GMT_method[M]);
290 	if (M == GMT_IS_DUPLICATE) return (GMT_method[3]);
291 	if (M == GMT_IS_REFERENCE) return (GMT_method[4]);
292 	return NULL;
293 }
294 
295 GMT_LOCAL void gmtapi_get_record_init (struct GMTAPI_CTRL *API);
296 
gmtapi_sort_on_classic(const void * vA,const void * vB)297 GMT_LOCAL int gmtapi_sort_on_classic (const void *vA, const void *vB) {
298 	const struct GMT_MODULEINFO *A = vA, *B = vB;
299 	if (A == NULL) return +1;	/* Get the NULL entry to the end */
300 	if (B == NULL) return -1;	/* Get the NULL entry to the end */
301 	return strcmp(A->cname, B->cname);
302 }
303 
gmtapi_sort_on_modern(const void * vA,const void * vB)304 GMT_LOCAL int gmtapi_sort_on_modern (const void *vA, const void *vB) {
305 	const struct GMT_MODULEINFO *A = vA, *B = vB;
306 	if (A == NULL) return +1;	/* Get the NULL entry to the end */
307 	if (B == NULL) return -1;	/* Get the NULL entry to the end */
308 	return strcmp(A->mname, B->mname);
309 }
310 
311 
312 /* Function to exclude some special core modules from being reported by gmt --help|show-modules */
gmtapi_skip_this_module(const char * name)313 GMT_LOCAL int gmtapi_skip_this_module (const char *name) {
314 	if (!strncmp (name, "gmtread", 7U)) return 1;	/* Skip the gmtread module */
315 	if (!strncmp (name, "gmtwrite", 8U)) return 1;	/* Skip the gmtwrite module */
316 	return 0;	/* Display this one */
317 }
318 
319 /* Function to exclude modern mode modules from being reported by gmt --show-classic */
gmtapi_skip_modern_module(const char * name)320 GMT_LOCAL int gmtapi_skip_modern_module (const char *name) {
321 	if (!strncmp (name, "subplot", 7U)) return 1;	/* Skip the subplot module */
322 	if (!strncmp (name, "figure", 6U)) return 1;	/* Skip the figure module */
323 	if (!strncmp (name, "begin", 5U)) return 1;		/* Skip the begin module */
324 	if (!strncmp (name, "clear", 5U)) return 1;		/* Skip the clear module */
325 	if (!strncmp (name, "inset", 5U)) return 1;		/* Skip the inset module */
326 	if (!strncmp (name, "movie", 5U)) return 1;		/* Skip the movie module */
327 	if (!strncmp (name, "docs", 4U)) return 1;		/* Skip the docs module */
328 	if (!strncmp (name, "end", 3U)) return 1;		/* Skip the end module */
329 	return 0;	/* Display this one */
330 }
331 
332 struct GMT_WORD {	/* Used by GMT_Wrap_Line only */
333 	char *word;
334 	unsigned int space;
335 };
336 
337 EXTERN_MSC int gmt_nc_write_cube (struct GMT_CTRL *GMT, struct GMT_CUBE *C, double wesn[], const char *file);
338 
339 /* Pretty print all GMT core module names and their purposes for gmt --help */
gmtlib_module_show_all(void * V_API,struct GMT_MODULEINFO M[],const char * title)340 void gmtlib_module_show_all (void *V_API, struct GMT_MODULEINFO M[], const char *title) {
341 	unsigned int module_id = 0, n;
342 	char message[GMT_LEN256];
343 	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
344 
345 	GMT_Message (V_API, GMT_TIME_NONE, "\n===  %s  ===\n", title);
346 	while (M[module_id].cname != NULL) {
347 		if (module_id == 0 || strcmp (M[module_id-1].component, M[module_id].component)) {
348 			/* Start of new supplemental group */
349 			snprintf (message, GMT_LEN256, "\nModule name:     Purpose of %s module:\n", M[module_id].component);
350 			GMT_Message (V_API, GMT_TIME_NONE, message);
351 			GMT_Message (V_API, GMT_TIME_NONE, "----------------------------------------------------------------\n");
352 		}
353 		n = module_id + 1;	/* Determine extent of this component lib */
354 		while (M[n].cname != NULL && !strcmp (M[n-1].component, M[n].component)) n++;
355 		/* Sort array on modern names */
356 		qsort (&M[module_id], n-module_id, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_modern);
357 
358 		if (API->external || !gmtapi_skip_this_module (M[module_id].cname)) {
359 			snprintf (message, GMT_LEN256, "%-16s %s\n",
360 				M[module_id].mname, M[module_id].purpose);
361 				GMT_Message (V_API, GMT_TIME_NONE, message);
362 		}
363 		++module_id;
364 	}
365 }
366 
367 /* Produce single list on stdout of all GMT core module names for gmt --show-modules */
gmtlib_module_list_all(void * V_API,struct GMT_MODULEINFO M[])368 void gmtlib_module_list_all (void *V_API, struct GMT_MODULEINFO M[]) {
369 	unsigned int module_id = 0;
370 	size_t n_modules = 0;
371 	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
372 
373 	while (M[n_modules].cname != NULL)	/* Count the modules */
374 		++n_modules;
375 
376 	/* Sort array on modern names */
377 	qsort (M, n_modules, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_modern);
378 
379 	while (M[module_id].cname != NULL) {
380 		if (API->external || !gmtapi_skip_this_module (M[module_id].cname))
381 			printf ("%s\n", M[module_id].mname);
382 		++module_id;
383 	}
384 }
385 
386 /* Produce single list on stdout of all GMT core module names for gmt --show-classic [i.e., classic mode names] */
gmtlib_module_classic_all(void * V_API,struct GMT_MODULEINFO M[])387 void gmtlib_module_classic_all (void *V_API, struct GMT_MODULEINFO M[]) {
388 	unsigned int module_id = 0;
389 	size_t n_modules = 0;
390 	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);
391 
392 	while (M[n_modules].cname != NULL)	/* Count the modules */
393 		++n_modules;
394 
395 	/* Sort array on classic names */
396 	qsort (M, n_modules, sizeof (struct GMT_MODULEINFO), gmtapi_sort_on_classic);
397 
398 	while (M[module_id].cname != NULL) {
399 		if (API->external || !(gmtapi_skip_this_module (M[module_id].cname) || gmtapi_skip_modern_module (M[module_id].cname)))
400 			printf ("%s\n", M[module_id].cname);
401 		++module_id;
402 	}
403 }
404 
405 /* Lookup module id by name, return option keys pointer (for external API developers) */
gmtlib_module_keys(void * API,struct GMT_MODULEINFO M[],char * candidate)406 const char *gmtlib_module_keys (void *API, struct GMT_MODULEINFO M[], char *candidate) {
407 	int module_id = 0;
408 	gmt_M_unused(API);
409 
410 	/* Match actual_name against g_module[module_id].cname */
411 	while (M[module_id].cname != NULL &&
412 	       strcmp (candidate, M[module_id].cname))
413 		++module_id;
414 
415 	/* Return Module keys or NULL */
416 	return (M[module_id].keys);
417 }
418 
419 /* Lookup module id by name, return group char name (for external API developers) */
gmtlib_module_group(void * API,struct GMT_MODULEINFO M[],char * candidate)420 const char *gmtlib_module_group (void *API, struct GMT_MODULEINFO M[], char *candidate) {
421 	int module_id = 0;
422 	gmt_M_unused(API);
423 
424 	/* Match actual_name against g_module[module_id].cname */
425 	while (M[module_id].cname != NULL &&
426 	       strcmp (candidate, M[module_id].cname))
427 		++module_id;
428 
429 	/* Return Module keys or NULL */
430 	return (M[module_id].component);
431 }
432 
GMT_Show_ModuleInfo(void * API,struct GMT_MODULEINFO M[],char * arg,unsigned int mode)433 int GMT_Show_ModuleInfo (void *API, struct GMT_MODULEINFO M[], char *arg, unsigned int mode) {
434 	/* API function to display module information from shared libraries */
435 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
436 	switch (mode) {
437 		case GMT_MODULE_HELP:
438 			if (arg == NULL) return_error (API, GMT_ARG_IS_NULL);
439 			gmtlib_module_show_all (API, M, arg);
440 			break;
441 		case GMT_MODULE_SHOW_MODERN:
442 			gmtlib_module_list_all (API, M);
443 			break;
444 		case GMT_MODULE_SHOW_CLASSIC:
445 			gmtlib_module_classic_all (API, M);
446 			break;
447 		default:
448 			GMT_Report (API, GMT_MSG_ERROR, "Internal error in GMT_Show_ModuleInfo: Passed bad mode (%d)\n", mode);
449 			return_error (API, GMT_NOT_A_VALID_MODE);
450 			break;
451 	}
452 	return (GMT_NOERROR);
453 }
454 
GMT_Get_ModuleInfo(void * API,struct GMT_MODULEINFO M[],char * module,unsigned int mode)455 const char * GMT_Get_ModuleInfo (void *API, struct GMT_MODULEINFO M[], char *module, unsigned int mode) {
456 	/* API function to display module information from shared libraries */
457 	const char *answer = NULL;
458 	if (API == NULL) return_null (NULL, GMT_NOT_A_SESSION);
459 	if (module == NULL) return_null (NULL, GMT_ARG_IS_NULL);
460 	switch (mode) {
461 		case GMT_MODULE_KEYS:
462 			answer = gmtlib_module_keys (API, M, module);
463 			break;
464 		case GMT_MODULE_GROUP:
465 			answer = gmtlib_module_group (API, M, module);
466 			break;
467 		default:
468 			GMT_Report (API, GMT_MSG_ERROR, "Internal error in GMT_Get_ModuleInfo: Passed bad mode (%d)\n", mode);
469 			return_null (NULL, GMT_NOT_A_VALID_MODE);
470 			break;
471 	}
472 	return (answer);
473 }
474 
475 /* Series of one-line functions to assign val to a particular union member of array u at position row, rounding if integer output */
gmtapi_put_val_double(union GMT_UNIVECTOR * u,uint64_t row,double val)476 GMT_LOCAL void gmtapi_put_val_double (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->f8[row]  =                  val; }
gmtapi_put_val_float(union GMT_UNIVECTOR * u,uint64_t row,double val)477 GMT_LOCAL void gmtapi_put_val_float  (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->f4[row]  = (float)          val; }
gmtapi_put_val_ulong(union GMT_UNIVECTOR * u,uint64_t row,double val)478 GMT_LOCAL void gmtapi_put_val_ulong  (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui8[row] = (uint64_t)lrint(val); }
gmtapi_put_val_long(union GMT_UNIVECTOR * u,uint64_t row,double val)479 GMT_LOCAL void gmtapi_put_val_long   (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si8[row] =  (int64_t)lrint(val); }
gmtapi_put_val_uint(union GMT_UNIVECTOR * u,uint64_t row,double val)480 GMT_LOCAL void gmtapi_put_val_uint   (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui4[row] = (uint32_t)lrint(val); }
gmtapi_put_val_int(union GMT_UNIVECTOR * u,uint64_t row,double val)481 GMT_LOCAL void gmtapi_put_val_int    (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si4[row] =  (int32_t)lrint(val); }
gmtapi_put_val_ushort(union GMT_UNIVECTOR * u,uint64_t row,double val)482 GMT_LOCAL void gmtapi_put_val_ushort (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->ui2[row] = (uint16_t)lrint(val); }
gmtapi_put_val_short(union GMT_UNIVECTOR * u,uint64_t row,double val)483 GMT_LOCAL void gmtapi_put_val_short  (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->si2[row] =  (int16_t)lrint(val); }
gmtapi_put_val_uchar(union GMT_UNIVECTOR * u,uint64_t row,double val)484 GMT_LOCAL void gmtapi_put_val_uchar  (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->uc1[row] =  (uint8_t)lrint(val); }
gmtapi_put_val_char(union GMT_UNIVECTOR * u,uint64_t row,double val)485 GMT_LOCAL void gmtapi_put_val_char   (union GMT_UNIVECTOR *u, uint64_t row, double val) { u->sc1[row] =   (int8_t)lrint(val); }
486 
gmtapi_get_val_double(union GMT_UNIVECTOR * u,uint64_t row,double * val)487 GMT_LOCAL void gmtapi_get_val_double (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->f8[row]; }
gmtapi_get_val_float(union GMT_UNIVECTOR * u,uint64_t row,double * val)488 GMT_LOCAL void gmtapi_get_val_float  (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->f4[row]; }
gmtapi_get_val_ulong(union GMT_UNIVECTOR * u,uint64_t row,double * val)489 GMT_LOCAL void gmtapi_get_val_ulong  (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = (double)u->ui8[row]; }	/* Must cast/truncate since longs integer range exceed that of double */
gmtapi_get_val_long(union GMT_UNIVECTOR * u,uint64_t row,double * val)490 GMT_LOCAL void gmtapi_get_val_long   (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = (double)u->si8[row]; }	/* Must cast/truncate since longs integer range exceed that of double */
gmtapi_get_val_uint(union GMT_UNIVECTOR * u,uint64_t row,double * val)491 GMT_LOCAL void gmtapi_get_val_uint   (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->ui4[row]; }
gmtapi_get_val_int(union GMT_UNIVECTOR * u,uint64_t row,double * val)492 GMT_LOCAL void gmtapi_get_val_int    (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->si4[row]; }
gmtapi_get_val_ushort(union GMT_UNIVECTOR * u,uint64_t row,double * val)493 GMT_LOCAL void gmtapi_get_val_ushort (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->ui2[row]; }
gmtapi_get_val_short(union GMT_UNIVECTOR * u,uint64_t row,double * val)494 GMT_LOCAL void gmtapi_get_val_short  (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->si2[row]; }
gmtapi_get_val_uchar(union GMT_UNIVECTOR * u,uint64_t row,double * val)495 GMT_LOCAL void gmtapi_get_val_uchar  (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->uc1[row]; }
gmtapi_get_val_char(union GMT_UNIVECTOR * u,uint64_t row,double * val)496 GMT_LOCAL void gmtapi_get_val_char   (union GMT_UNIVECTOR *u, uint64_t row, double *val) { *val = u->sc1[row]; }
497 
gmtapi_select_put_function(struct GMTAPI_CTRL * API,unsigned int type)498 GMT_LOCAL inline GMT_putfunction gmtapi_select_put_function (struct GMTAPI_CTRL *API, unsigned int type) {
499 	switch (type) {	/* Use type to select the correct put function with which to place a value in the union */
500 		case GMT_DOUBLE:	return (gmtapi_put_val_double);	break;
501 		case GMT_FLOAT:		return (gmtapi_put_val_float);	break;
502 		case GMT_ULONG:		return (gmtapi_put_val_ulong);	break;
503 		case GMT_LONG:		return (gmtapi_put_val_long);	break;
504 		case GMT_UINT:		return (gmtapi_put_val_uint);	break;
505 		case GMT_INT:		return (gmtapi_put_val_int);	break;
506 		case GMT_USHORT:	return (gmtapi_put_val_ushort);	break;
507 		case GMT_SHORT:		return (gmtapi_put_val_short);	break;
508 		case GMT_UCHAR:		return (gmtapi_put_val_uchar);	break;
509 		case GMT_CHAR:		return (gmtapi_put_val_char);	break;
510 		default:
511 			GMT_Report (API, GMT_MSG_ERROR, "Internal error in gmtapi_select_put_function: Passed bad type (%d), Will be unable to place binary data\n", type);
512 			return NULL;
513 			break;
514 	}
515 }
516 
gmtapi_select_get_function(struct GMTAPI_CTRL * API,unsigned int type)517 GMT_LOCAL inline GMT_getfunction gmtapi_select_get_function (struct GMTAPI_CTRL *API, unsigned int type) {
518 	switch (type) {	/* Use type to select the correct get function with which to extract a value from the union */
519 		case GMT_DOUBLE:	return (gmtapi_get_val_double);	break;
520 		case GMT_FLOAT:		return (gmtapi_get_val_float);	break;
521 		case GMT_ULONG:		return (gmtapi_get_val_ulong);	break;
522 		case GMT_LONG:		return (gmtapi_get_val_long);	break;
523 		case GMT_UINT:		return (gmtapi_get_val_uint);	break;
524 		case GMT_INT:		return (gmtapi_get_val_int);	break;
525 		case GMT_USHORT:	return (gmtapi_get_val_ushort);	break;
526 		case GMT_SHORT:		return (gmtapi_get_val_short);	break;
527 		case GMT_UCHAR:		return (gmtapi_get_val_uchar);	break;
528 		case GMT_CHAR:		return (gmtapi_get_val_char);	break;
529 		default:
530 			GMT_Report (API, GMT_MSG_ERROR, "Internal error in gmtapi_select_get_function: Passed bad type (%d), will be unable to convert binary data\n", type);
531 			return NULL;
532 			break;
533 	}
534 }
535 
gmtapi_valid_input_family(unsigned int family)536 GMT_LOCAL bool gmtapi_valid_input_family (unsigned int family) {
537 	/* Return true for the main input types */
538 	return (family == GMT_IS_DATASET || family == GMT_IS_GRID || family == GMT_IS_CUBE \
539 	       || family == GMT_IS_IMAGE || family == GMT_IS_PALETTE || family == GMT_IS_POSTSCRIPT);
540 }
541 
gmtapi_valid_actual_family(unsigned int family)542 GMT_LOCAL bool gmtapi_valid_actual_family (unsigned int family) {
543 	/* Return true for the main actual family types */
544 	return (family < GMT_N_FAMILIES);
545 }
546 
gmtapi_valid_output_family(unsigned int family)547 GMT_LOCAL bool gmtapi_valid_output_family (unsigned int family) {
548 	if (family == GMT_IS_VECTOR || family == GMT_IS_MATRIX || family == GMT_IS_POSTSCRIPT) return true;
549 	return gmtapi_valid_input_family (family);
550 }
551 
gmtapi_valid_via_family(unsigned int family)552 GMT_LOCAL bool gmtapi_valid_via_family (unsigned int family) {
553 	if (family == GMT_IS_VECTOR || family == GMT_IS_MATRIX) return true;
554 	return false;
555 }
556 
gmtapi_valid_type(int type)557 GMT_LOCAL bool gmtapi_valid_type (int type) {	/* Check for valid matrix/vector data types */
558 	if (type < GMT_CHAR || type > GMT_DOUBLE) return false;
559 	return true;
560 }
561 
562 /*! . */
gmtapi_get_item(struct GMTAPI_CTRL * API,unsigned int family,void * data)563 GMT_LOCAL int gmtapi_get_item (struct GMTAPI_CTRL *API, unsigned int family, void *data) {
564 	/* Get the first item of requested family from list of objects, allowing for
565 	 * datasets and grids to masquerade as other things (Matrix, vector). */
566 	unsigned int i;
567 	int item;
568 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
569 
570 	API->error = GMT_NOERROR;
571 	for (i = 0, item = GMT_NOTSET; item == GMT_NOTSET && i < API->n_objects; i++) {
572 		if (!API->object[i]) continue;				/* Empty object */
573 		S_obj = API->object[i];
574 		if (!S_obj->resource) continue;		/* No resource */
575 		if (S_obj->family != (enum GMT_enum_family)family) {		/* Not the required data type; check for exceptions... */
576 			if (family == GMT_IS_DATASET && gmtapi_valid_via_family (S_obj->family))
577 				S_obj->family = GMT_IS_DATASET;	/* Vectors or Matrix masquerading as dataset are valid. Change their family here. */
578 			else if (family == GMT_IS_GRID && S_obj->family == GMT_IS_MATRIX)
579 				S_obj->family = GMT_IS_GRID;	/* Matrix masquerading as grid is valid. Change its family here. */
580 			else	/* We don't like your kind */
581 				continue;
582 		}
583 		if (S_obj->resource == data) item = i;	/* Found the requested data */
584 	}
585 	if (item == GMT_NOTSET) { API->error = GMT_NOT_A_VALID_ID; return (GMT_NOTSET); }	/* No such data found */
586 	return (item);
587 }
588 
589 /*! . */
gmtapi_n_cols_needed_for_gaps(struct GMT_CTRL * GMT,uint64_t n)590 GMT_LOCAL inline uint64_t gmtapi_n_cols_needed_for_gaps (struct GMT_CTRL *GMT, uint64_t n) {
591 	/* Return the actual items needed (which may be more than n if gap testing demands it) */
592 	if (GMT->common.g.active) return (MAX (n, GMT->common.g.n_col));	/* n or n_col (if larger) */
593 	return (n);	/* No gap checking, n it is */
594 }
595 
596 /*! . */
gmtapi_update_prev_rec(struct GMT_CTRL * GMT,uint64_t n_use)597 GMT_LOCAL inline void gmtapi_update_prev_rec (struct GMT_CTRL *GMT, uint64_t n_use) {
598 	/* Update previous record before reading the new record, but only if needed */
599 	if (GMT->current.io.need_previous) gmt_M_memcpy (GMT->current.io.prev_rec, GMT->current.io.curr_rec, n_use, double);
600 }
601 
602 /*! . */
gmtapi_alloc_grid(struct GMT_CTRL * GMT,struct GMT_GRID * G)603 GMT_LOCAL int gmtapi_alloc_grid (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
604 	/* Use information in Grid header to allocate the grid data array.
605 	 * We assume gmtapi_init_grdheader has already been called. */
606 	struct GMT_GRID_HEADER_HIDDEN *GH = NULL;
607 	if (G == NULL) return (GMT_PTR_IS_NULL);
608 	GH = gmt_get_H_hidden (G->header);
609 	if (G->data) return (GMT_PTR_NOT_NULL);
610 	if (G->header->size == 0U) return (GMT_SIZE_IS_ZERO);
611 	if ((G->data = gmt_M_memory_aligned (GMT, NULL, G->header->size, gmt_grdfloat)) == NULL) return (GMT_MEMORY_ERROR);
612 	GH->orig_datatype = (sizeof (gmt_grdfloat) == sizeof (float)) ? GMT_FLOAT : GMT_DOUBLE;
613 	return (GMT_NOERROR);
614 }
615 
616 /*! . */
gmtapi_grid_coord(struct GMTAPI_CTRL * API,int dim,struct GMT_GRID * G)617 GMT_LOCAL double * gmtapi_grid_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_GRID *G) {
618 	return (gmt_grd_coord (API->GMT, G->header, dim));
619 }
620 
621 /*! . */
gmtapi_cube_coord(struct GMTAPI_CTRL * API,int dim,struct GMT_CUBE * U)622 GMT_LOCAL double * gmtapi_cube_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_CUBE *U) {
623 	return (gmt_grd_coord (API->GMT, U->header, dim));
624 }
625 
626 /*! . */
gmtapi_alloc_grid_xy(struct GMTAPI_CTRL * API,struct GMT_GRID * G)627 GMT_LOCAL int gmtapi_alloc_grid_xy (struct GMTAPI_CTRL *API, struct GMT_GRID *G) {
628 	/* Use information in Grid header to allocate the grid x/y vectors.
629 	 * We assume gmtapi_init_grdheader has been called. */
630 	struct GMT_GRID_HIDDEN *GH = NULL;
631 	if (G == NULL) return (GMT_PTR_IS_NULL);
632 	if (G->x || G->y) return (GMT_PTR_NOT_NULL);
633 	GH = gmt_get_G_hidden (G);
634 	G->x = gmtapi_grid_coord (API, GMT_X, G);	/* Get array of x coordinates */
635 	G->y = gmtapi_grid_coord (API, GMT_Y, G);	/* Get array of y coordinates */
636 	GH->xy_alloc_mode[GMT_X] = GH->xy_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
637 	return (GMT_NOERROR);
638 }
639 
640 /*! . */
gmtapi_alloc_image(struct GMT_CTRL * GMT,uint64_t * dim,unsigned int mode,struct GMT_IMAGE * I)641 GMT_LOCAL int gmtapi_alloc_image (struct GMT_CTRL *GMT, uint64_t *dim, unsigned int mode, struct GMT_IMAGE *I) {
642 	/* Use information in Image header to allocate the image data.
643 	 * We assume gmtapi_init_grdheader has been called.
644 	 * If dim given and is 2 or 4 then we have 1 or 3 bands plus alpha channel
645 	 * Depending on mode, the alpha layer is part of image or separate array. */
646 	unsigned int n_bands = I->header->n_bands;
647 
648 	if (I == NULL) return (GMT_PTR_IS_NULL);
649 	if (I->data) return (GMT_PTR_NOT_NULL);
650 	if (I->header->size == 0U) return (GMT_SIZE_IS_ZERO);
651 	if (dim && (dim[GMT_Z] == 2 || dim[GMT_Z] == 4)) {	/* Transparency layer is requested */
652 		if ((mode & GMT_IMAGE_ALPHA_LAYER) == 0) {	/* Use a separate alpha array */
653 			if ((I->alpha = gmt_M_memory_aligned (GMT, NULL, I->header->size, unsigned char)) == NULL) return (GMT_MEMORY_ERROR);
654 			n_bands--;	/* One less layer to allocate */
655 		}
656 	}
657 	if ((I->data = gmt_M_memory_aligned (GMT, NULL, I->header->size * n_bands, unsigned char)) == NULL) return (GMT_MEMORY_ERROR);
658 	I->header->n_bands = n_bands;	/* Update as needed */
659 	return (GMT_NOERROR);
660 }
661 
662 /*! . */
gmtapi_image_coord(struct GMTAPI_CTRL * API,int dim,struct GMT_IMAGE * I)663 GMT_LOCAL double * gmtapi_image_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_IMAGE *I) {
664 	return (gmt_grd_coord (API->GMT, I->header, dim));
665 }
666 
667 /*! . */
gmtapi_alloc_image_xy(struct GMTAPI_CTRL * API,struct GMT_IMAGE * I)668 GMT_LOCAL int gmtapi_alloc_image_xy (struct GMTAPI_CTRL *API, struct GMT_IMAGE *I) {
669 	/* Use information in Grid header to allocate the image x,y vectors.
670 	 * We assume gmtapi_init_grdheader has been called. */
671 	if (I == NULL) return (GMT_PTR_IS_NULL);
672 	if (I->x || I->y) return (GMT_PTR_NOT_NULL);
673 	I->x = gmtapi_image_coord (API, GMT_X, I);	/* Get array of x coordinates */
674 	I->y = gmtapi_image_coord (API, GMT_Y, I);	/* Get array of y coordinates */
675 	return (GMT_NOERROR);
676 }
677 
678 /*! . */
gmtapi_print_func(FILE * fp,const char * message)679 GMT_LOCAL int gmtapi_print_func (FILE *fp, const char *message) {
680 	/* Just print this message to fp.  It is being used indirectly via
681 	 * API->print_func.  Purpose of this mechanism is to allow external APIs such
682 	 * as MATLAB (which cannot use printf) to reset API->print_func to
683 	 * mexPrintf or similar functions. Default is gmtapi_print_func. */
684 
685 	fprintf (fp, "%s", message);
686 	return 0;
687 }
688 
689 /*! . */
gmtapi_gmtry(unsigned int geometry)690 GMT_LOCAL unsigned int gmtapi_gmtry (unsigned int geometry) {
691 	/* Return index to text representation in the array GMT_geometry[] for debug messages */
692 	if (geometry == GMT_IS_POINT)   return 1;
693 	if (geometry == GMT_IS_LINE)    return 2;
694 	if (geometry == GMT_IS_POLY)    return 3;
695 	if (geometry == GMT_IS_PLP)     return 4;
696 	if ((geometry & GMT_IS_LINE) && (geometry & GMT_IS_POLY)) return 5;
697 	if (geometry == GMT_IS_SURFACE) return 6;
698 	if (geometry == GMT_IS_NONE)    return 7;
699 	if (geometry == GMT_IS_TEXT)    return 8;
700 	return 0;
701 }
702 /* We also need to return the pointer to an object given a void * address of that pointer.
703  * This needs to be done on a per data-type basis, e.g., to cast that void * to a struct GMT_GRID **
704  * so we may return the value at that address: */
705 
gmtapi_get_api_ptr(struct GMTAPI_CTRL * ptr)706 GMT_LOCAL inline struct GMTAPI_CTRL    * gmtapi_get_api_ptr      (struct GMTAPI_CTRL *ptr)     {return (ptr);}
gmtapi_get_cpt_ptr(struct GMT_PALETTE ** ptr)707 GMT_LOCAL inline struct GMT_PALETTE    * gmtapi_get_cpt_ptr      (struct GMT_PALETTE **ptr)    {return (*ptr);}
gmtapi_get_dataset_ptr(struct GMT_DATASET ** ptr)708 GMT_LOCAL inline struct GMT_DATASET    * gmtapi_get_dataset_ptr  (struct GMT_DATASET **ptr)    {return (*ptr);}
gmtapi_get_grid_ptr(struct GMT_GRID ** ptr)709 GMT_LOCAL inline struct GMT_GRID       * gmtapi_get_grid_ptr     (struct GMT_GRID **ptr)       {return (*ptr);}
gmtapi_get_ps_ptr(struct GMT_POSTSCRIPT ** ptr)710 GMT_LOCAL inline struct GMT_POSTSCRIPT * gmtapi_get_ps_ptr       (struct GMT_POSTSCRIPT **ptr) {return (*ptr);}
gmtapi_get_cube_ptr(struct GMT_CUBE ** ptr)711 GMT_LOCAL inline struct GMT_CUBE   * gmtapi_get_cube_ptr (struct GMT_CUBE **ptr)   {return (*ptr);}
gmtapi_get_matrix_ptr(struct GMT_MATRIX ** ptr)712 GMT_LOCAL inline struct GMT_MATRIX     * gmtapi_get_matrix_ptr   (struct GMT_MATRIX **ptr)     {return (*ptr);}
gmtapi_get_vector_ptr(struct GMT_VECTOR ** ptr)713 GMT_LOCAL inline struct GMT_VECTOR     * gmtapi_get_vector_ptr   (struct GMT_VECTOR **ptr)     {return (*ptr);}
gmtapi_get_coord_ptr(double ** ptr)714 GMT_LOCAL inline double      	       * gmtapi_get_coord_ptr    (double **ptr)                {return (*ptr);}
gmtapi_get_image_ptr(struct GMT_IMAGE ** ptr)715 GMT_LOCAL inline struct GMT_IMAGE      * gmtapi_get_image_ptr    (struct GMT_IMAGE **ptr)      {return (*ptr);}
716 /* Various inline functs to convert void pointer to specific type */
gmtapi_get_rbr_ptr(struct GMT_GRID_ROWBYROW * ptr)717 GMT_LOCAL inline struct GMT_GRID_ROWBYROW * gmtapi_get_rbr_ptr (struct GMT_GRID_ROWBYROW *ptr) {return (ptr);}
gmtapi_get_fftinfo_ptr(struct GMT_FFT_INFO * ptr)718 GMT_LOCAL inline struct GMT_FFT_INFO * gmtapi_get_fftinfo_ptr (struct GMT_FFT_INFO *ptr) {return (ptr);}
gmtapi_get_fftwave_ptr(struct GMT_FFT_WAVENUMBER * ptr)719 GMT_LOCAL inline struct GMT_FFT_WAVENUMBER * gmtapi_get_fftwave_ptr (struct GMT_FFT_WAVENUMBER *ptr) {return (ptr);}
gmtapi_get_fftwave_addr(struct GMT_FFT_WAVENUMBER ** ptr)720 GMT_LOCAL inline struct GMT_FFT_WAVENUMBER ** gmtapi_get_fftwave_addr (struct GMT_FFT_WAVENUMBER **ptr) {return (ptr);}
gmtapi_get_grid_data(struct GMT_GRID * ptr)721 GMT_LOCAL inline struct GMT_GRID    * gmtapi_get_grid_data (struct GMT_GRID *ptr) {return (ptr);}
gmtapi_get_image_data(struct GMT_IMAGE * ptr)722 GMT_LOCAL inline struct GMT_IMAGE   * gmtapi_get_image_data (struct GMT_IMAGE *ptr) {return (ptr);}
gmtapi_get_dataset_data(struct GMT_DATASET * ptr)723 GMT_LOCAL inline struct GMT_DATASET * gmtapi_get_dataset_data (struct GMT_DATASET *ptr) {return (ptr);}
gmtapi_get_vector_data(struct GMT_VECTOR * ptr)724 GMT_LOCAL inline struct GMT_VECTOR  * gmtapi_get_vector_data (struct GMT_VECTOR *ptr) {return (ptr);}
gmtapi_get_matrix_data(struct GMT_MATRIX * ptr)725 GMT_LOCAL inline struct GMT_MATRIX  * gmtapi_get_matrix_data (struct GMT_MATRIX *ptr) {return (ptr);}
gmtapi_get_cube_data(struct GMT_CUBE * ptr)726 GMT_LOCAL inline struct GMT_CUBE  * gmtapi_get_cube_data (struct GMT_CUBE *ptr) {return (ptr);}
gmtapi_get_postscript_data(struct GMT_POSTSCRIPT * ptr)727 GMT_LOCAL inline struct GMT_POSTSCRIPT  * gmtapi_get_postscript_data (struct GMT_POSTSCRIPT *ptr) {return (ptr);}
gmtapi_get_palette_data(struct GMT_PALETTE * ptr)728 GMT_LOCAL inline struct GMT_PALETTE  * gmtapi_get_palette_data (struct GMT_PALETTE *ptr) {return (ptr);}
gmtapi_get_char_char_ptr(char ** ptr)729 GMT_LOCAL inline char ** gmtapi_get_char_char_ptr     (char **ptr)  {return (ptr);}
730 
731 /*! gmtapi_return_address is a convenience function that, given type, calls the correct converter */
gmtapi_return_address(void * data,unsigned int type)732 GMT_LOCAL void * gmtapi_return_address (void *data, unsigned int type) {
733 	void *p = NULL;
734 	switch (type) {
735 		case GMT_IS_GRID:       p = gmtapi_get_grid_ptr (data);     break;
736 		case GMT_IS_DATASET:    p = gmtapi_get_dataset_ptr (data);  break;
737 		case GMT_IS_PALETTE:    p = gmtapi_get_cpt_ptr (data);      break;
738 		case GMT_IS_POSTSCRIPT: p = gmtapi_get_ps_ptr (data);       break;
739 		case GMT_IS_CUBE:	p = gmtapi_get_cube_ptr (data); break;
740 		case GMT_IS_MATRIX:     p = gmtapi_get_matrix_ptr (data);   break;
741 		case GMT_IS_VECTOR:     p = gmtapi_get_vector_ptr (data);   break;
742 		case GMT_IS_COORD:      p = gmtapi_get_coord_ptr (data);    break;
743 		case GMT_IS_IMAGE:      p = gmtapi_get_image_ptr (data);    break;
744 	}
745 	return (p);
746 }
747 
748 /*! . */
gmt_get_api_ptr(struct GMTAPI_CTRL * ptr)749 struct GMTAPI_CTRL * gmt_get_api_ptr (struct GMTAPI_CTRL *ptr) {
750 	/* Clean casting of void to API pointer at start of a module
751  	 * If ptr is NULL we are in deep trouble...
752 	 */
753 	if (ptr == NULL) return_null (NULL, GMT_NOT_A_SESSION);
754 	return (ptr);
755 }
756 
757 /*! gmtapi_alloc_object_array is a convenience function that, given type, allocates an array of pointers to the corresponding container */
gmtapi_alloc_object_array(struct GMTAPI_CTRL * API,unsigned int n_items,unsigned int type)758 GMT_LOCAL void * gmtapi_alloc_object_array (struct GMTAPI_CTRL *API, unsigned int n_items, unsigned int type) {
759 	void *p = NULL;
760 	switch (type) {
761 		case GMT_IS_GRID:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_GRID *);		break;
762 		case GMT_IS_DATASET:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_DATASET *);	break;
763 		case GMT_IS_PALETTE:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_PALETTE *);	break;
764 		case GMT_IS_CUBE:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_CUBE *);	break;
765 		case GMT_IS_POSTSCRIPT:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_POSTSCRIPT *);	break;
766 		case GMT_IS_IMAGE:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_IMAGE *);		break;
767 		case GMT_IS_MATRIX:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_MATRIX *);	break;
768 		case GMT_IS_VECTOR:	p = gmt_M_memory (API->GMT, NULL, n_items, struct GMT_VECTOR *);	break;
769 	}
770 	return (p);
771 }
772 
773 #ifdef DEBUG
774 /*! Can be used to display API->object info wherever it is called as part of a debug operation */
gmtapi_list_objects(struct GMTAPI_CTRL * API,char * txt)775 GMT_LOCAL void gmtapi_list_objects (struct GMTAPI_CTRL *API, char *txt) {
776 	unsigned int item, ext;
777 	struct GMTAPI_DATA_OBJECT *S;
778 	char message[GMT_LEN256] = {""}, O, M;
779 	/* if (API->deep_debug == false) return; */
780 	if (!gmt_M_is_verbose (API->GMT, GMT_MSG_DEBUG)) return;
781 	snprintf (message, GMT_LEN256, "==> %d API Objects at end of %s\n", API->n_objects, txt);
782 	GMT_Message (API, GMT_TIME_NONE, message);
783 	if (API->n_objects == 0) return;
784 	GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
785 	snprintf (message, GMT_LEN256,   "K.. ID RESOURCE.... FAMILY.... ACTUAL.... DIR... S O M L\n");
786 	GMT_Message (API, GMT_TIME_NONE, message);
787 	GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
788 	for (item = 0; item < API->n_objects; item++) {
789 		if ((S = API->object[item]) == NULL) continue;
790 		O = (S->no_longer_owner) ? 'N' : 'Y';
791 		M = (S->messenger) ? 'Y' : 'N';
792 		ext = (S->alloc_mode == GMT_ALLOC_EXTERNALLY) ? '*' : ' ';
793 		snprintf (message, GMT_LEN256, "%c%2d %2d %12" PRIxS " %-10s %-10s %-6s %d %c %c %d\n", ext, item, S->ID, (size_t)S->resource,
794 			GMT_family[S->family], GMT_family[S->actual_family], GMT_direction[S->direction], S->status, O, M, S->alloc_level);
795 		GMT_Message (API, GMT_TIME_NONE, message);
796 	}
797 	GMT_Message (API, GMT_TIME_NONE, "--------------------------------------------------------\n");
798 }
799 
800 /*! Mostly for debugging */
gmtapi_set_object(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * obj)801 GMT_LOCAL void gmtapi_set_object (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *obj) {
802 	/* This is mostly for debugging and may go away or remain under DEBUG */
803 	GMT_Report (API, GMT_MSG_DEBUG, "Set_Object for family: %d\n", obj->family);
804 	switch (obj->family) {
805 		case GMT_IS_GRID:	obj->G = obj->resource; break;
806 		case GMT_IS_DATASET:	obj->D = obj->resource; break;
807 		case GMT_IS_PALETTE:	obj->C = obj->resource; break;
808 		case GMT_IS_CUBE:		obj->U = obj->resource; break;
809 		case GMT_IS_POSTSCRIPT:		obj->P = obj->resource; break;
810 		case GMT_IS_MATRIX:	obj->M = obj->resource; break;
811 		case GMT_IS_VECTOR:	obj->V = obj->resource; break;
812 		case GMT_IS_COORD:	break;	/* No worries */
813 		case GMT_IS_IMAGE:	obj->I = obj->resource; break;
814 		case GMT_N_FAMILIES:	break;
815 	}
816 }
817 #endif
818 
gmtapi_modern_onliner(struct GMTAPI_CTRL * API,struct GMT_OPTION * head)819 GMT_LOCAL bool gmtapi_modern_onliner (struct GMTAPI_CTRL *API, struct GMT_OPTION *head) {
820 	/* Must check if a one-liner with special graphics format settings were given, e.g., "gmt pscoast -Rg -JH0/15c -Gred -png map" */
821 	bool modern = false;
822 	unsigned pos;
823 	size_t len;
824 	char format[GMT_LEN128] = {""}, p[GMT_LEN16] = {""}, *c = NULL;
825 	struct GMT_OPTION *opt;
826 
827 	for (opt = head; opt; opt = opt->next) {
828 		if (opt->option == GMT_OPT_INFILE || opt->option == GMT_OPT_OUTFILE) continue;	/* Skip file names */
829 		if (strchr ("bejpPt", opt->option) == NULL) continue;	/* Option not the first letter of a valid graphics format [UPDATE LIST IF ADDING MORE FORMATS IN FUTURE] */
830 		if ((len = strlen (opt->arg)) == 0 || len >= GMT_LEN128) continue;	/* No arg or very long args that are filenames can be skipped */
831 		snprintf (format, GMT_LEN128, "%c%s", opt->option, opt->arg);	/* Get a local copy so we can mess with it */
832 		if ((c = strchr (format, ','))) c[0] = 0;	/* Chop off other formats for the initial id test */
833 		if (gmt_get_graphics_id (API->GMT, format) != GMT_NOTSET) {	/* Found a valid graphics format option */
834 			modern = true;	/* Seems like it is, but check the rest of the formats, if there are more */
835 			if (c == NULL) continue;	/* Nothing else to check, go to next option */
836 			/* Make sure any other formats are valid, too */
837 			if (c) c[0] = ',';	/* Restore any comma we found */
838 			pos = 0;
839 			while (modern && gmt_strtok (format, ",", &pos, p)) {	/* Check each format to make sure each is OK */
840 				if (gmt_get_graphics_id (API->GMT, p) == GMT_NOTSET)	/* Oh, something wrong was given, cannot be modern */
841 					modern = false;
842 			}
843 		}
844 	}
845 	return modern;
846 }
847 
gmtapi_check_for_modern_oneliner(struct GMTAPI_CTRL * API,const char * module,int mode,void * args)848 GMT_LOCAL int gmtapi_check_for_modern_oneliner (struct GMTAPI_CTRL *API, const char *module, int mode, void *args) {
849 	/* Determine if user is attempting a modern mode one-liner plot, and if so, set run mode to GMT_MODERN.
850 	 * This is needed since there is not gmt begin | end sequence in this case.
851 	 * Also, if a user wants to get the usage message for a modern mode module then it is also a type
852 	 * of one-liner and thus we set to GMT_MODERN as well, but only for modern module names. */
853 
854 	bool usage = false, modern;
855 	int error = GMT_NOERROR;
856 	struct GMT_OPTION *opt, *head = NULL;
857 
858 	head = GMT_Create_Options (API, mode, args);	/* Get option list */
859 	modern = gmtapi_modern_onliner (API, head);		/* Return true if one-liner syntax was detected */
860 	if (API->GMT->current.setting.run_mode == GMT_MODERN) {	/* Need to check if a classic name was given or if user tried a one-liner in modern mode */
861 		if (modern) {	/* Yikes, someone is using one-liner within a GMT modern mode session */
862 			GMT_Report (API, GMT_MSG_ERROR, "Cannot run a one-liner modern command within an existing modern mode session\n");
863 			error = GMT_RUNTIME_ERROR;
864 			goto free_and_return;
865 		}
866 		if (!strncmp (module, "ps", 2U) && strncmp (module, "psconvert", 9U)) {	/* Gave classic ps* name in modern mode but not psconvert */
867 			char not_used[GMT_LEN32] = {""};
868 			const char *mod_name = gmt_current_name (module, not_used);
869 			GMT_Report (API, GMT_MSG_INFORMATION, "Detected a classic module name (%s) in modern mode - please use the modern mode name %s instead.\n", module, mod_name);
870 		}
871 		goto free_and_return;
872 	}
873 
874 	if ((opt = GMT_Find_Option (API, 'V', head)))	/* Remove -V here so that we can run gmt plot -? -Vd and still get modern mode usage plus debug info */
875 		GMT_Delete_Option (API, opt, &head);
876 
877 	if (!strcmp (module, "grdcontour") && GMT_Find_Option (API, 'N', head))	/* Special case of two module calls cannot be oneliner here */
878 		goto free_and_return;
879 
880 	API->GMT->current.setting.use_modern_name = gmtlib_is_modern_name (API, module);
881 
882 	if (API->GMT->current.setting.use_modern_name) {	/* Make some checks needed to handle synopsis and usage messages in classic vs modern mode */
883 		if (head == NULL) {	/* Gave none or a single argument */
884 			if (API->GMT->current.setting.run_mode == GMT_CLASSIC) {
885 				API->usage = true;	/* Modern mode name given with no args so not yet in modern mode - allow it to get usage */
886 				API->GMT->current.setting.run_mode = GMT_MODERN;	/* Safe here to flag it as modern since no session will be started */
887 			}
888 			return GMT_NOERROR;
889 		}
890 		if (head->next == NULL) {	/* Gave a single argument */
891 			if (head->option == GMT_OPT_USAGE || head->option == GMT_OPT_SYNOPSIS || (head->option == '+' && !head->arg[0])) modern = 1;
892 			if (modern) usage = true;
893 		}
894 	}
895 
896 	if (modern) {	/* This is indeed a modern mode one-liner command */
897 		API->GMT->current.setting.run_mode = GMT_MODERN;
898 		API->usage = usage;
899 	}
900 	if (API->GMT->current.setting.run_mode == GMT_MODERN)	/* If running in modern mode we want to use modern names */
901 		API->GMT->current.setting.use_modern_name = true;
902 
903 free_and_return:
904 
905 	if (GMT_Destroy_Options (API, &head))	/* Done with these here */
906 		GMT_Report (API, GMT_MSG_WARNING, "Unable to free options in gmtapi_check_for_modern_oneliner?\n");
907 	return error;
908 }
909 
910 /* Function to get PPID under Windows is a bit different */
911 #ifdef _WIN32
912 #include <TlHelp32.h>
gmtapi_winppid(int pidin)913 GMT_LOCAL int gmtapi_winppid (int pidin) {
914 	/* If pidin == 0 get the PPID of current process
915 	   otherwise, get the PPID of pidin process
916 	*/
917 	int pid, ppid = GMT_NOTSET;
918 	if (pidin)
919 		pid = pidin;
920 	else
921 		pid = GetCurrentProcessId ();
922 	HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
923 	PROCESSENTRY32 pe = { 0 };
924 	pe.dwSize = sizeof (PROCESSENTRY32);
925 
926 	if (Process32First(h, &pe)) {
927 		do {
928 			if (pe.th32ProcessID == (unsigned int)pid)
929 				ppid = pe.th32ParentProcessID;
930 		} while (ppid == GMT_NOTSET && Process32Next(h, &pe));
931 	}
932 	CloseHandle (h);
933 	return (ppid);
934 }
935 #endif
936 
937 /* Safety valve to remove non-alphanumeric characters =*/
gmtapi_alnum_only(struct GMTAPI_CTRL * API,char * string)938 GMT_LOCAL char * gmtapi_alnum_only (struct GMTAPI_CTRL *API, char *string) {
939 	unsigned int k = 0, n_changed = 0;
940 	while (string[k]) {
941 		if (!isalnum (string[k])) {
942 			n_changed++;
943 			string[k] = '#';
944 		}
945 		k++;
946 	}
947 	if (n_changed)
948 		GMT_Report (API, GMT_MSG_DEBUG, "Cleaned GMT_SESSION_NAME to %s\n", string);
949 	return (string);
950 }
951 
952 /*! . */
gmtapi_get_ppid(struct GMTAPI_CTRL * API)953 GMT_LOCAL char * gmtapi_get_ppid (struct GMTAPI_CTRL *API) {
954 	/* Return the parent process ID [i.e., shell for command line use or gmt app for API] */
955 	int ppid = GMT_NOTSET;
956 	unsigned int k = 0;
957 	static char *source[4] = {"GMT_SESSION_NAME", "parent", "app", "hardwired choice"};
958 	char *str = NULL, string[GMT_LEN8];
959 	if ((str = getenv ("GMT_SESSION_NAME")) != NULL) {	/* GMT_SESSION_NAME was set in the environment */
960 		char *tmp = strdup (str);	/* Duplicate the given string */
961 		GMT_Report (API, GMT_MSG_DEBUG, "Obtained GMT_SESSION_NAME from the environment: %s\n", str);
962 		return (gmtapi_alnum_only (API, tmp)); /* Replace any non-alphanumeric characters with # */
963 	}
964 	/* Here we just need to get the PPID and format to string */
965 #ifdef DEBUG_MODERN	/* To simplify debugging we set it to 1 */
966 	if (ppid == GMT_NOTSET) ppid = 1, k = 3;
967 #elif defined(WIN32)
968 	/* OK, the trouble is the following. On Win, if the Windows executables are run from within a bash window
969 	   gmtapi_get_ppid returns different values for each call, and this completely breaks the idea
970 	   of using the constant PPID (parent PID) to create unique file names for each session.
971 	   So, given that we didn't yet find a way to make this work from within MSYS (and likely Cygwin)
972 	   we are forcing PPID = 0 in all Windows variants unless set via GMT_SESSION_NAME. A corollary of this
973 	   is that Windows users running many bash windows concurrently should use GMT_SESSION_NAME in their scripts
974 	   to give unique values to different scripts.  */
975 	if ((str = getenv ("SHELL")) != NULL) {	/* GMT_SESSION_NAME was set in the environment */
976 		//if (ppid == GMT_NOTSET) ppid = 0, k = 3;
977 		ppid = gmtapi_winppid(0);		/* First time get PPID of current process */
978 		ppid = gmtapi_winppid(ppid);	/* Second time get PPPID of current process */
979 		k = 1;
980 	}
981 	else {
982 		if (ppid == GMT_NOTSET) ppid = gmtapi_winppid(0), k = 1;
983 	}
984 #else	/* Normal situation */
985 	else if (API->external)	/* Return PID of the controlling app instead for external interfaces */
986 		ppid = getpid (), k = 2;
987 	else	/* Here we are probably running from the command line and want the shell's PID */
988 		ppid = getppid(), k = 1; /* parent process id */
989 #endif
990 	GMT_Report (API, GMT_MSG_DEBUG, "Obtained the ppid from %s: %d\n", source[k], ppid);
991 	snprintf (string, GMT_LEN8, "%d", ppid);
992 	return (strdup (string));
993 }
994 
995 /*! . */
gmtapi_lib_tag(char * name)996 GMT_LOCAL char * gmtapi_lib_tag (char *name) {
997 	/* Pull out the tag from a name like <tag>[.extension] */
998 	char *extension = NULL, *pch = NULL, *tag = NULL;
999 	if (!strchr (name, '.')) return NULL;	/* No file with extension given, probably just a directory due to user confusion */
1000 	tag = strdup (name);
1001 	extension = strrchr (tag, '.'); /* last period in name */
1002 	if (extension) *extension = '\0'; /* remove extension */
1003 	/* if name has the "_w32|64" suffix or any other suffix that starts with a '_', remove it. */
1004 	pch = strrchr(tag, '_');
1005 	if (pch) *pch = '\0';
1006 	return (tag);
1007 }
1008 
1009 /*! . */
gmtapi_init_sharedlibs(struct GMTAPI_CTRL * API)1010 GMT_LOCAL int gmtapi_init_sharedlibs (struct GMTAPI_CTRL *API) {
1011 	/* At the end of GMT_Create_Session we are done with processing gmt.conf.
1012 	 * We can now determine how many shared libraries and plugins to consider, and open the core lib */
1013 	struct GMT_CTRL *GMT = API->GMT;
1014 	unsigned int n_custom_libs, k, e, n_alloc = GMT_TINY_CHUNK;
1015 	char text[PATH_MAX] = {""}, plugindir[PATH_MAX] = {""}, path[PATH_MAX] = {""};
1016 	char *libname = NULL, **list = NULL;
1017 #ifdef WIN32
1018 	char *extension[1] = {".dll"};
1019 	unsigned int n_extensions = 1;
1020 #elif  defined(__APPLE__)	/* Look for both .so and .dylib shared libs on OSX */
1021 	char *extension[2] = {".so", ".dylib"};
1022 	unsigned int n_extensions = 2;
1023 #else	/* Linux, etc. only use .so */
1024 	char *extension[1] = {".so"};
1025 	unsigned int n_extensions = 1;
1026 #endif
1027 
1028 #ifdef SUPPORT_EXEC_IN_BINARY_DIR
1029 	/* If SUPPORT_EXEC_IN_BINARY_DIR is defined we try to load plugins from the
1030 	 * build tree */
1031 
1032 	/* Only true, when we are running in a subdir of GMT_BINARY_DIR_SRC_DEBUG: */
1033 	bool running_in_bindir_src = !strncmp (GMT->init.runtime_bindir, GMT_BINARY_DIR_SRC_DEBUG, strlen(GMT_BINARY_DIR_SRC_DEBUG));
1034 #endif
1035 
1036 	API->lib = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_LIBINFO);
1037 
1038 	/* 1. Load the GMT core library by default [unless libgmt is used externally] */
1039 	/* Note: To extract symbols from the currently executing process we need to load it as a special library.
1040 	 * This is done by passing NULL under Linux and by calling GetModuleHandleEx under Windows, hence we
1041 	 * use the dlopen_special call which is defined in gmt_sharedlibs.c.  If the gmt core and supplemental
1042 	 * libraries are being used by 3rd party externals then no library is special and they are all opened
1043 	 * the first time we need access. */
1044 
1045 	API->lib[0].name = strdup ("core");
1046 	n_custom_libs = 1;	/* Always have at least one shared gmt library */
1047 	if (API->external) {	/* Determine the path to this library */
1048 		if (GMT->init.runtime_libdir) {	/* Successfully determined runtime dir for shared libs */
1049 			sprintf (path, "%s/%s", GMT->init.runtime_libdir, GMT_CORE_LIB_NAME);
1050 			API->lib[0].path = strdup (path);
1051 		}
1052 		else	/* Rely on the OS to find it */
1053 			API->lib[0].path = strdup (GMT_CORE_LIB_NAME);
1054 	}
1055 	else {	/* The handling of the core library is only special when gmt.c is used. */
1056 		API->lib[0].path = strdup (GMT_CORE_LIB_NAME);
1057 		GMT_Report (API, GMT_MSG_DEBUG, "Loading core GMT shared library: %s\n", API->lib[0].path);
1058 		if ((API->lib[0].handle = dlopen_special (API->lib[0].path)) == NULL) {
1059 			GMT_Report (API, GMT_MSG_ERROR, "Failure while loading core GMT shared library (%s): %s\n", API->lib[0].path, dlerror());
1060 			return -GMT_RUNTIME_ERROR;
1061 		}
1062 		dlerror (); /* Clear any existing error */
1063 	}
1064 	GMT_Report (API, GMT_MSG_DEBUG, "Shared Library # 0 (core). Path = %s\n", API->lib[0].path);
1065 
1066 	/* 3. Add any plugins installed in <installdir>/lib/gmt/plugins */
1067 
1068 	if (GMT->init.runtime_libdir) {	/* Successfully determined runtime dir for shared libs */
1069 #ifdef SUPPORT_EXEC_IN_BINARY_DIR
1070 		if ( running_in_bindir_src && access (GMT_BINARY_DIR_SRC_DEBUG "/plugins", R_OK|X_OK) == 0 ) {
1071 			/* Running in build dir: search plugins in build-dir/src/plugins */
1072 			strncat (plugindir, GMT_BINARY_DIR_SRC_DEBUG "/plugins", PATH_MAX-1);
1073 #ifdef XCODER
1074 			strcat (plugindir, "/Debug");	/* The Xcode plugin path for Debug */
1075 #endif
1076 		}
1077 		else
1078 #endif
1079 		{
1080 		/* Set full path to the core library */
1081 		snprintf (plugindir, PATH_MAX, "%s/%s", GMT->init.runtime_libdir, GMT_CORE_LIB_NAME);
1082 		if (!GMT->init.runtime_library) GMT->init.runtime_library = strdup (plugindir);
1083 
1084 #ifdef WIN32
1085 			snprintf (plugindir, PATH_MAX, "%s/gmt_plugins", GMT->init.runtime_libdir);	/* Generate the Win standard plugins path */
1086 #else
1087 			snprintf (plugindir, PATH_MAX, "%s/gmt" GMT_INSTALL_NAME_SUFFIX "/plugins", GMT->init.runtime_libdir);	/* Generate the *nix standard plugins path */
1088 #endif
1089 		}
1090 		if (!GMT->init.runtime_plugindir) GMT->init.runtime_plugindir = strdup (plugindir);
1091 		GMT_Report (API, GMT_MSG_DEBUG, "Loading GMT plugins from: %s\n", plugindir);
1092 		for (e = 0; e < n_extensions; e++) {	/* Handle case of more than one allowed shared library extension */
1093 			if ((list = gmtlib_get_dir_list (GMT, plugindir, extension[e]))) {	/* Add these files to the libs */
1094 				for (k = 0; list[k] && strncmp (list[k], GMT_SUPPL_LIB_NAME, strlen(GMT_SUPPL_LIB_NAME)); k++);	/* Look for official supplements */
1095 				if (list[k] && k) gmt_M_charp_swap (list[0], list[k]);	/* Put official supplements first if not first already */
1096 				k = 0;
1097 				while (list[k]) {
1098 					snprintf (path, PATH_MAX, "%s/%s", plugindir, list[k]);
1099 					if (access (path, R_OK))
1100 						GMT_Report (API, GMT_MSG_ERROR, "Shared Library %s cannot be found or read!\n", path);
1101 					else {
1102 						API->lib[n_custom_libs].name = gmtapi_lib_tag (list[k]);
1103 						API->lib[n_custom_libs].path = strdup (path);
1104 						GMT_Report (API, GMT_MSG_DEBUG, "Shared Library # %d (%s). Path = %s\n", n_custom_libs, API->lib[n_custom_libs].name, API->lib[n_custom_libs].path);
1105 						n_custom_libs++;			/* Add up entries found */
1106 						if (n_custom_libs == n_alloc) {		/* Allocate more memory for list */
1107 							n_alloc <<= 1;
1108 							API->lib = gmt_M_memory (GMT, API->lib, n_alloc, struct GMT_LIBINFO);
1109 						}
1110 					}
1111 					++k;
1112 				}
1113 				gmtlib_free_dir_list (GMT, &list);
1114 			}
1115 		}
1116 	}
1117 
1118 	/* 4. Add any custom GMT libraries to the list of libraries/plugins to consider, if specified.
1119 	      We will find when trying to open if any of these are actually available. */
1120 
1121 	if (GMT->session.CUSTOM_LIBS) {	/* We specified custom shared libraries */
1122 		k = (unsigned int)strlen (GMT->session.CUSTOM_LIBS) - 1;	/* Index of last char in CUSTOM_LIBS */
1123 		if (GMT->session.CUSTOM_LIBS[k] == '/' || GMT->session.CUSTOM_LIBS[k] == '\\') {	/* We gave CUSTOM_LIBS as a subdirectory, add all files found inside it to shared libs list */
1124 			strcpy (plugindir, GMT->session.CUSTOM_LIBS);
1125 			plugindir[k] = '\0';	/* Chop off trailing slash */
1126 			GMT_Report (API, GMT_MSG_DEBUG, "Loading custom GMT plugins from: %s\n", plugindir);
1127 			for (e = 0; e < n_extensions; e++) {
1128 				if ((list = gmtlib_get_dir_list (GMT, plugindir, extension[e]))) {	/* Add these to the libs */
1129 					k = 0;
1130 					while (list[k]) {
1131 						snprintf (path, PATH_MAX, "%s/%s", plugindir, list[k]);
1132 						if (access (path, R_OK)) {
1133 							GMT_Report (API, GMT_MSG_ERROR, "Shared Library %s cannot be found or read!\n", path);
1134 							GMT_Report (API, GMT_MSG_ERROR, "Check that your GMT_CUSTOM_LIBS (in %s, perhaps) is correct\n", GMT_SETTINGS_FILE);
1135 						}
1136 						else if ((API->lib[n_custom_libs].name = gmtapi_lib_tag (list[k]))) {
1137 							API->lib[n_custom_libs].path = strdup (path);
1138 							GMT_Report (API, GMT_MSG_DEBUG, "Shared Library # %d (%s). Path = \n", n_custom_libs, API->lib[n_custom_libs].name, API->lib[n_custom_libs].path);
1139 							n_custom_libs++;		/* Add up entries found */
1140 							if (n_custom_libs == n_alloc) {	/* Allocate more memory for list */
1141 								n_alloc <<= 1;
1142 								API->lib = gmt_M_memory (GMT, API->lib, n_alloc, struct GMT_LIBINFO);
1143 							}
1144 						}
1145 						else
1146 							GMT_Report (API, GMT_MSG_ERROR, "Shared Library %s has no extension! Ignored\n", list[k]);
1147 						++k;
1148 					}
1149 					gmtlib_free_dir_list (GMT, &list);
1150 				}
1151 			}
1152 		}
1153 		else {	/* Just a list with one or more comma-separated library paths */
1154 			unsigned int pos = 0;
1155 			while (gmt_strtok (GMT->session.CUSTOM_LIBS, ",", &pos, text)) {
1156 				libname = strdup (basename (text));		/* Last component from the pathname */
1157 				if (access (text, R_OK)) {
1158 					GMT_Report (API, GMT_MSG_ERROR, "Shared Library %s cannot be found or read!\n", text);
1159 					GMT_Report (API, GMT_MSG_ERROR, "Check that your GMT_CUSTOM_LIBS (in %s, perhaps) is correct\n", GMT_SETTINGS_FILE);
1160 				}
1161 				else if ((API->lib[n_custom_libs].name = gmtapi_lib_tag (libname))) {
1162 					API->lib[n_custom_libs].path = strdup (text);
1163 					GMT_Report (API, GMT_MSG_DEBUG, "Shared Library # %d (%s). Path = \n", n_custom_libs, API->lib[n_custom_libs].name, API->lib[n_custom_libs].path);
1164 					n_custom_libs++;		/* Add up entries found */
1165 					if (n_custom_libs == n_alloc) {	/* Allocate more memory for list */
1166 						n_alloc <<= 1;
1167 						API->lib = gmt_M_memory (GMT, API->lib, n_alloc, struct GMT_LIBINFO);
1168 					}
1169 				}
1170 				else
1171 					GMT_Report (API, GMT_MSG_ERROR, "Shared Library %s has no extension! Ignored\n", text);
1172 				gmt_M_str_free (libname);
1173 			}
1174 		}
1175 	}
1176 
1177 	API->n_shared_libs = n_custom_libs;	/* Update total number of shared libraries */
1178 	API->lib = gmt_M_memory (GMT, API->lib, API->n_shared_libs, struct GMT_LIBINFO);
1179 
1180 	return (GMT_NOERROR);
1181 }
1182 
1183 /*! Free items in the shared lib list */
gmtapi_free_sharedlibs(struct GMTAPI_CTRL * API)1184 GMT_LOCAL void gmtapi_free_sharedlibs (struct GMTAPI_CTRL *API) {
1185 	unsigned int k;
1186 	for (k = 0; k < API->n_shared_libs; k++) {
1187 		if (k > 0 && API->lib[k].handle && dlclose (API->lib[k].handle))
1188 			GMT_Report (API, GMT_MSG_ERROR, "Failure while closing GMT %s shared library: %s\n", API->lib[k].name, dlerror());
1189 		gmt_M_str_free (API->lib[k].name);
1190 		gmt_M_str_free (API->lib[k].path);
1191 	}
1192 	gmt_M_free (API->GMT, API->lib);
1193 	API->n_shared_libs = 0;
1194 }
1195 
1196 /* The basic gmtread|write module meat; used by external APIs only, such as the GMT/MATLAB API */
1197 
1198 /*! Duplicate ifile on ofile.  Calling program is responsible to ensure correct args are passed */
gmt_copy(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int direction,char * ifile,char * ofile)1199 int gmt_copy (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int direction, char *ifile, char *ofile) {
1200 	double *wesn = NULL;	/* For grid and image subsets */
1201 	struct GMT_DATASET *D = NULL;
1202 	struct GMT_PALETTE *C = NULL;
1203 	struct GMT_GRID *G = NULL;
1204 	struct GMT_CUBE *U = NULL;
1205 	struct GMT_POSTSCRIPT *P = NULL;
1206 	struct GMT_IMAGE *I = NULL;
1207 	struct GMT_MATRIX *M = NULL;
1208 	struct GMT_VECTOR *V = NULL;
1209 	struct GMT_CTRL *GMT = NULL;
1210 	struct GMT_DATASET_HIDDEN *DH = NULL;
1211 
1212 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
1213 	API->error = GMT_NOERROR;
1214 	GMT_Report (API, GMT_MSG_INFORMATION, "Read %s from %s and write to %s\n", GMT_family[family], ifile, ofile);
1215 	GMT = API->GMT;
1216 
1217 	switch (family) {
1218 		case GMT_IS_DATASET:
1219 			if ((D = GMT_Read_Data (API, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_READ_NORMAL, NULL, ifile, NULL)) == NULL)
1220 				return (API->error);
1221 			DH = gmt_get_DD_hidden (D);
1222 			if (GMT_Write_Data (API, GMT_IS_DATASET, GMT_IS_FILE, D->geometry, DH->io_mode | GMT_IO_RESET, NULL, ofile, D) != GMT_NOERROR)
1223 				return (API->error);
1224 			break;
1225 		case GMT_IS_GRID:
1226 			wesn = (direction == GMT_IN && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1227 			if ((G = GMT_Read_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_READ_NORMAL, wesn, ifile, NULL)) == NULL)
1228 				return (API->error);
1229 			wesn = (direction == GMT_OUT && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1230 			if (GMT_Write_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA | GMT_IO_RESET, wesn, ofile, G) != GMT_NOERROR)
1231 				return (API->error);
1232 			break;
1233 		case GMT_IS_IMAGE:
1234 			wesn = (direction == GMT_IN && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1235 			if ((I = GMT_Read_Data (API, GMT_IS_IMAGE, GMT_IS_FILE, GMT_IS_SURFACE, GMT_READ_NORMAL, wesn, ifile, NULL)) == NULL)
1236 				return (API->error);
1237 			wesn = (direction == GMT_OUT && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1238 			if (GMT_Write_Data (API, GMT_IS_IMAGE, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA | GMT_IO_RESET, wesn, ofile, I) != GMT_NOERROR)
1239 				return (API->error);
1240 			break;
1241 		case GMT_IS_CUBE:
1242 			wesn = (direction == GMT_IN && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1243 			if ((U = GMT_Read_Data (API, GMT_IS_CUBE, GMT_IS_FILE, GMT_IS_VOLUME, GMT_READ_NORMAL, wesn, ifile, NULL)) == NULL)
1244 				return (API->error);
1245 			wesn = (direction == GMT_OUT && GMT->common.R.active[RSET]) ? GMT->common.R.wesn : NULL;
1246 			if (GMT_Write_Data (API, GMT_IS_CUBE, GMT_IS_FILE, GMT_IS_VOLUME, GMT_CONTAINER_AND_DATA | GMT_IO_RESET, wesn, ofile, U) != GMT_NOERROR)
1247 				return (API->error);
1248 			break;
1249 		case GMT_IS_PALETTE:
1250 			if ((C = GMT_Read_Data (API, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, ifile, NULL)) == NULL)
1251 				return (API->error);
1252 			if (GMT_Write_Data (API, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, C->mode | GMT_IO_RESET, NULL, ofile, C) != GMT_NOERROR)
1253 				return (API->error);
1254 			break;
1255 		case GMT_IS_POSTSCRIPT:
1256 			if ((P = GMT_Read_Data (API, GMT_IS_POSTSCRIPT, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, ifile, NULL)) == NULL)
1257 				return (API->error);
1258 			if (GMT_Write_Data (API, GMT_IS_POSTSCRIPT, GMT_IS_FILE, GMT_IS_NONE, GMT_IO_RESET, NULL, ofile, P) != GMT_NOERROR)
1259 				return (API->error);
1260 			break;
1261 		case GMT_IS_MATRIX:
1262 			if ((M = GMT_Read_Data (API, GMT_IS_MATRIX, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, ifile, NULL)) == NULL)
1263 				return (API->error);
1264 			if (GMT_Write_Data (API, GMT_IS_MATRIX, GMT_IS_FILE, GMT_IS_NONE, GMT_IO_RESET, NULL, ofile, M) != GMT_NOERROR)
1265 				return (API->error);
1266 			break;
1267 		case GMT_IS_VECTOR:
1268 			if ((V = GMT_Read_Data (API, GMT_IS_VECTOR, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, ifile, NULL)) == NULL)
1269 				return (API->error);
1270 			if (GMT_Write_Data (API, GMT_IS_VECTOR, GMT_IS_FILE, GMT_IS_NONE, GMT_IO_RESET, NULL, ofile, V) != GMT_NOERROR)
1271 				return (API->error);
1272 			break;
1273 		case GMT_IS_COORD:
1274 			GMT_Report (API, GMT_MSG_ERROR, "No external read or write support yet for object %s\n", GMT_family[family]);
1275 			return_error(API, GMT_NOT_A_VALID_FAMILY);
1276 			break;
1277 		default:
1278 			GMT_Report (API, GMT_MSG_ERROR, "Internal error, family = %d\n", family);
1279 			return_error(API, GMT_NOT_A_VALID_FAMILY);
1280 			break;
1281 	}
1282 
1283 	return (API->error);
1284 }
1285 
1286 /* Note: Many/all of these do not need to check if API == NULL since they are called from functions that do. */
1287 /* Private functions used by this library only.  These are not accessed outside this file. */
1288 
gmtapi_pick_out_col_number(struct GMT_CTRL * GMT,unsigned int col)1289 GMT_LOCAL unsigned int gmtapi_pick_out_col_number (struct GMT_CTRL *GMT, unsigned int col) {
1290 	/* Return the next column to be reported on output */
1291 	unsigned int col_pos;
1292 	if (GMT->common.o.select)	/* -o has selected some columns */
1293 		col_pos = GMT->current.io.col[GMT_OUT][col].col;	/* Which data column to pick */
1294 	else if (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && col < GMT_Z)	/* Worry about -: for lon,lat */
1295 		col_pos = 1 - col;	/* Write lat/lon instead of lon/lat */
1296 	else
1297 		col_pos = col;	/* Just goto that column */
1298 	return (col_pos);
1299 }
1300 
1301 /*! . */
gmtapi_select_dataset_value(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,unsigned int row,unsigned int col)1302 GMT_LOCAL double gmtapi_select_dataset_value (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, unsigned int row, unsigned int col) {
1303 	/* For binary output of a data table segment via external matrix, we must select correct col entry and possibly make adjustments */
1304 	double val;
1305 	unsigned int col_pos = gmtapi_pick_out_col_number (GMT, col);
1306 	val = (col_pos >= S->n_columns) ? GMT->session.d_NaN : S->data[col_pos][row];	/* If we request a column beyond length of array, return NaN */
1307 	if (GMT->common.d.active[GMT_OUT] && gmt_M_is_dnan (val)) val = GMT->common.d.nan_proxy[GMT_OUT];	/* Write this value instead of NaNs */
1308 	if (gmt_M_is_type (GMT, GMT_OUT, col_pos, GMT_IS_LON)) gmt_lon_range_adjust (GMT->current.io.geo.range, &val);	/* Set longitude range */
1309 	return (val);
1310 }
1311 
1312 /*! . */
gmtapi_select_record_value(struct GMT_CTRL * GMT,double * record,unsigned int col,unsigned int n_colums)1313 GMT_LOCAL double gmtapi_select_record_value (struct GMT_CTRL *GMT, double *record, unsigned int col, unsigned int n_colums) {
1314 	/* For binary output of data record via external matrix, we must select correct col entry and possibly make adjustments */
1315 	double val;
1316 	unsigned int col_pos = gmtapi_pick_out_col_number (GMT, col);
1317 	val = (col_pos >= n_colums) ? GMT->session.d_NaN : record[col_pos];	/* If we request a column beyond length of array, return NaN */
1318 	if (GMT->common.d.active[GMT_OUT] && gmt_M_is_dnan (val)) val = GMT->common.d.nan_proxy[GMT_OUT];	/* Write this value instead of NaNs */
1319 	if (gmt_M_is_type (GMT, GMT_OUT, col_pos, GMT_IS_LON)) gmt_lon_range_adjust (GMT->current.io.geo.range, &val);	/* Set longitude range */
1320 	return (val);
1321 }
1322 
gmtlib_pick_in_col_number(struct GMT_CTRL * GMT,unsigned int col,unsigned int * col_pos_in)1323 unsigned int gmtlib_pick_in_col_number (struct GMT_CTRL *GMT, unsigned int col, unsigned int *col_pos_in) {
1324     /* If -i is not active then we simply return col_pos_out = col_pos_in = col.  Done!
1325      *
1326      * With -i, we must return correct value for both the in and out column positions based on the info in the structure.
1327      * Here, col is a loop variable that goes from 0 to number of desired output columns.  However, that col number
1328      * is likely neither the input(col) we wish to get nor the output(col) where we want to place it in the output array.
1329      * Instead, we set these two different columns via col_pos_in and col_pos_out so that output(col_pos_out) = input(col_pos_in).
1330      * col_pos_out will uniquely touch the same values as col loops over, but col_pos_in may be outside that range
1331      * and even have repeated column values (e.g., due to -i0,1,2,2+s5). */
1332 	unsigned int col_pos_out;
1333 	if (GMT->common.i.select) {	/* -i has selected some columns */
1334         *col_pos_in = GMT->current.io.col[GMT_IN][col].col;   /* Which data column to take the value from input */
1335         col_pos_out = GMT->current.io.col[GMT_IN][col].order; /* Which data column to place it on output */
1336     }
1337 	else
1338 		col_pos_out = *col_pos_in = col;	/* They are all the same */
1339 	return (col_pos_out);
1340 }
1341 
1342 /*! . */
gmtapi_get_record_value(struct GMT_CTRL * GMT,double * record,uint64_t col,uint64_t n_colums,unsigned int * col_pos_out)1343 GMT_LOCAL double gmtapi_get_record_value (struct GMT_CTRL *GMT, double *record, uint64_t col, uint64_t n_colums, unsigned int *col_pos_out) {
1344 	/* For binary input of data record via external matrix, we must select correct col entry and possibly make adjustments */
1345 	double val;
1346 	unsigned int col_pos_in;
1347 	*col_pos_out = gmtlib_pick_in_col_number (GMT, (unsigned int)col, &col_pos_in);
1348 	val = (*col_pos_out >= n_colums) ? GMT->session.d_NaN : gmt_M_convert_col (GMT->current.io.col[GMT_IN][col], record[col_pos_in]);	/* If we request a column beyond length of array, return NaN */
1349 	if (GMT->common.d.active[GMT_IN] && gmt_M_is_dnan (val)) val = GMT->common.d.nan_proxy[GMT_IN];	/* Write this value instead of NaNs */
1350 	if (gmt_M_is_type (GMT, GMT_IN, *col_pos_out, GMT_IS_LON)) gmt_lon_range_adjust (GMT->current.io.geo.range, &val);	/* Set longitude range */
1351 	return (val);
1352 }
1353 
1354 /*! . */
gmtapi_bin_input_memory(struct GMT_CTRL * GMT,uint64_t n,uint64_t n_use)1355 GMT_LOCAL int gmtapi_bin_input_memory (struct GMT_CTRL *GMT, uint64_t n, uint64_t n_use) {
1356 	/* Read function which gets one record from the memory reference.
1357  	 * The current data record has already been read from wherever and is available in GMT->current.io.curr_rec */
1358 	unsigned int status;
1359 	gmt_M_unused(n);
1360 
1361 	GMT->current.io.status = GMT_IO_DATA_RECORD;	/* Default status we expect, but this may change below */
1362 	GMT->current.io.rec_no++;			/* One more input record read */
1363 	status = gmtlib_process_binary_input (GMT, n_use);	/* Check for segment headers */
1364 	if (status == 1) return (GMTAPI_GOT_SEGHEADER);	/* A segment header was found and we are done here */
1365 	if (gmtlib_gap_detected (GMT)) { gmtlib_set_gap (GMT); return (GMTAPI_GOT_SEGGAP); }	/* Gap forced a segment header to be issued and we get out */
1366 	GMT->current.io.data_record_number_in_set[GMT_IN]++;	/* Actually got a valid data record */
1367 	return (GMT_NOERROR);
1368 }
1369 
1370 /*! . */
gmtapi_tictoc_string(struct GMTAPI_CTRL * API,unsigned int mode)1371 GMT_LOCAL char * gmtapi_tictoc_string (struct GMTAPI_CTRL *API, unsigned int mode) {
1372 	/* Optionally craft a leading timestamp.
1373 	 * mode = 0:	No time stamp
1374 	 * mode = 1:	Abs time stamp formatted via GMT_TIME_STAMP
1375 	 * mode = 2:	Report elapsed time since last reset.
1376 	 * mode = 4:	Reset elapsed time to 0, no time stamp.
1377 	 * mode = 6:	Reset elapsed time and report it as well.
1378 	 */
1379 	time_t right_now;
1380 	clock_t toc = 0;
1381 	unsigned int H, M, S, milli;
1382 	double T;
1383 	static char stamp[GMT_LEN256] = {""};
1384 
1385 	if (mode == 0) return NULL;		/* no timestamp requested */
1386 	if (mode > 1) toc = clock ();		/* Elapsed time requested */
1387 	if (mode & 4) API->GMT->current.time.tic = toc;	/* Reset previous timestamp to now */
1388 
1389 	switch (mode) {	/* Form output timestamp string */
1390 		case 1:	/* Absolute time stamp */
1391 			right_now = time ((time_t *)0);
1392 			strftime (stamp, sizeof(stamp), API->GMT->current.setting.format_time_stamp, localtime (&right_now));
1393 			break;
1394 		case 2:	/* Elapsed time stamp */
1395 		case 6:
1396 			T = (double)(toc - (clock_t)API->GMT->current.time.tic);	/* Elapsed time in ticks */
1397 			T /= CLOCKS_PER_SEC;	/* Elapsed time in seconds */
1398 			H = urint (floor (T * GMT_SEC2HR));
1399 			T -= H * GMT_HR2SEC_I;
1400 			M = urint (floor (T * GMT_SEC2MIN));
1401 			T -= M * GMT_MIN2SEC_I;
1402 			S = urint (floor (T));
1403 			T -= S;
1404 			milli = urint (T*1000.0);	/* Residual milli-seconds */
1405 			snprintf (stamp, GMT_LEN256, "Elapsed time %2.2u:%2.2u:%2.2u.%3.3u", H, M, S, milli);
1406 			break;
1407 		default: break;
1408 	}
1409 	return (stamp);
1410 }
1411 
1412 /*! . */
gmtapi_add_existing(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int geometry,unsigned int direction,int * first_ID)1413 GMT_LOCAL unsigned int gmtapi_add_existing (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int geometry, unsigned int direction, int *first_ID) {
1414 	/* In this mode, we find all registrered resources of matching family,geometry,direction that are unused and turn variable selected to true. */
1415 	unsigned int i, n, this_geo;
1416 
1417 	*first_ID = GMT_NOTSET;	/* Not found yet */
1418 	for (i = n = 0; i < API->n_objects; i++) {
1419 		if (!API->object[i]) continue;	/* A freed object, skip */
1420 		if (API->object[i]->direction != (enum GMT_enum_std)direction) continue; /* Wrong direction */
1421 		if (API->object[i]->status    != GMT_IS_UNUSED) continue;  /* Already used */
1422 		if (family != API->object[i]->family) continue;		   /* Wrong data type */
1423 		//if (API->object[i]->geometry  != (enum GMT_enum_geometry)geometry) continue;  /* Wrong geometry */
1424 		/* More careful check for geometry that allows the PLP (1+2+4) be match by any of those using logical and */
1425 		this_geo = (unsigned int)API->object[i]->geometry;
1426 		if (!(this_geo & geometry)) continue;  /* Wrong geometry */
1427 		n++;	/* Found one that satisfied requirements */
1428 		if (*first_ID == GMT_NOTSET) *first_ID = API->object[i]->ID;	/* Get the ID of the first that passed the test */
1429 		API->object[i]->selected = true;	/* Make this an active object for the coming i/o operation */
1430 	}
1431 	return (n);
1432 }
1433 
1434 /* These functions are support functions for the API function GMT_Encode_Options:
1435  *	gmtapi_key_to_family
1436  *	gmtapi_process_keys
1437  *	gmtapi_get_key
1438  *	gmtapi_found_marker
1439  *
1440  * The "keys" refer to the contents of the THIS_MODULE_KEYS set in each module.
1441  */
1442 
1443 /* Indices into the keys triple codes */
1444 #define K_OPT			0
1445 #define K_FAMILY		1
1446 #define K_DIR			2
1447 #define K_EQUAL			3
1448 #define K_MODIFIER		4
1449 #define GMT_FILE_NONE		0
1450 #define GMT_FILE_EXPLICIT	1
1451 #define GMT_FILE_IMPLICIT	2
1452 
1453 #define K_PRIMARY			0
1454 #define K_SECONDARY			1
1455 
1456 #define K_OR			0
1457 #define K_AND			1
1458 
1459 #define API_PRIMARY_INPUT		'{'
1460 #define API_PRIMARY_OUTPUT		'}'
1461 #define API_SECONDARY_INPUT		'('
1462 #define API_SECONDARY_OUTPUT	')'
1463 
gmtapi_key_to_family(void * API,char * key,int * family,int * geometry)1464 GMT_LOCAL int gmtapi_key_to_family (void *API, char *key, int *family, int *geometry) {
1465 	/* Assign direction, family, and geometry based on the key.
1466 	   Note: No Vector or Matrix here since those always masquerade as DATASET in modules. */
1467 
1468 	switch (key[K_FAMILY]) {	/* 2nd char contains the data type code */
1469 		case 'G':
1470 			*family = GMT_IS_GRID;
1471 			*geometry = GMT_IS_SURFACE;
1472 			break;
1473 		case 'P':
1474 			*family = GMT_IS_DATASET;
1475 			*geometry = GMT_IS_POLY;
1476 			break;
1477 		case 'L':
1478 			*family = GMT_IS_DATASET;
1479 			*geometry = GMT_IS_LINE;
1480 			break;
1481 		case 'D':
1482 			*family = GMT_IS_DATASET;
1483 			*geometry = GMT_IS_POINT;
1484 			break;
1485 		case 'C':
1486 			*family = GMT_IS_PALETTE;
1487 			*geometry = GMT_IS_NONE;
1488 			break;
1489 		case 'I':
1490 			*family = GMT_IS_IMAGE;
1491 			*geometry = GMT_IS_SURFACE;
1492 			break;
1493 		case 'U':
1494 			*family = GMT_IS_CUBE;
1495 			*geometry = GMT_IS_VOLUME;
1496 			break;
1497 		case 'X':
1498 			*family = GMT_IS_POSTSCRIPT;
1499 			*geometry = GMT_IS_NONE;
1500 			break;
1501 		case '-':
1502 			*family = GMT_IS_NONE;
1503 			*geometry = GMT_IS_NONE;
1504 			break;
1505 		default:
1506 			GMT_Report (API, GMT_MSG_ERROR, "gmtapi_key_to_family: Key family (%c) not recognized\n", key[K_FAMILY]);
1507 			return GMT_NOTSET;
1508 			break;
1509 	}
1510 
1511 	/* Third key character contains the in/out code */
1512 	return ((key[K_DIR] == API_SECONDARY_OUTPUT || key[K_DIR] == API_PRIMARY_OUTPUT) ? GMT_OUT : GMT_IN);	/* Return the direction of the i/o */
1513 }
1514 
gmtapi_prepare_keys(struct GMTAPI_CTRL * API,const char * string)1515 GMT_LOCAL char * gmtapi_prepare_keys (struct GMTAPI_CTRL *API, const char *string) {
1516 	char *tmp = NULL, *c = NULL, *string_;
1517 	string_ = strdup(string);	/* Have to make a copy because "string" is const and the c[0] = '\0' make it crash on Win for non-debug builds */
1518 	if ((c = strchr (string_, '@'))) {	/* Split KEYS: classic@modern, must get the relevant half */
1519 		c[0] = '\0';	/* Chop into two */
1520 		tmp = (API->GMT->current.setting.run_mode == GMT_MODERN) ? strdup (&c[1]) : strdup (string_);
1521 		//c[0] = '@';	/* Restore */
1522 	}
1523 	else	/* Only one set of KEYS */
1524 		tmp = strdup (string);		/* Get a working copy of string */
1525 
1526 	free(string_);
1527 	return (tmp);
1528 }
1529 
gmtapi_process_keys(void * V_API,const char * string,char type,struct GMT_OPTION * head,int * n_to_add,unsigned int * n_items)1530 GMT_LOCAL char ** gmtapi_process_keys (void *V_API, const char *string, char type, struct GMT_OPTION *head, int *n_to_add, unsigned int *n_items) {
1531 	/* Turn the comma-separated list of 3-char codes in string into an array of such codes.
1532  	 * In the process, replace any ?-types with the selected type if type is not 0.
1533 	 * We return the array of strings and its number (n_items). */
1534 	size_t len, k, kk, n;
1535 	int o_id = GMT_NOTSET, family = GMT_NOTSET, geometry = GMT_NOTSET;
1536 	bool change_type = false;
1537 	char **s = NULL, *next = NULL, *tmp = NULL, magic = 0, revised[GMT_LEN64] = {""};
1538 	struct GMT_OPTION *opt = NULL;
1539 	struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
1540 
1541 	*n_items = 0;	/* No keys yet */
1542 
1543 	for (k = 0; k < GMT_N_FAMILIES; k++) n_to_add[k] = GMT_NOTSET;	/* Initially no input counts */
1544 	if (!string) return NULL;	/* Got NULL, so just give up */
1545 	tmp = gmtapi_prepare_keys (API, string);	/* Get the correct KEYS if there are separate ones for Classic and Modern mode */
1546 	len = strlen (tmp);			/* Get the length of this item */
1547 	if (len == 0) { 			/* Got no characters, so give up */
1548 		gmt_M_str_free (tmp);
1549 		return NULL;
1550 	}
1551 	/* Replace unknown types (marked as ?) in tmp with selected type give by input variable "type" */
1552 	if (type) {	/* Got a nonzero type */
1553 		for (k = 0; k < strlen (tmp); k++)
1554 			if (tmp[k] == '?') tmp[k] = type;
1555 	}
1556 	/* Count the number of items (start n at 1 since there are one less comma than items) */
1557 	for (k = 0, n = 1; k < len; k++)
1558 		if (tmp[k] == ',') n++;
1559 	/* Allocate and populate the character array, then return it and n_items */
1560 	s = (char **) calloc (n, sizeof (char *));
1561 	k = 0;
1562 	while ((next = strsep (&tmp, ",")) != NULL) {	/* Get each comma-separated key */
1563 		if (strlen (next) < 3) {
1564 			GMT_Report (API, GMT_MSG_WARNING,
1565 			            "gmtapi_process_keys: Key %s contains less than 3 characters\n", next);
1566 			continue;
1567 		}
1568 		if (strchr (next, '!')) {	/* Type did not get determined in GMT_Encode_Options so key is skipped */
1569 			GMT_Report (API, GMT_MSG_DEBUG,
1570 			            "gmtapi_process_keys: key %s contains type = ! so we skip it\n", next);
1571 			n--;
1572 			continue;
1573 		}
1574 		s[k] = strdup (next);
1575 		if (next[K_DIR] == API_PRIMARY_OUTPUT) {	/* Identified primary output key */
1576 			if (o_id >= 0)	/* Already had a primary output key */
1577 				GMT_Report (API, GMT_MSG_WARNING,
1578 				            "gmtapi_process_keys: Keys %s contain more than one primary output key\n", tmp);
1579 			else
1580 				o_id = (int)k;
1581 		}
1582 		k++;
1583 	}
1584 
1585 	/* While processing the array we also determine the key # for the primary output (if there is one) */
1586 	for (k = 0; k < n; k++) {	/* Check for presence of any of the magic X,Y,Z keys */
1587 		if (s[k][K_OPT] == '-') {	/* Key letter X missing: Means that option -Y, if given, changes the type of input|output */
1588 			/* Must first determine which data type we are dealing with via -Y<type> */
1589 			if ((opt = GMT_Find_Option (API, s[k][K_FAMILY], head))) {	/* A -Y<type> option was passed to the module */
1590 				type = (char)toupper (opt->arg[0]);	/* Find type and replace any ? in keys with this type in uppercase (CDGIP) in gmtapi_process_keys below */
1591 				if (type == 'T')	/* There is no longer a T type but we may honor T from GMT5.  The gmtread|write module will decide depending on compatibility level set */
1592 					type = 'D';	/* opt->arg will still be 't' and is handled in the modules */
1593 				if (!strchr ("CDGIPU", type)) {
1594 					GMT_Report (API, GMT_MSG_ERROR, "gmtapi_process_keys: No or bad data type given to read|write (%c)\n", type);
1595 					return_null (NULL, GMT_NOT_A_VALID_TYPE);	/* Unknown type */
1596 				}
1597 				if (type == 'P') type = 'X';	/* We use X for PostScript internally since P may stand for polygon... */
1598 				for (kk = 0; kk < n; kk++) {	/* Do the substitution for all keys that matches ? */
1599 					if (s[kk][K_FAMILY] == '?' && strchr ("-({", s[kk][K_DIR])) s[kk][K_FAMILY] = type;	/* Want input to handle this type of data */
1600 					if (s[kk][K_FAMILY] == '?' && strchr ("-)}", s[kk][K_DIR])) s[kk][K_FAMILY] = type;	/* Want output to handle this type of data */
1601 				}
1602 			}
1603 			else
1604 				GMT_Report (API, GMT_MSG_WARNING,
1605 				            "gmtapi_process_keys: Required runtime type-getting option (-%c) was not given\n", s[k][K_FAMILY]);
1606 			gmt_M_str_free (s[k]);		/* Free the inactive key that has now served its purpose */
1607 		}
1608 		else if (s[k][K_FAMILY] == '-') {	/* Key letter Y missing: Means that -X, if given, changes primary input|output set by -Z to secondary (i.e., not required) */
1609 			/* However, if +<mod> is appended then the primary input setting is left as is */
1610 			if ((opt = GMT_Find_Option (API, s[k][K_OPT], head))) {	/* Got the option that removes the requirement of an input or output dataset */
1611 				if (!(s[k][3] == '+' && strstr (opt->arg, &s[k][3]))) {	/* Got the option and no modifier to turn it off */
1612 					for (kk = 0; kk < n; kk++) {	/* Change all primary input|output flags to secondary, depending on Z */
1613 						if (!s[kk]) continue;		/* A previously processed/freed key */
1614 						if (s[kk][K_OPT] != s[k][K_DIR]) continue;		/* Not the "-Z "option */
1615 						if (s[kk][K_DIR] == API_PRIMARY_INPUT) s[kk][K_DIR] = API_SECONDARY_INPUT;		/* No longer an implicit input */
1616 						else if (s[kk][K_DIR] == API_PRIMARY_OUTPUT) s[kk][K_DIR] = API_SECONDARY_OUTPUT;	/* No longer an implicit output */
1617 					}
1618 				}
1619 			}
1620 			gmt_M_str_free (s[k]);		/* Free the inactive key that has served its purpose */
1621 		}
1622 		else if (!strchr ("{}()-", s[k][K_DIR])) {	/* Key letter Z not in {|(|}|)|-: which means that option -Z, if given, changes the type of primary output to Y */
1623 			/* E.g, pscoast has >DM and this turns >X} to >D} only when -M is used.  Also, modifiers may be involved.
1624 			   e.g, gmtspatial : New key ">TN+r" means if -N+r is given then set >T}.  Just giving -N will not trigger the change.
1625 			   e.g., pscoast ">TE+w-rR" means if -E is given with modifier +w _and_ one of +r or +R is then set to >T}.
1626 			   If X is not - then we will find the other KEY with X and select that as the one to change; this could
1627 			   be used to change the primary INPUT type.  For instance, grdimage expects grid input (<G{+) but with
1628 			   magic sequence <ID we change <G{+ to <I{+.  */
1629 			magic = s[k][K_DIR];
1630 			if ((opt = GMT_Find_Option (API, magic, head))) {	/* Got the magic option that changes output type */
1631 				char modifier[3] = {'+', '?', 0};	/* We will replace ? with an actual modifier */
1632 				size_t this_k;
1633 				if (o_id == GMT_NOTSET)
1634 					GMT_Report (API, GMT_MSG_WARNING, "gmtapi_process_keys: No primary output identified but magic Z key present\n");
1635 				/* Check if modifier(s) were given also and that one of them were selected */
1636 				if (strlen (s[k]) > 3) {	/* Not enough to just find option, must examine the modifiers */
1637 					/* Full syntax: XYZ+abc...-def...: We do the substitution of output type to Y only if
1638 					 * 1. -Z is given
1639 					 * 2. -Z contains ALL the modifiers +a, +b, +c, ... (if any "+"-list is given)
1640 					 * 3. -Z contains AT LEAST ONE of the modifiers +d, +e, +f, ... (if any "-"=list is given)
1641 					 * At least one item from 2 or 3 must be given.
1642 					 */
1643 					unsigned int kase = 0, count[2] = {0, 0}, given[2] = {0, 0};
1644 					change_type = false;
1645 					for (kk = 3; s[k][kk]; kk++) {	/* Examine characters in the modifier string */
1646 						if (strchr ("-+", s[k][kk])) {	/* Start of all (+) versus at least one (-) */
1647 							kase = (s[k][kk] == '-') ? K_OR : K_AND;	/* Set kase and go to next letter */
1648 							continue;
1649 						}
1650 						count[kase]++;	/* How many AND and how many OR modifiers (depending on kase) */
1651 						modifier[1] = s[k][kk];	/* Set current modifier */
1652 						if (strstr (opt->arg, modifier)) given[kase]++;	/* Match found with given option */
1653 					}
1654 					/* Only change the key if we found all the AND modifiers and at least one of the OR modifiers (if any were given) */
1655 					if ((count[K_OR] == 0 || (count[K_OR] && given[K_OR])) && count[K_AND] == given[K_AND]) change_type = true;
1656 				}
1657 				else	/* true since we found the option and no modifiers were given */
1658 					change_type = true;
1659 				if (s[k][K_OPT] != '-') {	/* Find the relevant option to change [primary output key] */
1660 					char match = (s[k][K_OPT] == '<') ? API_PRIMARY_INPUT : API_PRIMARY_OUTPUT;
1661 					for (kk = 0, this_k = n; kk < n; kk++) {
1662 						if (kk == k || s[kk] == NULL) continue;
1663 						if (s[kk][K_OPT] == s[k][K_OPT] && s[kk][K_DIR] == match)
1664 							this_k = kk;
1665 					}
1666 					if (this_k == n) this_k = o_id;
1667 				}
1668 				else	/* Select the primary output key */
1669 					this_k = o_id;
1670 				if (change_type) {
1671 					if (strchr ("{<", s[this_k][K_DIR])) {
1672 						int new_family = 0, old_family = 0;
1673 						(void)gmtapi_key_to_family (API, s[k], &new_family, &geometry);
1674 						(void)gmtapi_key_to_family (API, s[this_k], &old_family, &geometry);
1675 						if (new_family != old_family) gmt_M_int_swap (n_to_add[new_family], n_to_add[old_family]);	/* Must swap our counts */
1676 					}
1677 					s[this_k][K_FAMILY] = s[k][K_FAMILY];	/* Required input/output now implies this data type */
1678 					s[this_k][K_OPT]    = s[k][K_OPT];	/* Required input/output now implies this option */
1679 				}
1680 			}
1681 			gmt_M_str_free (s[k]);		/* Free the inactive key that has served its purpose */
1682 		}
1683 		else if (s[k][K_DIR] == API_PRIMARY_INPUT) {	/* Non-magic key: This one identified a primary input key */
1684 			(void)gmtapi_key_to_family (API, s[k], &family, &geometry);	/* Get datatype, and geometry, then set how many are requested */
1685 			if (family != GMT_NOTSET) {	/* Safeguard: If family not found then we don't want to crash below... */
1686 				if (s[k][K_DIR+1])	/* Gave an argument: This is either a number (a specific count) or + (1 or more) */
1687 					n_to_add[family] = (s[k][K_DIR+1] == '+') ? GMTAPI_UNLIMITED : atoi (&s[k][K_DIR+1]);
1688 				else
1689 					n_to_add[family] = (family == GMT_IS_DATASET) ? GMTAPI_UNLIMITED : 1;
1690 			}
1691 		}
1692 	}
1693 	/* Shuffle away any NULL entries as a result of magic key processing */
1694 	for (k = kk = 0; k < n; k++) {
1695 		if (s[k]) {	/* Must keep this guy */
1696 			if (k > kk) s[kk] = s[k];
1697 			kk++;
1698 		}
1699 	}
1700 	n = kk;	/* May have lost some NULLs.  Make a revised string for debug output */
1701 	for (k = 0; k < n; k++) {
1702 		if (k) strcat (revised, ",");
1703 		strncat (revised, s[k], GMT_LEN64-1);
1704 	}
1705 	if (revised[0]) GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_process_keys: Revised keys string is %s\n", revised);
1706 	*n_items = (unsigned int)n;	/* Total number of remaining keys for this module */
1707 	gmt_M_str_free (tmp);
1708 	return s;	/* The array of remaining keys */
1709 }
1710 
gmtapi_get_key(void * API,char option,char * keys[],int n_keys)1711 GMT_LOCAL int gmtapi_get_key (void *API, char option, char *keys[], int n_keys) {
1712 	/* Returns the position in the keys array that matches this option, or GMT_NOTSET if not found */
1713 	int k;
1714 	if (n_keys && keys == NULL)
1715 		GMT_Report (API, GMT_MSG_WARNING, "gmtapi_get_key: Keys array is NULL but n_keys = %d\n", n_keys);
1716 	for (k = 0; keys && k < n_keys; k++) if (keys[k][K_OPT] == option) return (k);
1717 	return (GMT_NOTSET);
1718 }
1719 
gmtapi_found_marker(char * text)1720 GMT_LOCAL bool gmtapi_found_marker (char *text) {
1721 	/* A single questionmark and nothing else indicates a file marker */
1722 	if (text[0] == '?' && text[1] == '\0') return true;
1723 	return false;	/* Not found */
1724 }
1725 
gmtapi_determine_dimension(struct GMTAPI_CTRL * API,char * text)1726 GMT_LOCAL unsigned int gmtapi_determine_dimension (struct GMTAPI_CTRL *API, char *text) {
1727 	/* Examine greenspline's -R option to learn the dimensionality of the domain (1, 2, or 3) */
1728 	unsigned int n_slashes = 0;
1729 	size_t k;
1730 	const size_t s_length = strlen(text);
1731 
1732 	/* First catch the simple -R? which means a grid is passed by the API, hence dimension is 2 */
1733 	if (text[0] == '?' && text[1] == '\0') return 2;	/* A marker means a grid only, so done */
1734 	for (k = 0; k < s_length; k++)
1735 		if (text[k] == '/') n_slashes++;			/* Count slashes just in case */
1736 	if ((text[0] == 'g' || text[0] == 'd') && (text[1] == '\0' || text[1] == '/')) {	/* Got -Rg or -Rd, possibly with trailing /zmin/zmax */
1737 		if (text[1] == '\0') return 2;	/* Got -Rg or -Rd and no more */
1738 		if (n_slashes == 2) return 3;	/* Got -Rg/zmin/zmax or -Rd/zmin/zmax */
1739 		GMT_Report (API, GMT_MSG_ERROR, "Option -R: Give 2, 4, or 6 coordinates, a gridfile, or use -Rd|g[/zmin/zmax]\n");
1740 		return 0;
1741 	}
1742 	if (!gmt_access (API->GMT, text, R_OK))	/* Gave a readable file, we assume it is a grid since that is all that is allowed */
1743 		return 2;
1744 	/* Only get here if the above cases did not trip */
1745 	if (!(n_slashes == 1 || n_slashes == 3 || n_slashes == 5)) {
1746 		GMT_Report (API, GMT_MSG_ERROR, "Option -R: Give 2, 4, or 6 coordinates\n");
1747 		return 0;
1748 	}
1749 	return ((n_slashes + 1) / 2);	/* Turns 1,3,5 into 1,2,3 */
1750 }
1751 
1752 /*! . */
gmtapi_retrieve_data(void * V_API,int object_ID)1753 GMT_LOCAL void * gmtapi_retrieve_data (void *V_API, int object_ID) {
1754 	/* Function to return pointer to the container for a registered data set.
1755 	 * Typically used when we wish a module to "write" its results to a memory
1756 	 * location that we wish to access from the calling program.  The procedure
1757 	 * is to use GMT_Register_IO with GMT_REF|COPY|READONLY and GMT_OUT but use
1758 	 * NULL as the source/destination.  Data are "written" by GMT allocating a
1759 	 * output container and updating the objects->resource pointer to this container.
1760 	 * gmtapi_retrieve_data simply returns that pointer given the registered ID.
1761 	 */
1762 
1763 	int item;
1764 	struct GMTAPI_CTRL *API = NULL;
1765 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
1766 
1767 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
1768 
1769 	/* Determine the item in the object list that matches this object_ID */
1770 	API = gmtapi_get_api_ptr (V_API);
1771 	API->error = GMT_NOERROR;
1772 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET) {
1773 		return_null (API, API->error);
1774 	}
1775 	S_obj = API->object[item];	/* Short hand */
1776 	/* Make sure the resource is present */
1777 	if (S_obj->resource == NULL) {
1778 		return_null (API, GMT_PTR_IS_NULL);
1779 	}
1780 
1781 #ifdef DEBUG
1782 	//gmtapi_list_objects (API, "gmtapi_retrieve_data");
1783 #endif
1784 	return (S_obj->resource);	/* Return pointer to the resource container */
1785 }
1786 
1787 /*! . */
gmtapi_begin_io(struct GMTAPI_CTRL * API,unsigned int direction)1788 GMT_LOCAL int gmtapi_begin_io (struct GMTAPI_CTRL *API, unsigned int direction) {
1789 	/* Initializes the i/o mechanism for either input or output (depends on direction).
1790 	 * gmtapi_begin_io must be called before any bulk data i/o is allowed.
1791 	 * direction:	Either GMT_IN or GMT_OUT.
1792 	 * Returns:	false if successful, true if error.
1793 	 */
1794 
1795 	struct GMT_CTRL *GMT = NULL;
1796 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
1797 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (API, GMT_NOT_A_VALID_DIRECTION);
1798 	API->error = GMT_NOERROR;
1799 	if (!API->registered[direction])
1800 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_begin_io: No %s resources registered\n", GMT_direction[direction]);
1801 	/* Passed basic sanity checks */
1802 	GMT = API->GMT;
1803 	API->io_mode[direction] = GMTAPI_BY_SET;
1804 	API->io_enabled[direction] = true;	/* OK to access resources */
1805 	GMT->current.io.ogr = GMT_OGR_UNKNOWN;
1806 	GMT->current.io.variable_in_columns = false;
1807 	GMT->current.io.need_previous = (GMT->common.g.active || GMT->current.io.skip_duplicates);
1808 	GMT->current.io.segment_header[0] = GMT->current.io.curr_text[0] = 0;
1809 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_begin_io: %s resource access is now enabled [container]\n", GMT_direction[direction]);
1810 
1811 	return (GMT_NOERROR);	/* No error encountered */
1812 }
1813 
1814 /* Mapping of internal [row][col] indices to a single 1-D index.
1815  * Internally, row and col both starts at 0.  These will be accessed
1816  * via pointers to these functions, hence they are not macros.
1817  * They apply to GMT_MATRIX items, NOT grids/images with pads.
1818  */
1819 
1820 /*! . */
gmtapi_2d_to_index_c_normal(uint64_t row,uint64_t col,uint64_t dim)1821 GMT_LOCAL uint64_t gmtapi_2d_to_index_c_normal (uint64_t row, uint64_t col, uint64_t dim) {
1822 	/* Maps (row,col) to 1-D index for C normal row-major grid */
1823 	return ((row * dim) + col);	/* Normal scanline grid */
1824 }
1825 
1826 /*! . */
gmtapi_2d_to_index_c_cplx_real(uint64_t row,uint64_t col,uint64_t dim)1827 GMT_LOCAL uint64_t gmtapi_2d_to_index_c_cplx_real (uint64_t row, uint64_t col, uint64_t dim) {
1828 	/* Maps (row,col) to 1-D index for C complex row-major grid, real component */
1829 	return (2ULL*(row * dim) + col);	/* Complex scanline grid, real(1) component */
1830 }
1831 
1832 /*! . */
gmtapi_2d_to_index_c_cplx_imag(uint64_t row,uint64_t col,uint64_t dim)1833 GMT_LOCAL uint64_t gmtapi_2d_to_index_c_cplx_imag (uint64_t row, uint64_t col, uint64_t dim) {
1834 	/* Maps (row,col) to 1-D index for C complex row-major grid, imaginary component */
1835 	return (2ULL*(row * dim) + col + 1ULL);	/* Complex grid, imag(2) component */
1836 }
1837 
1838 /*! . */
gmtapi_2d_to_index_f_normal(uint64_t row,uint64_t col,uint64_t dim)1839 GMT_LOCAL uint64_t gmtapi_2d_to_index_f_normal (uint64_t row, uint64_t col, uint64_t dim) {
1840 	/* Maps (row,col) to 1-D index for Fortran column-major grid */
1841 	return ((col * dim) + row);
1842 }
1843 
1844 /*! . */
gmtapi_2d_to_index_f_cplx_real(uint64_t row,uint64_t col,uint64_t dim)1845 GMT_LOCAL uint64_t gmtapi_2d_to_index_f_cplx_real (uint64_t row, uint64_t col, uint64_t dim) {
1846 	/* Maps (row,col) to 1-D index for Fortran complex column-major grid, real component */
1847 	return (2ULL*(col * dim) + row);	/* Complex grid, real(1) */
1848 }
1849 
1850 /*! . */
gmtapi_2d_to_index_f_cplx_imag(uint64_t row,uint64_t col,uint64_t dim)1851 GMT_LOCAL uint64_t gmtapi_2d_to_index_f_cplx_imag (uint64_t row, uint64_t col, uint64_t dim) {
1852 	/* Maps (row,col) to 1-D index for Fortran complex column-major grid, imaginary component  */
1853 	return (2ULL*(col * dim) + row + 1ULL);	/* Complex grid, imag(2) component */
1854 }
1855 
1856 /*! . */
gmtapi_get_2d_to_index(struct GMTAPI_CTRL * API,enum GMT_enum_fmt shape,unsigned int mode)1857 GMT_LOCAL p_func_uint64_t gmtapi_get_2d_to_index (struct GMTAPI_CTRL *API, enum GMT_enum_fmt shape, unsigned int mode) {
1858 	/* Return pointer to the required 2D-index function above for MATRIX.  Here
1859 	 * shape is either GMT_IS_ROW_FORMAT (C) or GMT_IS_COL_FORMAT (Fortran);
1860 	 * mode is either 0 (regular grid), GMT_GRID_IS_COMPLEX_REAL (complex real) or GMT_GRID_IS_COMPLEX_IMAG (complex imag)
1861 	 */
1862 	p_func_uint64_t p = NULL;
1863 
1864 	switch (mode & GMT_GRID_IS_COMPLEX_MASK) {
1865 		case GMT_GRID_IS_REAL:
1866 			p = (shape == GMT_IS_ROW_FORMAT) ? gmtapi_2d_to_index_c_normal : gmtapi_2d_to_index_f_normal;
1867 			break;
1868 		case GMT_GRID_IS_COMPLEX_REAL:
1869 			p = (shape == GMT_IS_ROW_FORMAT) ? gmtapi_2d_to_index_c_cplx_real : gmtapi_2d_to_index_f_cplx_real;
1870 			break;
1871 		case GMT_GRID_IS_COMPLEX_IMAG:
1872 			p = (shape == GMT_IS_ROW_FORMAT) ? gmtapi_2d_to_index_c_cplx_imag : gmtapi_2d_to_index_f_cplx_imag;
1873 			break;
1874 		default:
1875 			GMT_Report (API, GMT_MSG_ERROR, "gmtapi_get_2d_to_index: Illegal mode passed\n");
1876 			return (NULL);
1877 	}
1878 	return (p);
1879 }
1880 
1881 #if 0	/* Unused at the present time */
1882 GMT_LOCAL void gmtapi_index_to_2d_c (int *row, int *col, size_t index, int dim, int mode) {
1883 	/* Maps 1-D index to (row,col) for C */
1884 	if (mode) index /= 2;
1885 	*col = (index % dim);
1886 	*row = (index / dim);
1887 }
1888 
1889 GMT_LOCAL void gmtapi_index_to_2d_f (int *row, int *col, size_t index, int dim, int mode) {
1890 	/* Maps 1-D index to (row,col) for Fortran */
1891 	if (mode) index /= 2;
1892 	*col = (index / dim);
1893 	*row = (index % dim);
1894 }
1895 #endif
1896 
1897 /* Mapping of internal [row][col][layer] indices to a single 1-D index for images.
1898  * Internally, row and col starts at 0.  These will be accessed
1899  * via pointers to these functions, hence they are not macros.
1900  */
1901 
gmtapi_get_index_from_TRB(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1902 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRB (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1903 	/* Get linear index of an array with a band-interleaved layout RRR...RGGG...GBBB...B */
1904 	return (h->pad[XLO] + col) + ((row + h->pad[YHI]) * h->mx) + (layer * h->size);
1905 }
1906 
gmtapi_get_index_from_TRP(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1907 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRP (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1908 	/* Get linear index of an array with a pixel-interleaved layout RGBRGBRGB...*/
1909 	return ((h->pad[XLO] + col) * h->n_bands) + layer + ((row + h->pad[YHI]) * h->mx * h->n_bands);
1910 }
1911 
gmtapi_get_index_from_TRL(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1912 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRL (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1913 	/* Get linear index of an array with a line-interleaved layout R...RG..GB...BR...RG...GB...B...*/
1914 	return (h->pad[XLO] + col) + (layer * h->mx) + ((row + h->pad[YHI]) * h->mx * h->n_bands);
1915 }
1916 
gmtapi_get_index_from_TRS(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1917 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRS (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1918 	/* Get linear index of an default GMT grid */
1919 	gmt_M_unused(layer);
1920 	return (gmt_M_ijp (h, row, col));
1921 }
1922 
gmtapi_get_index_from_TRR(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1923 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRR (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1924 	/* Get linear index to the real component of an default complex GMT grid */
1925 	gmt_M_unused(layer);
1926 	return (2ULL*gmt_M_ijp (h, row, col));
1927 }
1928 
gmtapi_get_index_from_TRI(struct GMT_GRID_HEADER * h,uint64_t row,uint64_t col,uint64_t layer)1929 GMT_LOCAL inline uint64_t gmtapi_get_index_from_TRI (struct GMT_GRID_HEADER *h, uint64_t row, uint64_t col, uint64_t layer) {
1930 	/* Get linear index to the imag component of an default complex GMT grid */
1931 	gmt_M_unused(layer);
1932 	return (2ULL*gmt_M_ijp (h, row, col)+1ULL);
1933 }
1934 
1935 /*! . */
gmtapi_decode_layout(struct GMTAPI_CTRL * API,const char * code,enum GMT_enum_family * family)1936 GMT_LOCAL unsigned int gmtapi_decode_layout (struct GMTAPI_CTRL *API, const char *code, enum GMT_enum_family *family) {
1937 	/* Convert the 3-letter grid/image layout code to a single integer mode.
1938 	 * Defaults are TRS for grids and TRB for images. */
1939 	unsigned int bits = 0;	/* Default value */
1940 	*family = GMT_IS_IMAGE;	/* Default value, may be changed later */
1941 	switch (code[0]) {	/* Char 1: The Y direction */
1942 		case 'T':	break;			 /* Top-to-bottom [Default] */
1943 		case 'B':	bits = 1; break; /* Bottom-to-top */
1944 		default:
1945 			GMT_Report (API, GMT_MSG_ERROR, "Illegal code [%c] for y-direction grid/image layout.  Must be T or B\n", code[0]);
1946 			break;
1947 	}
1948 	switch (code[1]) {	/* Char 2: The storage mode (rows vs columns) */
1949 		case 'R':	break;		 	  /* rows, i.e., scanlines [Default] */
1950 		case 'C':	bits |= 2; break; /* columns (e.g., Fortran) */
1951 		default:
1952 			GMT_Report (API, GMT_MSG_ERROR, "Illegal code [%c] for grid/image storage mode.  Must be R or C\n", code[1]);
1953 			break;
1954 	}
1955 	switch (code[2]) {	/* Char 3: Grids: Single, complex-Real, complex-Imag.  Images: band interleaving mode B|L|P */
1956 		case 'S':	*family = GMT_IS_GRID; break;	/* Single-valued grid [Default] */
1957 		case 'R':	bits |= 4; *family = GMT_IS_GRID; break;	/* Real component of complex grid */
1958 		case 'I':	bits |= 8; *family = GMT_IS_GRID; break;	/* Imaginary component of complex grid */
1959 		case 'B':	break;			/* r/g/b separated into three bands (layers) */
1960 		case 'L':	bits |= 4; break;	/* r/g/b separated into three lines */
1961 		case 'P':	bits |= 8; break;	/* r/g/b separated into three values per pixel */
1962 		default:
1963 			GMT_Report (API, GMT_MSG_ERROR, "Illegal code [%c] for type of grid or image layout.  Must be SRI (grids) or BLP (images)\n", code[1]);
1964 			break;
1965 	}
1966 	return (bits);
1967 }
1968 
gmtapi_init_grdheader(struct GMT_CTRL * GMT,unsigned int direction,struct GMT_GRID_HEADER * header,struct GMT_OPTION * options,uint64_t dim[],double wesn[],double inc[],unsigned int registration,unsigned int mode)1969 GMT_LOCAL int gmtapi_init_grdheader (struct GMT_CTRL *GMT, unsigned int direction, struct GMT_GRID_HEADER *header, struct GMT_OPTION *options,
1970                              uint64_t dim[], double wesn[], double inc[], unsigned int registration, unsigned int mode) {
1971 	/* Convenient way of setting a header struct wesn, inc, and registration, then compute dimensions, etc. */
1972 	double wesn_dup[4] = {0.0, 0.0, 0.0, 0.0}, inc_dup[2] = {0.0, 0.0};
1973 	unsigned int n_layers = 1;
1974 	char *regtype[2] = {"gridline", "pixel"};
1975 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1976 	gmt_M_unused(mode);
1977 
1978 	if (registration & GMT_GRID_DEFAULT_REG) registration |= GMT->common.R.registration;	/* Set the default registration */
1979 	registration = (registration & 1);	/* Knock off any GMT_GRID_DEFAULT_REG bit */
1980 	if (dim && (wesn == NULL || (gmt_M_is_zero (wesn[XLO]) && gmt_M_is_zero (wesn[XHI]) && gmt_M_is_zero (wesn[YLO]) && gmt_M_is_zero (wesn[YHI]))) && (inc == NULL || (gmt_M_is_zero (inc[GMT_X]) && gmt_M_is_zero (inc[GMT_Y])))) {	/* Gave dimension instead, set range and inc (1/1) while considering registration */
1981 		gmt_M_memset (wesn_dup, 4, double);
1982 		wesn_dup[XHI] = (double)(dim[GMT_X]);
1983 		wesn_dup[YHI] = (double)(dim[GMT_Y]);
1984 		inc_dup[GMT_X] = inc_dup[GMT_Y] = 1.0;
1985 		if (registration == GMT_GRID_NODE_REG) wesn_dup[XHI] -= 1.0, wesn_dup[YHI] -= 1.0;
1986 		if (dim[GMT_Z] > 1) n_layers = (unsigned int)dim[GMT_Z];
1987 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Grid/Image dimensions imply w/e/s/n = 0/%g/0/%g, inc = 1/1, %s registration, n_layers = %u\n",
1988 			wesn_dup[XHI], wesn_dup[YHI], regtype[registration], n_layers);
1989 	}
1990 	else {	/* Must infer dimension etc from wesn, inc, registration */
1991 		if (wesn == NULL) {	/* Must select -R setting */
1992 			if (!GMT->common.R.active[RSET]) {
1993 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "No w/e/s/n given and no -R in effect.  Cannot initialize new grid\n");
1994 				return GMT_ARG_IS_NULL;
1995 			}
1996 		}
1997 		else	/* In case user is passing header->wesn etc we must save them first as gmt_grd_init will clobber them */
1998 			gmt_M_memcpy (wesn_dup, wesn, 4, double);
1999 		if (inc == NULL) {	/* Must select -I setting */
2000 			if (!GMT->common.R.active[ISET]) {
2001 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "No increment given and no -I in effect.  Cannot initialize new grid\n");
2002 				return GMT_ARG_IS_NULL;
2003 			}
2004 		}
2005 		else	/* In case user is passing header->inc etc we must save them first as gmt_grd_init will clobber them */
2006 			gmt_M_memcpy (inc_dup, inc, 2, double);
2007 		if (dim && dim[GMT_Z] > 1) n_layers = (unsigned int)dim[GMT_Z];
2008 		if (inc != NULL) {
2009 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Grid/Image dimensions imply w/e/s/n = %g/%g/%g/%g, inc = %g/%g, %s registration, n_layers = %u\n",
2010 			            wesn_dup[XLO], wesn_dup[XHI], wesn_dup[YLO], wesn_dup[YHI], inc[GMT_X], inc[GMT_Y], regtype[registration], n_layers);
2011 		}
2012 	}
2013 	/* Clobber header and reset */
2014 	gmt_grd_init (GMT, header, options, false);	/* This is for new grids only so update is always false */
2015 	if (dim == NULL && wesn == NULL)
2016 		gmt_M_memcpy (header->wesn, GMT->common.R.wesn, 4, double);
2017 	else
2018 		gmt_M_memcpy (header->wesn, wesn_dup, 4, double);
2019 	if (dim == NULL && inc == NULL)
2020 		gmt_M_memcpy (header->inc, GMT->common.R.inc, 2, double);
2021 	else
2022 		gmt_M_memcpy (header->inc, inc_dup, 2, double);
2023 	header->registration = registration;
2024 	/* Copy row-order from R.row_order, if set */
2025 	if (GMT->common.R.row_order) HH->row_order = GMT->common.R.row_order;
2026 	/* Mode may contain complex mode information */
2027 	header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);
2028 	HH->grdtype = gmtlib_get_grdtype (GMT, direction, header);
2029 	gmt_RI_prepare (GMT, header);	/* Ensure -R -I consistency and set n_columns, n_rows in case of meter units etc. */
2030 	gmt_M_err_pass (GMT, gmt_grd_RI_verify (GMT, header, 1), "");
2031 	gmt_M_grd_setpad (GMT, header, GMT->current.io.pad);	/* Assign default GMT pad */
2032 	if (dim) header->n_bands = n_layers;
2033 	gmt_set_grddim (GMT, header);	/* Set all dimensions before returning */
2034 	gmtlib_grd_get_units (GMT, header);
2035 	gmt_BC_init (GMT, header);	/* Initialize grid interpolation and boundary condition parameters */
2036 	HH->grdtype = gmtlib_get_grdtype (GMT, direction, header);	/* Set grid type (i.e. periodicity for global grids) */
2037 #ifdef DOUBLE_PRECISION_GRID
2038 	header->type = GMT_GRID_IS_ND;
2039 #else
2040 	header->type = GMT_GRID_IS_NF;
2041 #endif
2042 	return (GMT_NOERROR);
2043 }
2044 
2045 /*! . */
gmtapi_init_grid(struct GMTAPI_CTRL * API,struct GMT_OPTION * opt,uint64_t dim[],double * range,double * inc,int registration,unsigned int mode,unsigned int direction,struct GMT_GRID * G)2046 GMT_LOCAL int gmtapi_init_grid (struct GMTAPI_CTRL *API, struct GMT_OPTION *opt, uint64_t dim[], double *range, double *inc, int registration, unsigned int mode, unsigned int direction, struct GMT_GRID *G) {
2047 	if (direction == GMT_OUT) return (GMT_NOERROR);	/* OK for creating a blank container for output */
2048 	return (gmtapi_init_grdheader (API->GMT, direction, G->header, opt, dim, range, inc, registration, mode));
2049 }
2050 
2051 /*! . */
gmtapi_init_image(struct GMTAPI_CTRL * API,struct GMT_OPTION * opt,uint64_t dim[],double * range,double * inc,int registration,unsigned int mode,unsigned int direction,struct GMT_IMAGE * I)2052 GMT_LOCAL int gmtapi_init_image (struct GMTAPI_CTRL *API, struct GMT_OPTION *opt, uint64_t dim[], double *range, double *inc, int registration, unsigned int mode, unsigned int direction, struct GMT_IMAGE *I) {
2053 	if (direction == GMT_OUT) return (GMT_NOERROR);	/* OK for creating blank container for output */
2054 	return (gmtapi_init_grdheader (API->GMT, direction, I->header, opt, dim, range, inc, registration, mode));
2055 }
2056 
2057 /*! . */
gmtapi_init_matrix(struct GMTAPI_CTRL * API,uint64_t dim[],double * range,double * inc,int registration,unsigned int mode,unsigned int direction,struct GMT_MATRIX * M)2058 GMT_LOCAL int gmtapi_init_matrix (struct GMTAPI_CTRL *API, uint64_t dim[], double *range, double *inc, int registration, unsigned int mode, unsigned int direction, struct GMT_MATRIX *M) {
2059 	/* If range = inc = NULL then add dimensioning is set via dim: ncols, nrow, nlayers, type.
2060 	 * else, ncols,nrows is set via range and inc and registration. dim, if not null, sets dim[2] = nlayers [1] and dim[3] = type [double]
2061 	 */
2062 	int error;
2063 	unsigned int dims = (M->n_layers > 1) ? 3 : 2;
2064 	size_t size = 0;
2065 	struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M);
2066 
2067 	GMT_Report (API, GMT_MSG_DEBUG, "Initializing a matrix for handing external %s [mode = %u]\n", GMT_direction[direction], mode);
2068 	if (direction == GMT_OUT) {	/* OK to create blank container for output unless dims or range/inc is also set */
2069 		if (dim && dim[GMTAPI_DIM_ROW]) {	/* Dimensions are given when we are using external memory for the matrix and must specify the dimensions specifically */
2070 			M->n_rows    = dim[GMTAPI_DIM_ROW];
2071 			M->n_columns = dim[GMTAPI_DIM_COL];
2072 			M->dim = (M->shape == GMT_IS_ROW_FORMAT) ? M->n_columns : M->n_rows;	/* Matrix layout order */
2073 		}
2074 		else if (range) {	/* Giving dimensions via range and inc when using external memory */
2075 			if (!inc || (inc[GMT_X] == 0.0 && inc[GMT_Y] == 0.0)) return (GMT_VALUE_NOT_SET);
2076 			gmt_M_memcpy (M->range, range, 2 * dims, double);
2077 			gmt_M_memcpy (M->inc, inc, dims, double);
2078 			M->n_rows    = gmt_M_get_n (API->GMT, range[YLO], range[YHI], inc[GMT_Y], registration);
2079 			M->n_columns = gmt_M_get_n (API->GMT, range[XLO], range[XHI], inc[GMT_X], registration);
2080 			M->dim = (M->shape == GMT_IS_ROW_FORMAT) ? M->n_columns : M->n_rows;	/* Matrix layout order */
2081 		}
2082 		return (GMT_NOERROR);
2083 	}
2084 	if (full_region (range) && (dims == 2 || (!range || range[ZLO] == range[ZHI]))) {	/* Not an equidistant vector arrangement, use dim */
2085 		double dummy_range[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};	/* Flag vector as such */
2086 		if (dim == NULL) return (GMT_VALUE_NOT_SET);
2087 		gmt_M_memcpy (M->range, dummy_range, 2 * dims, double);
2088 		gmt_M_memcpy (M->inc, dummy_range, dims, double);
2089 		M->n_rows    = dim[GMTAPI_DIM_ROW];
2090 		M->n_columns = dim[GMTAPI_DIM_COL];
2091 	}
2092 	else {	/* Was apparently given valid range and inc */
2093 		if (!inc || (inc[GMT_X] == 0.0 && inc[GMT_Y] == 0.0)) return (GMT_VALUE_NOT_SET);
2094 		gmt_M_memcpy (M->range, range, 2 * dims, double);
2095 		gmt_M_memcpy (M->inc, inc, dims, double);
2096 		M->n_rows    = gmt_M_get_n (API->GMT, range[YLO], range[YHI], inc[GMT_Y], registration);
2097 		M->n_columns = gmt_M_get_n (API->GMT, range[XLO], range[XHI], inc[GMT_X], registration);
2098 	}
2099 	M->type = (dim == NULL) ? API->GMT->current.setting.export_type : dim[3];	/* Use selected data type for export or default to GMT setting */
2100 	M->dim = (M->shape == GMT_IS_ROW_FORMAT) ? M->n_columns : M->n_rows;
2101 	M->registration = registration;
2102 	size = M->n_rows * M->n_columns * ((size_t)M->n_layers);	/* Size of the initial matrix allocation (number of elements) */
2103 	MH->grdtype = gmtlib_get_matrixtype (API->GMT, direction, M);
2104 	if ((mode & GMT_CONTAINER_ONLY) == 0) {	/* Must allocate data memory */
2105 		if (size) {	/* Must allocate data matrix and possibly string array */
2106 			if ((error = gmtlib_alloc_univector (API->GMT, &(M->data), M->type, size)) != GMT_NOERROR)
2107 				return (error);
2108 			if (mode & GMT_WITH_STRINGS) {	/* Must allocate text pointer array */
2109 				if ((M->text = gmt_M_memory (API->GMT, NULL, M->n_rows, char *)) == NULL)
2110 					return (GMT_MEMORY_ERROR);
2111 			}
2112 		}
2113 		MH->alloc_mode = GMT_ALLOC_INTERNALLY;
2114 	}
2115 	return (GMT_NOERROR);
2116 }
2117 
2118 /*! . */
gmtapi_vector_nrows(uint64_t dim[],double * range,double * inc,int registration,unsigned int dir)2119 GMT_LOCAL uint64_t gmtapi_vector_nrows (uint64_t dim[], double *range, double *inc, int registration, unsigned int dir) {
2120 	if (dim && dim[GMTAPI_DIM_ROW]) return dim[GMTAPI_DIM_ROW];	/* Gave the dimension directly */
2121 	if (dir == GMT_IN && (!inc || inc[GMT_X] == 0.0)) return ((uint64_t)GMT_NOTSET);
2122 	if (dir == GMT_IN && (!range || (range[XLO] == 0.0 && range[XHI] == 0.0))) return ((uint64_t)GMT_NOTSET);
2123 	if (range && inc) return (gmt_M_get_n (API->GMT, range[XLO], range[XHI], inc[GMT_X], registration));
2124 	return (0);	/* When dir == GMT_OUT */
2125 }
2126 
2127 /*! . */
gmtapi_vector_ncols(uint64_t dim[],unsigned int dir)2128 GMT_LOCAL int64_t gmtapi_vector_ncols (uint64_t dim[], unsigned int dir) {
2129 	if (dim) return (int64_t) dim[GMTAPI_DIM_COL];	/* Gave the dimension directly */
2130 	if (dir == GMT_OUT) return (0);		/* Not set for output to be allocated later */
2131 	return (GMT_NOTSET);	/* When dir == GMT_IN and we fail */
2132 }
2133 
2134 /*! . */
gmtapi_init_vector(struct GMTAPI_CTRL * API,uint64_t dim[],double * range,double * inc,int registration,unsigned int mode,unsigned int direction,struct GMT_VECTOR * V)2135 GMT_LOCAL int gmtapi_init_vector (struct GMTAPI_CTRL *API, uint64_t dim[], double *range, double *inc, int registration, unsigned int mode, unsigned int direction, struct GMT_VECTOR *V) {
2136 	/* If range = inc = NULL then add dimensioning is set via dim: ncols, nrow, type.
2137 	 * else, ncols,nrows is set via range and inc and registration. dim[2], if not null, sets type [double]
2138 	 */
2139 	int error;
2140 	uint64_t col;
2141 	struct GMT_VECTOR_HIDDEN *HV = gmt_get_V_hidden (V);
2142 
2143 	GMT_Report (API, GMT_MSG_DEBUG, "Initializing a vector for handing external %s\n", GMT_direction[direction]);
2144 	if (direction == GMT_OUT) {	/* OK for creating blank container for output, but sometimes there are dimensions */
2145 		if (dim && dim[GMTAPI_DIM_ROW] && V->n_columns)
2146 			V->n_rows = dim[GMTAPI_DIM_ROW];	/* Set n_rows in case when vectors will be hook on from external memory */
2147 		else if (range && V->n_columns)	/* Giving dimensions via range and inc when using external memory */
2148 			V->n_rows = gmtapi_vector_nrows (dim, range, inc, registration, direction);
2149 		return (GMT_NOERROR);
2150 	}
2151 	else if (V->n_columns == 0)
2152 		return (GMT_VALUE_NOT_SET);	/* Must know the number of columns to do this */
2153 	if ((range && inc == NULL) || (range == NULL && inc)) {
2154 		GMT_Report (API, GMT_MSG_ERROR, "Passed one of range, inc as NULL\n");
2155 		return (GMT_VALUE_NOT_SET);
2156 	}
2157 	if ((range == NULL && inc == NULL) || (range[XLO] == range[XHI] && inc[GMT_X] == 0.0)) {	/* Not an equidistant vector arrangement, use dim */
2158 		double dummy_range[2] = {0.0, 0.0};	/* Flag vector as such */
2159 		V->n_rows = dim[GMTAPI_DIM_ROW];		/* If so, n_rows is passed via dim[GMTAPI_DIM_ROW], unless it is GMT_OUT when it is zero */
2160 		gmt_M_memcpy (V->range, dummy_range, 2, double);
2161 	}
2162 	else {	/* Equidistant vector defined by dimension or range/inc */
2163 		int64_t n = gmtapi_vector_nrows (dim, range, inc, registration, direction);
2164 		if (n == GMT_NOTSET) return (GMT_VALUE_NOT_SET);
2165 		V->n_rows = (uint64_t)n;
2166 		gmt_M_memcpy (V->range, range, 2, double);
2167 	}
2168 	for (col = 0; col < V->n_columns; col++)	/* Set the same export data type for all vectors (or default to double) */
2169 		V->type[col] = (dim == NULL) ? API->GMT->current.setting.export_type : dim[GMT_Z];
2170 	if ((mode & GMT_CONTAINER_ONLY) == 0) {	/* Must allocate space */
2171 		struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V);
2172 		if (V->n_rows) {	/* Must allocate vector space and possibly strings */
2173 			if ((error = gmtlib_alloc_vectors (API->GMT, V, V->n_rows)) != GMT_NOERROR)
2174 				return (error);
2175 			if (mode & GMT_WITH_STRINGS) {	/* Must allocate text pointer array */
2176 				if ((V->text = gmt_M_memory (API->GMT, NULL, V->n_rows, char *)) == NULL)
2177 					return (GMT_MEMORY_ERROR);
2178 				VH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
2179 			}
2180 		}
2181 	}
2182 	if (gmt_M_is_geographic (API->GMT, direction)) HV->geographic = 1;
2183 	return (GMT_NOERROR);
2184 }
2185 
2186 /*! . */
gmtapi_matrix_coord(struct GMTAPI_CTRL * API,int dim,struct GMT_MATRIX * M)2187 GMT_LOCAL double * gmtapi_matrix_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_MATRIX *M) {
2188 	/* Allocate and compute coordinates along one dimension of a matrix */
2189 	double *coord = NULL, off;
2190 	unsigned int min, max;
2191 	uint64_t k, n;
2192 
2193 	if (M->n_layers <= 1 && dim == GMT_Z) return (NULL);	/* No z-dimension */
2194 	n = (dim == GMT_X) ? M->n_columns : ((dim == GMT_Y) ? M->n_rows : M->n_layers);
2195 	min = 2*dim, max = 2*dim + 1;	/* Indices into the min/max triplets in range */
2196 	coord = gmt_M_memory (API->GMT, NULL, n, double);
2197 	off = 0.5 * M->registration;
2198 	for (k = 0; k < n; k++) coord[k] = gmt_M_col_to_x (API->GMT, k, M->range[min], M->range[max], M->inc[dim], off, n);
2199 	return (coord);
2200 }
2201 
2202 /*! . */
gmtapi_vector_coord(struct GMTAPI_CTRL * API,int dim,struct GMT_VECTOR * V)2203 GMT_LOCAL double * gmtapi_vector_coord (struct GMTAPI_CTRL *API, int dim, struct GMT_VECTOR *V) {
2204 	/* Allocate and compute coordinates for a vector, if equidistantly defined */
2205 	unsigned int k;
2206 	double *coord = NULL, off, inc;
2207 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_vector_coord called: dim = %d\n", dim);
2208 	if (V->range[0] == 0.0 && V->range[1] == 0.0) return (NULL);	/* Not an equidistant vector */
2209 	coord = gmt_M_memory (API->GMT, NULL, V->n_rows, double);
2210 	off = 0.5 * V->registration;
2211 	inc = gmt_M_get_inc (API->GMT, V->range[0], V->range[1], V->n_rows, V->registration);
2212 	for (k = 0; k < V->n_rows; k++) coord[k] = gmt_M_col_to_x (API->GMT, k, V->range[0], V->range[1], inc, off, V->n_rows);
2213 	return (coord);
2214 }
2215 
2216 /*! . */
gmtapi_grdheader_to_matrixinfo(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,struct GMT_MATRIX * M_obj)2217 GMT_LOCAL void gmtapi_grdheader_to_matrixinfo (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, struct GMT_MATRIX *M_obj) {
2218 	/* Packs the necessary items of the grid header into the matrix parameters */
2219 	struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M_obj);
2220 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2221 	gmt_M_unused(GMT);
2222 
2223 	M_obj->n_columns = h->n_columns;
2224 	M_obj->n_rows = h->n_rows;
2225 	M_obj->registration = h->registration;
2226 	gmt_M_memcpy (M_obj->range, h->wesn, 4, double);
2227 	gmt_M_memcpy (M_obj->inc, h->inc, 2, double);
2228 	MH->grdtype = HH->grdtype;	/* Pass whatever we know about being geographic or not */
2229 }
2230 
2231 /*! . */
gmtapi_matrixinfo_to_grdheader(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,struct GMT_MATRIX * M_obj)2232 GMT_LOCAL void gmtapi_matrixinfo_to_grdheader (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, struct GMT_MATRIX *M_obj) {
2233 	/* Unpacks the necessary items into the grid header from the matrix parameters */
2234 	struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M_obj);
2235 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2236 	gmt_M_unused(GMT);
2237 
2238 	h->n_columns = (unsigned int)M_obj->n_columns;
2239 	h->n_rows = (unsigned int)M_obj->n_rows;
2240 	h->registration = M_obj->registration;
2241 	if (M_obj->range[XLO] == M_obj->range[XHI] && M_obj->range[YLO] == M_obj->range[YHI]) {	/* No range data given */
2242 		h->wesn[XHI] = h->n_columns - 1.0;
2243 		h->wesn[YHI] = h->n_rows - 1.0;
2244 		h->inc[GMT_X] = h->inc[GMT_Y] = 1.0;
2245 	}
2246 	else {
2247 		gmt_M_memcpy (h->wesn, M_obj->range, 4, double);
2248 		gmt_M_memcpy (h->inc, M_obj->inc, 2, double);
2249 	}
2250 	/* External matrices have no padding but the internal grid will */
2251 	/* Compute xy_off  */
2252 	h->xy_off = (h->registration == GMT_GRID_NODE_REG) ? 0.0 : 0.5;
2253 	HH->grdtype = MH->grdtype;	/* Pass whatever we know about being geographic or not */
2254 	gmt_set_grddim (GMT, h);
2255 }
2256 
2257 /*! . */
gmtapi_adjust_grdpadding(struct GMT_GRID_HEADER * h,unsigned int * pad)2258 GMT_LOCAL bool gmtapi_adjust_grdpadding (struct GMT_GRID_HEADER *h, unsigned int *pad) {
2259 	/* Compares current grid pad status to output pad requested.  If we need
2260 	 * to adjust a pad we return true here, otherwise false. */
2261 	unsigned int side;
2262 
2263 	for (side = 0; side < 4; side++) if (h->pad[side] != pad[side]) return (true);
2264 	return (false);
2265 }
2266 
2267 /*! . */
gmt_get_header(struct GMT_CTRL * GMT)2268 struct GMT_GRID_HEADER * gmt_get_header (struct GMT_CTRL *GMT) {
2269 	struct GMT_GRID_HEADER *h = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID_HEADER);
2270 	h->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID_HEADER_HIDDEN);
2271 	return (h);
2272 }
2273 
2274 /*! . */
gmtapi_set_grdarray_size(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,unsigned int mode,double * wesn)2275 GMT_LOCAL size_t gmtapi_set_grdarray_size (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, unsigned int mode, double *wesn) {
2276 	/* Determines size of grid given grid spacing and grid domain in h.
2277  	 * However, if wesn is given and not empty we compute size using the sub-region instead.
2278  	 * Finally, the current pad is used when calculating the grid size.
2279 	 * NOTE: This function leaves h unchanged by testing on a temporary header. */
2280 	struct GMT_GRID_HEADER *h_tmp = NULL;
2281 	size_t size;
2282 
2283 	/* Must duplicate header and possibly reset wesn, then set pad and recalculate all dims */
2284 	h_tmp = gmt_get_header (GMT);
2285 	gmt_copy_gridheader (GMT, h_tmp, h);
2286 	h_tmp->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the mode-to-be so that if complex the size is doubled */
2287 
2288 	if (!full_region (wesn)) {
2289 		gmt_M_memcpy (h_tmp->wesn, wesn, 4, double);    /* Use wesn instead of header info */
2290 		gmt_adjust_loose_wesn (GMT, wesn, h);	/* Subset requested; make sure wesn matches header spacing */
2291 		gmt_M_memcpy(h_tmp->wesn, wesn, 4, double);	    /* And update the eventually adjusted wesn */
2292 	}
2293 	gmt_M_grd_setpad (GMT, h_tmp, GMT->current.io.pad);	/* Use the system pad setting by default */
2294 	gmt_set_grddim (GMT, h_tmp);				/* Computes all integer parameters */
2295 	size = h_tmp->size;					/* This is the size needed to hold grid + padding */
2296 	gmt_M_free (GMT, h_tmp->hidden);
2297 	gmt_M_free (GMT, h_tmp);
2298 	return (size);
2299 }
2300 
2301 /*! . */
gmtapi_open_grd(struct GMT_CTRL * GMT,char * file,struct GMT_GRID * G,char mode,unsigned int access_mode)2302 GMT_LOCAL int gmtapi_open_grd (struct GMT_CTRL *GMT, char *file, struct GMT_GRID *G, char mode, unsigned int access_mode) {
2303 	/* Read or write the header structure and initialize row-by-row machinery for grids.
2304 	 * We fill the GMT_GRID_ROWBYROW structure with all the required information.
2305 	 * mode can be w or r.  Upper case W or R refers to headerless native
2306 	 * grid files.  The access_mode dictates if we automatically advance
2307 	 * row counter to next row after read/write or if we use the rec_no to seek
2308 	 * first.
2309 	 */
2310 
2311 	int r_w, err;
2312 	bool header = true, magic = true, alloc = false;
2313 	int cdf_mode[3] = { NC_NOWRITE, NC_WRITE, NC_WRITE};	/* These MUST be ints */
2314 	char *bin_mode[3] = { "rb", "rb+", "wb"};
2315 	char *fmt = NULL;
2316 	struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (G);
2317 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2318 	struct GMT_GRID_ROWBYROW *R = gmtapi_get_rbr_ptr (GH->extra);	/* Shorthand to row-by-row book-keeping structure */
2319 
2320 	if (mode == 'r' || mode == 'R') {	/* Open file for reading */
2321 		if (mode == 'R') {	/* File has no header; can only work if G->header has been set already, somehow */
2322 			header = false;
2323 			if (G->header->n_columns == 0 || G->header->n_rows == 0) {
2324 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to read header-less grid file %s without a preset header structure\n", file);
2325 				return (GMT_GRDIO_OPEN_FAILED);
2326 			}
2327 		}
2328 		r_w = 0;	mode = 'r';
2329 	}
2330 	else if (mode == 'W') {	/* Write headerless grid */
2331 		r_w = 2;	mode = 'w';
2332 		header = magic = false;
2333 	}
2334 	else {	/* Regular writing of grid with header */
2335 		r_w = 1;
2336 		magic = false;
2337 	}
2338 	if (header) {
2339 		if (mode == 'r' && !R->open)	/* First time reading the info */
2340 			gmtlib_read_grd_info (GMT, file, G->header);
2341 		else if (R->open)		/* Coming back to update the header */
2342 			gmt_update_grd_info (GMT, file, G->header);
2343 		else				/* First time writing the header */
2344 			gmtlib_write_grd_info (GMT, file, G->header);
2345 	}
2346 	else /* Fallback to existing header */
2347 		gmt_M_err_trap (gmt_grd_get_format (GMT, file, G->header, magic));
2348 	if (R->open) return (GMT_NOERROR);	/* Already set the first time */
2349 	fmt = GMT->session.grdformat[G->header->type];
2350 	if (fmt[0] == 'c') {		/* Open netCDF file, old format */
2351 		gmt_M_err_trap (gmt_nc_open (GMT, HH->name, cdf_mode[r_w], &R->fid));
2352 		R->edge[0] = G->header->n_columns;
2353 		R->start[0] = 0;
2354 		R->start[1] = 0;
2355 	}
2356 	else if (fmt[0] == 'n') {	/* Open netCDF file, COARDS-compliant format */
2357 		gmt_M_err_trap (gmt_nc_open (GMT, HH->name, cdf_mode[r_w], &R->fid));
2358 		R->edge[0] = 1;
2359 		R->edge[1] = G->header->n_columns;
2360 		R->start[0] = HH->row_order == k_nc_start_north ? 0 : G->header->n_rows-1;
2361 		R->start[1] = 0;
2362 	}
2363 	else {		/* Regular binary file with/w.o standard GMT header, or Sun rasterfile */
2364 		if (r_w == 0) {	/* Open for plain reading */
2365 			if ((R->fp = gmt_fopen (GMT, HH->name, bin_mode[0])) == NULL)
2366 				return (GMT_GRDIO_OPEN_FAILED);
2367 		}
2368 		else if ((R->fp = gmt_fopen (GMT, HH->name, bin_mode[r_w])) == NULL)
2369 			return (GMT_GRDIO_CREATE_FAILED);
2370 		/* Seek past the grid header, unless there is none */
2371 		if (header && fseek (R->fp, (off_t)GMT_GRID_HEADER_SIZE, SEEK_SET)) return (GMT_GRDIO_SEEK_FAILED);
2372 		alloc = (fmt[1] != GMT_GRD_FORMAT);	/* Only need to allocate the v_row array if grid is not grdfloat */
2373 #ifdef DEBUG
2374 		R->pos = ftell (R->fp);	/* Where we are */
2375 #endif
2376 	}
2377 
2378 	R->size = gmtlib_grd_data_size (GMT, G->header->type, &G->header->nan_value);
2379 	R->check = !isnan (G->header->nan_value);
2380 	R->open = true;
2381 
2382 	if (fmt[1] == 'm')	/* Bit mask */
2383 		R->n_byte = lrint (ceil (G->header->n_columns / 32.0)) * R->size;
2384 	else if (fmt[0] == 'r' && fmt[1] == 'b')	/* Sun Raster uses multiple of 2 bytes */
2385 		R->n_byte = lrint (ceil (G->header->n_columns / 2.0)) * 2 * R->size;
2386 	else	/* All other */
2387 		R->n_byte = G->header->n_columns * R->size;
2388 
2389 	if (alloc) R->v_row = gmt_M_memory (GMT, NULL, R->n_byte, char);
2390 
2391 	R->row = 0;
2392 	R->auto_advance = (access_mode & GMT_GRID_ROW_BY_ROW_MANUAL) ? false : true;	/* Read sequentially or random-access rows */
2393 	return (GMT_NOERROR);
2394 }
2395 
2396 /*! . */
gmtapi_update_txt_item(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,size_t length,char string[])2397 GMT_LOCAL void gmtapi_update_txt_item (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, size_t length, char string[]) {
2398 	/* Place desired text in string (fixed size array) which can hold up to length bytes */
2399 	size_t lim;
2400 	static char buffer[GMT_BUFSIZ];
2401 	char *txt = (mode & GMT_COMMENT_IS_OPTION) ? GMT_Create_Cmd (API, arg) : (char *)arg;
2402 	gmt_M_memset (buffer, GMT_BUFSIZ, char);	/* Start with a clean slate */
2403 	if ((mode & GMT_COMMENT_IS_OPTION) == 0 && (mode & GMT_COMMENT_IS_RESET) == 0 && string[0])
2404 		strncat (buffer, string, length-1);	/* Use old text if we are not resetting */
2405 	lim = length - strlen (buffer) - 1;	/* Remaining characters that we can use */
2406 	if (mode & GMT_COMMENT_IS_OPTION) {	/* Must start with module name since it is not part of the option args */
2407 		strncat (buffer, API->GMT->init.module_name, lim);
2408 		lim = length - strlen (buffer) - 1;	/* Remaining characters that we can use */
2409 		strncat (buffer, " ", lim);
2410 	}
2411 	lim = length - strlen (buffer) - 1;	/* Remaining characters that we can use */
2412 	strncat (buffer, txt, lim);		/* Append new text */
2413 	gmt_M_memset (string, length, char);	/* Wipe string completely */
2414 	strncpy (string, buffer, length);	/* Only copy over max length bytes */
2415 	if (mode & GMT_COMMENT_IS_OPTION) gmt_M_free (API->GMT, txt);
2416 }
2417 
2418 /*! . */
gmtapi_GI_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_GRID_HEADER * H)2419 GMT_LOCAL void gmtapi_GI_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_GRID_HEADER *H) {
2420 	/* Replace or Append either command or remark field with text or command-line options */
2421 	if (mode & GMT_COMMENT_IS_REMARK) 	gmtapi_update_txt_item (API, mode, arg, GMT_GRID_REMARK_LEN160,  H->remark);
2422 	else if (mode & GMT_COMMENT_IS_COMMAND) gmtapi_update_txt_item (API, mode, arg, GMT_GRID_COMMAND_LEN320, H->command);
2423 	else if (mode & GMT_COMMENT_IS_TITLE)   gmtapi_update_txt_item (API, mode, arg, GMT_GRID_TITLE_LEN80,    H->title);
2424 	else if (mode & GMT_COMMENT_IS_NAME_X)  gmtapi_update_txt_item (API, mode, arg, GMT_GRID_UNIT_LEN80,     H->x_units);
2425 	else if (mode & GMT_COMMENT_IS_NAME_Y)  gmtapi_update_txt_item (API, mode, arg, GMT_GRID_UNIT_LEN80,     H->y_units);
2426 	else if (mode & GMT_COMMENT_IS_NAME_Z)  gmtapi_update_txt_item (API, mode, arg, GMT_GRID_UNIT_LEN80,     H->z_units);
2427 }
2428 
2429 /*! Replace or Append either command or remark field with text or command-line options */
gmtapi_grid_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_GRID * G)2430 GMT_LOCAL void gmtapi_grid_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_GRID *G) {
2431 	gmtapi_GI_comment (API, mode, arg, G->header);
2432 }
2433 
gmtapi_cube_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_CUBE * U)2434 GMT_LOCAL void gmtapi_cube_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_CUBE *U) {
2435 	gmtapi_GI_comment (API, mode, arg, U->header);
2436 }
2437 
2438 /*! Update either command or remark field with text or command-line options */
gmtapi_image_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_IMAGE * I)2439 GMT_LOCAL void gmtapi_image_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_IMAGE *I) {
2440 	gmtapi_GI_comment (API, mode, arg, I->header);
2441 }
2442 
2443 /*! Update either command or remark field with text or command-line options */
gmtapi_vector_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_VECTOR * V)2444 GMT_LOCAL void gmtapi_vector_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_VECTOR *V) {
2445 	if (mode & GMT_COMMENT_IS_REMARK)  gmtapi_update_txt_item (API, mode, arg, GMT_GRID_REMARK_LEN160,  V->remark);
2446 	if (mode & GMT_COMMENT_IS_COMMAND) gmtapi_update_txt_item (API, mode, arg, GMT_GRID_COMMAND_LEN320, V->command);
2447 }
2448 
2449 /*! Update either command or remark field with text or command-line options */
gmtapi_matrix_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_MATRIX * M)2450 GMT_LOCAL void gmtapi_matrix_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_MATRIX *M) {
2451 	if (mode & GMT_COMMENT_IS_REMARK)  gmtapi_update_txt_item (API, mode, arg, GMT_GRID_REMARK_LEN160,  M->remark);
2452 	if (mode & GMT_COMMENT_IS_COMMAND) gmtapi_update_txt_item (API, mode, arg, GMT_GRID_COMMAND_LEN320, M->command);
2453 }
2454 
2455 /*! Update common.h's various text items; return 1 if successful else 0 */
gmtapi_add_comment(struct GMTAPI_CTRL * API,unsigned int mode,char * txt)2456 GMT_LOCAL int gmtapi_add_comment (struct GMTAPI_CTRL *API, unsigned int mode, char *txt) {
2457 	unsigned int k = 0;
2458 	struct GMT_COMMON *C = &API->GMT->common;	/* Short-hand to the common arg structs */
2459 
2460 	if (mode & GMT_COMMENT_IS_TITLE)  { gmt_M_str_free (C->h.title); C->h.title = strdup (txt); k++; }
2461 	if (mode & GMT_COMMENT_IS_REMARK) { gmt_M_str_free (C->h.remark); C->h.remark = strdup (txt); k++; }
2462 	if (mode & GMT_COMMENT_IS_COLNAMES) { gmt_M_str_free (C->h.colnames); C->h.colnames = strdup (txt); k++; }
2463 	return (k);	/* 1 if we did any of the three above; 0 otherwise */
2464 }
2465 
2466 /*! Append or replace data table headers with given text or command-line options */
gmtapi_dataset_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_DATASET * D)2467 GMT_LOCAL void gmtapi_dataset_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_DATASET *D) {
2468 	unsigned int tbl, k;
2469 	struct GMT_DATATABLE *T = NULL;
2470 	char *txt = gmtlib_create_header_item (API, mode, arg);
2471 
2472 	if (gmtapi_add_comment (API, mode, txt)) return;	/* Updated one -h item, or nothing */
2473 
2474 	if (D->table == NULL) {
2475 		GMT_Report (API, GMT_MSG_WARNING, "gmtapi_dataset_comment: Trying to access an empty D->table object\n");
2476 		return;
2477 	}
2478 
2479 	/* Here we process free-form comments; these go into the dataset's header structures */
2480 	for (tbl = 0; tbl < D->n_tables; tbl++) {	/* For each table in the dataset */
2481 		T = D->table[tbl];	/* Short-hand for this table */
2482 		if (mode & GMT_COMMENT_IS_RESET) {	/* Eliminate all existing headers */
2483 			for (k = 0; k < T->n_headers; k++) gmt_M_str_free (T->header[k]);
2484 			T->n_headers = 0;
2485 		}
2486 		T->header = gmt_M_memory (API->GMT, T->header, T->n_headers + 1, char *);
2487 		T->header[T->n_headers++] = strdup (txt);
2488 	}
2489 }
2490 
2491 /*! Append or replace CPT headers with given text or command-line options */
gmtapi_cpt_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_PALETTE * P)2492 GMT_LOCAL void gmtapi_cpt_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_PALETTE *P) {
2493 	unsigned int k;
2494 	char *txt = gmtlib_create_header_item (API, mode, arg);
2495 
2496 	if (!gmtapi_add_comment (API, mode, txt)) return;	/* Updated one -h item or nothing */
2497 
2498 	/* Here we process free-form comments; these go into the CPT's header structures */
2499 	if (mode & GMT_COMMENT_IS_RESET) {	/* Eliminate all existing headers */
2500 		for (k = 0; k < P->n_headers; k++) gmt_M_str_free (P->header[k]);
2501 		P->n_headers = 0;
2502 	}
2503 	P->header = gmt_M_memory (API->GMT, P->header, P->n_headers + 1, char *);
2504 	P->header[P->n_headers++] = strdup (txt);
2505 }
2506 
2507 /*! Append or replace Postscript container headers with given text or command-line options */
gmtapi_ps_comment(struct GMTAPI_CTRL * API,unsigned int mode,void * arg,struct GMT_POSTSCRIPT * P)2508 GMT_LOCAL void gmtapi_ps_comment (struct GMTAPI_CTRL *API, unsigned int mode, void *arg, struct GMT_POSTSCRIPT *P) {
2509 	unsigned int k;
2510 	char *txt = gmtlib_create_header_item (API, mode, arg);
2511 
2512 	if (!gmtapi_add_comment (API, mode, txt)) return;	/* Updated one -h item or nothing */
2513 
2514 	/* Here we process free-form comments; these go into the CPT's header structures */
2515 	if (mode & GMT_COMMENT_IS_RESET) {	/* Eliminate all existing headers */
2516 		for (k = 0; k < P->n_headers; k++) gmt_M_str_free (P->header[k]);
2517 		P->n_headers = 0;
2518 	}
2519 	P->header = gmt_M_memory (API->GMT, P->header, P->n_headers + 1, char *);
2520 	P->header[P->n_headers++] = strdup (txt);
2521 }
2522 
gmtapi_set_method(struct GMTAPI_DATA_OBJECT * S)2523 GMT_LOCAL unsigned int gmtapi_set_method (struct GMTAPI_DATA_OBJECT *S) {
2524 	/* Most objects have a one-to-one path but for vectors and matrices
2525 	 * we need to set the bit that correspond to their type */
2526 	unsigned int m;
2527 	if (S->method < GMT_IS_DUPLICATE) return S->method;
2528 	switch (S->actual_family) {
2529 		case GMT_IS_VECTOR: m = S->method | GMT_VIA_VECTOR; break;
2530 		case GMT_IS_MATRIX: m = S->method | GMT_VIA_MATRIX; break;
2531 		default: m = S->method;
2532 	}
2533 	return m;
2534 }
2535 
2536 /*! . */
gmtapi_next_io_source(struct GMTAPI_CTRL * API,unsigned int direction)2537 GMT_LOCAL int gmtapi_next_io_source (struct GMTAPI_CTRL *API, unsigned int direction) {
2538 	/* Get ready for the next source/destination (open file, initialize counters, etc.).
2539 	 * Note this is only a mechanism for dataset files where it is common
2540 	 * to give many files on the command line (e.g., *.txt) and we do rec-by-rec processing.
2541 	 * Not used by modules who read entire datasets in one go via GMT_{Read|Write}_Data,
2542 	 * such as grids, images, palettes, postscript, but also datasets and texsets when
2543 	 * GMT_Read_Data are used.  This section is strictly related to GMT_Get_Record. */
2544 
2545 	int *fd = NULL;	/* !!! This MUST be int* due to nature of UNIX system function */
2546 	unsigned int method, kind, first = 0;
2547 	static const char *dir[2] = {"from", "to"};
2548 	static const char *operation[3] = {"Reading", "Writing", "Appending"};
2549 	char *mode = NULL;
2550 	struct GMT_MATRIX *M_obj = NULL;
2551 	struct GMT_VECTOR *V_obj = NULL;
2552 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
2553 	struct GMT_CTRL *GMT = API->GMT;
2554 
2555 	S_obj = API->object[API->current_item[direction]];	/* For shorthand purposes only */
2556 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_next_io_source: Selected object %d\n", S_obj->ID);
2557 	gmt_M_memset (GMT->current.io.curr_pos[direction], 4U, int64_t);	/* Reset file, seg, point, header counters */
2558 	GMT->current.io.data_record_number_in_tbl[direction] = GMT->current.io.data_record_number_in_seg[direction] = 0;	/* Start at zero for new table */
2559 	if (direction == GMT_IN) {	/* Set reading mode */
2560 		mode = GMT->current.io.r_mode;
2561 		GMT->current.io.curr_pos[GMT_IN][GMT_SEG] = GMT_NOTSET;	/* First segment of input is set to -1 until first segment header have been dealt with */
2562 	}
2563 	else	/* Set writing mode (but could be changed to append if GMT_IS_FILE and filename starts with >) */
2564 		mode = GMT->current.io.w_mode;
2565 	S_obj->close_file = false;	/* Do not want to close file pointers passed to us unless WE open them below */
2566 	/* Either use binary n_columns settings or initialize to unknown if ascii input, i.e., GMT_MAX_COLUMNS */
2567 	S_obj->n_expected_fields = (GMT->common.b.ncol[direction]) ? GMT->common.b.ncol[direction] : GMT_MAX_COLUMNS;
2568 
2569 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use since may be MATRIX or VECTOR masquerading as DATASET */
2570 	switch (method) {	/* File, array, stream etc ? */
2571 		case GMT_IS_FILE:	/* Filename given; we must open the file here */
2572 			assert (S_obj->filename != NULL);
2573 			if (S_obj->family == GMT_IS_GRID || S_obj->family == GMT_IS_IMAGE) return (gmtlib_report_error (API, GMT_NOT_A_VALID_FAMILY));	/* Grids or images not allowed here */
2574 			if (direction == GMT_IN) {
2575 				first = gmt_download_file_if_not_found (API->GMT, S_obj->filename, 0);	/* Deal with downloadable GMT data sets first */
2576 			}
2577 			if (direction == GMT_OUT && S_obj->filename[0] == '>') {
2578 				mode = GMT->current.io.a_mode;	/* Must append to an existing file (we have already checked the file exists) */
2579 				first = 1;
2580 			}
2581 			if ((S_obj->fp = gmt_fopen (GMT, &(S_obj->filename[first]), mode)) == NULL) {	/* Trouble opening file */
2582 				GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s for %s\n", &(S_obj->filename[first]), GMT_direction[direction]);
2583 				return (GMT_ERROR_ON_FOPEN);
2584 			}
2585 			S_obj->close_file = true;	/* We do want to close files we are opening, but later */
2586 			strncpy (GMT->current.io.filename[direction], &(S_obj->filename[first]), PATH_MAX-1);
2587 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s file %s\n",
2588 				operation[direction+first], GMT_family[S_obj->family], dir[direction], &(S_obj->filename[first]));
2589 			if (gmt_M_binary_header (GMT, direction)) {
2590 				gmtlib_io_binary_header (GMT, S_obj->fp, direction);
2591 				GMT_Report (API, GMT_MSG_INFORMATION, "%s %d bytes of header %s binary file %s\n",
2592 					operation[direction], GMT->current.setting.io_n_header_items, dir[direction], &(S_obj->filename[first]));
2593 			}
2594 			break;
2595 
2596 		case GMT_IS_STREAM:	/* Given a stream; no need to open (or close) anything */
2597 #ifdef SET_IO_MODE
2598 			if (S_obj->family == GMT_IS_DATASET && S_obj->fp == GMT->session.std[direction])
2599 				gmt_setmode (GMT, (int)direction);	/* Windows may need to have its read mode changed from text to binary */
2600 #endif
2601 			kind = (S_obj->fp == GMT->session.std[direction]) ? 0 : 1;	/* For message only: 0 if stdin/out, 1 otherwise for user pointer */
2602 			snprintf (GMT->current.io.filename[direction], PATH_MAX-1, "<%s %s>", GMT_stream[kind], GMT_direction[direction]);
2603 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s %s %s stream\n",
2604 				operation[direction], GMT_family[S_obj->family], dir[direction], GMT_stream[kind], GMT_direction[direction]);
2605 			if (gmt_M_binary_header (GMT, direction)) {
2606 				gmtlib_io_binary_header (GMT, S_obj->fp, direction);
2607 				GMT_Report (API, GMT_MSG_INFORMATION, "%s %d bytes of header %s binary %s stream\n",
2608 					operation[direction], GMT->current.setting.io_n_header_items, dir[direction], GMT_stream[kind]);
2609 			}
2610 			break;
2611 
2612 		case GMT_IS_FDESC:	/* Given a pointer to a file handle; otherwise same as stream */
2613 			fd = (int *)S_obj->fp;	/* Extract the file handle integer */
2614 			if ((S_obj->fp = fdopen (*fd, mode)) == NULL) {	/* Reopen handle as stream */
2615 				GMT_Report (API, GMT_MSG_ERROR, "Unable to open file descriptor %d for %s\n", *fd, GMT_direction[direction]);
2616 				return (GMT_ERROR_ON_FDOPEN);
2617 			}
2618 			S_obj->method = S_obj->method - GMT_IS_FDESC + GMT_IS_STREAM;	/* Since fp now holds stream pointer an we have lost the handle */
2619 			kind = (S_obj->fp == GMT->session.std[direction]) ? 0 : 1;	/* For message only: 0 if stdin/out, 1 otherwise for user pointer */
2620 			snprintf (GMT->current.io.filename[direction], PATH_MAX-1, "<%s %s>", GMT_stream[kind], GMT_direction[direction]);
2621 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s %s %s stream via supplied file descriptor\n",
2622 				operation[direction], GMT_family[S_obj->family], dir[direction], GMT_stream[kind], GMT_direction[direction]);
2623 			if (gmt_M_binary_header (GMT, direction)) {
2624 				gmtlib_io_binary_header (GMT, S_obj->fp, direction);
2625 				GMT_Report (API, GMT_MSG_INFORMATION, "%s %d bytes of header %s binary %s stream via supplied file descriptor\n",
2626 					operation[direction], GMT->current.setting.io_n_header_items, dir[direction], GMT_stream[kind]);
2627 			}
2628 			break;
2629 
2630 	 	case GMT_IS_DUPLICATE:	/* Copy, nothing to do [PW: not tested] */
2631 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s memory copy supplied by pointer\n",
2632 				operation[direction], GMT_family[S_obj->family], dir[direction]);
2633 			break;
2634 
2635 	 	case GMT_IS_REFERENCE:	/* Reference, nothing to do [PW: not tested] */
2636 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s memory reference supplied by pointer\n",
2637 				operation[direction], GMT_family[S_obj->family], dir[direction]);
2638 			break;
2639 
2640 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* These 2 mean reading or writing a dataset record-by-record via a user matrix */
2641 		case GMT_IS_REFERENCE|GMT_VIA_MATRIX:
2642 			if (!(S_obj->family == GMT_IS_DATASET)) return (gmtlib_report_error (API, GMT_NOT_A_VALID_FAMILY));
2643 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s %s memory location via matrix\n",
2644 				operation[direction], GMT_family[S_obj->family], dir[direction], GMT_direction[direction]);
2645 			if (direction == GMT_IN) {	/* Hard-wired limits are passed in from calling program; for output we have nothing yet */
2646 				if ((M_obj = S_obj->resource) == NULL) {
2647 					GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_next_io_source got a matrix pointer that is NULL!!!\n");
2648 					return GMT_NOERROR;
2649 				}
2650 				S_obj->n_rows    = M_obj->n_rows;
2651 				S_obj->n_columns = M_obj->n_columns;
2652 				S_obj->rec = 0;	/* Start of this "file" */
2653 				GMT->common.b.ncol[direction] = M_obj->n_columns;	/* Basically doing binary i/o with specified number of columns */
2654 			}
2655 			GMT->common.b.active[direction] = true;	/* Basically, we are doing what GMT calls binary i/o since it is all in memory */
2656 			strcpy (GMT->current.io.filename[direction], "<matrix memory>");
2657 			break;
2658 
2659 		 case GMT_IS_DUPLICATE|GMT_VIA_VECTOR:	/* These 2 mean reading or writing a dataset record-by-record via user vector arrays */
2660 		 case GMT_IS_REFERENCE|GMT_VIA_VECTOR:
2661 			if (S_obj->family != GMT_IS_DATASET) return (gmtlib_report_error (API, GMT_NOT_A_VALID_FAMILY));
2662 			GMT_Report (API, GMT_MSG_INFORMATION, "%s %s %s %s memory location via vector\n",
2663 					operation[direction], GMT_family[S_obj->family], dir[direction], GMT_direction[direction]);
2664 			if (direction == GMT_IN) {	/* Hard-wired limits are passed in from calling program; for output we have nothing yet */
2665 				if ((V_obj = S_obj->resource) == NULL) {
2666 					GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_next_io_source got a vector pointer that is NULL!!!\n");
2667 					return GMT_NOERROR;
2668 				}
2669 				S_obj->n_rows    = V_obj->n_rows;
2670 				S_obj->n_columns = V_obj->n_columns;
2671 				S_obj->rec = 0;	/* Start of this "file" */
2672 				GMT->common.b.ncol[direction] = V_obj->n_columns;	/* Basically doing binary i/o with specified number of columns */
2673 			}
2674 			GMT->common.b.active[direction] = true;	/* Basically, we are doing what GMT calls binary i/o */
2675 			strcpy (GMT->current.io.filename[direction], "<vector memory>");
2676 			break;
2677 
2678 		default:
2679 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_next_io_source called with illegal method\n");
2680 			break;
2681 	}
2682 
2683 	/* A few things pertaining only to data/text tables */
2684 	GMT->current.io.rec_in_tbl_no = 0;	/* Start on new table */
2685 	if (direction == GMT_IN) API->current_get_obj = S_obj;
2686 	if (S_obj->geometry == GMT_IS_TEXT) {	/* Reading pure text, no coordinates */
2687 		S_obj->import = &gmtlib_ascii_textinput;
2688 		GMT->current.io.record.data = NULL;	/* Since there isn't any data */
2689 	}
2690 	else
2691 		S_obj->import = GMT->current.io.input;	/* import may point to ASCII or binary (if -b) input functions */
2692 
2693 	return (GMT_NOERROR);
2694 }
2695 
2696 /*! . */
gmtapi_next_data_object(struct GMTAPI_CTRL * API,enum GMT_enum_family family,enum GMT_enum_std direction)2697 GMT_LOCAL int gmtapi_next_data_object (struct GMTAPI_CTRL *API, enum GMT_enum_family family, enum GMT_enum_std direction) {
2698 	/* Sets up current_item to be the next unused item of the required direction; or return EOF.
2699 	 * When EOF is returned, API->current_item[direction] holds the last object ID used. */
2700 	bool found = false;
2701 	int item = API->current_item[direction] + 1;	/* Advance to next item, if it exists */
2702 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
2703 	while (item < (int)API->n_objects && !found) {
2704 		S_obj = API->object[item];	/* Current object in list */
2705 		if (S_obj && S_obj->selected && S_obj->status == GMT_IS_UNUSED && S_obj->direction == direction && S_obj->family == family)
2706 			found = true;	/* Got item that is selected and unused, has correct direction and family */
2707 		else
2708 			item++;	/* No, keep looking */
2709 	}
2710 	if (found) {	/* Update to use next item */
2711 		API->current_item[direction] = item;	/* The next item */
2712 		return (gmtapi_next_io_source (API, direction));	/* Initialize the next source/destination */
2713 	}
2714 	else
2715 		return (EOF);	/* No more objects available for this direction; return EOF */
2716 }
2717 
2718 /*! Hook object to end of linked list and assign unique id (> 0) which is returned */
gmtapi_add_data_object(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * object)2719 GMT_LOCAL int gmtapi_add_data_object (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *object) {
2720 	/* Find the first entry in the API->object array which is unoccupied, and if
2721 	 * they are all occupied then reallocate the array to make more space.
2722 	 * We thus find and return the lowest available ID. */
2723 	API->error = GMT_NOERROR;
2724 	API->n_objects++;		/* Must add one more entry to the tally */
2725 	if (API->n_objects == API->n_objects_alloc) {	/* Must allocate more space to hold all data descriptors */
2726 		size_t old_n_alloc = API->n_objects_alloc;
2727 		API->n_objects_alloc <<= 1;	/* Double it */
2728 		API->object = gmt_M_memory (API->GMT, API->object, API->n_objects_alloc, struct GMTAPI_DATA_OBJECT *);
2729 		if (!(API->object)) {		/* Failed to allocate more memory */
2730 			API->n_objects--;	/* Undo our premature increment */
2731 			return_value (API, GMT_MEMORY_ERROR, GMT_NOTSET);
2732 		}
2733 		else	/* Set new ones to NULL */
2734 			gmt_M_memset (&(API->object[old_n_alloc]), API->n_objects_alloc - old_n_alloc, struct GMTAPI_DATA_OBJECT *);
2735 	}
2736 	object->ID = API->unique_ID++;	/* Assign a unique object ID */
2737 	API->object[API->n_objects-1] = object;		/* Hook the current object onto the end of the list */
2738 
2739 	return (object->ID);
2740 }
2741 
2742 /*! Sanity check that geometry and family are compatible; note they may not be set (GMT_NOTSET) hence the use of signed ints */
gmtapi_validate_geometry(struct GMTAPI_CTRL * API,int family,int geometry)2743 GMT_LOCAL bool gmtapi_validate_geometry (struct GMTAPI_CTRL *API, int family, int geometry) {
2744 	bool problem = false;
2745 	gmt_M_unused(API);
2746 	if (geometry == GMT_NOTSET || family == GMT_NOTSET) return false;	/* No errors if nothing to check yet */
2747 	switch (family) {
2748 		case GMT_IS_DATASET:     if (!(geometry == GMT_IS_NONE || geometry == GMT_IS_TEXT || (geometry & GMT_IS_PLP))) problem = true; break;	/* Datasets can hold many things... */
2749 		case GMT_IS_GRID:        if (geometry != GMT_IS_SURFACE) problem = true;    break;	/* Only surface is valid */
2750 		case GMT_IS_IMAGE:       if (geometry != GMT_IS_SURFACE) problem = true;    break;	/* Only surface is valid */
2751 		case GMT_IS_PALETTE:     if (geometry != GMT_IS_NONE) problem = true;       break;	/* Only text is valid */
2752 		case GMT_IS_POSTSCRIPT:  if (geometry != GMT_IS_NONE) problem = true;       break;	/* Only text is valid */
2753 		case GMT_IS_CUBE:        if (geometry != GMT_IS_VOLUME) problem = true;     break;	/* Only volume is valid */
2754 		case GMT_IS_VECTOR:      if ((geometry & GMT_IS_PLP) == 0) problem = true;  break; 	/* Must be one of those three */
2755 		case GMT_IS_MATRIX:      if (geometry == GMT_IS_NONE) problem = true;       break;	/* Matrix can hold surfaces or DATASETs */
2756 		case GMT_IS_COORD:       if (geometry != GMT_IS_NONE) problem = true;       break;	/* Only text is valid */
2757 	}
2758 	return (problem);
2759 }
2760 
2761 /*! . */
gmtapi_decode_id(const char * filename)2762 GMT_LOCAL int gmtapi_decode_id (const char *filename) {
2763 	/* Checking if filename contains a name with embedded GMTAPI Object ID.
2764 	 * If found we return the ID, otherwise we return GMT_NOTSET.
2765  	*/
2766 	int object_ID = GMT_NOTSET;
2767 
2768 	if (gmt_M_file_is_memory (filename)) {	/* Passing ID of a registered object */
2769 		if (sscanf (&filename[GMTAPI_OBJECT_ID_START], "%d", &object_ID) != 1) return (GMT_NOTSET);	/* Get the object ID unless we fail scanning */
2770 	}
2771 	return (object_ID);	/* Returns GMT_NOTSET if no embedded ID was found */
2772 }
2773 
2774 /*! . */
gmtlib_data_is_geographic(struct GMTAPI_CTRL * API,const char * file)2775 bool gmtlib_data_is_geographic (struct GMTAPI_CTRL *API, const char *file) {
2776 	/* Here file is a memory file. If dataset, grid, image, matrix, or vector we determine if geographic */
2777 	bool geo = false;	/* Default is Cartesian */
2778 	int object_ID, item;
2779 	struct GMT_DATASET *D;
2780 	struct GMT_GRID *G;
2781 	struct GMT_IMAGE *I;
2782 	struct GMT_MATRIX *M;
2783 	struct GMT_VECTOR *V;
2784 	struct GMT_DATASET_HIDDEN *HD;
2785 	struct GMT_GRID_HEADER_HIDDEN *HH;
2786 	struct GMT_MATRIX_HIDDEN *HM;
2787 	struct GMT_VECTOR_HIDDEN *HV;
2788 
2789 	if (!gmt_M_file_is_memory (file)) return false;	/* Not a memory file */
2790 
2791 	if (strchr ("DGIMV", file[GMTAPI_OBJECT_FAMILY_START]) == NULL) return false;	/* Not geographic since not dataset, grid, image, vector or matrix */
2792 
2793 	/* Must get pointer to the hidden structure */
2794 	if ((object_ID = gmtapi_decode_id (file)) == GMT_NOTSET)
2795 		return false;	/* Should not happen but return as not geographic */
2796 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET)
2797 		return false;	/* Should not happen but return as not geographic */
2798 
2799 	switch (file[GMTAPI_OBJECT_FAMILY_START]) {
2800 		case 'D':	/* Memory dataset */
2801 			D = gmtapi_get_dataset_data (API->object[item]->resource);
2802 			HD = gmt_get_DD_hidden (D);
2803 			if (HD->geographic) geo = true;
2804 			break;
2805 		case 'G':	/* Memory grid */
2806 			G = gmtapi_get_grid_data (API->object[item]->resource);
2807 			HH = gmt_get_H_hidden (G->header);
2808 			if (HH->grdtype > GMT_GRID_CARTESIAN) geo = true;
2809 			break;
2810 		case 'I':	/* Memory image */
2811 			I = gmtapi_get_image_data (API->object[item]->resource);
2812 			HH = gmt_get_H_hidden (I->header);
2813 			if (HH->grdtype > GMT_GRID_CARTESIAN) geo = true;
2814 			break;
2815 		case 'M':	/* Memory matrix */
2816 			M = gmtapi_get_matrix_data (API->object[item]->resource);
2817 			HM = gmt_get_M_hidden (M);
2818 			if (HM->grdtype > GMT_GRID_CARTESIAN) geo = true;
2819 			break;
2820 		case 'V':	/* Memory vector */
2821 			V = gmtapi_get_vector_data (API->object[item]->resource);
2822 			HV = gmt_get_V_hidden (V);
2823 			if (HV->geographic) geo = true;
2824 			break;
2825 		default: /* For Coverity mostly */
2826 			break;
2827 	}
2828 	return (geo);
2829 }
2830 
2831 /*! . */
gmtapi_expand_headerpad(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,double * new_wesn,unsigned int * orig_pad,double * orig_wesn)2832 GMT_LOCAL unsigned int gmtapi_expand_headerpad (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, double *new_wesn, unsigned int *orig_pad, double *orig_wesn) {
2833 	unsigned int tmp_pad[4] = {0, 0, 0, 0}, delta[4] = {0, 0, 0, 0}, k = 0;
2834 	/* When using subset with memory grids we cannot actually cut the grid but instead
2835 	 * must temporarily change the pad to match the desired inner region wesn.  This means
2836 	 * the pads will change and can be quite large. */
2837 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2838 
2839 	gmt_M_memcpy (tmp_pad, h->pad, 4, unsigned int);    /* Initialize new pad to the original pad */
2840 	/* First determine which (and how many, k) of the 4 new boundaries are inside the original region and update the padding: */
2841 	if (new_wesn[XLO] > h->wesn[XLO]) k++, tmp_pad[XLO] += urint ((new_wesn[XLO] - h->wesn[XLO]) * HH->r_inc[GMT_X]);
2842 	if (new_wesn[XHI] < h->wesn[XHI]) k++, tmp_pad[XHI] += urint ((h->wesn[XHI] - new_wesn[XHI]) * HH->r_inc[GMT_X]);
2843 	if (new_wesn[YLO] > h->wesn[YLO]) k++, tmp_pad[YLO] += urint ((new_wesn[YLO] - h->wesn[YLO]) * HH->r_inc[GMT_Y]);
2844 	if (new_wesn[YHI] < h->wesn[YHI]) k++, tmp_pad[YHI] += urint ((h->wesn[YHI] - new_wesn[YHI]) * HH->r_inc[GMT_Y]);
2845 	if (k) {    /* Yes, pad will change since region is different for k of the 4 sides */
2846 		for (k = 0; k < 4; k++) delta[k] = tmp_pad[k] - h->pad[k];  /* Columns with data being passed as padding */
2847 		gmt_M_memcpy (orig_pad, h->pad, 4, unsigned int);   /* Place the original grid pad in the provided array */
2848 		gmt_M_memcpy (orig_wesn, h->wesn, 4, double);       /* Place the original grid wesn in the provided array */
2849 		gmt_M_memcpy (h->pad, tmp_pad, 4, unsigned int);    /* Place the new pad in the grid header */
2850 		gmt_M_memcpy (h->wesn, new_wesn, 4, double);        /* Place the new wesn in the grid header */
2851 		gmt_set_grddim (GMT, h);    /* This recomputes n_columns|n_rows. */
2852 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtapi_expand_headerpad: %d pad sides changed. Now %u/%u/%u/%u\n",
2853 			k, h->pad[XLO], h->pad[XHI], h->pad[YLO], h->pad[YHI]);
2854 		for (k = 0; k < 4; k++) {   /* If pad now contains data then change the BC to reflect this */
2855 			if (delta[k] >= orig_pad[k]) HH->BC[k] = GMT_BC_IS_DATA;
2856 		}
2857 	}
2858     else
2859         GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtapi_expand_headerpad: No pad adjustment needed\n");
2860     return k;
2861 }
2862 
2863 /*! . */
gmtapi_update_grid_minmax(struct GMT_CTRL * GMT,struct GMT_GRID * G)2864 GMT_LOCAL void gmtapi_update_grid_minmax (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
2865 	/* Update grid header z_min/z_max to reflect the range within the subset */
2866 	uint64_t ij;
2867 	unsigned int row, col;
2868 	struct GMT_GRID_HEADER *h = G->header;
2869 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2870 	gmt_M_unused (GMT);
2871 
2872 	h->z_min = +DBL_MAX;    h->z_max = -DBL_MAX;    /* Reset the min/max before we search */
2873 	HH->has_NaNs = GMT_GRID_NO_NANS;    /* We are about to check for NaNs and if none are found we retain 1, else 2 */
2874 	gmt_M_grd_loop (GMT, G, row, col, ij) {
2875 		if (gmt_M_is_fnan (G->data[ij]))
2876 			HH->has_NaNs = GMT_GRID_HAS_NANS;
2877 		else {
2878 			h->z_min = MIN (h->z_min, G->data[ij]);
2879 			h->z_max = MAX (h->z_max, G->data[ij]);
2880 		}
2881 	}
2882 }
2883 
2884 /*! . */
gmtapi_update_cube_minmax(struct GMT_CTRL * GMT,struct GMT_CUBE * U)2885 GMT_LOCAL void gmtapi_update_cube_minmax (struct GMT_CTRL *GMT, struct GMT_CUBE *U) {
2886 	/* Update cube header z_min/z_max to reflect the range within the subset */
2887 	uint64_t ij, node, here = 0;
2888 	unsigned int k, row, col;
2889 	struct GMT_GRID_HEADER *h = U->header;
2890 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2891 	gmt_M_unused (GMT);
2892 
2893 	h->z_min = +DBL_MAX;    h->z_max = -DBL_MAX;    /* Reset the min/max before we search */
2894 	HH->has_NaNs = GMT_GRID_NO_NANS;    /* We are about to check for NaNs and if none are found we retain 1, else 2 */
2895 	for (k = 0; k < h->n_bands; k++) {
2896 		gmt_M_grd_loop (GMT, U, row, col, ij) {
2897 			node = ij + here;
2898 			if (gmt_M_is_fnan (U->data[node]))
2899 				HH->has_NaNs = GMT_GRID_HAS_NANS;
2900 			else {
2901 				h->z_min = MIN (h->z_min, U->data[node]);
2902 				h->z_max = MAX (h->z_max, U->data[node]);
2903 			}
2904 		}
2905 		here += h->size;
2906 	}
2907 }
2908 
2909 /*! . */
gmtapi_contract_headerpad(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,unsigned int * orig_pad,double * orig_wesn)2910 GMT_LOCAL void gmtapi_contract_headerpad (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, unsigned int *orig_pad, double *orig_wesn) {
2911 	/* When using subset with memory grids we must reset the pad back to the original setting when done */
2912 	if (h == NULL) return;	/* Nothing for us to work with */
2913 	gmt_M_memcpy (h->pad, orig_pad, 4, unsigned int);	/* Place the original pad in the grid header */
2914 	gmt_M_memcpy (h->wesn, orig_wesn, 4, double);		/* Place the orig_pad wesn in the grid header */
2915 	gmt_set_grddim (GMT, h);	/* This recomputes n_columns|n_rows. */
2916 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtapi_contract_headerpad: Pad and wesn reset to original values\n");
2917 }
2918 
2919 /*! . */
gmtapi_contract_pad(struct GMT_CTRL * GMT,void * object,int family,unsigned int * orig_pad,double * orig_wesn)2920 GMT_LOCAL void gmtapi_contract_pad (struct GMT_CTRL *GMT, void *object, int family, unsigned int *orig_pad, double *orig_wesn) {
2921 	/* When using subset with memory grids we must reset the pad back to the original setting when done */
2922 	struct GMT_GRID_HEADER *h = NULL;
2923 	if (family == GMT_IS_GRID) {
2924 		struct GMT_GRID *G = gmtapi_get_grid_data (object);
2925 		if (G) h = G->header;
2926 	}
2927 	else if (family == GMT_IS_IMAGE) {
2928 		struct GMT_IMAGE *I = gmtapi_get_image_data (object);
2929 		if (I) h = I->header;
2930 	}
2931 	gmtapi_contract_headerpad (GMT, h, orig_pad, orig_wesn);
2932 }
2933 
2934 /*! . */
gmtapi_get_object(struct GMTAPI_CTRL * API,int sfamily,void * ptr)2935 GMT_LOCAL int gmtapi_get_object (struct GMTAPI_CTRL *API, int sfamily, void *ptr) {
2936 	/* Returns the ID of the first object whose resource pointer matches ptr.
2937 	 * Unless family is GMT_NOTSET the object must be of the specified family.
2938 	 */
2939 	unsigned int item;
2940 	enum GMT_enum_family family = GMT_NOTSET;
2941 	int object_ID = GMT_NOTSET;	/* Not found yet */
2942 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
2943 
2944 	if (sfamily != GMT_NOTSET) family = sfamily;
2945 	for (item = 0; object_ID == GMT_NOTSET && item < API->n_objects; item++) {	/* Loop over all objects */
2946 		if ((S_obj = API->object[item]) == NULL) continue;	/* Skip freed objects */
2947 		if (S_obj->resource == NULL) continue;	/* No resource pointer */
2948 		if (sfamily != GMT_NOTSET && S_obj->family != family) continue;	/* Not the right family */
2949 		if (S_obj->resource == ptr && object_ID == GMT_NOTSET) object_ID = S_obj->ID;	/* Found a matching data pointer */
2950 	}
2951 	return (object_ID);	/* Return ID or GMT_NOTSET if not found */
2952 }
2953 
2954 /*! . */
gmtapi_pass_object(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * object,unsigned int family,unsigned int mode,double * wesn)2955 GMT_LOCAL void * gmtapi_pass_object (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *object, unsigned int family, unsigned int mode, double *wesn) {
2956 	/* Passes back the input object pointer after possibly performing some minor adjustments to metadata.
2957 	 * For grids and images we must worry about possible subset requests */
2958 	void *data = object->resource;
2959 	struct GMT_GRID    *G = NULL;
2960 	struct GMT_IMAGE   *I = NULL;
2961 	struct GMT_DATASET *D = NULL;
2962 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
2963 
2964 	if (object->resource == NULL)
2965 		GMT_Report (API, GMT_MSG_ERROR, "gmtapi_pass_object given a NULL resource!\n");
2966 
2967 	switch (family) {	/* Do family-specific prepping before passing back the input object */
2968 		case GMT_IS_PALETTE:	/* Make sure the hidden support arrays etc. have been initialized as external interfaces may not care */
2969 			if (data) gmtlib_init_cpt (API->GMT, data);
2970 			break;
2971 		case GMT_IS_GRID:	/* Grids need to update the grdtype setting, possibly rotate geographic grids, and maybe deal with subsets */
2972 			G = gmtapi_get_grid_data (data);	/* Get the right grid pointer */
2973 			HH = gmt_get_H_hidden (G->header);
2974 			gmtlib_grd_get_units (API->GMT, G->header);	/* Set the unit names */
2975 			HH->grdtype = gmtlib_get_grdtype (API->GMT, GMT_IN, G->header);
2976 			if (wesn && G->data) {	/* Subset or global rotation was requested */
2977 				if (gmt_grd_is_global (API->GMT, G->header)) {	/* May have to rotate a geographic grid since we are not reading from file this time */
2978 					double shift_amount = wesn[XLO] - G->header->wesn[XLO];
2979 					if (fabs (shift_amount) >= G->header->inc[GMT_X]) {	/* Must do it */
2980 						GMT_Report (API, GMT_MSG_DEBUG, "Shifting longitudes in grid by %g degrees to fit -R\n", shift_amount);
2981 						gmt_grd_shift (API->GMT, G, shift_amount);	/* In-memory rotation */
2982 					}
2983 				}
2984 				else if (object->region) {	/* Possibly adjust the pad so inner region matches requested wesn */
2985 					/* NOTE: This assumes the memory cannot be adjusted. Probably should distinguish between GMT_IS_REFERENCE and GMT_IS_DUPLICATE
2986 					 * and offer different behavior.  As it is we assume read-only grids */
2987 					if (object->reset_pad) {	/* First undo any prior sub-region used with this memory grid */
2988 						gmtapi_contract_headerpad (API->GMT, G->header, object->orig_pad, object->orig_wesn);
2989 						object->reset_pad = HH->reset_pad = 0;
2990 					}
2991 					/* Then apply the new pad adjustment.  Basically we cannot mess with the data so we change what constitute the pad */
2992 					if (gmtapi_expand_headerpad (API->GMT, G->header, object->wesn, object->orig_pad, object->orig_wesn)) {
2993 						object->reset_pad = HH->reset_pad = 1;
2994 						gmtapi_update_grid_minmax (API->GMT, G);	/* Update z-range */
2995 					}
2996 				}
2997 			}
2998 			if (mode & GMT_CONTAINER_ONLY) break;	/* No grid yet */
2999 			gmt_BC_init (API->GMT, G->header);	/* Initialize grid interpolation and boundary condition parameters */
3000 			if (gmt_M_err_pass (API->GMT, gmt_grd_BC_set (API->GMT, G, GMT_IN), "Grid memory"))
3001 				return_null (API, GMT_GRID_BC_ERROR);	/* Failed to set boundary conditions */
3002 			break;
3003 		case GMT_IS_IMAGE:	/* Images need to update the grdtype setting, possibly rotate geographic grids, and maybe deal with subsets */
3004 			I = gmtapi_get_image_data (data);	/* Get the right image pointer */
3005 			HH = gmt_get_H_hidden (I->header);
3006 			gmtlib_grd_get_units (API->GMT, I->header);	/* Set the unit names */
3007 			HH->grdtype = gmtlib_get_grdtype (API->GMT, GMT_IN, I->header);
3008 			if (wesn && I->data) {	/* Subset or global rotation was requested */
3009 				if (gmt_grd_is_global (API->GMT, I->header)) {	/* May have to rotate geographic grid since we are not reading from file here */
3010 					double shift_amount = wesn[XLO] - I->header->wesn[XLO];
3011 					if (fabs (shift_amount) >= I->header->inc[GMT_X]) {	/* Must do it */
3012 						GMT_Report (API, GMT_MSG_WARNING, "Longitudinal roll for images not implemented yet\n");
3013 #if 0
3014 						GMT_Report (API, GMT_MSG_DEBUG, "Shifting longitudes in grid by %g degrees to fit -R\n", shift_amount);
3015 						gmt_grd_shift (API->GMT, I, shift_amount);
3016 #endif
3017 					}
3018 				}
3019 				else if (object->region) {	/* Possibly adjust the pad so inner region matches wesn */
3020 					/* NOTE: This assumes the memory cannot be adjusted. Probably should distinaguish between GMT_IS_REFERENCE and GMT_IS_DUPLICATE
3021 					 * and offer different behavior.  As it is we assume read-only images */
3022 					if (object->reset_pad) {	/* First undo a prior sub-region used with this memory grid */
3023 						gmtapi_contract_headerpad (API->GMT, I->header, object->orig_pad, object->orig_wesn);
3024 						object->reset_pad = HH->reset_pad = 0;
3025 					}
3026 					/* Then apply the new pad adjustment.  Basically we cannot mess with the data so we change what constitute the pad */
3027 					if (gmtapi_expand_headerpad (API->GMT, I->header, object->wesn, object->orig_pad, object->orig_wesn))
3028 						object->reset_pad = HH->reset_pad = 1;
3029 				}
3030 			}
3031 			if (mode & GMT_CONTAINER_ONLY) break;	/* No image yet */
3032 			gmt_BC_init (API->GMT, I->header);	/* Initialize image interpolation and boundary condition parameters */
3033 			if (gmt_M_err_pass (API->GMT, gmtlib_image_BC_set (API->GMT, I), "Image memory"))
3034 				return_null (API, GMT_IMAGE_BC_ERROR);	/* Set boundary conditions */
3035 			break;
3036 		case GMT_IS_DATASET:	/* Just make sure the min/max values are updated for tables and dataset  */
3037 		 	D = gmtapi_get_dataset_data (data);	/* Get the right dataset pointer */
3038 			gmt_set_dataset_minmax (API->GMT, D);	/* Set the min/max values for the entire dataset */
3039 			break;
3040 		default:	/* Nothing yet for other types */
3041 			break;
3042 	}
3043 	return (data);
3044 }
3045 
3046 /*! . */
gmtapi_get_object_id_from_data_ptr(struct GMTAPI_CTRL * API,void * ptr)3047 GMT_LOCAL int gmtapi_get_object_id_from_data_ptr (struct GMTAPI_CTRL *API, void *ptr) {
3048 	/* Returns the ID of the first object whose data pointer matches *ptr.
3049  	 * This is necessary since many objects may have the same pointer
3050 	 * but we only want to destroy the memory once.  This function is
3051 	 * only used in GMT_Destroy_Data and gmt_is_an_object.
3052 	 */
3053 	unsigned int item;
3054 	int object_ID = GMT_NOTSET;	/* Not found yet */
3055 	void *data = NULL;
3056 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3057 
3058 	for (item = 0; object_ID == GMT_NOTSET && item < API->n_objects; item++) {	/* Loop over all objects */
3059 		if ((S_obj = API->object[item]) == NULL) continue;	/* Skip freed objects */
3060 		data = gmtapi_return_address (ptr, S_obj->family);	/* Get void* pointer to resource of this family */
3061 		/* Try to look for either data or resource pointers since Open_VirtualFile shuffles these two and Destroy needs to find them even if the
3062 		 * function level test will tell it not to free anything */
3063 		if (object_ID == GMT_NOTSET && S_obj->resource == data) object_ID = S_obj->ID;	/* Found a matching data pointer */
3064 	}
3065 	return (object_ID);	/* Return ID or GMT_NOTSET if not found */
3066 }
3067 
3068 /*! . */
gmtapi_destroy_grid(struct GMTAPI_CTRL * API,struct GMT_GRID ** G_obj)3069 GMT_LOCAL int gmtapi_destroy_grid (struct GMTAPI_CTRL *API, struct GMT_GRID **G_obj) {
3070 	/* Delete the given grid resource. */
3071 	struct GMT_GRID_HIDDEN *GH = NULL;
3072 	if (!(*G_obj)) {	/* Probably not a good sign */
3073 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_grid: Passed NULL pointer - skipped\n");
3074 		return (GMT_PTR_IS_NULL);
3075 	}
3076 	GH = gmt_get_G_hidden (*G_obj);
3077 	if (GH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3078 
3079 	gmt_free_grid (API->GMT, G_obj, true);
3080 	return GMT_NOERROR;
3081 }
3082 
3083 /*! . */
gmtapi_destroy_cube(struct GMTAPI_CTRL * API,struct GMT_CUBE ** U_obj)3084 GMT_LOCAL int gmtapi_destroy_cube (struct GMTAPI_CTRL *API, struct GMT_CUBE **U_obj) {
3085 	/* Delete the given grid resource. */
3086 	struct GMT_CUBE_HIDDEN *UH = NULL;
3087 	if (!(*U_obj)) {	/* Probably not a good sign */
3088 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_cube: Passed NULL pointer - skipped\n");
3089 		return (GMT_PTR_IS_NULL);
3090 	}
3091 	UH = gmt_get_U_hidden (*U_obj);
3092 	if (UH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3093 
3094 	gmtlib_free_cube (API->GMT, U_obj, true);
3095 	return GMT_NOERROR;
3096 }
3097 
3098 /*! . */
gmtapi_destroy_dataset(struct GMTAPI_CTRL * API,struct GMT_DATASET ** D_obj)3099 GMT_LOCAL int gmtapi_destroy_dataset (struct GMTAPI_CTRL *API, struct GMT_DATASET **D_obj) {
3100 	/* Delete the given dataset resource. */
3101 	struct GMT_DATASET_HIDDEN *DH = NULL;
3102 
3103 	if (!(*D_obj)) {	/* Probably not a good sign */
3104 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_dataset: Passed NULL pointer - skipped\n");
3105 		return (GMT_PTR_IS_NULL);
3106 	}
3107 	DH = gmt_get_DD_hidden (*D_obj);
3108 
3109 	if (DH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3110 
3111 	gmt_free_dataset (API->GMT, D_obj);
3112 	return GMT_NOERROR;
3113 }
3114 
3115 /*! . */
gmtapi_destroy_palette(struct GMTAPI_CTRL * API,struct GMT_PALETTE ** P_obj)3116 GMT_LOCAL int gmtapi_destroy_palette (struct GMTAPI_CTRL *API, struct GMT_PALETTE **P_obj) {
3117 	/* Delete the given CPT resource. */
3118 	struct GMT_PALETTE_HIDDEN *PH = NULL;
3119 
3120 	if (!(*P_obj)) {	/* Probably not a good sign */
3121 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_palette: Passed NULL pointer - skipped\n");
3122 		return (GMT_PTR_IS_NULL);
3123 	}
3124 	PH = gmt_get_C_hidden (*P_obj);
3125 	if (PH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3126 
3127 	gmtlib_free_palette (API->GMT, P_obj);
3128 	return GMT_NOERROR;
3129 }
3130 
3131 /*! . */
gmtapi_destroy_postscript(struct GMTAPI_CTRL * API,struct GMT_POSTSCRIPT ** P_obj)3132 GMT_LOCAL int gmtapi_destroy_postscript (struct GMTAPI_CTRL *API, struct GMT_POSTSCRIPT **P_obj) {
3133 	/* Delete the given GMT_POSTSCRIPT resource. */
3134 	struct GMT_POSTSCRIPT_HIDDEN *PH = NULL;
3135 	if (!(*P_obj)) {	/* Probably not a good sign */
3136 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_postscript: Passed NULL pointer - skipped\n");
3137 		return (GMT_PTR_IS_NULL);
3138 	}
3139 	PH = gmt_get_P_hidden (*P_obj);
3140 	if (PH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3141 
3142 	gmtlib_free_ps (API->GMT, P_obj);
3143 	return GMT_NOERROR;
3144 }
3145 
3146 /*! . */
gmtapi_destroy_matrix(struct GMTAPI_CTRL * API,struct GMT_MATRIX ** M_obj)3147 GMT_LOCAL int gmtapi_destroy_matrix (struct GMTAPI_CTRL *API, struct GMT_MATRIX **M_obj) {
3148 	/* Delete the given Matrix resource. */
3149 	struct GMT_MATRIX_HIDDEN *MH = NULL;
3150 	if (!(*M_obj)) {	/* Probably not a good sign */
3151 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_matrix: Passed NULL pointer - skipped\n");
3152 		return (GMT_PTR_IS_NULL);
3153 	}
3154 	MH = gmt_get_M_hidden (*M_obj);
3155 	if (MH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3156 
3157 	gmtlib_free_matrix (API->GMT, M_obj, true);
3158 	return GMT_NOERROR;
3159 }
3160 
3161 /*! . */
gmtapi_destroy_vector(struct GMTAPI_CTRL * API,struct GMT_VECTOR ** V_obj)3162 GMT_LOCAL int gmtapi_destroy_vector (struct GMTAPI_CTRL *API, struct GMT_VECTOR **V_obj) {
3163 	/* Delete the given Matrix resource. */
3164 	struct GMT_VECTOR_HIDDEN *VH = NULL;
3165 	if (!(*V_obj)) {	/* Probably not a good sign */
3166 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_vector: Passed NULL pointer - skipped\n");
3167 		return (GMT_PTR_IS_NULL);
3168 	}
3169 	VH = gmt_get_V_hidden (*V_obj);
3170 	if (VH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
3171 
3172 	gmt_free_vector (API->GMT, V_obj, true);
3173 	return GMT_NOERROR;
3174 }
3175 
3176 /*! . */
gmt_is_an_object(struct GMT_CTRL * GMT,void * ptr)3177 bool gmt_is_an_object (struct GMT_CTRL *GMT, void *ptr) {
3178 	/* Needed by g*math.c so exported as part of the gmt_dev library */
3179 	return (gmtapi_get_object_id_from_data_ptr (GMT->parent, ptr) == GMT_NOTSET) ? false : true;
3180 }
3181 
3182 /*! Determine if resource is a filename that has already been registered */
3183 #if 0
3184 GMT_LOCAL int gmtapi_memory_registered (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int direction, void *resource) {
3185 	int object_ID = 0, item;
3186 	unsigned int module_input = (family & GMT_VIA_MODULE_INPUT);	/* Are we dealing with a resource that is a module input? */
3187 	family -= module_input;
3188 
3189 	if (family == GMT_IS_COORD) return (GMT_NOTSET);	/* Coordinate arrays cannot be a registered memory resource */
3190 	if ((object_ID = gmtapi_decode_id (resource)) == GMT_NOTSET) return (GMT_NOTSET);	/* Not a registered resource */
3191 	if ((item = gmtlib_validate_id (API, family, object_ID, direction, GMT_NOTSET)) == GMT_NOTSET) return (GMT_NOTSET);	/* Not the right attributes */
3192 	if (module_input && direction == GMT_IN) API->object[item]->module_input = true;	/* Flag this object as a module input resource */
3193 	return (object_ID);	/* resource is a registered and valid item */
3194 }
3195 #endif
3196 
3197 /*! Determine if resource is a filename that has already been registered */
gmtapi_memory_registered(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int direction,char * filename)3198 GMT_LOCAL int gmtapi_memory_registered (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int direction, char *filename) {
3199 	char SP, D, F, A, G, M;
3200 	int k, object_ID;
3201 	gmt_M_unused(family);
3202 	if (!gmt_M_file_is_memory (filename)) return GMT_NOTSET;	/* If not a memory reference then there is no ID etc */
3203 	/* Name template: @GMTAPI@-S-D-F-A-G-M-###### where # is the 6-digit integer object code.
3204 	 * S stands for P(rimary) or S(econdary) input or output object (command line is primary, files via options are secondary).
3205 	 * D stands for Direction and is either I(n) or O(ut).
3206 	 * F stands for Family and is one of D(ataset), G(rid), I(mage), C(PT), X(PostScript), M(atrix), V(ector), U(ndefined).
3207 	 * A stands for Actual Family and is one of D, G, I, C, X, M, V, and U as well.
3208 	 *   Actual family may differ from family if a Dataset is actually passed as a Matrix, for instance.
3209 	 * G stands for Geometry and is one of (poin)T, L(ine), P(olygon), C(Line|Polygon), A(POint|Line|Polygon), G(rid), N(one), X(text), or U(ndefined).
3210 	 * M stands for Messenger and is either Y(es) or N(o).
3211 	 * Limitation:  object_ID must be <= GMTAPI_MAX_ID */
3212 
3213 	SP = D = F = A = G = M = 0;	/* Initialize */
3214 	if ((k = sscanf (&filename[GMTAPI_PREFIX_LEN], "%c-%c-%c-%c-%c-%c-%d", &SP, &D, &F, &A, &G, &M, &object_ID)) != 7) {
3215 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_memory_registered: Failed to decode memory name [%s], only %d conversions were successful.\n", filename, k);
3216 		return (GMT_NOTSET);	/* Get the object ID unless we fail scanning */
3217 	}
3218 	if (direction == GMT_IN && D != 'I') return GMT_NOTSET;	/* Not the right direction */
3219 	return object_ID;
3220 }
3221 
3222 /*! . */
gmtapi_is_registered(struct GMTAPI_CTRL * API,int family,int geometry,int direction,unsigned int mode,char * filename,void * resource)3223 GMT_LOCAL int gmtapi_is_registered (struct GMTAPI_CTRL *API, int family, int geometry, int direction, unsigned int mode, char *filename, void *resource) {
3224 	/* Checks to see if the given data pointer has already been registered.
3225  	 * This can happen for grids which first gets registered reading the header
3226  	 * and then is registered again when reading the whole grid.  In those cases
3227 	 * we don't want to register them twice.
3228 	 */
3229 	unsigned int i;
3230 	int item;
3231 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3232 
3233 	if (API->n_objects == 0) return (GMT_NOTSET);	/* There are no known resources yet */
3234 
3235 	if ((item = gmtapi_memory_registered (API, family, direction, filename)) != GMT_NOTSET)
3236 		return (item);	/* OK, return the object ID */
3237 
3238 	 /* Search for the object in the active list.  However, if object_ID == GMT_NOTSET we instead pick the first in that direction */
3239 
3240 	for (i = 0, item = GMT_NOTSET; item == GMT_NOTSET && i < API->n_objects; i++) {
3241 		if (!(S_obj = API->object[i])) continue;	/* Skip empty objects */
3242 		if (S_obj->status != GMT_IS_UNUSED) {	/* Has already been read - do we wish to reset this status ? */
3243 			if (family == GMT_IS_GRID && (mode & GMT_DATA_ONLY)) {	/* Requesting data only means we already did the header so OK to reset status */
3244 				if (mode & GMT_GRID_IS_COMPLEX_MASK) {	/* Complex grids are read in stages so handled separately */
3245 					/* Check if complex grid already has one layer and that we are reading the next layer */
3246 					struct GMT_GRID *G = gmtapi_get_grid_data (resource);	/* Get pointer to this grid */
3247 					unsigned int cmplx = mode & GMT_GRID_IS_COMPLEX_MASK;
3248 					if (G->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK && G->header->complex_mode != cmplx && filename) {
3249 						/* Apparently so, either had real and now getting imag, or vice versa. */
3250 						gmt_M_str_free (S_obj->filename);	/* Free previous grid name and replace with current name */
3251 						S_obj->filename = strdup (filename);
3252 						mode |= GMT_IO_RESET;	/* Reset so we may read in the 2nd component grid */
3253 					}
3254 				}
3255 				else	/* Just read the header earlier, do the reset */
3256 					mode |= GMT_IO_RESET;	/* Reset so we may read in the grid data */
3257 			}
3258 			else if (family == GMT_IS_IMAGE && (mode & GMT_DATA_ONLY))	/* Requesting data only means we already did the header so OK to reset status */
3259 				mode |= GMT_IO_RESET;	/* Reset so we may read in the image data */
3260 
3261 			if (!(mode & GMT_IO_RESET)) continue;	/* No reset above so we refuse to do the work */
3262 			S_obj->status = GMT_IS_UNUSED;	/* Reset so we may continue to read it */
3263 		}
3264 		if (direction != GMT_NOTSET && (int)S_obj->direction != direction) continue;	/* Wrong direction */
3265 		if (family != GMT_NOTSET && (int)S_obj->family != family) continue;			/* Wrong family */
3266 		if (geometry != GMT_NOTSET && (int)S_obj->geometry != geometry) continue;	/* Wrong geometry */
3267 		if (resource && S_obj->resource == resource) item = S_obj->ID;	/* Yes: already registered. */
3268 	}
3269 	return (item);		/* The ID of the object (or GMT_NOTSET) */
3270 }
3271 
3272 /*! . */
gmtapi_import_palette(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode)3273 GMT_LOCAL struct GMT_PALETTE * gmtapi_import_palette (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode) {
3274 	/* Does the actual work of loading in a CPT palette table.
3275  	 * The mode controls how the back-, fore-, NaN-color entries are handled.
3276 	 * Note: Memory is allocated to hold the GMT_PALETTE structure except for method GMT_IS_REFERENCE.
3277 	 */
3278 
3279 	int item;
3280 	unsigned int flag = 0, kind;
3281 	char tmp_cptfile[GMT_LEN64] = {""};
3282 	struct GMT_PALETTE *P_obj = NULL, *P_orig = NULL;
3283 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3284 	struct GMT_CTRL *GMT = API->GMT;
3285 
3286 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_palette: Passed ID = %d and mode = %d\n", object_ID, mode);
3287 
3288 	if (object_ID == GMT_NOTSET) return_null (API, GMT_NO_INPUT);	/* Need to know the ID to do anything */
3289 	if ((item = gmtlib_validate_id (API, GMT_IS_PALETTE, object_ID, GMT_IN, GMTAPI_OPTION_INPUT)) == GMT_NOTSET)
3290 		return_null (API, API->error);	/* Failed basic sanity check */
3291 
3292 	S_obj = API->object[item];	/* Use S_obj as shorthand */
3293 	if (S_obj->status != GMT_IS_UNUSED) { /* Already read this resource before; are we allowed to re-read? */
3294 		if (S_obj->method == GMT_IS_STREAM || S_obj->method == GMT_IS_FDESC)
3295 			return_null (API, GMT_READ_ONCE); /* Not allowed to re-read streams since they are gone */
3296 		if (!(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Not authorized to re-read */
3297 	}
3298 
3299 	/* OK, passed sanity and is allowed to read */
3300 
3301 	switch (S_obj->method) {	/* From where are we getting the palette ? */
3302 		case GMT_IS_FILE:
3303 			/* gmtlib_read_cpt will report where it is reading from if level is GMT_MSG_INFORMATION */
3304 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading CPT from %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
3305 			snprintf (tmp_cptfile, GMT_LEN64, "gmtapi_colors2cpt_%d.cpt", (int)getpid());
3306 			if (!strcmp (tmp_cptfile, S_obj->filename))	/* This file was created when we gave "name" as red,blue,... instead */
3307 			 	flag = GMT_CPT_TEMPORARY;	/* So we can take action later when we learn if user wanted a discrete or continuous CPT */
3308 			if ((P_obj = gmtlib_read_cpt (GMT, S_obj->filename, S_obj->method, mode|flag)) == NULL)
3309 				return_null (API, GMT_CPT_READ_ERROR);
3310 			if (flag == GMT_CPT_TEMPORARY) {	/* Remove the temporary file */
3311 				GMT_Report (API, GMT_MSG_DEBUG, "Remove temporary CPT %s\n", S_obj->filename);
3312 				remove (tmp_cptfile);
3313 			}
3314 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3315 			break;
3316 		case GMT_IS_STREAM:
3317  			/* gmtlib_read_cpt will report where it is reading from if level is GMT_MSG_INFORMATION */
3318 			kind = (S_obj->fp == GMT->session.std[GMT_IN]) ? 0 : 1;	/* 0 if stdin, 1 otherwise for user pointer */
3319 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading CPT from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3320 			if ((P_obj = gmtlib_read_cpt (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
3321 				return_null (API, GMT_CPT_READ_ERROR);
3322 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3323 			break;
3324 		case GMT_IS_FDESC:
3325 			/* gmtlib_read_cpt will report where it is reading from if level is GMT_MSG_INFORMATION */
3326 			kind = (*((int *)S_obj->fp) == GMT_IN) ? 0 : 1;	/* 0 if stdin, 1 otherwise for user pointer */
3327 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading CPT from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3328 			if ((P_obj = gmtlib_read_cpt (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
3329 				return_null (API, GMT_CPT_READ_ERROR);
3330 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3331 			break;
3332 		case GMT_IS_DUPLICATE:	/* Duplicate the input CPT palette */
3333 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating CPT from GMT_PALETTE memory location\n");
3334 			if ((P_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
3335 			if ((P_obj = GMT_Duplicate_Data (API, GMT_IS_PALETTE, mode, P_orig)) == NULL)
3336 				return_null (API, GMT_MEMORY_ERROR);
3337 			break;
3338 		case GMT_IS_REFERENCE:	/* Just pass memory location, so nothing is allocated */
3339 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing CPT from GMT_PALETTE memory location\n");
3340 			if ((P_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
3341 			gmtlib_init_cpt (GMT, P_obj);	/* Make sure derived quantities are set */
3342 			break;
3343 		default:	/* Barking up the wrong tree here... */
3344 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import GMT_PALETTE\n");
3345 			return_null (API, GMT_NOT_A_VALID_METHOD);
3346 			break;
3347 	}
3348 	S_obj->status = GMT_IS_USED;	/* Mark as read */
3349 
3350 	return (P_obj);	/* Pass back the palette */
3351 }
3352 
3353 /*! . */
gmtapi_export_palette(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_PALETTE * P_obj)3354 GMT_LOCAL int gmtapi_export_palette (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_PALETTE *P_obj) {
3355 	/* Does the actual work of writing out the specified CPT to a destination.
3356 	 * The mode controls how the back, for, NaN color entries are handled.
3357 	 */
3358 	int item, error;
3359 	unsigned int kind;
3360 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3361 	struct GMT_PALETTE *P_copy = NULL;
3362 	struct GMT_PALETTE_HIDDEN *PH = NULL;
3363 	struct GMT_CTRL *GMT = API->GMT;
3364 
3365 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_palette: Passed ID = %d and mode = %d\n", object_ID, mode);
3366 
3367 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
3368 	if ((item = gmtlib_validate_id (API, GMT_IS_PALETTE, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
3369 
3370 	S_obj = API->object[item];	/* This is the API object for the output destination */
3371 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET)) {	/* Only allow writing of a data set once, unless we override by resetting the mode */
3372 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));
3373 	}
3374 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
3375 
3376 	/* Passed sanity and allowed to write */
3377 
3378 	/* If need to assign non-default BFN do it now sow external interfaces can access that too */
3379 	if (mode & GMT_CPT_EXTEND_BNF) {	/* Use low and high colors as back and foreground */
3380 		gmt_M_rgb_copy (P_obj->bfn[GMT_BGD].rgb, P_obj->data[0].rgb_low);
3381 		gmt_M_rgb_copy (P_obj->bfn[GMT_FGD].rgb, P_obj->data[P_obj->n_colors-1].rgb_high);
3382 		gmt_M_rgb_copy (P_obj->bfn[GMT_BGD].hsv, P_obj->data[0].hsv_low);
3383 		gmt_M_rgb_copy (P_obj->bfn[GMT_FGD].hsv, P_obj->data[P_obj->n_colors-1].hsv_high);
3384 	}
3385 
3386 	switch (S_obj->method) {	/* File, array, stream etc ? */
3387 		case GMT_IS_FILE:
3388 			/* gmtlib_write_cpt will report where it is writing from if level is GMT_MSG_INFORMATION */
3389 			GMT_Report (API, GMT_MSG_INFORMATION, "Write CPT to %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
3390 			if ((error = gmtlib_write_cpt (GMT, S_obj->filename, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3391 			break;
3392 	 	case GMT_IS_STREAM:
3393 			/* gmtlib_write_cpt will report where it is writing from if level is GMT_MSG_INFORMATION */
3394 			kind = (S_obj->fp == GMT->session.std[GMT_OUT]) ? 0 : 1;	/* 0 if stdout, 1 otherwise for user pointer */
3395 			GMT_Report (API, GMT_MSG_INFORMATION, "Write CPT to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3396 			if ((error = gmtlib_write_cpt (GMT, S_obj->fp, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3397 			break;
3398 	 	case GMT_IS_FDESC:
3399 			/* gmtlib_write_cpt will report where it is writing from if level is GMT_MSG_INFORMATION */
3400 			kind = (*((int *)S_obj->fp) == GMT_OUT) ? 0 : 1;	/* 0 if stdout, 1 otherwise for user pointer */
3401 			GMT_Report (API, GMT_MSG_INFORMATION, "Write CPT to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3402 			if ((error = gmtlib_write_cpt (GMT, S_obj->fp, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3403 			break;
3404 		case GMT_IS_DUPLICATE:	/* Duplicate the input cpt */
3405 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
3406 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating CPT to GMT_PALETTE memory location\n");
3407 			P_copy = gmtlib_create_palette (GMT, P_obj->n_colors);
3408 			gmtlib_copy_palette (GMT, P_copy, P_obj);
3409 			S_obj->resource = P_copy;	/* Set resource pointer from object to this palette */
3410 			break;
3411 		case GMT_IS_REFERENCE:	/* Just pass memory location */
3412 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
3413 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing CPT to GMT_PALETTE memory location\n");
3414 			PH = gmt_get_C_hidden (P_obj);
3415 			PH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
3416 			S_obj->resource = P_obj;	/* Set resource pointer from object to this palette */
3417 			break;
3418 		default:
3419 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export CPTs\n");
3420 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
3421 			break;
3422 	}
3423 	S_obj->status = GMT_IS_USED;	/* Mark as written */
3424 
3425 	return GMT_NOERROR;
3426 }
3427 
3428 /*! . */
gmtapi_import_postscript(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode)3429 GMT_LOCAL struct GMT_POSTSCRIPT * gmtapi_import_postscript (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode) {
3430 	/* Does the actual work of loading in a PS struct.
3431  	 * The mode is not used yet.
3432 	 * Note: Memory is allocated to hold the GMT_POSTSCRIPT structure except for method GMT_IS_REFERENCE.
3433 	 */
3434 
3435 	int item;
3436 	unsigned int kind;
3437 	struct GMT_POSTSCRIPT *P_obj = NULL, *P_orig = NULL;
3438 	struct GMT_POSTSCRIPT_HIDDEN *PH = NULL;
3439 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3440 	struct GMT_CTRL *GMT = API->GMT;
3441 
3442 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_postscript: Passed ID = %d and mode = %d\n", object_ID, mode);
3443 
3444 	if (object_ID == GMT_NOTSET) return_null (API, GMT_NO_INPUT);
3445 	if ((item = gmtlib_validate_id (API, GMT_IS_POSTSCRIPT, object_ID, GMT_IN, GMTAPI_OPTION_INPUT)) == GMT_NOTSET)
3446 		return_null (API, API->error);
3447 
3448 	S_obj = API->object[item];	/* Use S_obj as shorthand */
3449 	if (S_obj->status != GMT_IS_UNUSED) { /* Already read this resource before; are we allowed to re-read? */
3450 		if (S_obj->method == GMT_IS_STREAM || S_obj->method == GMT_IS_FDESC) return_null (API, GMT_READ_ONCE); /* Not allowed to re-read streams */
3451 		if (!(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Not authorized to re-read */
3452 	}
3453 
3454 	/* Passed sanity and allowed to read */
3455 
3456 	switch (S_obj->method) {	/* File, array, stream etc ? */
3457 		case GMT_IS_FILE:
3458 			/* gmtlib_read_ps will report where it is reading from if level is GMT_MSG_INFORMATION */
3459 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading PS from %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
3460 			if ((P_obj = gmtlib_read_ps (GMT, S_obj->filename, S_obj->method, mode)) == NULL)
3461 				return_null (API, GMT_CPT_READ_ERROR);
3462 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3463 			break;
3464 		case GMT_IS_STREAM:
3465  			/* gmtlib_read_ps will report where it is reading from if level is GMT_MSG_INFORMATION */
3466 			kind = (S_obj->fp == GMT->session.std[GMT_IN]) ? 0 : 1;	/* 0 if stdin, 1 otherwise for user pointer */
3467 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading PS from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3468 			if ((P_obj = gmtlib_read_ps (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
3469 				return_null (API, GMT_CPT_READ_ERROR);
3470 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3471 			break;
3472 		case GMT_IS_FDESC:
3473 			/* gmtlib_read_ps will report where it is reading from if level is GMT_MSG_INFORMATION */
3474 			kind = (*((int *)S_obj->fp) == GMT_IN) ? 0 : 1;	/* 0 if stdin, 1 otherwise for user pointer */
3475 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading PS from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3476 			if ((P_obj = gmtlib_read_ps (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
3477 				return_null (API, GMT_CPT_READ_ERROR);
3478 			S_obj->resource = P_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
3479 			break;
3480 		case GMT_IS_DUPLICATE:	/* Duplicate the input CPT palette */
3481 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating PS from GMT_POSTSCRIPT memory location\n");
3482 			if ((P_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
3483 			if ((P_obj = GMT_Duplicate_Data (API, GMT_IS_POSTSCRIPT, mode, P_orig)))
3484 				return_null (API, GMT_MEMORY_ERROR);
3485 			break;
3486 		case GMT_IS_REFERENCE:	/* Just pass memory location, so nothing is allocated */
3487 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing PS from GMT_POSTSCRIPT memory location\n");
3488 			if ((P_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
3489 			break;
3490 		default:	/* Barking up the wrong tree here... */
3491 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import PS\n");
3492 			return_null (API, GMT_NOT_A_VALID_METHOD);
3493 			break;
3494 	}
3495 	PH = gmt_get_P_hidden (P_obj);
3496 	S_obj->alloc_mode = PH->alloc_mode;
3497 	S_obj->status = GMT_IS_USED;	/* Mark as read */
3498 
3499 	return (P_obj);	/* Pass back the PS */
3500 }
3501 
3502 /*! . */
gmtapi_export_postscript(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_POSTSCRIPT * P_obj)3503 GMT_LOCAL int gmtapi_export_postscript (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_POSTSCRIPT *P_obj) {
3504 	/* Does the actual work of writing out the specified PS to a destination.
3505 	 * The mode not used yet.
3506 	 */
3507 	int item, error;
3508 	unsigned int kind;
3509 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3510 	struct GMT_POSTSCRIPT *P_copy = NULL;
3511 	struct GMT_POSTSCRIPT_HIDDEN *PH = NULL;
3512 	struct GMT_CTRL *GMT = API->GMT;
3513 
3514 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_postscript: Passed ID = %d and mode = %d\n", object_ID, mode);
3515 
3516 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
3517 	if ((item = gmtlib_validate_id (API, GMT_IS_POSTSCRIPT, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
3518 
3519 	S_obj = API->object[item];	/* This is the API object for the output destination */
3520 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET)) {	/* Only allow writing of a data set once, unless we override by resetting the mode */
3521 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));
3522 	}
3523 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
3524 
3525 	/* Passed sanity and allowed to write */
3526 
3527 	switch (S_obj->method) {	/* File, array, stream etc ? */
3528 		case GMT_IS_FILE:
3529 			/* gmtlib_write_ps will report where it is writing from if level is GMT_MSG_INFORMATION */
3530 			GMT_Report (API, GMT_MSG_INFORMATION, "Write PS to %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
3531 			if ((error = gmtlib_write_ps (GMT, S_obj->filename, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3532 			break;
3533 	 	case GMT_IS_STREAM:
3534 			/* gmtlib_write_ps will report where it is writing from if level is GMT_MSG_INFORMATION */
3535 			kind = (S_obj->fp == GMT->session.std[GMT_OUT]) ? 0 : 1;	/* 0 if stdout, 1 otherwise for user pointer */
3536 			GMT_Report (API, GMT_MSG_INFORMATION, "Write PS to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3537 			if ((error = gmtlib_write_ps (GMT, S_obj->fp, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3538 			break;
3539 	 	case GMT_IS_FDESC:
3540 			/* gmtlib_write_ps will report where it is writing from if level is GMT_MSG_INFORMATION */
3541 			kind = (*((int *)S_obj->fp) == GMT_OUT) ? 0 : 1;	/* 0 if stdout, 1 otherwise for user pointer */
3542 			GMT_Report (API, GMT_MSG_INFORMATION, "Write PS to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
3543 			if ((error = gmtlib_write_ps (GMT, S_obj->fp, S_obj->method, mode, P_obj))) return (gmtlib_report_error (API, error));
3544 			break;
3545 		case GMT_IS_DUPLICATE:		/* Duplicate the input cpt */
3546 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
3547 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating PS to GMT_POSTSCRIPT memory location\n");
3548 			if ((P_copy = GMT_Duplicate_Data (API, GMT_IS_POSTSCRIPT, mode, P_obj)))
3549 				return (gmtlib_report_error (API, GMT_MEMORY_ERROR));
3550 			S_obj->resource = P_copy;	/* Set resource pointer from object to this PS */
3551 			break;
3552 		case GMT_IS_REFERENCE:	/* Just pass memory location */
3553 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
3554 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing PS to GMT_POSTSCRIPT memory location\n");
3555 			PH = gmt_get_P_hidden (P_obj);
3556 			PH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
3557 			S_obj->resource = P_obj;	/* Set resource pointer from object to this PS */
3558 			break;
3559 		default:
3560 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export PS\n");
3561 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
3562 			break;
3563 	}
3564 	S_obj->status = GMT_IS_USED;	/* Mark as written */
3565 
3566 	return GMT_NOERROR;
3567 }
3568 
3569 #if 0
3570 GMT_LOCAL bool gmtapi_col_check (struct GMT_DATATABLE *T, uint64_t *n_cols) {
3571 	uint64_t seg;
3572 	/* Checks that all segments in this table has the correct number of columns.
3573 	 * If *n_cols == 0 we set it to the number of columns found in the first segment. */
3574 
3575 	for (seg = 0; seg < T->n_segments; seg++) {
3576 		if ((*n_cols) == 0 && seg == 0) *n_cols = T->segment[seg]->n_columns;
3577 		if (T->segment[seg]->n_columns != (*n_cols)) return (true);
3578 	}
3579 	return (false);	/* All is well */
3580 }
3581 #endif
3582 
3583 /*! . */
gmtapi_increment_d(struct GMT_DATASET * D_obj,uint64_t n_rows,uint64_t n_columns,uint64_t n_seg)3584 GMT_LOCAL void gmtapi_increment_d (struct GMT_DATASET *D_obj, uint64_t n_rows, uint64_t n_columns, uint64_t n_seg) {
3585 	/* Increment dimensions for this single dataset's single table's last segment */
3586 	uint64_t last_seg = n_seg - 1;
3587 	assert (n_seg > 0);
3588 	D_obj->table[D_obj->n_tables]->segment[last_seg]->n_rows = n_rows;
3589 	D_obj->table[D_obj->n_tables]->segment[last_seg]->n_columns = D_obj->table[D_obj->n_tables]->n_columns = n_columns;
3590 	D_obj->table[D_obj->n_tables]->n_records += n_rows;
3591 	D_obj->table[D_obj->n_tables]->n_segments = n_seg;
3592 	D_obj->n_tables++;	/* Since we just read one table */
3593 }
3594 
gmtapi_switch_cols(struct GMT_CTRL * GMT,struct GMT_DATASET * D,unsigned int direction)3595 GMT_LOCAL void gmtapi_switch_cols (struct GMT_CTRL *GMT, struct GMT_DATASET *D, unsigned int direction) {
3596 	uint64_t tbl, seg;
3597 	struct GMT_DATASEGMENT *S = NULL;
3598 
3599 	/* Implements the effect of -: when we are not writing to file */
3600 
3601 	if (D->n_columns < 2 || !GMT->current.setting.io_lonlat_toggle[direction]) return;	/* Nothing to do */
3602 	for (tbl = 0; tbl < D->n_tables; tbl++) {
3603 		for (seg = 0; seg < D->table[tbl]->n_segments; seg++) {
3604 			S = D->table[tbl]->segment[seg];
3605 			gmt_M_doublep_swap (S->data[GMT_X], S->data[GMT_Y]);
3606 		}
3607 	}
3608 }
3609 
gmtapi_vector_data_must_be_duplicated(struct GMTAPI_CTRL * API,struct GMT_VECTOR * V)3610 GMT_LOCAL bool gmtapi_vector_data_must_be_duplicated (struct GMTAPI_CTRL *API, struct GMT_VECTOR *V) {
3611     /* Check if referenced vector data arrays must be scaled/offset and hence must be duplicated instead */
3612     for (unsigned int col = 0; col < V->n_columns; col++) {
3613         if (API->GMT->common.i.select && API->GMT->current.io.col[GMT_IN][col].convert) return (true); /* Cannot pass as read-only if it must be converted */
3614     }
3615     return false;    /* Seems OK */
3616 }
3617 
3618 /*! . */
gmtapi_import_dataset(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode)3619 GMT_LOCAL struct GMT_DATASET * gmtapi_import_dataset (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode) {
3620 	/* Does the actual work of loading in the entire virtual data set (possibly via many sources)
3621 	 * If object_ID == GMT_NOTSET we get all registered input tables, otherwise we just get the one requested.
3622 	 * Note: Memory is allocated for the Dataset except for method GMT_IS_REFERENCE.
3623 	 */
3624 
3625 	int item, first_item = 0, this_item = GMT_NOTSET, last_item, new_item, new_ID, status;
3626 	unsigned int geometry = GMT_IS_PLP, n_used = 0, method, smode, type = GMT_READ_DATA, col_pos_out;
3627 	bool allocate = false, update = false, diff_types, use_GMT_io, greenwich = true;
3628 	bool via = false, got_data = false, check_col_switch = false, regit = false;
3629 	size_t n_alloc, s_alloc = GMT_SMALL_CHUNK;
3630 	uint64_t tbl = 0, tbl_in, row, seg, col, ij, n_records = 0, n_columns = 0, col_pos, n_use;
3631 	p_func_uint64_t GMT_2D_to_index = NULL;
3632 	GMT_getfunction api_get_val = NULL;
3633 	struct GMT_DATASET *D_obj = NULL, *Din_obj = NULL;
3634 	struct GMT_DATASEGMENT *S = NULL;
3635 	struct GMT_MATRIX *M_obj = NULL;
3636 	struct GMT_VECTOR *V_obj = NULL;
3637 	struct GMT_DATASET_HIDDEN *DH = NULL, *DHi = NULL;
3638 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
3639 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
3640 	struct GMT_VECTOR_HIDDEN *VH = NULL;
3641 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
3642 	struct GMT_CTRL *GMT = API->GMT;
3643 
3644 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_dataset: Passed ID = %d and mode = %d\n", object_ID, mode);
3645 
3646 	if (object_ID == GMT_NOTSET) {	/* Means there is more than one source: Merge all registered data tables into a single virtual data set */
3647 		last_item = API->n_objects - 1;	/* Must check all registered objects */
3648 		allocate = true;
3649 		n_alloc = GMT_TINY_CHUNK;	/* We don't expect that many files to be given initially */
3650 	}
3651 	else {		/* Requested a single, specific data table/file */
3652 		int flag = (API->module_input) ? GMTAPI_MODULE_INPUT : GMTAPI_OPTION_INPUT;	/* Needed by Validate_ID */
3653 		if ((first_item = gmtlib_validate_id (API, GMT_IS_DATASET, object_ID, GMT_IN, flag)) == GMT_NOTSET)
3654 			return_null (API, API->error);
3655 		last_item = first_item;
3656 		n_alloc = 1;
3657 	}
3658 
3659 	/* Allocate a single data set and an initial allocated list of n_alloc tables */
3660 	D_obj = gmt_get_dataset (GMT);
3661 	DH = gmt_get_DD_hidden (D_obj);
3662 	D_obj->table = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_DATATABLE *);
3663 	DH->alloc_mode = GMT_ALLOC_INTERNALLY;	/* So GMT_* modules can free this memory (may override below) */
3664 	DH->alloc_level = GMT->hidden.func_level;	/* So GMT_* modules can free this memory (may override below) */
3665 	use_GMT_io = !(mode & GMT_IO_ASCII);		/* false if we insist on ASCII reading */
3666 	GMT->current.io.seg_no = GMT->current.io.rec_no = GMT->current.io.rec_in_tbl_no = GMT->current.io.data_record_number_in_tbl[GMT_IN] = GMT->current.io.data_record_number_in_seg[GMT_IN] = 0;	/* Reset for each new dataset */
3667 	if (GMT->common.R.active[RSET] && GMT->common.R.wesn[XLO] < -180.0 && GMT->common.R.wesn[XHI] > -180.0) greenwich = false;
3668 
3669 	for (item = first_item; item <= last_item; item++) {	/* Look through all sources for registered inputs (or just one) */
3670 		S_obj = API->object[item];	/* S_obj is the current data object */
3671 		if (!S_obj) {	/* Probably not a good sign. NOTE: Probably cannot happen since skipped in api_next_source, no? */
3672 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_dataset: Skipped empty object (item = %d)\n", item);
3673 			continue;
3674 		}
3675 		if (!S_obj->selected) continue;			/* Registered, but not selected */
3676 		if (S_obj->direction == GMT_OUT) continue;	/* We're doing reading here, so skip output objects */
3677 		if (S_obj->family != GMT_IS_DATASET) continue;	/* We're doing datasets here, so skip other data types */
3678 		if (API->module_input && !S_obj->module_input) continue;	/* Do not mix module-inputs and option inputs if knowable */
3679 		if (S_obj->status != GMT_IS_UNUSED) { 	/* Already read this resource before; are we allowed to re-read? */
3680 			if (S_obj->method == GMT_IS_STREAM || S_obj->method == GMT_IS_FDESC) {
3681 				gmt_M_free (GMT, D_obj->table);		gmt_M_free (GMT, D_obj);
3682 				return_null (API, GMT_READ_ONCE);	/* Cannot re-read streams */
3683 			}
3684 			if (!(mode & GMT_IO_RESET)) {
3685 				gmt_M_free (GMT, D_obj->table);		gmt_M_free (GMT, D_obj);
3686 				return_null (API, GMT_READ_ONCE);	/* Not authorized to re-read */
3687 			}
3688 		}
3689 		if (this_item == GMT_NOTSET) this_item = item;	/* First item that worked */
3690 		via = false;
3691 		geometry = (GMT->common.a.output) ? GMT->common.a.geometry : S_obj->geometry;	/* When reading GMT and writing OGR/GMT we must make sure we set this first */
3692 		method = gmtapi_set_method (S_obj);	/* Get the actual method to use */
3693         /* At the time an external vector was created via GMT_Open_VirtualFile there is not yet any knowledge if this data
3694          * will be passed to a module with options -i that could require scaling, offsetting, or taking the log of the data.
3695          * If that is the case then we cannot pass via reference but must switch method to duplicate. */
3696         if (method == (GMT_IS_REFERENCE|GMT_VIA_VECTOR) && gmtapi_vector_data_must_be_duplicated (API, S_obj->resource))
3697             method = GMT_IS_DUPLICATE|GMT_VIA_VECTOR;   /* We need to adjust at least one vector due to -i+s+o+l so must duplicate input rather than reference */
3698 
3699 		switch (method) {	/* File, array, stream, reference, etc ? */
3700 	 		case GMT_IS_FILE:	/* Import all the segments, then count total number of records */
3701 #ifdef SET_IO_MODE
3702 				if (item == first_item) gmt_setmode (GMT, GMT_IN);	/* Windows may need to switch read mode from text to binary */
3703 #endif
3704 				/* gmtlib_read_table will report where it is reading from if level is GMT_MSG_INFORMATION */
3705 				GMT->current.io.first_rec = true;
3706 				if (GMT->current.io.ogr == GMT_OGR_TRUE && D_obj->n_tables > 0) {	/* Only single tables if GMT/OGR */
3707 					gmt_M_free (GMT, D_obj->table);		gmt_M_free (GMT, D_obj);
3708 					return_null (API, GMT_OGR_ONE_TABLE_ONLY);
3709 				}
3710 				GMT_Report (API, GMT_MSG_INFORMATION,
3711 				            "Reading %s from %s %s\n", GMT_family[S_obj->family], gmtapi_method (S_obj->method), S_obj->filename);
3712 				if ((D_obj->table[D_obj->n_tables] = gmtlib_read_table (GMT, S_obj->filename, S_obj->method, greenwich, &geometry, &type, use_GMT_io)) == NULL)
3713 					continue;		/* Ran into an empty file (e.g., /dev/null or equivalent). Skip to next item, */
3714 				TH = gmt_get_DT_hidden (D_obj->table[D_obj->n_tables]);
3715 				TH->id = D_obj->n_tables;	/* Give sequential internal object_ID numbers to tables */
3716 				D_obj->n_tables++;	/* Since we just read one */
3717 				update = true;		/* Have reason to update min/max when done */
3718 				break;
3719 
3720 			case GMT_IS_STREAM:	/* Import all the segments, then count total number of records */
3721 	 		case GMT_IS_FDESC:
3722 				/* gmtlib_read_table will report where it is reading from if level is GMT_MSG_INFORMATION */
3723 #ifdef SET_IO_MODE
3724 				if (item == first_item) gmt_setmode (GMT, GMT_IN);	/* Windows may need to switch read mode from text to binary */
3725 #endif
3726 				GMT->current.io.first_rec = true;
3727 				if (GMT->current.io.ogr == GMT_OGR_TRUE && D_obj->n_tables > 0)	{	/* Only single tables if GMT/OGR */
3728 					gmt_M_free (GMT, D_obj);	return_null (API, GMT_OGR_ONE_TABLE_ONLY);
3729 				}
3730 				GMT_Report (API, GMT_MSG_INFORMATION, "Reading %s from %s %" PRIxS "\n", GMT_family[S_obj->family], gmtapi_method (S_obj->method), (size_t)S_obj->fp);
3731 				if ((D_obj->table[D_obj->n_tables] = gmtlib_read_table (GMT, S_obj->fp, S_obj->method, greenwich, &geometry, &type, use_GMT_io)) == NULL) continue;		/* Ran into an empty file (e.g., /dev/null or equivalent). Skip to next item, */
3732 				TH = gmt_get_DT_hidden (D_obj->table[D_obj->n_tables]);
3733 				TH->id = D_obj->n_tables;	/* Give sequential internal object_ID numbers to tables */
3734 				D_obj->n_tables++;	/* Since we just read one */
3735 				update = true;		/* Have reason to update min/max when done */
3736 				break;
3737 
3738 			case GMT_IS_DUPLICATE:	/* Duplicate the input dataset */
3739 				if (S_obj->resource == NULL) return_null (API, GMT_PTR_IS_NULL);
3740 				if (GMT->common.q.mode == GMT_RANGE_ROW_IN || GMT->common.q.mode == GMT_RANGE_DATA_IN)
3741 					GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qi is not implemented for GMT_IS_DUPLICATE with GMT_IS_DATASET external memory objects\n");
3742 				GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table from GMT_DATASET memory location\n");
3743 				Din_obj = gmt_duplicate_dataset (GMT, S_obj->resource, GMT_ALLOC_NORMAL|GMT_ALLOC_VIA_ICOLS, NULL);
3744 				if ((tbl + Din_obj->n_tables) >= n_alloc) {	/* Need more space to hold these new tables */
3745 					n_alloc += Din_obj->n_tables;
3746 					D_obj->table = gmt_M_memory (GMT, D_obj->table, n_alloc, struct GMT_DATATABLE *);
3747 				}
3748 				for (tbl_in = 0; tbl_in < Din_obj->n_tables; tbl_in++, tbl++)	/* Pass over the pointers only */
3749 					D_obj->table[tbl] = Din_obj->table[tbl_in];
3750 				gmtlib_free_dataset_misc (GMT, Din_obj);	/* Free this object but not its tables */
3751 				gmt_M_free (GMT, Din_obj);
3752 				D_obj->n_tables = tbl;
3753 				D_obj->geometry = S_obj->geometry;	/* Since provided when registered */
3754 				check_col_switch = true;
3755 				update = regit = via = true;		/* Have reason to update min/max as well as registering D_obj when done */
3756 				break;
3757 
3758 			case GMT_IS_REFERENCE:	/* Just pass memory locations to tables */
3759 				if ((Din_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
3760 				if (GMT->common.q.mode == GMT_RANGE_ROW_IN || GMT->common.q.mode == GMT_RANGE_DATA_IN)
3761 					GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qi is not implemented for GMT_IS_REFERENCE with GMT_IS_DATASET external memory objects\n");
3762 				GMT_Report (API, GMT_MSG_INFORMATION, "Referencing data table from GMT_DATASET memory location\n");
3763 				DHi = gmt_get_DD_hidden (Din_obj);
3764 				if ((tbl + Din_obj->n_tables) >= n_alloc) {	/* Need more space to hold these new tables */
3765 					n_alloc += Din_obj->n_tables;
3766 					D_obj->table = gmt_M_memory (GMT, D_obj->table, n_alloc, struct GMT_DATATABLE *);
3767 				}
3768 				for (tbl_in = 0; tbl_in < Din_obj->n_tables; tbl_in++, tbl++) {	/* Pass over the pointers only */
3769 					D_obj->table[tbl] = Din_obj->table[tbl_in];
3770 					Din_obj->table[tbl_in] = NULL;	/* Since passed to D_obj */
3771 				}
3772 				Din_obj->n_tables = 0;	/* Only the husk remains of this fruit */
3773 				D_obj->n_tables = tbl;
3774 				D_obj->geometry = S_obj->geometry;	/* Since provided when registered */
3775 				DH->alloc_mode = DHi->alloc_mode;	/* Must use whatever alloc_mode the input reference had */
3776 				DH->alloc_level = DHi->alloc_level;	/* Must use whatever alloc_level the input reference had */
3777 				check_col_switch = true;
3778 				update = regit = via = true;		/* Have reason to update min/max as well as registering D_obj when done */
3779 				break;
3780 
3781 		 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* There is no difference since in both cases we must allocate dataset arrays */
3782 		 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:
3783 				/* Each matrix source becomes a separate table with a single segment unless there are NaN-records as segment headers */
3784 				if ((M_obj = S_obj->resource) == NULL) {
3785 					gmt_M_free (GMT, D_obj);
3786 					return_null (API, GMT_PTR_IS_NULL);
3787 				}
3788 				GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table from user matrix location\n");
3789 				if (GMT->common.q.mode == GMT_RANGE_ROW_IN || GMT->common.q.mode == GMT_RANGE_DATA_IN)
3790 					GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qi is not implemented for [GMT_IS_DUPLICATE,GMT_IS_REFERENCE]|GMT_IS_MATRIX external memory objects\n");
3791 				/* Allocate a table with a single segment given matrix dimensions, but if nan-record we may end up with more segments */
3792 				smode = (M_obj->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
3793 				if (smode) type = GMT_READ_MIXED;	/* If a matrix has text we have a mixed record */
3794 				n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : M_obj->n_columns;
3795 				D_obj->table[D_obj->n_tables] = gmt_get_table (GMT);
3796 				D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, NULL, s_alloc, struct GMT_DATASEGMENT *);
3797 				S = D_obj->table[D_obj->n_tables]->segment[0] = GMT_Alloc_Segment (API, smode, M_obj->n_rows, n_columns, NULL, NULL);
3798 				if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
3799 					return_null (API, GMT_WRONG_MATRIX_SHAPE);
3800 				if ((api_get_val = gmtapi_select_get_function (API, M_obj->type)) == NULL)
3801 					return_null (API, GMT_NOT_A_VALID_TYPE);
3802 
3803 				n_use = gmtapi_n_cols_needed_for_gaps (GMT, M_obj->n_columns);	/* Number of input columns to process */
3804 				for (row = seg = n_records = 0; row < M_obj->n_rows; row++) {	/* This loop may include NaN-records and data records */
3805 					gmtapi_update_prev_rec (GMT, n_use);	/* Make last current record the previous record if it is required by gap checking */
3806 					for (col = 0; col < M_obj->n_columns; col++) {	/* Extract cols for a single record and store result in curr_rec */
3807 						ij = GMT_2D_to_index (row, col, M_obj->dim);	/* Index into the user data matrix depends on layout (M->shape) */
3808 						api_get_val (&(M_obj->data), ij, &(GMT->current.io.curr_rec[col]));
3809 					}
3810 					/* Now process the current record */
3811 					if ((status = gmtapi_bin_input_memory (GMT, M_obj->n_columns, n_use)) < 0) {	/* Segment header found, finish the segment we worked on and goto next */
3812 						if (status == GMTAPI_GOT_SEGGAP) API->current_rec[GMT_IN]--;	/* Since we inserted a segment header we must revisit this record as the first in next segment */
3813 						if (got_data) {	/* If first input segment has header then we already have that segment allocated */
3814 							(void)GMT_Alloc_Segment (API, GMT_IS_DATASET, n_records, n_columns, NULL, S);	/* Reallocate to exact length */
3815 							D_obj->table[D_obj->n_tables]->n_records += n_records;			/* Update record count for this table */
3816 							seg++;	/* Increment number of segments */
3817 							if (seg == s_alloc) {	/* Allocate more space for additional segments */
3818 								s_alloc <<= 1;	/* Double current alloc limit for segments, then allocate space for more segments */
3819 								D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, D_obj->table[D_obj->n_tables]->segment, s_alloc, struct GMT_DATASEGMENT *);
3820 							}
3821 							/* Allocate next segment with initial size the remainder of the data, which is the maximum length possible */
3822 							S = D_obj->table[D_obj->n_tables]->segment[seg] = GMT_Alloc_Segment (API, GMT_IS_DATASET, M_obj->n_rows-n_records, n_columns, NULL, NULL);
3823 							n_records = 0;	/* This is number of recs in current segment so we reset it to zero */
3824 						}
3825 					}
3826 					else {	/* Found a data record */
3827 						for (col = 0; col < n_columns; col++) {	/* Place the record into the dataset segment structure */
3828 							double val = gmtapi_get_record_value (GMT, GMT->current.io.curr_rec, col, M_obj->n_columns, &col_pos_out);
3829 							S->data[col_pos_out][n_records] = val;
3830 						}
3831 						got_data = true;	/* No longer before first data record */
3832 						if (smode) S->text[n_records] = strdup (M_obj->text[row]);
3833 						n_records++;	/* Update count of records in current segment */
3834 					}
3835 				}
3836 				if (seg)	/* Got more than one segment, so finalize the reallocation of last segment to exact record count */
3837 					(void)GMT_Alloc_Segment (API, smode, n_records, n_columns, NULL, S);	/* Reallocate to exact length */
3838 				seg++;	/* Now holds the total number of segments */
3839 				/* Realloc this table's segment array to the actual length [i.e., seg] */
3840 				D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, D_obj->table[D_obj->n_tables]->segment, seg, struct GMT_DATASEGMENT *);
3841 				gmtapi_increment_d (D_obj, n_records, n_columns, seg);	/* Update counters for D_obj's only table */
3842 				new_ID = GMT_Register_IO (API, GMT_IS_DATASET, GMT_IS_DUPLICATE, geometry, GMT_IN, NULL, D_obj);	/* Register a new resource to hold D_obj */
3843 				if ((new_item = gmtlib_validate_id (API, GMT_IS_DATASET, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
3844 					return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
3845 				API->object[new_item]->resource = D_obj;
3846 				API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
3847 				DH = gmt_get_DD_hidden (D_obj);
3848 				DH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
3849 				D_obj->geometry = S_obj->geometry;	/* Since provided when registered */
3850 				update = via = true;
3851 				break;
3852 
3853 	 		case GMT_IS_DUPLICATE|GMT_VIA_VECTOR:
3854 				/* Each column array source becomes column arrays in a separate table with one (or more if NaN-records) segments */
3855 				if ((V_obj = S_obj->resource) == NULL) {
3856 					gmt_M_free (GMT, D_obj);	return_null (API, GMT_PTR_IS_NULL);
3857 				}
3858 				GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table from user %" PRIu64 " column arrays of length %" PRIu64 "\n",
3859 				            V_obj->n_columns, V_obj->n_rows);
3860 				if (GMT->common.q.mode == GMT_RANGE_ROW_IN || GMT->common.q.mode == GMT_RANGE_DATA_IN)
3861 					GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qi is not implemented for GMT_IS_DUPLICATE|GMT_VIA_VECTOR external memory objects\n");
3862 				/* Allocate a single table with one segment - there may be more if there are nan-records */
3863 				smode = (V_obj->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
3864 				if (smode) type = GMT_READ_MIXED;	/* If a vector has text we have a mixed record */
3865 				n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : V_obj->n_columns;
3866 				D_obj->table[D_obj->n_tables] = gmt_get_table (GMT);
3867 				D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, NULL, s_alloc, struct GMT_DATASEGMENT *);
3868 				S = D_obj->table[D_obj->n_tables]->segment[0] = GMT_Alloc_Segment (API, smode, V_obj->n_rows, n_columns, NULL, NULL);
3869 				for (col = 1, diff_types = false; !diff_types && col < V_obj->n_columns; col++) if (V_obj->type[col] != V_obj->type[col-1]) diff_types = true;
3870 				if (!diff_types && (api_get_val = gmtapi_select_get_function (API, V_obj->type[0])) == NULL)
3871 					return_null (API, GMT_NOT_A_VALID_TYPE);
3872 
3873 				for (row = seg = n_records = 0; row < V_obj->n_rows; row++) {	/* This loop may include NaN-records and data records */
3874 					n_use = gmtapi_n_cols_needed_for_gaps (GMT, V_obj->n_columns);
3875 					gmtapi_update_prev_rec (GMT, n_use);
3876 					for (col = 0; col < V_obj->n_columns; col++) {	/* Process a single record into curr_rec */
3877 						if (diff_types && (api_get_val = gmtapi_select_get_function (API, V_obj->type[col])) == NULL)
3878 							return_null (API, GMT_NOT_A_VALID_TYPE);
3879 						api_get_val (&(V_obj->data[col]), row, &(GMT->current.io.curr_rec[col]));
3880 					}
3881 					if ((status = gmtapi_bin_input_memory (GMT, V_obj->n_columns, n_use)) < 0) {	/* Segment header found, finish the one we had and add more */
3882 						if (status == GMTAPI_GOT_SEGGAP) API->current_rec[GMT_IN]--;	/* Since we inserted a segment header we must revisit this record as first in next segment */
3883 						if (got_data) {	/* If first input segment has header then we already have a segment allocated */
3884 							(void)GMT_Alloc_Segment (API, GMT_IS_DATASET, n_records, n_columns, NULL, S);
3885 							D_obj->table[D_obj->n_tables]->n_records += n_records;
3886 							seg++;	/* Increment number of segments */
3887 							if (seg == s_alloc) {	/* Allocate more space for segments */
3888 								s_alloc <<= 1;
3889 								D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, D_obj->table[D_obj->n_tables]->segment, s_alloc, struct GMT_DATASEGMENT *);
3890 							}
3891 							/* Allocate next segment with initial size the remainder of the data */
3892 							S = D_obj->table[D_obj->n_tables]->segment[seg] = GMT_Alloc_Segment (API, GMT_IS_DATASET, V_obj->n_rows-n_records, n_columns, NULL, NULL);
3893 							n_records = 0;	/* This is number of recs in current segment */
3894 						}
3895 					}
3896 					else {	/* Data record */
3897 						for (col = 0; col < n_columns; col++) {	/* Place the record into the structure */
3898 							double val = gmtapi_get_record_value (GMT, GMT->current.io.curr_rec, col, V_obj->n_columns, &col_pos_out);
3899 							S->data[col_pos_out][n_records] = val;
3900 						}
3901 						if (smode) S->text[n_records] = strdup (V_obj->text[row]);
3902 						got_data = true;
3903 						n_records++;
3904 					}
3905 				}
3906 				if (seg)	/* Got more than one segment, finalize the realloc of last segment */
3907 					(void)GMT_Alloc_Segment (API, smode, n_records, n_columns, NULL, S);	/* Reallocate to exact length */
3908 				seg++;	/* Total number of segments */
3909 				/* Realloc this table's segment array to the actual length [i.e., seg] */
3910 				D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, D_obj->table[D_obj->n_tables]->segment, seg, struct GMT_DATASEGMENT *);
3911 				gmtapi_increment_d (D_obj, n_records, n_columns, seg);	/* Update counters for D_obj's only table */
3912 				new_ID = GMT_Register_IO (API, GMT_IS_DATASET, GMT_IS_DUPLICATE, geometry, GMT_IN, NULL, D_obj);	/* Register a new resource to hold D_obj */
3913 				if ((new_item = gmtlib_validate_id (API, GMT_IS_DATASET, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
3914 					return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
3915 				API->object[new_item]->resource = D_obj;
3916 				API->object[new_item]->status = GMT_IS_USED;			/* Mark as read */
3917 				DH = gmt_get_DD_hidden (D_obj);
3918 				DH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
3919 				D_obj->geometry = S_obj->geometry;	/* Since provided when registered */
3920 				update = via = true;
3921 				break;
3922 
3923 			case GMT_IS_REFERENCE|GMT_VIA_VECTOR:
3924 				if ((V_obj = S_obj->resource) == NULL) {
3925 					gmt_M_free (GMT, D_obj);	return_null (API, GMT_PTR_IS_NULL);
3926 				}
3927 				if (V_obj->type[0] != GMT_DOUBLE) {
3928 					GMT_Report (API, GMT_MSG_ERROR, "Only double-precision vectors can be passed via reference to datasets\n");
3929 					gmt_M_free (GMT, D_obj);	return_null (API, GMT_NOT_A_VALID_TYPE);
3930 				}
3931 				VH = gmt_get_V_hidden (V_obj);
3932 				if (GMT->common.q.mode == GMT_RANGE_ROW_IN || GMT->common.q.mode == GMT_RANGE_DATA_IN)
3933 					GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qi is not implemented for GMT_IS_REFERENCE|GMT_VIA_VECTOR external memory objects\n");
3934 				/* Each column double array source becomes preallocated column arrays in a separate table with a single segment */
3935 				smode = (V_obj->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
3936 				if (smode) type = GMT_READ_MIXED;	/* If a matrix has text we have a mixed record */
3937 				n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : V_obj->n_columns;
3938 				GMT_Report (API, GMT_MSG_INFORMATION, "Referencing data table from user %" PRIu64 " column arrays of length %" PRIu64 "\n",
3939 				            V_obj->n_columns, V_obj->n_rows);
3940 				D_obj->table[D_obj->n_tables] = gmt_get_table (GMT);
3941 				D_obj->table[D_obj->n_tables]->segment = gmt_M_memory (GMT, NULL, 1, struct GMT_DATASEGMENT *);
3942 				S = D_obj->table[D_obj->n_tables]->segment[0] = GMT_Alloc_Segment (API, smode, 0, n_columns, NULL, NULL);
3943 				SH = gmt_get_DS_hidden (S);
3944 				for (col = 0; col < V_obj->n_columns; col++) {
3945 					if (GMT->common.i.select) {	/* -i has selected some columns */
3946 						col_pos = GMT->current.io.col[GMT_IN][col].col;	/* Which data column to pick */
3947 						col_pos_out = GMT->current.io.col[GMT_IN][col].order; /* Which data column to place it on output */
3948 					}
3949 					else if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && col < GMT_Z) {	/* Worry about -: for lon,lat */
3950 						col_pos = 1 - col;	/* Read lat/lon instead of lon/lat */
3951 						col_pos_out = col;
3952 					}
3953 					else
3954 						col_pos = col_pos_out = col;	/* Just goto that column */
3955 					S->data[col_pos_out] = V_obj->data[col_pos].f8;
3956 					SH->alloc_mode[col_pos_out] = VH->alloc_mode[col];	/* Inherit from what we got */
3957 				}
3958 				DH = gmt_get_DD_hidden (D_obj);
3959 				if (smode) S->text = V_obj->text;
3960 				gmtapi_increment_d (D_obj, V_obj->n_rows, n_columns, 1U);	/* Update counters for D_obj with 1 segment */
3961 				DH->alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Since we just hooked on the arrays */
3962 				new_ID = GMT_Register_IO (API, GMT_IS_DATASET, GMT_IS_REFERENCE, geometry, GMT_IN, NULL, D_obj);	/* Register a new resource to hold D_obj */
3963 				if ((new_item = gmtlib_validate_id (API, GMT_IS_DATASET, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
3964 					return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
3965 				API->object[new_item]->resource = D_obj;
3966 				API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
3967 				DH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
3968 				D_obj->geometry = S_obj->geometry;	/* Since provided when registered */
3969 				S_obj->family = GMT_IS_VECTOR;	/* Done with the via business now */
3970 				update = via = check_col_switch = true;
3971 				break;
3972 
3973 			default:	/* Barking up the wrong tree here... */
3974 				GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import data tables\n");
3975 				gmt_M_free (GMT, D_obj->table);
3976 				gmt_M_free (GMT, D_obj);
3977 				return_null (API, GMT_NOT_A_VALID_METHOD);
3978 				break;
3979 		}
3980 		if (update) {	/* Means we got stuff and need to update the total dataset statistics so far */
3981 			D_obj->n_segments += D_obj->table[D_obj->n_tables-1]->n_segments;	/* Sum up total number of segments in the entire data set */
3982 			D_obj->n_records  += D_obj->table[D_obj->n_tables-1]->n_records;		/* Sum up total number of records in the entire data set */
3983 			/* Update segment IDs so they are sequential across many tables (gmtlib_read_table sets the ids relative to current table). */
3984 			if (D_obj->n_tables > 1) {
3985 				for (seg = 0; seg < D_obj->table[D_obj->n_tables-1]->n_segments; seg++) {
3986 					SH = gmt_get_DS_hidden (D_obj->table[D_obj->n_tables-1]->segment[seg]);
3987 					SH->id += D_obj->table[D_obj->n_tables-2]->n_segments;
3988 				}
3989 			}
3990 			if (allocate && D_obj->n_tables == n_alloc) {	/* Must allocate more space for additional tables */
3991 				size_t old_n_alloc = n_alloc;
3992 				n_alloc += GMT_TINY_CHUNK;
3993 				D_obj->table = gmt_M_memory (GMT, D_obj->table, n_alloc, struct GMT_DATATABLE *);
3994 				gmt_M_memset (&(D_obj->table[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_DATATABLE *);	/* Set new memory to NULL */
3995 			}
3996 		}
3997 		S_obj->alloc_mode = DH->alloc_mode;	/* Clarify allocation mode for this object */
3998 #if 0
3999 		if (gmtapi_col_check (D_obj->table[D_obj->n_tables-1], &n_cols)) {	/* Different tables have different number of columns, which is not good */
4000 			return_null (API, GMT_N_COLS_VARY);
4001 		}
4002 #endif
4003 		S_obj->status = GMT_IS_USED;	/* Mark input object as read */
4004 		S_obj->n_expected_fields = GMT_MAX_COLUMNS;	/* Since need to start over if this object is used again */
4005 		n_used++;	/* Number of items actually processed */
4006 	}
4007 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_dataset processed %u resources\n", n_used);
4008 	if (regit) {	/* Register the output */
4009 		new_ID = GMT_Register_IO (API, GMT_IS_DATASET, GMT_IS_DUPLICATE, geometry, GMT_IN, NULL, D_obj);	/* Register a new resource to hold D_obj */
4010 		if ((new_item = gmtlib_validate_id (API, GMT_IS_DATASET, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
4011 			return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
4012 		API->object[new_item]->resource = D_obj;
4013 		API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
4014 		API->object[new_item]->alloc_mode = DH->alloc_mode;	/* Clarify allocation mode for this object */
4015 		API->object[new_item]->alloc_level = DH->alloc_level;
4016 	}
4017 	if (D_obj->n_tables == 0) {	/* Only found empty files (e.g., /dev/null) and we have nothing to show for our efforts.  Return an single empty table with no segments. */
4018 		D_obj->table = gmt_M_memory (GMT, D_obj->table, 1, struct GMT_DATATABLE *);
4019 		D_obj->table[0] = gmt_get_table (GMT);
4020 		D_obj->n_tables = 1;	/* But we must indicate we found one (empty) table */
4021 	}
4022 	else {	/* Found one or more tables, finalize table allocation, set number of columns, and possibly allocate min/max arrays if not there already */
4023 		if (allocate && D_obj->n_tables < n_alloc) D_obj->table = gmt_M_memory (GMT, D_obj->table, D_obj->n_tables, struct GMT_DATATABLE *);
4024 		D_obj->n_columns = D_obj->table[0]->n_columns;
4025 		if (!D_obj->min) D_obj->min = gmt_M_memory (GMT, NULL, D_obj->n_columns, double);
4026 		if (!D_obj->max) D_obj->max = gmt_M_memory (GMT, NULL, D_obj->n_columns, double);
4027 	}
4028 	D_obj->geometry = geometry;		/* Since gmtlib_read_table may have changed it */
4029 	D_obj->type = type;			/* Since gmtlib_read_table may have changed it */
4030 	if (check_col_switch) gmtapi_switch_cols (GMT, D_obj, GMT_IN);	/* Deals with -:, if it was selected */
4031 	gmt_set_dataset_minmax (GMT, D_obj);	/* Set the min/max values for the entire dataset */
4032 	if (!via) API->object[this_item]->resource = D_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
4033 	return (D_obj);
4034 }
4035 
4036 /*! . */
gmtapi_destroy_data_ptr(struct GMTAPI_CTRL * API,enum GMT_enum_family family,void * ptr)4037 GMT_LOCAL int gmtapi_destroy_data_ptr (struct GMTAPI_CTRL *API, enum GMT_enum_family family, void *ptr) {
4038 	/* Like GMT_Destroy_Data but takes pointer to data rather than address of pointer.
4039 	 * We pass true to make sure we free the memory.  Some objects (grid, matrix, vector) may
4040 	 * point to externally allocated memory so we return the alloc_mode for those items.
4041 	 * This is mostly for information since the pointers to such external memory have now
4042 	 * been set to NULL instead of being freed.
4043 	 * The containers are always allocated by GMT so those are freed at the end.
4044 	 */
4045 
4046 	struct GMT_CTRL *GMT;
4047 	if (API == NULL) return (GMT_NOT_A_SESSION);
4048 	if (!ptr) return (GMT_NOERROR);	/* Null pointer */
4049 	GMT = API->GMT;
4050 
4051 	switch (family) {
4052 		case GMT_IS_GRID:
4053 			gmtlib_free_grid_ptr (GMT, ptr, true);
4054 			break;
4055 		case GMT_IS_DATASET:
4056 			gmtlib_free_dataset_ptr (GMT, ptr);
4057 			break;
4058 		case GMT_IS_PALETTE:
4059 			gmtlib_free_cpt_ptr (GMT, ptr);
4060 			break;
4061 		case GMT_IS_IMAGE:
4062 			gmtlib_free_image_ptr (GMT, ptr, true);
4063 			break;
4064 		case GMT_IS_POSTSCRIPT:
4065 			gmtlib_free_ps_ptr (GMT, ptr);
4066 			break;
4067 		case GMT_IS_CUBE:
4068 			gmtlib_free_cube_ptr (GMT, ptr, true);
4069 			break;
4070 		case GMT_IS_COORD:
4071 			/* Nothing to do as gmt_M_free below will do it */
4072 			break;
4073 
4074 		/* Also allow destroying of intermediate vector and matrix containers */
4075 		case GMT_IS_MATRIX:
4076 			gmtlib_free_matrix_ptr (GMT, ptr, true);
4077 			break;
4078 		case GMT_IS_VECTOR:
4079 			gmtlib_free_vector_ptr (GMT, ptr, true);
4080 			break;
4081 		default:
4082 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_FAMILY));
4083 			break;
4084 	}
4085 	gmt_M_free (GMT, ptr);	/* OK to free container */
4086 	return (GMT_NOERROR);	/* Null pointer */
4087 }
4088 
gmtapi_flip_vectors(struct GMT_CTRL * GMT,struct GMT_VECTOR * V,unsigned int direction)4089 void gmtapi_flip_vectors (struct GMT_CTRL *GMT, struct GMT_VECTOR *V, unsigned int direction) {
4090 	enum GMT_enum_type etmp;
4091 	union GMT_UNIVECTOR utmp;
4092 
4093 	/* Implements the effect of -: on output via vectors */
4094 
4095 	if (V->n_columns < 2 || !GMT->current.setting.io_lonlat_toggle[direction]) return;	/* Nothing to do */
4096 	/* Flip first two vector pointers */
4097 	etmp = V->type[GMT_X];	V->type[GMT_X] = V->type[GMT_Y];	V->type[GMT_Y] = etmp;
4098 	utmp = V->data[GMT_X];	V->data[GMT_X] = V->data[GMT_Y];	V->data[GMT_Y] = utmp;
4099 }
4100 
4101 /*! . */
gmtapi_export_dataset(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_DATASET * D_obj)4102 GMT_LOCAL int gmtapi_export_dataset (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_DATASET *D_obj) {
4103  	/* Does the actual work of writing out the specified data set to a single destination.
4104 	 * If object_ID == GMT_NOTSET we use the first registered output destination, otherwise we just use the one specified.
4105 	 * See the GMT API documentation for how mode is used to create multiple files from segments or tables of a dataset.
4106 	 */
4107 	int item, error, default_method;
4108 	unsigned int method, hdr;
4109 	uint64_t tbl, col, kol, row_out, row, seg, ij, n_columns, n_rows;
4110 	bool save, diff_types = false, toggle;
4111 	double value;
4112 	p_func_uint64_t GMT_2D_to_index = NULL;
4113 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
4114 	struct GMT_DATASET *D_copy = NULL;
4115 	struct GMT_MATRIX *M_obj = NULL;
4116 	struct GMT_VECTOR *V_obj = NULL;
4117 	struct GMT_MATRIX_HIDDEN *MH = NULL;
4118 	struct GMT_VECTOR_HIDDEN *VH = NULL;
4119 	struct GMT_DATASEGMENT *S = NULL;
4120 	struct GMT_DATASET_HIDDEN *DH = NULL;
4121 	struct GMT_CTRL *GMT = API->GMT;
4122 	void *ptr = NULL;
4123 	GMT_putfunction api_put_val = NULL;
4124 
4125 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_dataset: Passed ID = %d and mode = %d\n", object_ID, mode);
4126 
4127 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
4128 	if ((item = gmtlib_validate_id (API, GMT_IS_DATASET, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
4129 
4130 	S_obj = API->object[item];	/* S is the object whose data we will export */
4131 	if (S_obj->family != GMT_IS_DATASET) return (gmtlib_report_error (API, GMT_NOT_A_VALID_FAMILY));	/* Called with wrong data type */
4132 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET))	/* Only allow writing of a data set once unless overridden by mode */
4133 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));
4134 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;	/* Remove the reset bit */
4135 	if (mode >= GMT_WRITE_TABLE && !S_obj->filename) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));	/* Must have filename when segments are to be written */
4136 	default_method = GMT_IS_FILE;
4137 	if (S_obj->filename)	/* Write to this file */
4138 		ptr = S_obj->filename;
4139 	else {			/* No filename so we switch default method to writing to a stream or fdesc */
4140 		default_method = (S_obj->method == GMT_IS_FILE) ? GMT_IS_STREAM : S_obj->method;
4141 		ptr = S_obj->fp;
4142 #ifdef SET_IO_MODE
4143 		gmt_setmode (GMT, GMT_OUT);	/* Windows may need to switch write mode from text to binary */
4144 #endif
4145 	}
4146 	gmt_set_dataset_minmax (GMT, D_obj);	/* Update all counters and min/max arrays */
4147 	if (API->GMT->common.o.end || GMT->common.o.text)	/* Asked for unspecified last column on input (e.g., -i3,2,5:), supply the missing last column number */
4148 		gmtlib_reparse_o_option (GMT, (GMT->common.o.text) ? 0 : D_obj->n_columns);
4149 	toggle = (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && D_obj->n_columns >= 2);
4150 	GMT->current.io.data_record_number_in_tbl[GMT_OUT] = GMT->current.io.data_record_number_in_seg[GMT_OUT] = 0;
4151 	DH = gmt_get_DD_hidden (D_obj);
4152 	DH->io_mode = mode;	/* Handles if tables or segments should be written to separate files, according to mode */
4153 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use */
4154 	switch (method) {	/* File, array, stream, etc. */
4155 	 	case GMT_IS_STREAM:
4156 #ifdef SET_IO_MODE
4157 			gmt_setmode (GMT, GMT_OUT);	/* Windows may need to switch write mode from text to binary */
4158 #endif
4159 		case GMT_IS_FILE:
4160 	 	case GMT_IS_FDESC:
4161 			/* gmtlib_write_dataset (or lower) will report where it is reading from if level is GMT_MSG_INFORMATION */
4162 			if ((error = gmtlib_write_dataset (GMT, ptr, default_method, D_obj, true, GMT_NOTSET))) return (gmtlib_report_error (API, error));
4163 			break;
4164 
4165 		case GMT_IS_DUPLICATE:		/* Duplicate the input dataset on output */
4166 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
4167 			if (GMT->common.q.mode == GMT_RANGE_ROW_OUT || GMT->common.q.mode == GMT_RANGE_DATA_OUT)
4168 				GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qo is not implemented for GMT_IS_DUPLICATE GMT_IS_DATASET external memory objects\n");
4169 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table to GMT_DATASET memory location\n");
4170 			D_copy = gmt_duplicate_dataset (GMT, D_obj, GMT_ALLOC_NORMAL, NULL);
4171 			gmtlib_change_out_dataset (GMT, D_copy);	/* Deal with any -o settings */
4172 			gmtapi_switch_cols (GMT, D_copy, GMT_OUT);	/* Deals with -:, if it was selected */
4173 			S_obj->resource = D_copy;	/* Set resource pointer from object to this dataset */
4174 			break;
4175 
4176 		case GMT_IS_REFERENCE:	/* Just pass memory location */
4177 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource must be NULL */
4178 			if (GMT->common.q.mode == GMT_RANGE_ROW_OUT || GMT->common.q.mode == GMT_RANGE_DATA_OUT)
4179 				GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qo is not implemented for GMT_IS_REFERENCE GMT_IS_DATASET external memory objects\n");
4180 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing data table to GMT_DATASET memory location\n");
4181 			gmtlib_change_out_dataset (GMT, D_obj);	/* Deal with any -o settings */
4182 			gmtapi_switch_cols (GMT, D_obj, GMT_OUT);	/* Deals with -:, if it was selected */
4183 			S_obj->resource = D_obj;		/* Set resource pointer from object to this dataset */
4184 			DH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
4185 			break;
4186 
4187 		case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:
4188 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table to user matrix location\n");
4189 			if (GMT->common.q.mode == GMT_RANGE_ROW_OUT || GMT->common.q.mode == GMT_RANGE_DATA_OUT)
4190 				GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qo is not implemented for GMT_IS_DUPLICATE|GMT_VIA_MATRIX external memory objects\n");
4191 			save = GMT->current.io.multi_segments[GMT_OUT];
4192 			if (GMT->current.io.skip_headers_on_outout) GMT->current.io.multi_segments[GMT_OUT] = false;
4193 			n_rows = (GMT->current.io.multi_segments[GMT_OUT]) ? D_obj->n_records + D_obj->n_segments : D_obj->n_records;	/* Number of rows needed to hold the data [incl any segment headers] */
4194 			n_columns = (GMT->common.o.select) ? GMT->common.o.n_cols : D_obj->n_columns;					/* Number of columns needed to hold the data records */
4195 			if ((M_obj = S_obj->resource) == NULL) {	/* Must allocate suitable matrix */
4196 				M_obj = gmtlib_create_matrix (GMT, 1U, GMT_OUT, 0);	/* 1-layer matrix (i.e., 2-D) */
4197 				/* Allocate final output space since we now know all dimensions */
4198 				MH = gmt_get_M_hidden (M_obj);
4199 				M_obj->n_rows = n_rows;
4200 				M_obj->n_columns = n_columns;
4201 				M_obj->dim = (M_obj->shape == GMT_IS_ROW_FORMAT) ? M_obj->n_columns : M_obj->n_rows;						/* Matrix layout order */
4202 				S_obj->n_alloc = M_obj->n_rows * M_obj->n_columns;	/* Get total number of elements as n_rows * n_columns */
4203 				M_obj->type = S_obj->type;	/* Use selected data type for the export */
4204 				/* Allocate output matrix space or die */
4205 				if ((error = gmtlib_alloc_univector (GMT, &(M_obj->data), M_obj->type, S_obj->n_alloc)) != GMT_NOERROR) return (gmtlib_report_error (API, error));
4206 				MH->alloc_mode = GMT_ALLOC_INTERNALLY;
4207 				if (D_obj->type >= GMT_READ_TEXT) { /* Also has trailing text */
4208 					M_obj->text = gmt_M_memory (GMT, NULL, n_rows, char *);
4209 					MH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
4210 				}
4211 			}
4212 			else {	/* We passed in a matrix so must check it is big enough */
4213 				if (M_obj->n_rows < n_rows || M_obj->n_columns < n_columns)
4214 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
4215 				MH = gmt_get_M_hidden (M_obj);
4216 			}
4217 			/* Consider header records from first table only */
4218 			if (D_obj->table[0]->n_headers) {
4219 				M_obj->header = gmt_M_memory (GMT, NULL, D_obj->table[0]->n_headers, char *);
4220 				for (hdr = M_obj->n_headers = 0; hdr < D_obj->table[0]->n_headers; hdr++)
4221 					M_obj->header[M_obj->n_headers++] = strdup (D_obj->table[0]->header[hdr]);
4222 			}
4223 
4224 			/* Set up index and put-value functions for this matrix */
4225 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
4226 				return (gmtlib_report_error (API, GMT_WRONG_MATRIX_SHAPE));
4227 			if ((api_put_val = gmtapi_select_put_function (API, M_obj->type)) == NULL)
4228 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_TYPE));
4229 
4230 			for (tbl = row_out = 0; tbl < D_obj->n_tables; tbl++) {	/* Loop over tables and segments */
4231 				for (seg = 0; seg < D_obj->table[tbl]->n_segments; seg++) {
4232 					S = D_obj->table[tbl]->segment[seg];	/* Shorthand for the current segment */
4233 					if (GMT->current.io.multi_segments[GMT_OUT]) {	/* Must write a NaN-segment record to indicate segment break */
4234 						for (col = 0; col < M_obj->n_columns; col++) {
4235 							ij = GMT_2D_to_index (row_out, col, M_obj->dim);
4236 							api_put_val (&(M_obj->data), ij, GMT->session.d_NaN);
4237 						}
4238 						row_out++;	/* Due to the extra NaN-data header record we just wrote */
4239 					}
4240 					for (row = 0; row < S->n_rows; row++, row_out++) {	/* Write this segment's data records to the matrix */
4241 						for (col = 0; col < M_obj->n_columns; col++) {
4242 							if (col < 2 && toggle)	/* Deal with -: since we are writing to matrix memory and not file */
4243 								kol = 1 - col;
4244 							else
4245 								kol = col;
4246 							ij = GMT_2D_to_index (row_out, kol, M_obj->dim);
4247 							value = gmtapi_select_dataset_value (GMT, S, (unsigned int)row, (unsigned int)col);
4248 							api_put_val (&(M_obj->data), ij, value);
4249 						}
4250 						if (S->text) M_obj->text[row_out] = strdup (S->text[row]);
4251 					}
4252 				}
4253 			}
4254 			assert (M_obj->n_rows == row_out);	/* Sanity check */
4255 			MH->alloc_level = S_obj->alloc_level;
4256 			S_obj->resource = M_obj;		/* Set resource pointer from object to this matrix */
4257 			GMT->current.io.multi_segments[GMT_OUT] = save;
4258 			break;
4259 
4260 		case GMT_IS_DUPLICATE|GMT_VIA_VECTOR:
4261 			if (GMT->common.q.mode == GMT_RANGE_ROW_OUT || GMT->common.q.mode == GMT_RANGE_DATA_OUT)
4262 				GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qo is not implemented for GMT_IS_DUPLICATE|GMT_VIA_VECTOR external memory objects\n");
4263 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table to user column arrays location\n");
4264 			save = GMT->current.io.multi_segments[GMT_OUT];
4265 			if (GMT->current.io.skip_headers_on_outout) GMT->current.io.multi_segments[GMT_OUT] = false;
4266 			n_columns = (GMT->common.o.select) ? GMT->common.o.n_cols : D_obj->n_columns;	/* Number of columns needed to hold the data records */
4267 			n_rows = (GMT->current.io.multi_segments[GMT_OUT]) ? D_obj->n_records + D_obj->n_segments : D_obj->n_records;	/* Number of data records [and any segment headers] */
4268 			if ((V_obj = S_obj->resource) == NULL) {	/* Must create output container given data dimensions */
4269 				if ((V_obj = gmt_create_vector (GMT, n_columns, GMT_OUT)) == NULL)
4270 					return (gmtlib_report_error (API, GMT_PTR_IS_NULL));
4271 				for (col = 0; col < V_obj->n_columns; col++) V_obj->type[col] = S_obj->type;	/* Set same data type for all columns */
4272 				V_obj->n_rows = n_rows;
4273 				if ((error = gmtlib_alloc_vectors (GMT, V_obj, n_rows)) != GMT_NOERROR) return (gmtlib_report_error (API, error));	/* Allocate space for all columns */
4274 				if (D_obj->type >= GMT_READ_TEXT) { /* Also has trailing text */
4275 					struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V_obj);
4276 					V_obj->text = gmt_M_memory (GMT, NULL, n_rows, char *);
4277 					VH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
4278 				}
4279 			}
4280 			else {	/* Got a preallocated container */
4281 				if (V_obj->n_rows < n_rows || V_obj->n_columns < n_columns)
4282 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
4283 				for (col = 1, diff_types = false; !diff_types && col < V_obj->n_columns; col++) if (V_obj->type[col] != V_obj->type[col-1]) diff_types = true;
4284 			}
4285 			/* Consider header records from first table only */
4286 			if (D_obj->table[0]->n_headers) {
4287 				V_obj->header = gmt_M_memory (GMT, NULL, D_obj->table[0]->n_headers, char *);
4288 				for (hdr = V_obj->n_headers = 0; hdr < D_obj->table[0]->n_headers; hdr++)
4289 					V_obj->header[V_obj->n_headers++] = strdup (D_obj->table[0]->header[hdr]);
4290 			}
4291 
4292 			/* Set up put-value functions for this vector */
4293 			if (!diff_types && (api_put_val = gmtapi_select_put_function (API, V_obj->type[0])) == NULL)	/* Get function to write 1st column (possibly all columns) */
4294 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_TYPE));
4295 			for (tbl = row_out = 0; tbl < D_obj->n_tables; tbl++) {	/* Loop over all tables and segments */
4296 				for (seg = 0; seg < D_obj->table[tbl]->n_segments; seg++) {
4297 					S = D_obj->table[tbl]->segment[seg];	/* Shorthand for this segment */
4298 					if (GMT->current.io.multi_segments[GMT_OUT]) {		/* Must write a NaN-segment record */
4299 						for (col = 0; col < V_obj->n_columns; col++)
4300 							api_put_val (&(V_obj->data[col]), row_out, GMT->session.d_NaN);
4301 						row_out++;	/* Due to the extra NaN-data header */
4302 					}
4303 					for (row = 0; row < S->n_rows; row++, row_out++) {	/* Copy the data records */
4304 						for (col = 0; col < V_obj->n_columns; col++) {
4305 							if (diff_types && (api_put_val = gmtapi_select_put_function (API, V_obj->type[col])) == NULL)
4306 								return (gmtlib_report_error (API, GMT_NOT_A_VALID_TYPE));
4307 							value = gmtapi_select_dataset_value (GMT, S, (unsigned int)row, (unsigned int)col);
4308 							api_put_val (&(V_obj->data[col]), row_out, value);
4309 						}
4310 						if (S->text) V_obj->text[row_out] = strdup (S->text[row]);
4311 					}
4312 				}
4313 			}
4314 			assert (V_obj->n_rows == row_out);	/* Sanity check */
4315 			if (toggle) gmtapi_flip_vectors (GMT, V_obj, GMT_OUT);
4316 			VH = gmt_get_V_hidden (V_obj);
4317 			VH->alloc_level = S_obj->alloc_level;
4318 			S_obj->resource = V_obj;
4319 			GMT->current.io.multi_segments[GMT_OUT] = save;
4320 			break;
4321 
4322 		case GMT_IS_REFERENCE|GMT_VIA_VECTOR:
4323 			if (GMT->common.q.mode == GMT_RANGE_ROW_OUT || GMT->common.q.mode == GMT_RANGE_DATA_OUT)
4324 				GMT_Report (API, GMT_MSG_WARNING, "Row-selection via -qo is not implemented for GMT_IS_REFERENCE|GMT_VIA_VECTOR external memory objects\n");
4325 			GMT_Report (API, GMT_MSG_DEBUG, "Referencing data table to users column-vector location\n");
4326 			if (D_obj->n_tables > 1 || D_obj->n_segments > 1) {
4327 				GMT_Report (API, GMT_MSG_WARNING, "Reference by vector requires a single segment!\n");
4328 				GMT_Report (API, GMT_MSG_WARNING, "Output may be truncated or an error may occur!\n");
4329 			}
4330 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating data table to user column arrays location\n");
4331 			n_columns = (GMT->common.o.select) ? GMT->common.o.n_cols : D_obj->n_columns;	/* Number of columns needed to hold the data records */
4332 			n_rows = D_obj->n_records;	/* Number of data records */
4333 			S = D_obj->table[0]->segment[0];	/* Shorthand for this single segment */
4334 			if ((V_obj = S_obj->resource) == NULL) {	/* Must create output container given data dimensions */
4335 				struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
4336 				if ((V_obj = gmt_create_vector (GMT, n_columns, GMT_OUT)) == NULL)
4337 					return (gmtlib_report_error (API, GMT_PTR_IS_NULL));
4338 				VH = gmt_get_V_hidden (V_obj);
4339 				for (col = 0; col < V_obj->n_columns; col++) {
4340 					V_obj->type[col] = S_obj->type;	/* Set same data type for all columns */
4341 					V_obj->data[col].f8 = S->data[col];	/* Set pointer only */
4342 					VH->alloc_mode[col] = GMT_ALLOC_EXTERNALLY;	/* Since not duplicated, just pointed to */
4343 					SH->alloc_mode[col] = GMT_ALLOC_EXTERNALLY;	/* To prevent freeing in D_obj */
4344 				}
4345 				if (S->text) {
4346 					V_obj->text = S->text;
4347 					VH->alloc_mode_text = GMT_ALLOC_EXTERNALLY;	/* Since not duplicated, just pointed to */
4348 				}
4349 				V_obj->n_rows = n_rows;
4350 				VH->alloc_level = S_obj->alloc_level;	/* Otherwise D_obj will be freed before we get to use data */
4351 				S_obj->alloc_mode = DH->alloc_mode;	/* Otherwise D_obj will be freed before we get to use data */
4352 			}
4353 			else {	/* Got a preallocated container */
4354 				if (V_obj->n_rows < n_rows || V_obj->n_columns < n_columns)
4355 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
4356 				for (col = 0; col < V_obj->n_columns; col++)
4357 					gmt_M_memcpy (V_obj->data[col].f8, S->data[col], n_rows, double);	/* Duplicate data */
4358 			}
4359 			/* Consider header records from first table only and will set pointers only */
4360 			if (D_obj->table[0]->n_headers) {
4361 				V_obj->header = gmt_M_memory (GMT, NULL, D_obj->table[0]->n_headers, char *);
4362 				for (hdr = V_obj->n_headers = 0; hdr < D_obj->table[0]->n_headers; hdr++)
4363 					V_obj->header[V_obj->n_headers++] = strdup (D_obj->table[0]->header[hdr]);
4364 			}
4365 			if (toggle) gmtapi_flip_vectors (GMT, V_obj, GMT_OUT);
4366 			S_obj->resource = V_obj;
4367 			break;
4368 
4369 		default:
4370 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export data tables\n");
4371 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
4372 			break;
4373 	}
4374 	S_obj->alloc_mode = DH->alloc_mode;	/* Clarify allocation mode for this entity */
4375 	S_obj->status = GMT_IS_USED;	/* Mark as written */
4376 
4377 	return GMT_NOERROR;
4378 }
4379 
gmtapi_import_ppm_header(struct GMT_CTRL * GMT,char * fname,bool close,FILE ** fp_ppm,struct GMT_IMAGE * I)4380 GMT_LOCAL int gmtapi_import_ppm_header (struct GMT_CTRL *GMT, char *fname, bool close, FILE **fp_ppm, struct GMT_IMAGE *I) {
4381 	/* Reads a Portable Pixel Map (PPM) file header if fname extension is .ppm, else returns nonzero value */
4382 	char *ext = gmt_get_ext (fname), text[GMT_LEN128] = {""}, c;
4383 	int k = 0, max, n;
4384 	FILE *fp = NULL;
4385 	if (ext == NULL || strcmp (ext, "ppm")) return GMT_NOT_A_VALID_FAMILY;	/* Not requesting a PPM file - return GMT_NOT_A_VALID_FAMILY and let GDAL take over */
4386 
4387 	if ((fp = gmt_fopen (GMT, fname, GMT->current.io.r_mode)) == NULL) {	/* Return GMT_ERROR_ON_FOPEN to signify failure */
4388 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", fname);
4389 		return GMT_ERROR_ON_FOPEN;
4390 	}
4391 	while ((c = fgetc (fp)) != '\n' && k < GMT_LEN128) text[k++] = c;	/* Get first record up to newline */
4392 	text[MIN(k,GMT_LEN128-1)] = '\0';	/* Terminate line & check that we don't overflow */
4393 	if (text[1] == '5') /* Used P5 for grayscale image */
4394 		I->header->n_bands = 1;
4395 	else if (text[1] == '6')	/* Used P6 for rgb image */
4396 		I->header->n_bands = 3;
4397 	else {
4398 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot decode PPM magic key (%s) from file %s\n", text, fname);
4399 		gmt_fclose (GMT, fp);
4400 		return GMT_NOT_A_VALID_TYPE;
4401 	}
4402 	c = fgetc (fp);	/* Need to peak ahead to know what record we are dealing with.  PPM can have comments */
4403 	while (c == '#') {	/* Wind past comment */
4404 		while ((c = fgetc (fp)) != '\n' ) k++;	/* Ends when c is newline */
4405 		c = fgetc (fp);	/* Peak ahead again */
4406 	}
4407 	/* Put back last read character to the stream */
4408 	ungetc (c, fp);
4409 	k = 0;
4410 	while ((c = fgetc (fp)) != '\n' && k < GMT_LEN128) text[k++] = c;	/* Get next record up to newline */
4411 	text[MIN(k,GMT_LEN128-1)] = '\0';	/* Terminate line & check that we don't overflow */
4412 	n = sscanf (text, "%d %d %d", &I->header->n_rows, &I->header->n_columns, &max);
4413 	if (n == 2) {	/* Must skip past a separate record with the max pixel value */
4414 		while ((c = fgetc (fp)) != '\n' ) k++;
4415 	}
4416 	/* Any read now would start reading the image pixels; done in gmtapi_import_ppm */
4417 	I->header->registration = GMT_GRID_PIXEL_REG;
4418 	if (GMT->common.R.active[RSET]) {	/* Got -Rw/e/s/n, we use that as the region for this image */
4419 		gmt_M_memcpy (I->header->wesn, GMT->common.R.wesn, 4, double);
4420 		I->header->inc[GMT_X] = gmt_M_get_inc (GMT, I->header->wesn[XLO], I->header->wesn[XHI], I->header->n_columns, GMT_GRID_PIXEL_REG);
4421 		I->header->inc[GMT_Y] = gmt_M_get_inc (GMT, I->header->wesn[YLO], I->header->wesn[YHI], I->header->n_rows, GMT_GRID_PIXEL_REG);
4422 	}
4423 	else {	/* Must just use dimensions to set a dummy -R -I */
4424 		I->header->wesn[XLO] = I->header->wesn[YLO] = 0.0;
4425 		I->header->wesn[XHI] = I->header->n_columns;
4426 		I->header->wesn[YHI] = I->header->n_rows;
4427 		I->header->inc[GMT_X] = I->header->inc[GMT_Y] = 1.0;
4428 	}
4429 	gmt_M_memset (I->header->pad, 4, unsigned int);
4430 	gmt_set_grddim (GMT, I->header);	/* Update all header dimensions */
4431 	strcpy (I->header->mem_layout, "TRP");	/* Layout use in all PPM files */
4432 	if (close)	/* Close file, we only wanted the header information */
4433 		gmt_fclose (GMT, fp);
4434 	else	/* Pass back FILE pointers since we want to read the rest as well */
4435 		*fp_ppm = fp;
4436 	return GMT_NOERROR;
4437 }
4438 
gmtapi_import_ppm(struct GMT_CTRL * GMT,char * fname,struct GMT_IMAGE * I)4439 GMT_LOCAL int gmtapi_import_ppm (struct GMT_CTRL *GMT, char *fname, struct GMT_IMAGE *I) {
4440 	/* Reads a Portable Pixel Map (PPM) file if fname extension is .ppm, else returns 1 */
4441 	FILE *fp = NULL;
4442 	size_t size;
4443 
4444 	if (gmtapi_import_ppm_header (GMT, fname, false, &fp, I)) return GMT_NOT_A_VALID_FAMILY;	/* Not a PPM */
4445 	/* Now read the image in scanline order, with each pixel as (R, G, B) or (gray) */
4446 	size = I->header->nm * I->header->n_bands;
4447 	if (fread (I->data, sizeof(char), size, fp) != size) {
4448 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to read the image from %s\n", fname);
4449 		gmt_fclose (GMT, fp);
4450 		return GMT_IMAGE_READ_ERROR;
4451 	}
4452 	gmt_fclose (GMT, fp);
4453 	return GMT_NOERROR;
4454 }
4455 
4456 #ifdef HAVE_GDAL
gmtapi_expand_index_image(struct GMT_CTRL * GMT,struct GMT_IMAGE * I_in,struct GMT_IMAGE ** I_out)4457 GMT_LOCAL bool gmtapi_expand_index_image (struct GMT_CTRL *GMT, struct GMT_IMAGE *I_in, struct GMT_IMAGE **I_out) {
4458 	/* In most situations we can use an input image given to a module as the dataset to
4459 	 * plot.  However, if the image is indexed then we must expand it to rgb since we may
4460 	 * need to interpolate the r/g/b planes due to projections. If the image is read-only
4461 	 * then we cannot reallocate the array and must duplicate, otherwise we reallocate the
4462 	 * image array and expand to rgb.  This function is called at the end of gmtapi_import_image
4463 	 * if the GMT_IMAGE_NO_INDEX bitflag is passed. The image layout honors the current setting
4464 	 * of API_IMAGE_LAYOUT. */
4465 	bool new = false;
4466 	unsigned char *data = NULL;
4467 	uint64_t node, off[3];
4468 	unsigned int c, index;
4469 	struct GMT_IMAGE *I = NULL;
4470 	struct GMT_IMAGE_HIDDEN *IH = gmt_get_I_hidden (I_in);
4471 	struct GMT_GRID_HEADER *h = I_in->header;
4472 
4473 	if (I_in->n_indexed_colors == 0) {	/* Regular gray or r/g/b image - use as is */
4474 		(*I_out) = I_in;
4475 		return (false);
4476 	}
4477 	/* Here we have an indexed image */
4478 	if (IH->alloc_mode == GMT_ALLOC_EXTERNALLY) {	/* Cannot reallocate a non-GMT read-only input array */
4479 		if ((I = GMT_Duplicate_Data (GMT->parent, GMT_IS_IMAGE, GMT_DUPLICATE_DATA, I_in)) == NULL) {
4480 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to duplicate image! - this is not a good thing and may crash this module\n");
4481 			(*I_out) = I_in;
4482 		}
4483 		else {
4484 			struct GMT_IMAGE_HIDDEN *IH = gmt_get_I_hidden (I);
4485 			IH->alloc_mode = GMT_ALLOC_INTERNALLY;
4486 		}
4487 		new = true;
4488 	}
4489 	else	/* Here we may overwrite the input image and just pass the pointer back */
4490 		I = I_in;
4491 
4492 	/* Here, I is an image we can reallocate the array when expanding the colors */
4493 
4494 	h = I->header;
4495 	data = gmt_M_memory_aligned (GMT, NULL, h->size * 3, unsigned char);	/* The new r,g,b image */
4496 
4497 	size_t n_colors = I->n_indexed_colors;
4498 	if (n_colors > 2000)			/* If colormap is Mx4 or has encoded the alpha color */
4499 		n_colors = (uint64_t)(floor(n_colors / 1000.0));
4500 
4501 	if (GMT->parent->GMT->current.gdal_read_in.O.mem_layout[0] && strncmp (GMT->parent->GMT->current.gdal_read_in.O.mem_layout, "TRB", 3U) == 0) {	/* Band interleave */
4502 		strncpy (h->mem_layout, "TRB ", 4);	/* Fill out red, green, and blue bands */
4503 		for (c = 0; c < 3; c++) off[c] = c * h->size;
4504 		for (node = 0; node < h->size; node++) {	/* For all pixels, including the pad */
4505 			index = I->data[node];	/* Pixel index into color table */
4506 			for (c = 0; c < 3; c++) data[node+off[c]] = gmt_M_get_rgba (I->colormap, index, c, n_colors);	/* Place r,g,b in separate bands */
4507 		}
4508 	}
4509 	else {	/* Pixel interleave */
4510 		uint64_t k;
4511 		strncpy (h->mem_layout, "TRP ", 4);	/* Fill out red, green, and blue pixels */
4512 		for (node = k = 0; node < h->size; node++) {	/* For all pixels, including the pad */
4513 			index = I->data[node];	/* Pixel index into color table */
4514 			for (c = 0; c < 3; c++, k++) data[k] = gmt_M_get_rgba (I->colormap, index, c, n_colors);	/* Place r,g,b in separate bands */
4515 		}
4516 		/* If neither TRB or TRP we call for a changed layout, which may or may not have been implemented */
4517 		GMT_Change_Layout (GMT->parent, GMT_IS_IMAGE, GMT->parent->GMT->current.gdal_read_in.O.mem_layout, 0, I, NULL, NULL);
4518 	}
4519 	gmt_M_free_aligned (GMT, I->data);	/* Free previous aligned image memory */
4520 	I->data = data;	/* Pass the reallocated rgb TRB image back */
4521 	/* Reset meta data to reflect a regular 3-band r,g,b image */
4522 	h->n_bands = 3;
4523 	I->n_indexed_colors = 0;
4524 	gmt_M_free (GMT, I->colormap);	/* Free the colormap */
4525 	I->color_interp = NULL;
4526 
4527 	(*I_out) = I;
4528 	return (new);
4529 }
4530 
gmtlib_ind2rgb(struct GMT_CTRL * GMT,struct GMT_IMAGE ** I_in)4531 int gmtlib_ind2rgb (struct GMT_CTRL *GMT, struct GMT_IMAGE **I_in) {
4532 	/* Convert an indexed image to RGB. Other than indirect calls to gmtapi_expand_index_image, e.g., the one
4533 	   called by gmtapi_import_image, there are other cases when we need also to convert from indexed to RGB.
4534 	   For example in grdimage when the image was sent in via an external wrapper. In this case the code flow goes
4535 	   through gmtapi_get_image_data() (in GMT_Read_Data -> gmtapi_pass_object (API, S_obj, family, mode, wesn))
4536 	   and deliver that Image object directly to the calling module and may thus have indexed pixels.
4537 	*/
4538 	struct GMT_IMAGE* Irgb = NULL;
4539 	if ((*I_in)->header->n_bands == 1 && (*I_in)->n_indexed_colors > 0) {		/* Indexed image, convert to RGB */
4540 		gmtapi_expand_index_image (GMT, *I_in, &Irgb);	/* true if we have a read-only indexed image and we had to allocate a new one */
4541 		if (GMT_Destroy_Data (GMT->parent, I_in) != GMT_NOERROR) {
4542 			gmtlib_report_error(GMT->parent, GMT->parent->error);
4543 			return GMT->parent->error;
4544 		}
4545 		(*I_in) = Irgb;
4546 	}
4547 	return GMT_NOERROR;
4548 }
4549 
gmtlib_GDALDestroyDriverManager(struct GMTAPI_CTRL * API)4550 void gmtlib_GDALDestroyDriverManager (struct GMTAPI_CTRL *API) {
4551     /* Cannot close connection to GDAL if calling environment expect it to be open */
4552 	if (API->external < 2) GDALDestroyDriverManager();
4553 }
4554 #endif
4555 
4556 /*! . */
gmtapi_import_image(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_IMAGE * image)4557 GMT_LOCAL struct GMT_IMAGE *gmtapi_import_image (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_IMAGE *image) {
4558 	/* Handles the reading of a 2-D image given in one of several ways.
4559 	 * Get the entire image:
4560  	 * 	mode = GMT_CONTAINER_AND_DATA reads both header and image;
4561 	 * Get a subset of the image:  Call gmtapi_import_image twice:
4562 	 * 	1. first with mode = GMT_CONTAINER_ONLY which reads header only.  Then, pass
4563 	 *	   the new S_obj-> wesn to match your desired subregion
4564 	 *	2. 2nd with mode = GMT_DATA_ONLY, which reads image based on header's settings
4565 	 * If the image->data array is NULL it will be allocated for you.
4566 	 */
4567 
4568 	int item, new_item, new_ID;
4569 	bool done = true, via = false, must_be_image = true, no_index = false, bc_not_set = true;
4570 	uint64_t i0, i1, j0, j1, ij, ij_orig, row, col;
4571 	unsigned int both_set = (GMT_CONTAINER_ONLY | GMT_DATA_ONLY);
4572 	double dx, dy, d;
4573 	p_func_uint64_t GMT_2D_to_index = NULL;
4574 	GMT_getfunction api_get_val = NULL;
4575 	struct GMT_IMAGE *I_obj = NULL, *I_orig = NULL;
4576 	struct GMT_MATRIX *M_obj = NULL;
4577 	struct GMT_MATRIX_HIDDEN  *MH = NULL;
4578 	struct GMT_IMAGE_HIDDEN *IH = NULL;
4579 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
4580 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
4581 	struct GMT_CTRL *GMT = API->GMT;
4582 #ifdef HAVE_GDAL
4583 	bool new = false;
4584 	size_t size;
4585 	struct GMT_IMAGE *Irgb = NULL;
4586 #endif
4587 
4588 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_image: Passed ID = %d and mode = %d\n", object_ID, mode);
4589 
4590 	if ((item = gmtlib_validate_id (API, GMT_IS_IMAGE, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET) return_null (API, API->error);
4591 
4592 	S_obj = API->object[item];		/* Current data object */
4593 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET))
4594 		return_null (API, GMT_READ_ONCE);	/* Already read this resources before, so fail unless overridden by mode */
4595 	if ((mode & GMT_IMAGE_NO_INDEX)) no_index = true, mode -= GMT_IMAGE_NO_INDEX;	/* Must expand any index to rgb */
4596 	if ((mode & both_set) == both_set) mode -= both_set;	/* Allow users to have set GMT_CONTAINER_ONLY | GMT_DATA_ONLY; reset to GMT_CONTAINER_AND_DATA */
4597 	if ((mode & GMT_GRID_IS_IMAGE) == GMT_GRID_IS_IMAGE) {	/* Only allowed when fishing the image header and it may in fact be a grid */
4598 		if (mode & GMT_DATA_ONLY) {
4599 			GMT_Report (API, GMT_MSG_ERROR, "Cannot pass mode = GMT_GRID_IS_IMAGE when reading the image for file %s\n", S_obj->filename);
4600 			return_null (API, GMT_IMAGE_READ_ERROR);
4601 		}
4602 		mode -= GMT_GRID_IS_IMAGE;
4603 		must_be_image = false;
4604 	}
4605 
4606 	switch (S_obj->method) {
4607 		case GMT_IS_FILE:	/* Name of an image file on disk */
4608 #ifdef HAVE_GDAL
4609 			if (image == NULL) {	/* Only allocate image struct when not already allocated */
4610 				if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY image must already be allocated */
4611 				I_obj = gmtlib_create_image (GMT);
4612 				new = true;
4613 			}
4614 			else
4615 				I_obj = image;	/* We are passing in an image already allocated */
4616 			HH = gmt_get_H_hidden (I_obj->header);
4617 			I_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);		/* Pass on any bitflags */
4618 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read image */
4619 			if (! (mode & GMT_DATA_ONLY)) {		/* Must init header and read the header information from file */
4620 				if (gmtapi_import_ppm_header (GMT, S_obj->filename, true, NULL, I_obj) == GMT_NOERROR)
4621 					d = 0.0;	/* Placeholder */
4622 				else if (gmt_M_err_pass (GMT, gmtlib_read_image_info (GMT, S_obj->filename, must_be_image, I_obj), S_obj->filename)) {
4623 					if (new) gmtlib_free_image (GMT, &I_obj, false);
4624 					return_null (API, GMT_IMAGE_READ_ERROR);
4625 				}
4626 				if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
4627 			}
4628 			/* Here we will read the image data themselves. */
4629 			/* To get a subset we use wesn that is not NULL or contain 0/0/0/0.
4630 			 * Otherwise we extract the entire file domain */
4631             if (GMT->common.R.active[RSET] && !S_obj->region) { /* subregion not passed to object yet */
4632                 gmt_M_memcpy (S_obj->wesn, GMT->common.R.wesn, 4U, double);
4633                 S_obj->region = true;
4634             }
4635 			size = gmtapi_set_grdarray_size (GMT, I_obj->header, mode, S_obj->wesn);    /* Get array dimension only, which includes padding. DANGER DANGER JL*/
4636 			if (!I_obj->data) {	/* Array is not allocated yet, do so now. We only expect header (and possibly w/e/s/n subset) to have been set correctly */
4637 				if (I_obj->type <= GMT_UCHAR)
4638 					I_obj->data = gmt_M_memory (GMT, NULL, size * I_obj->header->n_bands, unsigned char);
4639 				else if (I_obj->type <= GMT_USHORT)
4640 					I_obj->data = gmt_M_memory (GMT, NULL, size * I_obj->header->n_bands, unsigned short);
4641                 else if (I_obj->type <= GMT_UINT)
4642                     I_obj->data = gmt_M_memory (GMT, NULL, size * I_obj->header->n_bands, unsigned int);
4643                 else if (I_obj->type <= GMT_FLOAT)
4644                     I_obj->data = gmt_M_memory (GMT, NULL, size * I_obj->header->n_bands, float);
4645                 else if (I_obj->type <= GMT_DOUBLE)
4646                     I_obj->data = gmt_M_memory (GMT, NULL, size * I_obj->header->n_bands, float);   /* Not doing double yet */
4647 				else {
4648 					GMT_Report (API, GMT_MSG_ERROR, "Unsupported image data type %d\n", I_obj->type);
4649 					return_null (API, GMT_NOT_A_VALID_TYPE);
4650 				}
4651 			}
4652 			else {	/* Already have allocated space; check that it is enough */
4653 				if (size > I_obj->header->size) return_null (API, GMT_IMAGE_READ_ERROR);
4654 			}
4655 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading image from file %s\n", S_obj->filename);
4656 			if (gmtapi_import_ppm (GMT, S_obj->filename, I_obj) == GMT_NOERROR)
4657 				d = 0.0;	/* Placeholder */
4658 			else if (gmt_M_err_pass (GMT, gmtlib_read_image (GMT, S_obj->filename, I_obj, S_obj->wesn,
4659 				I_obj->header->pad, mode), S_obj->filename))
4660 				return_null (API, GMT_IMAGE_READ_ERROR);
4661 			if (I_obj->n_indexed_colors == 0) {	/* May set the BCs */
4662 				if (gmt_M_err_pass (GMT, gmtlib_image_BC_set (GMT, I_obj), S_obj->filename))
4663 					return_null (API, GMT_IMAGE_BC_ERROR);	/* Set boundary conditions */
4664 				bc_not_set = false;
4665 			}
4666 			IH = gmt_get_I_hidden (I_obj);
4667 			IH->alloc_mode = GMT_ALLOC_INTERNALLY;
4668 #else
4669 			GMT_Report (API, GMT_MSG_ERROR, "GDAL required to read image from file %s\n", S_obj->filename);
4670 #endif
4671 			break;
4672 
4673 	 	case GMT_IS_DUPLICATE:	/* GMT image and header in a GMT_IMAGE container object. */
4674 			if ((I_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
4675 			if (image == NULL) {	/* Only allocate when not already allocated */
4676 				if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY image must already be allocated */
4677 				I_obj = gmtlib_create_image (GMT);
4678 			}
4679 			else
4680 				I_obj = image;	/* We are passing in an image already */
4681 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read image */
4682 			if (! (mode & GMT_DATA_ONLY)) {	/* Must init header and copy the header information from the existing image */
4683 				gmt_copy_gridheader (GMT, I_obj->header, I_orig->header);
4684 				if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
4685 			}
4686 			/* Here we will read image data. */
4687 			/* To get a subset we use wesn that is not NULL or contain 0/0/0/0.
4688 			 * Otherwise we use everything passed in */
4689 			if (!I_obj->data) {	/* Array is not allocated, do so now. We only expect header (and possibly subset w/e/s/n) to have been set correctly */
4690 				I_obj->header->size = gmtapi_set_grdarray_size (GMT, I_obj->header, mode, S_obj->wesn);	/* Get array dimension only, which may include padding */
4691 				I_obj->data = gmt_M_memory (GMT, NULL, I_obj->header->size * I_obj->header->n_bands, unsigned char);
4692 				if (I_orig->alpha) I_obj->alpha = gmt_M_memory (GMT, NULL, I_obj->header->size , unsigned char);
4693 			}
4694 			IH = gmt_get_I_hidden (I_obj);
4695 			IH->alloc_mode = GMT_ALLOC_INTERNALLY;
4696 			if (!S_obj->region && gmt_grd_pad_status (GMT, I_obj->header, GMT->current.io.pad)) {	/* Want an exact copy with no subset and same padding */
4697 				GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating image data from GMT_IMAGE memory location\n");
4698 				gmt_M_memcpy (I_obj->data, I_orig->data, I_orig->header->size * I_orig->header->n_bands, char);
4699 				if (I_orig->alpha) gmt_M_memcpy (I_obj->alpha, I_orig->alpha, I_orig->header->size, char);
4700 				break;		/* Done with this image */
4701 			}
4702 			GMT_Report (API, GMT_MSG_INFORMATION, "Extracting subset image data from GMT_IMAGE memory location\n");
4703 			/* Here we need to do more work: Either extract subset or add/change padding, or both. */
4704 			/* Get start/stop row/cols for subset (or the entire domain) */
4705 			/* dx,dy are needed when the image is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
4706 			dx = I_obj->header->inc[GMT_X] * I_obj->header->xy_off;	dy = I_obj->header->inc[GMT_Y] * I_obj->header->xy_off;
4707 			j1 = (uint64_t) gmt_M_grd_y_to_row (GMT, I_obj->header->wesn[YLO]+dy, I_orig->header);
4708 			j0 = (uint64_t) gmt_M_grd_y_to_row (GMT, I_obj->header->wesn[YHI]-dy, I_orig->header);
4709 			i0 = (uint64_t) gmt_M_grd_x_to_col (GMT, I_obj->header->wesn[XLO]+dx, I_orig->header);
4710 			i1 = (uint64_t) gmt_M_grd_x_to_col (GMT, I_obj->header->wesn[XHI]-dx, I_orig->header);
4711 			gmt_M_memcpy (I_obj->header->pad, GMT->current.io.pad, 4, int);	/* Set desired padding */
4712 			for (row = j0; row <= j1; row++) {
4713 				for (col = i0; col <= i1; col++, ij++) {
4714 					ij_orig = gmt_M_ijp (I_orig->header, row, col);	/* Position of this (row,col) in original image organization */
4715 					ij = gmt_M_ijp (I_obj->header, row, col);		/* Position of this (row,col) in output image organization */
4716 					I_obj->data[ij] = I_orig->data[ij_orig];
4717 					if (I_orig->alpha) I_obj->alpha[ij] = I_orig->alpha[ij_orig];
4718 				}
4719 			}
4720 			break;
4721 
4722 	 	case GMT_IS_REFERENCE:	/* GMT image and header in a GMT_IMAGE container object by reference */
4723 			if (S_obj->region) return_null (API, GMT_SUBSET_NOT_ALLOWED);
4724 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing image data from GMT_IMAGE memory location\n");
4725 			if ((I_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
4726 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read image */
4727 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_image: Change alloc mode\n");
4728 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_image: Check pad\n");
4729 			if (!gmtapi_adjust_grdpadding (I_obj->header, GMT->current.io.pad)) break;	/* Pad is correct so we are done */
4730 			/* Here we extend G_obj->data to allow for padding, then rearrange rows, but only if item was allocated by GMT */
4731 			IH = gmt_get_I_hidden (I_obj);
4732 			if (IH->alloc_mode == GMT_ALLOC_EXTERNALLY) return_null (API, GMT_PADDING_NOT_ALLOWED);
4733 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_image: Add pad\n");
4734 #if 0
4735 			gmt_grd_pad_on (GMT, image, GMT->current.io.pad);
4736 #endif
4737 			if (done && S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
4738 				HH = gmt_get_H_hidden (I_obj->header);
4739 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory image */
4740 					gmtapi_contract_headerpad (GMT, I_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
4741 					S_obj->reset_pad = HH->reset_pad = 0;
4742 				}
4743 				if (gmtapi_expand_headerpad (GMT, I_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn))
4744 					S_obj->reset_pad = HH->reset_pad = 1;
4745 			}
4746 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_image: Return from GMT_IS_REFERENCE\n");
4747 			break;
4748 
4749 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* The user's 2-D image array of some sort, + info in the args [NOT YET FULLY TESTED] */
4750 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
4751 			if (S_obj->region) return_null (API, GMT_SUBSET_NOT_ALLOWED);
4752 			I_obj = (image == NULL) ? gmtlib_create_image (GMT) : image;	/* Only allocate when not already allocated */
4753 			HH = gmt_get_H_hidden (I_obj->header);
4754 			I_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
4755 			if (! (mode & GMT_DATA_ONLY)) {
4756 				gmtapi_matrixinfo_to_grdheader (GMT, I_obj->header, M_obj);	/* Populate a GRD header structure */
4757 				if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header */
4758 			}
4759 			IH = gmt_get_I_hidden (I_obj);
4760 			IH->alloc_mode = GMT_ALLOC_INTERNALLY;
4761 			/* Must convert to new array */
4762 			GMT_Report (API, GMT_MSG_INFORMATION, "Importing image data from user memory location\n");
4763 			gmt_set_grddim (GMT, I_obj->header);	/* Set all dimensions */
4764 			I_obj->data = gmt_M_memory (GMT, NULL, I_obj->header->size, unsigned char);
4765 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
4766 				return_null (API, GMT_WRONG_MATRIX_SHAPE);
4767 			if ((api_get_val = gmtapi_select_get_function (API, M_obj->type)) == NULL)
4768 				return_null (API, GMT_NOT_A_VALID_TYPE);
4769 			gmt_M_grd_loop (GMT, I_obj, row, col, ij) {
4770 				ij_orig = GMT_2D_to_index (row, col, M_obj->dim);
4771 				api_get_val (&(M_obj->data), ij_orig, &d);
4772 				I_obj->data[ij] = (char)d;
4773 			}
4774 			new_ID = GMT_Register_IO (API, GMT_IS_IMAGE, GMT_IS_DUPLICATE, S_obj->geometry, GMT_IN, NULL, I_obj);	/* Register a new resource to hold I_obj */
4775 			if ((new_item = gmtlib_validate_id (API, GMT_IS_IMAGE, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
4776 				return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
4777 			API->object[new_item]->resource = I_obj;
4778 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
4779 			API->object[new_item]->method = S_obj->method;
4780 			IH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
4781 			via = true;
4782 			if (S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
4783 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory image */
4784 					gmtapi_contract_headerpad (GMT, I_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
4785 					S_obj->reset_pad = HH->reset_pad = 0;
4786 				}
4787 				if (gmtapi_expand_headerpad (GMT, I_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn))
4788 					S_obj->reset_pad = HH->reset_pad = 1;
4789 			}
4790 			break;
4791 
4792 	 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:	/* The user's 2-D image array of some sort, + info in the args [NOT YET FULLY TESTED] */
4793 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
4794 			if (S_obj->region) return_null (API, GMT_SUBSET_NOT_ALLOWED);
4795 			I_obj = (image == NULL) ? gmtlib_create_image (GMT) : image;	/* Only allocate when not already allocated */
4796 			HH = gmt_get_H_hidden (I_obj->header);
4797 			I_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
4798 			if (! (mode & GMT_DATA_ONLY)) {
4799 				gmtapi_matrixinfo_to_grdheader (GMT, I_obj->header, M_obj);	/* Populate a GRD header structure */
4800 				if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header */
4801 			}
4802 			MH = gmt_get_M_hidden (M_obj);
4803 			if (!(M_obj->shape == GMT_IS_ROW_FORMAT && M_obj->type == GMT_FLOAT && MH->alloc_mode == GMT_ALLOC_EXTERNALLY && (mode & GMT_GRID_IS_COMPLEX_MASK))) {
4804 				return_null (API, GMT_NOT_A_VALID_IO_ACCESS);
4805 			}
4806 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing image data from user memory location\n");
4807 			IH = gmt_get_I_hidden (I_obj);
4808 			I_obj->data = (unsigned char *)(M_obj->data.sc1);
4809 			S_obj->alloc_mode = MH->alloc_mode;	/* Pass on allocation mode of matrix */
4810 			IH->alloc_mode = MH->alloc_mode;
4811 			if (!gmtapi_adjust_grdpadding (I_obj->header, GMT->current.io.pad)) break;	/* Pad is correct so we are done */
4812 			if (IH->alloc_mode == GMT_ALLOC_EXTERNALLY) {
4813 				return_null (API, GMT_PADDING_NOT_ALLOWED);
4814 			}
4815 			/* Here we extend I_obj->data to allow for padding, then rearrange rows */
4816 			/* gmt_grd_pad_on (GMT, I, GMT->current.io.pad);*/
4817 			new_ID = GMT_Register_IO (API, GMT_IS_IMAGE, GMT_IS_REFERENCE, S_obj->geometry, GMT_IN, NULL, I_obj);	/* Register a new resource to hold I_obj */
4818 			if ((new_item = gmtlib_validate_id (API, GMT_IS_IMAGE, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
4819 				return_null (API, GMT_OBJECT_NOT_FOUND);	/* Some internal error... */
4820 			API->object[new_item]->resource = I_obj;
4821 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
4822 			IH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
4823 			via = true;
4824 			if (S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
4825 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory image */
4826 					gmtapi_contract_headerpad (GMT, I_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
4827 					S_obj->reset_pad = HH->reset_pad = 0;
4828 				}
4829 				if (gmtapi_expand_headerpad (GMT, I_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn))
4830 					S_obj->reset_pad = HH->reset_pad = 1;
4831 			}
4832 			break;
4833 
4834 		default:
4835 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import image\n");
4836 			return_null (API, GMT_NOT_A_VALID_METHOD);
4837 			break;
4838 	}
4839 	if ((mode & GMT_CONTAINER_ONLY) == 0) {	/* Also allocate and initialize the x and y vectors */
4840 		I_obj->x = gmtapi_image_coord (API, GMT_X, I_obj);	/* Get array of x coordinates */
4841 		I_obj->y = gmtapi_image_coord (API, GMT_Y, I_obj);	/* Get array of y coordinates */
4842 	}
4843 
4844 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as read (unless we just got the header) */
4845 
4846 #ifdef HAVE_GDAL
4847 	if (no_index) {   /* true if we have an indexed image and we had to allocate a new one */
4848 		if (gmtapi_expand_index_image (API->GMT, I_obj, &Irgb)) {   /* true if we have a read-only indexed image and we had to allocate a new one */
4849 			if (GMT_Destroy_Data (API, &I_obj) != GMT_NOERROR) {
4850 				return_null (API, API->error);
4851 			}
4852 			I_obj = Irgb;
4853 		}
4854 		/* If we were unable to set BCs earlier we must do it now */
4855 		if (bc_not_set && gmt_M_err_pass (GMT, gmtlib_image_BC_set (GMT, I_obj), S_obj->filename))
4856 			return_null (API, GMT_IMAGE_BC_ERROR);	/* Failed to set boundary conditions */
4857 	}
4858 #endif
4859 
4860 	if (!via) S_obj->resource = I_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
4861 
4862 	return (I_obj);	/* Pass back out what we have so far */
4863 }
4864 
gmtapi_export_ppm(struct GMT_CTRL * GMT,char * fname,struct GMT_IMAGE * I)4865 GMT_LOCAL int gmtapi_export_ppm (struct GMT_CTRL *GMT, char *fname, struct GMT_IMAGE *I) {
4866 	/* Write a Portable Pixel Map (PPM) file if fname extension is .ppm, else returns 1.
4867 	 * We assume there is no pad, otherwise the pad will be part of the image on output. */
4868 	//uint32_t row, col, band;
4869 	static char *comment = "# Produced by GMT\n";
4870 	char *ext = gmt_get_ext (fname), dim[GMT_LEN32] = {""};
4871 	size_t n;
4872 	FILE *fp = NULL;
4873 	if (ext == NULL || strcmp (ext, "ppm")) return GMT_NOT_A_VALID_FAMILY;	/* Not requesting a PPM file - return GMT_NOT_A_VALID_FAMILY and let GDAL take over */
4874 
4875 	if ((fp = gmt_fopen (GMT, fname, GMT->current.io.w_mode)) == NULL) {	/* Return GMT_ERROR_ON_FOPEN to signify failure */
4876 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot create PPM file %s\n", fname);
4877 		return GMT_ERROR_ON_FOPEN;
4878 	}
4879 	if (I->header->n_bands == 1) /* Use P5 for grayscale image */
4880 		n = fwrite ("P5\n", sizeof (char), 3U, fp);	/* Write magic number, linefeed */
4881 	else	/* Use P6 for rgb image */
4882 		n = fwrite ("P6\n", sizeof (char), 3U, fp);	/* Write magic number, linefeed */
4883 	if (n != 3U) {
4884 		gmt_fclose (GMT, fp);
4885 		return GMT_IMAGE_WRITE_ERROR;
4886 	}
4887 	n = strlen (comment);
4888 	if (fwrite (comment, sizeof (char), n, fp) != n) {
4889 		gmt_fclose (GMT, fp);
4890 		return GMT_IMAGE_WRITE_ERROR;	/* Write comment and linefeed */
4891 	}
4892 	snprintf (dim, GMT_LEN32, "%d %d\n255\n", I->header->mx, I->header->my);
4893 	n = strlen (dim);
4894 	if (fwrite (dim, sizeof (char), n, fp) != n) return GMT_IMAGE_WRITE_ERROR;	/* Write dimensions and max color value + linefeeds */
4895 	/* Now dump the image in scanline order, with each pixel as (R, G, B) */
4896 	if (I->alpha)
4897 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Alpha-channel not supported by PPM format - ignored\n");
4898 	n = I->header->size * I->header->n_bands;
4899 	if (!strncmp (I->header->mem_layout, "TRP", 3U)) { /* Easy street! */
4900 		if (fwrite (I->data, sizeof(char), n, fp) != n) {
4901 			gmt_fclose (GMT, fp);
4902 			return GMT_IMAGE_WRITE_ERROR;
4903 		}
4904 	}
4905 	else {	/* Must change image layout first as PPM is strictly TRP */
4906 		char *data = NULL;
4907 		GMT_Report (GMT->parent, GMT_MSG_VERBOSE, "Must convert image from %s to TRP in order to write PPM file\n", I->header->mem_layout);
4908 		if ((data = gmt_M_memory_aligned (GMT, NULL, n, char)) == NULL) {
4909 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to allocate image memory in gmtapi_export_ppm to force TRP format - written as is\n");
4910 			if (fwrite (I->data, sizeof(char), n, fp) != n) {
4911 				gmt_fclose (GMT, fp);
4912 				return GMT_IMAGE_WRITE_ERROR;
4913 			}
4914 		}
4915 		else {	/* Convert from TRB to TRP */
4916 			GMT_Change_Layout (GMT->parent, GMT_IS_IMAGE, "TRP", 0, I, data, NULL);
4917 			if (fwrite (data, sizeof(char), n, fp) != n) return GMT_IMAGE_WRITE_ERROR;
4918 			gmt_M_free_aligned (GMT, data);
4919 		}
4920 	}
4921 	gmt_fclose (GMT, fp);
4922 
4923 	return GMT_NOERROR;
4924 }
4925 
4926 /*! Writes out a single image to destination */
gmtapi_export_image(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_IMAGE * I_obj)4927 GMT_LOCAL int gmtapi_export_image (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_IMAGE *I_obj) {
4928 	int item, error;
4929 	bool done = true;
4930 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
4931 	struct GMT_IMAGE *I_copy = NULL;
4932 	struct GMT_IMAGE_HIDDEN *IH = gmt_get_I_hidden (I_obj);
4933 
4934 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_image: Passed ID = %d and mode = %d\n", object_ID, mode);
4935 
4936 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
4937 	if (I_obj->data == NULL && !(mode & GMT_CONTAINER_ONLY)) return (gmtlib_report_error (API, GMT_PTR_IS_NULL));
4938 	if ((item = gmtlib_validate_id (API, GMT_IS_IMAGE, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
4939 
4940 	S_obj = API->object[item];	/* The current object whose data we will export */
4941 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET))
4942 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));	/* Only allow writing of a data set once, unless overridden by mode */
4943 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
4944 	switch (S_obj->method) {
4945 		case GMT_IS_FILE:	/* Name of an image file on disk */
4946 			GMT_Report (API, GMT_MSG_INFORMATION, "Writing image to file %s\n", S_obj->filename);
4947 			if ((error = gmtapi_export_ppm (API->GMT, S_obj->filename, I_obj)) == GMT_NOERROR)
4948 				break;	/* OK, wrote a PPM and we are done */
4949 			else if (error == GMT_ERROR_ON_FOPEN) {	/* Failed to open file */
4950 				GMT_Report (API, GMT_MSG_ERROR, "Unable to export image\n");
4951 				return (gmtlib_report_error (API, GMT_ERROR_ON_FOPEN));
4952 			}
4953 #ifdef HAVE_GDAL
4954 			else if (gmt_M_err_pass (API->GMT, gmt_export_image (API->GMT, S_obj->filename, I_obj), S_obj->filename))
4955 				return (gmtlib_report_error (API, GMT_IMAGE_WRITE_ERROR));
4956 #else
4957 			else
4958 				GMT_Report (API, GMT_MSG_ERROR, "GDAL required to write image to file %s\n", S_obj->filename);
4959 #endif
4960 			break;
4961 
4962 	 	case GMT_IS_DUPLICATE:	/* Duplicate GMT image to a new GMT_IMAGE container object */
4963 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource pointer must be NULL */
4964 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
4965 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating image data to GMT_IMAGE memory location\n");
4966 			if ((I_copy = GMT_Duplicate_Data (API, GMT_IS_IMAGE, mode, I_obj)) == NULL)
4967 				return (gmtlib_report_error (API, GMT_MEMORY_ERROR));
4968 			S_obj->resource = I_copy;	/* Set resource pointer to the image */
4969 			break;		/* Done with this image */
4970 
4971 	 	case GMT_IS_REFERENCE:	/* GMT image and header in a GMT_IMAGE container object - just pass the reference */
4972 			if (S_obj->region) return (gmtlib_report_error (API, GMT_SUBSET_NOT_ALLOWED));
4973 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
4974 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing image data to GMT_IMAGE memory location\n");
4975 			S_obj->resource = I_obj;	/* Set resource pointer to the image */
4976 			IH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
4977 			break;
4978 
4979 		default:
4980 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export image\n");
4981 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
4982 			break;
4983 	}
4984 
4985 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as written (unless we only updated header) */
4986 
4987 	return (GMT_NOERROR);
4988 }
4989 
gmt_whole_earth(struct GMT_CTRL * GMT,double we_in[],double we_out[])4990 unsigned int gmt_whole_earth (struct GMT_CTRL *GMT, double we_in[], double we_out[]) {
4991 	/* Determines if this is a global geographic grid and we want the whole world, regardless of central longitude */
4992 	if (!gmt_M_is_geographic (GMT, GMT_IN)) return 0;
4993 	if (!gmt_M_360_range (we_in[XLO],  we_in[XHI])) return 0;
4994 	if (!gmt_M_360_range (we_out[XLO], we_out[XHI])) return 0;
4995 	if (doubleAlmostEqualZero (we_in[XLO], we_out[XLO])) return 2;	/* Both regions are the same */
4996 	return 1;	/* Different central meridians */
4997 }
4998 
gmtapi_switch_method(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S,unsigned int * method,char * message)4999 GMT_LOCAL unsigned int gmtapi_switch_method (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S, unsigned int *method, char *message) {
5000 	/* Flip input method from GMT_IS_REFERENCE to GMT_IS_DUPLICATE */
5001 	*method -= GMT_IS_REFERENCE;
5002 	*method += GMT_IS_DUPLICATE;
5003 	S->method -= GMT_IS_REFERENCE;
5004 	S->method += GMT_IS_DUPLICATE;
5005 	GMT_Report (API, GMT_MSG_INFORMATION, message);
5006 	return GMT_IS_DUPLICATE;
5007 }
5008 
5009 /*! . */
gmtapi_import_grid(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_GRID * grid)5010 GMT_LOCAL struct GMT_GRID *gmtapi_import_grid (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_GRID *grid) {
5011 	/* Handles the reading of a 2-D grid given in one of several ways.
5012 	 * Get the entire grid:
5013  	 * 	mode = GMT_CONTAINER_AND_DATA reads both header and grid;
5014 	 * Get a subset of the grid:  Call gmtapi_import_grid twice:
5015 	 * 	1. first with mode = GMT_CONTAINER_ONLY which reads header only.  Then, pass
5016 	 *	   the new S_obj-> wesn to match your desired subregion
5017 	 *	2. 2nd with mode = GMT_DATA_ONLY, which reads grid based on header's settings
5018 	 * If the grid->data array is NULL it will be allocated for you.
5019 	 */
5020 
5021 	int item, new_item, new_ID, err;
5022 	bool done = true, new = false, row_by_row;
5023  	uint64_t row, col, kol, row_out, i0, i1, j0, j1, ij, ij_orig;
5024 	size_t size;
5025 	unsigned int both_set = (GMT_CONTAINER_ONLY | GMT_DATA_ONLY);
5026 	unsigned int method, start_over_method = 0;
5027 	double dx, dy, d;
5028 	p_func_uint64_t GMT_2D_to_index = NULL;
5029 	struct GMT_GRID *G_obj = NULL, *G_orig = NULL;
5030 	struct GMT_MATRIX *M_obj = NULL;
5031 	struct GMT_MATRIX_HIDDEN *MH = NULL;
5032 	struct GMT_GRID_HIDDEN *GH = NULL, *GH2 = NULL;
5033 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
5034 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
5035 	struct GMT_CTRL *GMT = API->GMT;
5036 	GMT_getfunction api_get_val = NULL;
5037 
5038 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_grid: Passed ID = %d and mode = %d\n", object_ID, mode);
5039 
5040 	if ((item = gmtlib_validate_id (API, GMT_IS_GRID, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET) return_null (API, API->error);
5041 
5042 	S_obj = API->object[item];		/* Current data object */
5043 	if (S_obj->status != GMT_IS_UNUSED && S_obj->method == GMT_IS_FILE && !(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Already read this file before, so fail unless overridden by mode */
5044 	if ((mode & both_set) == both_set) mode -= both_set;	/* Allow users to have set GMT_CONTAINER_ONLY | GMT_DATA_ONLY; reset to GMT_CONTAINER_AND_DATA */
5045 	row_by_row = ((mode & GMT_GRID_ROW_BY_ROW) || (mode & GMT_GRID_ROW_BY_ROW_MANUAL));
5046 	if (row_by_row && S_obj->method != GMT_IS_FILE) {
5047 		GMT_Report (API, GMT_MSG_ERROR, "Can only use method GMT_IS_FILE when row-by-row reading of grid is selected\n");
5048 		return_null (API, GMT_NOT_A_VALID_METHOD);
5049 	}
5050 	if ((mode & GMT_CONTAINER_ONLY) && S_obj->region && S_obj->method == GMT_IS_FILE) {
5051 		GMT_Report (API, GMT_MSG_ERROR, "Cannot request a subset when just inquiring about the grid header\n");
5052 		return_null (API, GMT_SUBSET_NOT_ALLOWED);
5053 	}
5054 
5055 	if (S_obj->region && grid) {	/* See if this is really a subset or just the same region as the grid */
5056 		if (grid->header->wesn[XLO] == S_obj->wesn[XLO] && grid->header->wesn[XHI] == S_obj->wesn[XHI] && grid->header->wesn[YLO] == S_obj->wesn[YLO] && grid->header->wesn[YHI] == S_obj->wesn[YHI]) S_obj->region = false;
5057 	}
5058 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use since may be MATRIX or VECTOR masquerading as GRID */
5059 
5060 start_over_import_grid:		/* We may get here if we cannot honor a GMT_IS_REFERENCE from below */
5061 
5062 	switch (method) {
5063 		/* Status: This case is fully tested and operational */
5064 		case GMT_IS_FILE:	/* Name of a grid file on disk */
5065 			if (gmt_file_is_tiled_list (API, S_obj->filename, NULL, NULL, NULL)) {	/* Special list file of individual grid tiles */
5066 				if (grid == NULL) {	/* Only allocate grid struct when not already allocated */
5067 					if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY the grid must already be allocated */
5068                     if (API->got_remote_wesn) { /* Use the tile information */
5069 						unsigned g_reg = (API->tile_reg == 'g') ? GMT_GRID_NODE_REG : GMT_GRID_PIXEL_REG;
5070 						double g_inc[2] = {API->tile_inc, API->tile_inc}; /* Must duplicate due to syntax below */
5071 						if (!full_region (API->tile_wesn)) {	/* Ensure any -R via DCW is rounded by these known increments */
5072 							API->tile_wesn[XLO] = floor ((API->tile_wesn[XLO] / g_inc[GMT_X]) + GMT_CONV8_LIMIT) * g_inc[GMT_X];
5073 							API->tile_wesn[XHI] = ceil  ((API->tile_wesn[XHI] / g_inc[GMT_X]) - GMT_CONV8_LIMIT) * g_inc[GMT_X];
5074 							API->tile_wesn[YLO] = floor ((API->tile_wesn[YLO] / g_inc[GMT_Y]) + GMT_CONV8_LIMIT) * g_inc[GMT_Y];
5075 							API->tile_wesn[YHI] = ceil  ((API->tile_wesn[YHI] / g_inc[GMT_Y]) - GMT_CONV8_LIMIT) * g_inc[GMT_Y];
5076 						}
5077 	 					if ((G_obj = gmt_create_grid (API->GMT)) == NULL)
5078 	 						return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
5079 						if (gmtapi_init_grid (API, NULL, NULL, API->tile_wesn, g_inc, g_reg, GMT_CONTAINER_ONLY, GMT_IN, G_obj))
5080 	 						return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
5081 						S_obj->resource = grid = G_obj;	/* Set resource pointer to the grid */
5082 					}
5083                     else {  /* Should not (cannot) happen */
5084                         GMT_Report (API, GMT_MSG_ERROR, "No w/e/s/n dx/dy reg found for this tiled dataset? Internal error\n");
5085                            return_null (API, GMT_RUNTIME_ERROR);    /* Allocation error */
5086                     }
5087 					if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
5088 				}
5089 				/* Here we must assemble to grid from the list of tiles */
5090 				if ((G_obj = gmtlib_assemble_tiles (API, NULL, S_obj->filename)) == NULL)
5091 					return_null (API, GMT_GRID_READ_ERROR);
5092 				if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), S_obj->filename))
5093 					return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5094 				/* Sneaky internal recycling of the contents of the grid structure based on final results in G_obj */
5095 				GH  = gmt_get_G_hidden (grid);
5096 				GH2 = gmt_get_G_hidden (G_obj);
5097                 /* Copy over the hidden grid settings obtained in grdblend */
5098 				GH->alloc_mode  = GH2->alloc_mode;
5099 				GH->alloc_level = GH2->alloc_level;
5100 				GH->xy_alloc_mode[GMT_X] = GH2->xy_alloc_mode[GMT_X];
5101 				GH->xy_alloc_mode[GMT_Y] = GH2->xy_alloc_mode[GMT_Y];
5102 				gmt_copy_gridheader (API->GMT, grid->header, G_obj->header);	/* Update the header with more info, such as z_min/z_max */
5103 				grid->data = G_obj->data;	/* Pass long the data pointer... */
5104 				grid->x = G_obj->x;	/* ...and the x and y arrays */
5105 				grid->y = G_obj->y;
5106 				G_obj->x = G_obj->y = NULL;	/* Wipe them from the G_obj structure so we can free G_obj without clobbering the arrays now pointed to by grid */
5107 				G_obj->data = NULL;
5108 				GMT_Destroy_Data (API, &G_obj);	/* Destroy this registered object which has nothing of value anymore */
5109 				G_obj = grid;	/* Refresh the G_obj to be the original pointer so it can be returned */
5110 				break;	/* DOne with all operations involving reading a tiled dataset */
5111 			}
5112 
5113 			/* When source is an actual file we place the grid container into the S_obj->resource slot; no new object required */
5114 			if (grid == NULL) {	/* Only allocate grid struct when not already allocated */
5115 				if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY grid must already be allocated */
5116 				G_obj = gmt_create_grid (GMT);
5117 				new = true;
5118 			}
5119 			else
5120 				G_obj = grid;	/* We are working on a grid already allocated */
5121 			S_obj->resource = G_obj;	/* Set resource pointer to the grid */
5122 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read grid */
5123 			GH = gmt_get_G_hidden (G_obj);
5124 			if (! (mode & GMT_DATA_ONLY)) {		/* Must init header and read the header information from file */
5125 				if (row_by_row) {	/* Special row-by-row processing mode */
5126 					char r_mode = (mode & GMT_GRID_NO_HEADER) ? 'R' : 'r';
5127 					/* If we get here more than once we only allocate extra once */
5128 					if (GH->extra == NULL) GH->extra = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID_ROWBYROW);
5129 					if (gmtapi_open_grd (GMT, S_obj->filename, G_obj, r_mode, mode)) {	/* Open the grid for incremental row reading */
5130 						if (new) gmt_free_grid (GMT, &G_obj, false);
5131 						return_null (API, GMT_GRID_READ_ERROR);
5132 					}
5133 				}
5134 				else if (gmt_M_err_pass (GMT, gmtlib_read_grd_info (GMT, S_obj->filename, G_obj->header), S_obj->filename)) {
5135 					if (new) gmt_free_grid (GMT, &G_obj, false);
5136 					return_null (API, GMT_GRID_READ_ERROR);
5137 				}
5138 				if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
5139 			}
5140 			/* Here we will read the grid data themselves. */
5141 			/* To get a subset we use wesn that is not NULL or contain 0/0/0/0.
5142 			 * Otherwise we extract the entire file domain */
5143 			HH = gmt_get_H_hidden (G_obj->header);
5144 			/* Ensure a region set via DCW is properly rounded */
5145 			if (!full_region (S_obj->wesn) && (err = gmt_M_err_fail (GMT, gmt_adjust_loose_wesn (GMT, S_obj->wesn, G_obj->header), "")))
5146 				return_null (API, GMT_RUNTIME_ERROR);
5147 			size = gmtapi_set_grdarray_size (GMT, G_obj->header, mode, S_obj->wesn);	/* Get array dimension only, which includes padding */
5148 			if (!G_obj->data) {	/* Array is not allocated yet, do so now. We only expect header (and possibly w/e/s/n subset) to have been set correctly */
5149 				G_obj->header->size = size;
5150 				G_obj->data = gmt_M_memory_aligned (GMT, NULL, G_obj->header->size, gmt_grdfloat);
5151 			}
5152 			else {	/* Already have allocated space; check that it is enough */
5153 				if (size > G_obj->header->size) return_null (API, GMT_GRID_READ_ERROR);
5154 			}
5155 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading grid from file %s\n", S_obj->filename);
5156 			if (gmt_M_err_pass (GMT, gmtlib_read_grd (GMT, S_obj->filename, G_obj->header, G_obj->data, S_obj->wesn,
5157 							GMT->current.io.pad, mode), S_obj->filename))
5158 				return_null (API, GMT_GRID_READ_ERROR);
5159 			if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), S_obj->filename))
5160 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5161 			GH->alloc_mode = GMT_ALLOC_INTERNALLY;
5162 			break;
5163 
5164 	 	case GMT_IS_DUPLICATE:	/* GMT grid and header in a GMT_GRID container object. */
5165 			/* Must duplicate the grid container from S_obj->resource and hence a new object is required */
5166 			if ((G_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
5167 			if (grid == NULL) {	/* Only allocate when not already allocated */
5168 				if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY grid must already be allocated */
5169 				if ((G_obj = GMT_Duplicate_Data (API, GMT_IS_GRID, GMT_DUPLICATE_NONE, G_orig)) == NULL)
5170 					return_null (API, GMT_MEMORY_ERROR);
5171 			}
5172 			else
5173 				G_obj = grid;	/* We are passing in a grid already */
5174 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read grid */
5175             if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
5176 			/* Here we will read grid data. */
5177 			/* To get a subset we use wesn that is not NULL or contain 0/0/0/0.
5178 			 * Otherwise we use everything passed in */
5179 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating grid data from GMT_GRID memory location\n");
5180 			if (!G_obj->data) {	/* Array is not allocated, do so now. We only expect header (and possibly subset w/e/s/n) to have been set correctly */
5181 				G_obj->header->size = gmtapi_set_grdarray_size (GMT, G_obj->header, mode, S_obj->wesn);	/* Get array dimension only, which may include padding */
5182 				G_obj->data = gmt_M_memory_aligned (GMT, NULL, G_obj->header->size, gmt_grdfloat);
5183 			}
5184 			GH = gmt_get_G_hidden (G_obj);
5185 			GH->alloc_mode = GMT_ALLOC_INTERNALLY;
5186 			HH = gmt_get_H_hidden (G_obj->header);
5187 			if (HH->grdtype > GMT_GRID_CARTESIAN) gmt_set_geographic (GMT, GMT_IN);
5188 			if (!S_obj->region && gmt_grd_pad_status (GMT, G_obj->header, GMT->current.io.pad)) {	/* Want an exact copy with no subset and same padding */
5189 				gmt_M_memcpy (G_obj->data, G_orig->data, G_orig->header->size, gmt_grdfloat);
5190 				gmt_BC_init (GMT, G_obj->header);	/* Initialize grid interpolation and boundary condition parameters */
5191 				if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), "Grid memory"))
5192 					return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5193 				break;		/* Done with this grid */
5194 			}
5195 			/* Here we need to do more work: Either extract subset or add/change padding, or both. */
5196 			/* Get start/stop row/cols for subset (or the entire domain) */
5197 			/* dx,dy are needed when the grid is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
5198 			dx = G_obj->header->inc[GMT_X] * G_obj->header->xy_off;	dy = G_obj->header->inc[GMT_Y] * G_obj->header->xy_off;
5199 			j1 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YLO]+dy, G_orig->header);
5200 			j0 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YHI]-dy, G_orig->header);
5201 			i0 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XLO]+dx, G_orig->header);
5202 			i1 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XHI]-dx, G_orig->header);
5203 			gmt_M_memcpy (G_obj->header->pad, GMT->current.io.pad, 4, int);	/* Set desired padding */
5204 			gmt_M_memcpy (G_obj->header->wesn, S_obj->wesn, 4U, double);	/* Update the grid header region to match subset request */
5205 			gmt_set_grddim (GMT, G_obj->header);	/* Adjust all dimensions accordingly before accessing the grid for output */
5206 			/* get stats */
5207 			HH = gmt_get_H_hidden (G_obj->header);
5208 			G_obj->header->z_min = DBL_MAX;
5209 			G_obj->header->z_max = -DBL_MAX;
5210 			HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
5211 			for (row = j0, row_out = 0; row <= j1; row++, row_out++) {
5212 				ij = gmt_M_ijp (G_obj->header, row_out, 0);	/* Position in output grid at start of current row */
5213 				for (col = i0; col <= i1; col++, ij++) {
5214 					kol = col % G_orig->header->n_columns;
5215 					ij_orig = gmt_M_ijp (G_orig->header, row, kol);	/* Position of this (row,col) in original grid organization */
5216 					G_obj->data[ij] = G_orig->data[ij_orig];
5217 					if (gmt_M_is_fnan (G_obj->data[ij]))
5218 						HH->has_NaNs = GMT_GRID_HAS_NANS;
5219 					else {
5220 						G_obj->header->z_min = MIN (G_obj->header->z_min, G_obj->data[ij]);
5221 						G_obj->header->z_max = MAX (G_obj->header->z_max, G_obj->data[ij]);
5222 					}
5223 				}
5224 			}
5225 			gmt_BC_init (GMT, G_obj->header);	/* Initialize grid interpolation and boundary condition parameters */
5226 			if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), "Grid memory"))
5227 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5228 			break;
5229 
5230 	 	case GMT_IS_REFERENCE:	/* GMT grid and header in a GMT_GRID container object by reference [NOT SURE ABOUT THIS] */
5231 			if (S_obj->region) return_null (API, GMT_SUBSET_NOT_ALLOWED);
5232 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing grid data from GMT_GRID memory location\n");
5233 			if ((G_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
5234 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read grid */
5235 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_grid: Change alloc mode\n");
5236 			GH = gmt_get_G_hidden (G_obj);
5237 			S_obj->alloc_mode = GH->alloc_mode;
5238 			HH = gmt_get_H_hidden (G_obj->header);
5239 			if (HH->grdtype > GMT_GRID_CARTESIAN) gmt_set_geographic (GMT, GMT_IN);
5240 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_grid: Check pad\n");
5241 			gmt_BC_init (GMT, G_obj->header);	/* Initialize grid interpolation and boundary condition parameters */
5242 			if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), "Grid memory"))
5243 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5244 			if (!gmtapi_adjust_grdpadding (G_obj->header, GMT->current.io.pad)) break;	/* Pad is correct so we are done */
5245 			/* Here we extend G_obj->data to allow for padding, then rearrange rows */
5246 			if (GH->alloc_mode == GMT_ALLOC_EXTERNALLY) return_null (API, GMT_PADDING_NOT_ALLOWED);
5247 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_grid: Add pad\n");
5248 			gmt_grd_pad_on (GMT, G_obj, GMT->current.io.pad);
5249 			if (done && S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
5250 				HH = gmt_get_H_hidden (G_obj->header);
5251 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory grid */
5252 					gmtapi_contract_headerpad (GMT, G_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
5253 					S_obj->reset_pad = HH->reset_pad = 0;
5254 				}
5255 				if (gmtapi_expand_headerpad (GMT, G_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn)) {
5256 					S_obj->reset_pad = HH->reset_pad = 1;
5257 					gmtapi_update_grid_minmax (API->GMT, G_obj);	/* Update z-range */
5258 				}
5259 			}
5260 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_grid: Return from GMT_IS_REFERENCE\n");
5261 			break;
5262 
5263 		/* Status: This case is fully tested and operational */
5264 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* The user's 2-D grid array of some sort, + info in the matrix header */
5265 			/* Must create a grid container from matrix info S_obj->resource and hence a new object is required */
5266 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
5267 			if (grid == NULL) {	/* Only allocate when not already allocated, and only allocate container */
5268 				uint64_t dim[3] = {M_obj->n_columns, M_obj->n_rows, 1};
5269 				if ((G_obj = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, dim, M_obj->range, M_obj->inc, M_obj->registration, GMT_NOTSET, NULL)) == NULL)
5270 					return_null (API, GMT_MEMORY_ERROR);
5271 			}
5272 			else
5273 				G_obj = grid;
5274 			if ((new_ID = gmtapi_get_object (API, GMT_IS_GRID, G_obj)) == GMT_NOTSET)
5275 				return_null (API, GMT_OBJECT_NOT_FOUND);
5276 			if ((new_item = gmtlib_validate_id (API, GMT_IS_GRID, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
5277 				return_null (API, GMT_OBJECT_NOT_FOUND);
5278 			API->object[new_item]->method = S_obj->method;
5279 			GH = gmt_get_G_hidden (G_obj);
5280 			HH = gmt_get_H_hidden (G_obj->header);
5281 			G_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
5282 			GH->alloc_mode = GMT_ALLOC_INTERNALLY;
5283 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read grid */
5284 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
5285 				return_null (API, GMT_WRONG_MATRIX_SHAPE);
5286 			if ((api_get_val = gmtapi_select_get_function (API, M_obj->type)) == NULL)
5287 				return_null (API, GMT_NOT_A_VALID_TYPE);
5288 			HH = gmt_get_H_hidden (G_obj->header);
5289 
5290 			if (! (mode & GMT_DATA_ONLY)) {	/* Must first init header and copy the header information from the matrix header */
5291 				gmtapi_matrixinfo_to_grdheader (GMT, G_obj->header, M_obj);	/* Populate a GRD header structure */
5292 				if (HH->grdtype > GMT_GRID_CARTESIAN) gmt_set_geographic (GMT, GMT_IN);
5293 				/* Must get the full zmin/max range since not provided by the matrix header */
5294 					G_obj->header->z_min = +DBL_MAX;
5295 					G_obj->header->z_max = -DBL_MAX;
5296 					HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
5297 					gmt_M_grd_loop (GMT, G_obj, row, col, ij) {
5298 					ij_orig = GMT_2D_to_index (row, col, M_obj->dim);
5299 					api_get_val (&(M_obj->data), ij_orig, &d);
5300 					if (gmt_M_is_dnan (d))
5301 						HH->has_NaNs = GMT_GRID_HAS_NANS;
5302 					else {
5303 						G_obj->header->z_min = MIN (G_obj->header->z_min, (gmt_grdfloat)d);
5304 						G_obj->header->z_max = MAX (G_obj->header->z_max, (gmt_grdfloat)d);
5305 					}
5306 				}
5307 				if (mode & GMT_CONTAINER_ONLY)	/* Just needed the header */
5308 					break;	/* Done for now */
5309 			}
5310 
5311 			GMT_Report (API, GMT_MSG_INFORMATION, "Importing grid data from user matrix memory location\n");
5312 
5313 			/* Get start/stop row/cols for subset (or the entire domain) */
5314 			/* dx,dy are needed when the grid is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
5315 			if (!S_obj->region || gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {	/* Easy, get the whole enchilada */
5316 				j0 = i0 = 0;
5317 				j1 = G_obj->header->n_rows    - 1;	/* Minus 1 since we loop up to and including below */
5318 				i1 = G_obj->header->n_columns - 1;
5319 			}
5320 			else {	/* Want a subset */
5321 				/* Use dx/dy which will be nonzero for pixel grids */
5322 				dx = G_obj->header->inc[GMT_X] * G_obj->header->xy_off;	dy = G_obj->header->inc[GMT_Y] * G_obj->header->xy_off;
5323 				if (gmt_M_is_geographic (GMT, GMT_IN)) { /* Must first wrap S_obj->wesn to fit the data if necessary */
5324 					if (S_obj->wesn[XLO] > G_obj->header->wesn[XHI]) { /* Must first wrap G_obj->header->wesn west to fit the data */
5325 						G_obj->header->wesn[XLO] += 360.0;	G_obj->header->wesn[XHI] += 360.0;
5326 					}
5327 					else if (S_obj->wesn[XHI] < G_obj->header->wesn[XLO]) { /* Must first wrap G_obj->header->wesn east to fit the data */
5328 						G_obj->header->wesn[XLO] -= 360.0;	G_obj->header->wesn[XHI] -= 360.0;
5329 					}
5330 				}
5331 				j1 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YLO]+dy, G_obj->header);
5332 				j0 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YHI]-dy, G_obj->header);
5333 				i0 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XLO]+dx, G_obj->header);
5334 				i1 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XHI]-dx, G_obj->header);
5335 				gmt_M_memcpy (G_obj->header->wesn, S_obj->wesn, 4U, double);	/* Update the grid header region to match subset request */
5336 				gmt_set_grddim (GMT, G_obj->header);	/* Adjust all dimensions accordingly before allocating space */
5337 			}
5338 			if (G_obj->data) {	/* This is an error - there cannot be a data pointer yet */
5339 				GMT_Report (API, GMT_MSG_ERROR, "G->data is not NULL when memory allocation is about to happen\n");
5340 				return_null (API, GMT_PTR_IS_NULL);
5341 			}
5342 			else
5343 				G_obj->data = gmt_M_memory_aligned (GMT, NULL, G_obj->header->size, gmt_grdfloat);
5344 
5345 			G_obj->header->z_min = DBL_MAX;
5346 			G_obj->header->z_max = -DBL_MAX;
5347 			for (row = j0, row_out = 0; row <= j1; row++, row_out++) {
5348 				ij = gmt_M_ijp (G_obj->header, row_out, 0);	/* Position in output grid at start of current row */
5349 				for (col = i0; col <= i1; col++, ij++) {
5350 					kol = col % M_obj->n_columns;
5351 					ij_orig = GMT_2D_to_index (row, kol, M_obj->dim);	/* Position of this (row,col) in input matrix organization */
5352 					api_get_val (&(M_obj->data), ij_orig, &d);	/* Get the next item from the matrix */
5353 					G_obj->data[ij] = (gmt_grdfloat)d;
5354 					if (gmt_M_is_dnan (d))
5355 						HH->has_NaNs = GMT_GRID_HAS_NANS;
5356 					else {
5357 						G_obj->header->z_min = MIN (G_obj->header->z_min, (gmt_grdfloat)d);
5358 						G_obj->header->z_max = MAX (G_obj->header->z_max, (gmt_grdfloat)d);
5359 					}
5360 				}
5361 			}
5362 			if (gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {
5363 				/* Global grids passed via matrix are not rotated to fit the desired global region, so we need to correct the wesn for this grid to match the matrix */
5364 				gmt_M_memcpy (G_obj->header->wesn, M_obj->range, 4U, double);
5365 			}
5366 			gmt_BC_init (GMT, G_obj->header);	/* Initialize grid interpolation and boundary condition parameters */
5367 			if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_IN), "Grid memory"))
5368 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5369 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
5370 			API->object[new_item]->actual_family = GMT_IS_GRID;	/* Done reading from matrix */
5371 			if (start_over_method) API->object[new_item]->method = start_over_method;	/* We changed our mind from reference to duplicate due to region */
5372 			GH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
5373 			break;
5374 
5375 	 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:	/* The user's 2-D grid array of some sort, + info in the args [NOT YET FULLY TESTED] */
5376 			/* Getting a matrix info S_obj->resource. Create grid header and then pass the grid pointer via the matrix pointer */
5377 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
5378 			/* If passed by reference and the module requires a pad then we must switch to duplication */
5379 			if (mode & GMT_GRID_NEEDS_PAD1 || mode & GMT_GRID_NEEDS_PAD2) {	/* Cannot do this by reference, switch to duplication */
5380 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Grid via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE due to a padding requirement - method has been switched\n");
5381 				goto start_over_import_grid;
5382 			}
5383 			/* This method requires the input data to be a GMT_GRD_FORMAT matrix - otherwise we should be DUPLICATING */
5384 			if (!(M_obj->shape == GMT_IS_ROW_FORMAT && M_obj->type == GMT_GRDFLOAT && (mode & GMT_GRID_IS_COMPLEX_MASK) == 0)) {
5385 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Grid via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE due to incompatible data type for a grid - method has been switched\n");
5386 				goto start_over_import_grid;
5387 			}
5388 			/* Determine if it is possible to use the matrix given the region selected and the fact we chose GMT_IS_REFERENCE. This test will
5389 			 * only kick in after we allocate the G_obj and come back the second time (after getting header) since otherwise S_obj->wesn is not set yet */
5390 			if (!(!S_obj->region ||
5391 				(S_obj->wesn[XLO] >= M_obj->range[XLO] && S_obj->wesn[XHI] <= M_obj->range[XHI] && S_obj->wesn[YLO] >= M_obj->range[YLO] && S_obj->wesn[YHI] <= M_obj->range[YHI]) ||
5392 				gmt_whole_earth (GMT, M_obj->range, S_obj->wesn))) {	/* Cannot do this by reference, switch to duplication */
5393 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Subset selection for grid via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE - method has been switched\n");
5394 				goto start_over_import_grid;
5395 			}
5396 
5397 			if (grid == NULL) {	/* Only allocate when not already allocated, and only get container.  Note: Cannot have pad since input matrix won't have one */
5398 				uint64_t dim[3] = {M_obj->n_rows, M_obj->n_columns, 1};
5399 				if ((G_obj = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, dim, M_obj->range, M_obj->inc, M_obj->registration, 0, NULL)) == NULL)
5400 					return_null (API, GMT_MEMORY_ERROR);
5401 			}
5402 			else
5403 				G_obj = grid;
5404 			HH = gmt_get_H_hidden (G_obj->header);
5405 			G_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
5406 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read grid */
5407 			if (! (mode & GMT_DATA_ONLY)) {
5408 				gmtapi_matrixinfo_to_grdheader (GMT, G_obj->header, M_obj);	/* Populate a GRD header structure */
5409 				if (HH->grdtype > GMT_GRID_CARTESIAN) gmt_set_geographic (GMT, GMT_IN);
5410 				/* Temporarily set data pointer for convenience; removed later */
5411 #ifdef DOUBLE_PRECISION_GRID
5412 				G_obj->data = M_obj->data.f8;
5413 #else
5414 				G_obj->data = M_obj->data.f4;
5415 #endif
5416 				G_obj->header->z_min = +DBL_MAX;
5417 				G_obj->header->z_max = -DBL_MAX;
5418 				HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
5419 				gmt_M_grd_loop (GMT, G_obj, row, col, ij) {
5420 					if (gmt_M_is_fnan (G_obj->data[ij]))
5421 						HH->has_NaNs = GMT_GRID_HAS_NANS;
5422 					else {
5423 						G_obj->header->z_min = MIN (G_obj->header->z_min, G_obj->data[ij]);
5424 						G_obj->header->z_max = MAX (G_obj->header->z_max, G_obj->data[ij]);
5425 					}
5426 				}
5427 				G_obj->data = NULL;	/* Since data are not requested yet */
5428 				if (mode & GMT_CONTAINER_ONLY)	/* Just needed the header but had to set zmin/zmax first */
5429 					break;
5430 			}
5431 			if ((new_ID = gmtapi_get_object (API, GMT_IS_GRID, G_obj)) == GMT_NOTSET)
5432 				return_null (API, GMT_OBJECT_NOT_FOUND);
5433 			if ((new_item = gmtlib_validate_id (API, GMT_IS_GRID, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
5434 				return_null (API, GMT_OBJECT_NOT_FOUND);
5435 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing grid data from user memory location\n");
5436 #ifdef DOUBLE_PRECISION_GRID
5437 			G_obj->data = M_obj->data.f8;
5438 #else
5439 			G_obj->data = M_obj->data.f4;
5440 #endif
5441 			GH = gmt_get_G_hidden (G_obj);
5442 			MH = gmt_get_M_hidden (M_obj);
5443 			S_obj->alloc_mode = MH->alloc_mode;	/* Pass on alloc_mode of matrix */
5444 			GH->alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Since we cannot have both M and G try to free */
5445 			API->object[new_item]->resource = G_obj;
5446 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
5447 			GH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
5448 			if (gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {
5449 				/* Global grids passed via matrix are not rotated to fit the desired global region, so we need to correct the wesn for this grid to match the matrix */
5450 				gmt_M_memcpy (G_obj->header->wesn, M_obj->range, 4U, double);
5451 			}
5452 			else if (S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
5453 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory grid */
5454 					gmtapi_contract_headerpad (GMT, G_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
5455 					S_obj->reset_pad = 0;
5456 				}
5457 				if (gmtapi_expand_headerpad (GMT, G_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn)) {
5458 					S_obj->reset_pad = 1;
5459 					gmtapi_update_grid_minmax (GMT, G_obj);	/* Update z-range */
5460 				}
5461 			}
5462 			break;
5463 
5464 		default:
5465 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import grid\n");
5466 			return_null (API, GMT_NOT_A_VALID_METHOD);
5467 			break;
5468 	}
5469 
5470 	if ((mode & GMT_CONTAINER_ONLY) == 0) {	/* Also allocate and initialize the x and y vectors unless already present  */
5471 		if (G_obj->x == NULL) {
5472 			GH->xy_alloc_mode[GMT_X] = GMT_ALLOC_INTERNALLY;
5473 			if (GMT->current.io.nc_xarray)	/* Got variable x-array and asked to used this instead */
5474 				G_obj->x = GMT->current.io.nc_xarray, GMT->current.io.nc_xarray = NULL;
5475 			else
5476 				G_obj->x = gmtapi_grid_coord (API, GMT_X, G_obj);	/* Get array of x coordinates */
5477 		}
5478 		if (G_obj->y == NULL) {
5479 			GH->xy_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
5480 			if (GMT->current.io.nc_yarray)	/* Got variable y-array and asked to used this instead */
5481 				G_obj->y = GMT->current.io.nc_yarray, GMT->current.io.nc_yarray = NULL;
5482 			else
5483 				G_obj->y = gmtapi_grid_coord (API, GMT_Y, G_obj);	/* Get array of y coordinates */
5484 		}
5485 	}
5486 
5487 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as read (unless we just got the header) */
5488 
5489 	return (G_obj);	/* Pass back out what we have so far */
5490 }
5491 
gmtapi_cube_set_units(struct GMT_CTRL * GMT,struct GMT_CUBE * U)5492 GMT_LOCAL void gmtapi_cube_set_units (struct GMT_CTRL *GMT, struct GMT_CUBE *U) {
5493 	/* Set unit strings for cube coordinates x, y and z based on
5494 	   output data types for columns 0, 1, and 2.
5495 	*/
5496 	unsigned int i;
5497 	char *string[4] = {NULL, NULL, NULL, NULL}, unit[GMT_GRID_UNIT_LEN80] = {""};
5498 	char date[GMT_LEN16] = {""}, clock[GMT_LEN16] = {""};
5499 	struct GMT_GRID_HEADER *header = U->header;
5500 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
5501 
5502 	/* Copy pointers to unit strings */
5503 	string[0] = header->x_units;
5504 	string[1] = header->y_units;
5505 	string[2] = U->units;
5506 	string[3] = header->z_units;
5507 
5508 	/* Use input data type as backup for output data type */
5509 	for (i = 0; i < 4; i++)
5510 		if (gmt_M_type (GMT, GMT_OUT, i) == GMT_IS_UNKNOWN) GMT->current.io.col_type[GMT_OUT][i] = GMT->current.io.col_type[GMT_IN][i];
5511 
5512 	/* Catch some anomalies */
5513 	if (gmt_M_type (GMT, GMT_OUT, GMT_X) == GMT_IS_LAT && gmt_M_type (GMT, GMT_OUT, GMT_Y) == GMT_IS_LAT) {
5514 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Output type for X-coordinate of grid %s is LAT. Replaced by LON.\n", HH->name);
5515 		gmt_set_column_type (GMT, GMT_OUT, GMT_X, GMT_IS_LON);
5516 
5517 	}
5518 	if (gmt_M_type (GMT, GMT_OUT, GMT_Y) == GMT_IS_LON && gmt_M_type (GMT, GMT_OUT, GMT_X) == GMT_IS_LON) {
5519 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Output type for Y-coordinate of grid %s is LON. Replaced by LAT.\n", HH->name);
5520 		gmt_set_column_type (GMT, GMT_OUT, GMT_Y, GMT_IS_LAT);
5521 	}
5522 
5523 	/* Set unit strings one by one based on output type - or fall through if Cartesian floats */
5524 	for (i = 0; i < 4; i++) {
5525 		switch (gmt_M_type (GMT, GMT_OUT, i)) {
5526 			case GMT_IS_LON:
5527 				strcpy (string[i], "longitude [degrees_east]"); break;
5528 			case GMT_IS_LAT:
5529 				strcpy (string[i], "latitude [degrees_north]"); break;
5530 			case GMT_IS_ABSTIME:
5531 			case GMT_IS_RELTIME:
5532 			case GMT_IS_RATIME:
5533 				/* Determine time unit */
5534 				switch (GMT->current.setting.time_system.unit) {
5535 					case 'y':
5536 						strcpy (unit, "years"); break;
5537 					case 'o':
5538 						strcpy (unit, "months"); break;
5539 					case 'd':
5540 						strcpy (unit, "days"); break;
5541 					case 'h':
5542 						strcpy (unit, "hours"); break;
5543 					case 'm':
5544 						strcpy (unit, "minutes"); break;
5545 					default:
5546 						strcpy (unit, "seconds"); break;
5547 				}
5548 				gmt_format_calendar (GMT, date, clock, &GMT->current.io.date_output, &GMT->current.io.clock_output, false, 1, 0.0);
5549 				snprintf (string[i], GMT_GRID_UNIT_LEN80, "time [%s since %s %s]", unit, date, clock);
5550 				/* Warning for non-double cubes */
5551 				if (i == 3 && GMT->session.grdformat[header->type][1] != 'd')
5552 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Use double precision output cube to avoid loss of significance of time coordinate.\n");
5553 				break;
5554 		}
5555 	}
5556 }
5557 
5558 /*! Writes out a single grid to destination */
gmtapi_export_grid(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_GRID * G_obj)5559 GMT_LOCAL int gmtapi_export_grid (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_GRID *G_obj) {
5560 	int item, error;
5561 	bool done = true, row_by_row;
5562 	unsigned int method;
5563 	uint64_t row, col, i0, i1, j0, j1, ij, ijp, ij_orig;
5564 	size_t size;
5565 	double dx, dy;
5566 	p_func_uint64_t GMT_2D_to_index = NULL;
5567 	GMT_putfunction api_put_val = NULL;
5568 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
5569 	struct GMT_GRID *G_copy = NULL;
5570 	struct GMT_MATRIX *M_obj = NULL;
5571 	struct GMT_MATRIX_HIDDEN *MH = NULL;
5572 	struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (G_obj), *GH2 = NULL;
5573 	struct GMT_CTRL *GMT = API->GMT;
5574 
5575 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_grid: Passed ID = %d and mode = %d\n", object_ID, mode);
5576 
5577 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
5578 	if (G_obj->data == NULL && !(mode & GMT_CONTAINER_ONLY)) return (gmtlib_report_error (API, GMT_PTR_IS_NULL));
5579 	if ((item = gmtlib_validate_id (API, GMT_IS_GRID, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
5580 
5581 	S_obj = API->object[item];	/* The current object whose data we will export */
5582 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET))
5583 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));	/* Only allow writing of a data set once, unless overridden by mode */
5584 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
5585 	row_by_row = ((mode & GMT_GRID_ROW_BY_ROW) || (mode & GMT_GRID_ROW_BY_ROW_MANUAL));
5586 	if (row_by_row && S_obj->method != GMT_IS_FILE) {
5587 		GMT_Report (API, GMT_MSG_ERROR, "Can only use method GMT_IS_FILE when row-by-row writing of grid is selected\n");
5588 		return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
5589 	}
5590 	if (S_obj->region) {	/* See if this is really a subset or just the same region as the grid */
5591 		if (G_obj->header->wesn[XLO] == S_obj->wesn[XLO] && G_obj->header->wesn[XHI] == S_obj->wesn[XHI] && G_obj->header->wesn[YLO] == S_obj->wesn[YLO] && G_obj->header->wesn[YHI] == S_obj->wesn[YHI]) S_obj->region = false;
5592 	}
5593 	if (mode & GMT_DATA_IS_GEO) gmt_set_geographic (GMT, GMT_OUT);	/* From API to tell grid is geographic */
5594 	gmtlib_grd_set_units (GMT, G_obj->header);	/* Ensure unit strings are set, regardless of destination */
5595 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use since may be MATRIX or VECTOR masquerading as GRID */
5596 	switch (method) {
5597 		case GMT_IS_FILE:	/* Name of a grid file on disk */
5598 			if (mode & GMT_CONTAINER_ONLY) {	/* Update header structure only */
5599 				GMT_Report (API, GMT_MSG_INFORMATION, "Updating grid header for file %s\n", S_obj->filename);
5600 				if (row_by_row) {	/* Special row-by-row processing mode */
5601 					char w_mode = (mode & GMT_GRID_NO_HEADER) ? 'W' : 'w';
5602 					/* Since we may get here twice (initial write; later update) we only allocate extra if NULL */
5603 					if (GH->extra == NULL) GH->extra = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID_ROWBYROW);
5604 					if (gmtapi_open_grd (GMT, S_obj->filename, G_obj, w_mode, mode))	/* Open the grid for incremental row writing */
5605 						return (gmtlib_report_error (API, GMT_GRID_WRITE_ERROR));
5606 				}
5607 				else if (gmt_update_grd_info (GMT, NULL, G_obj->header))
5608 					return (gmtlib_report_error (API, GMT_GRID_WRITE_ERROR));
5609 				done = false;	/* Since we are not done with writing */
5610 			}
5611 			else {
5612 				GMT_Report (API, GMT_MSG_INFORMATION, "Writing grid to file %s\n", S_obj->filename);
5613 				if (gmt_M_err_pass (GMT, gmtlib_write_grd (GMT, S_obj->filename, G_obj->header, G_obj->data, S_obj->wesn, G_obj->header->pad, mode), S_obj->filename)) return (gmtlib_report_error (API, GMT_GRID_WRITE_ERROR));
5614 			}
5615 			break;
5616 
5617 	 	case GMT_IS_DUPLICATE:	/* Duplicate GMT grid and header to a GMT_GRID container object. Subset allowed */
5618 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource pointer must be NULL */
5619 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
5620 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating grid data to GMT_GRID memory location\n");
5621 			if (!S_obj->region) {	/* No subset, possibly same padding */
5622 				G_copy = gmt_duplicate_grid (API->GMT, G_obj, GMT_DUPLICATE_DATA);
5623 				GH2 = gmt_get_G_hidden (G_copy);
5624 				GH2->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
5625 				if (gmtapi_adjust_grdpadding (G_copy->header, GMT->current.io.pad))
5626 					gmt_grd_pad_on (GMT, G_copy, GMT->current.io.pad);
5627 				gmt_BC_init (GMT, G_copy->header);	/* Initialize grid interpolation and boundary condition parameters */
5628 				if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_copy, GMT_OUT), "Grid memory")) return (gmtlib_report_error (API, GMT_GRID_BC_ERROR));	/* Set boundary conditions */
5629 				S_obj->resource = G_copy;	/* Set resource pointer to the grid */
5630 				break;		/* Done with this grid */
5631 			}
5632 			/* Here we need to extract subset, and possibly change padding. */
5633 			/* Get start/stop row/cols for subset (or the entire domain) */
5634 			G_copy = gmt_create_grid (GMT);
5635 			GH2 = gmt_get_G_hidden (G_copy);
5636 			GH2->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
5637 			gmt_copy_gridheader (GMT, G_copy->header, G_obj->header);
5638 			gmt_M_memcpy (G_copy->header->wesn, S_obj->wesn, 4, double);
5639 			/* dx,dy are needed when the grid is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
5640 			dx = G_obj->header->inc[GMT_X] * G_obj->header->xy_off;	dy = G_obj->header->inc[GMT_Y] * G_obj->header->xy_off;
5641 			j1 = (unsigned int) gmt_M_grd_y_to_row (GMT, G_obj->header->wesn[YLO]+dy, G_obj->header);
5642 			j0 = (unsigned int) gmt_M_grd_y_to_row (GMT, G_obj->header->wesn[YHI]-dy, G_obj->header);
5643 			i0 = (unsigned int) gmt_M_grd_x_to_col (GMT, G_obj->header->wesn[XLO]+dx, G_obj->header);
5644 			i1 = (unsigned int) gmt_M_grd_x_to_col (GMT, G_obj->header->wesn[XHI]-dx, G_obj->header);
5645 			gmt_M_memcpy (G_obj->header->pad, GMT->current.io.pad, 4, int);		/* Set desired padding */
5646 			G_copy->header->size = gmtapi_set_grdarray_size (GMT, G_obj->header, mode, S_obj->wesn);	/* Get array dimension only, which may include padding */
5647 			G_copy->data = gmt_M_memory_aligned (GMT, NULL, G_copy->header->size, gmt_grdfloat);
5648 			G_copy->header->z_min = DBL_MAX;	G_copy->header->z_max = -DBL_MAX;	/* Must set zmin/zmax since we are not writing to file */
5649 			for (row = j0; row <= j1; row++) {
5650 				for (col = i0; col <= i1; col++, ij++) {
5651 					ij_orig = gmt_M_ijp (G_obj->header, row, col);	/* Position of this (row,col) in original grid organization */
5652 					ij = gmt_M_ijp (G_copy->header, row, col);	/* Position of this (row,col) in output grid organization */
5653 					G_copy->data[ij] = G_obj->data[ij_orig];
5654 					if (gmt_M_is_fnan (G_copy->data[ij])) continue;
5655 					/* Update z_min, z_max */
5656 					G_copy->header->z_min = MIN (G_copy->header->z_min, (double)G_copy->data[ij]);
5657 					G_copy->header->z_max = MAX (G_copy->header->z_max, (double)G_copy->data[ij]);
5658 				}
5659 			}
5660 			S_obj->resource = G_copy;	/* Set resource pointer to the grid */
5661 			break;
5662 
5663 	 	case GMT_IS_REFERENCE:	/* GMT grid and header in a GMT_GRID container object - just pass the reference */
5664 			if (S_obj->region) return (gmtlib_report_error (API, GMT_SUBSET_NOT_ALLOWED));
5665 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
5666 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing grid data to GMT_GRID memory location\n");
5667 			gmt_grd_zminmax (GMT, G_obj->header, G_obj->data);	/* Must set zmin/zmax since we are not writing to file */
5668 			gmt_BC_init (GMT, G_obj->header);	/* Initialize grid interpolation and boundary condition parameters */
5669 			if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G_obj, GMT_OUT), "Grid memory")) return (gmtlib_report_error (API, GMT_GRID_BC_ERROR));	/* Set boundary conditions */
5670 			S_obj->resource = G_obj;	/* Set resource pointer to the grid */
5671 			GH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
5672 			break;
5673 
5674 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* The user's 2-D grid array of some sort, + info in the args [NOT FULLY TESTED] */
5675 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
5676 			if (S_obj->resource) {	/* The output resource pointer already exist for matrix */
5677 				M_obj = gmtapi_get_matrix_data (S_obj->resource);
5678 				if (M_obj->n_rows < G_obj->header->n_rows || M_obj->n_columns < G_obj->header->n_columns)
5679 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
5680 			}
5681 			else {	/* Must allocate stuff */
5682 		 		M_obj = gmtlib_create_matrix (API->GMT, 1, GMT_IS_OUTPUT, 0);
5683 				M_obj->type = S_obj->type;
5684 			}
5685 			MH = gmt_get_M_hidden (M_obj);
5686 			gmtapi_grdheader_to_matrixinfo (GMT, G_obj->header, M_obj);	/* Populate an array with GRD header information */
5687 			M_obj->dim = (M_obj->shape == GMT_IS_ROW_FORMAT) ? M_obj->n_columns : M_obj->n_rows;	/* Matrix layout order */
5688 			GMT_Report (API, GMT_MSG_INFORMATION, "Exporting grid data to user memory location\n");
5689 			if (S_obj->resource == NULL) {	/* Must allocate output */
5690 				size = gmt_M_get_nm (GMT, G_obj->header->n_columns, G_obj->header->n_rows);
5691 				if ((error = gmtlib_alloc_univector (GMT, &(M_obj->data), M_obj->type, size)) != GMT_NOERROR) return (error);
5692 				MH->alloc_mode = GMT_ALLOC_INTERNALLY;
5693 			}
5694 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
5695 				return (gmtlib_report_error (API, GMT_WRONG_MATRIX_SHAPE));
5696 			if ((api_put_val = gmtapi_select_put_function (API, M_obj->type)) == NULL)
5697 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_TYPE));
5698 			gmt_M_grd_loop (GMT, G_obj, row, col, ijp) {
5699 				ij = GMT_2D_to_index (row, col, M_obj->dim);
5700 				api_put_val (&(M_obj->data), ij, (double)G_obj->data[ijp]);
5701 			}
5702 			MH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
5703 			S_obj->resource = M_obj;	/* Set resource pointer to the matrix */
5704 			break;
5705 
5706 	 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:	/* Write to a user matrix of type gmt_grdfloat */
5707 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
5708 			if (mode & GMT_GRID_IS_COMPLEX_MASK)	/* Cannot do a complex grid this way */
5709 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_IO_ACCESS));
5710 			if (S_obj->resource) {	/* The output resource pointer already exist for matrix */
5711 				M_obj = gmtapi_get_matrix_data (S_obj->resource);
5712 				if (M_obj->n_rows < G_obj->header->n_rows || M_obj->n_columns < G_obj->header->n_columns)
5713 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
5714 				assert (M_obj->type == GMT_GRDFLOAT);	/* That is the whole point of getting here, no? */
5715 			}
5716 			else {	/* Must allocate stuff */
5717 		 		M_obj = gmtlib_create_matrix (API->GMT, 1, GMT_IS_OUTPUT, 1);
5718 				M_obj->type = GMT_GRDFLOAT;	/* A grid is always gmt_grdfloat */
5719 			}
5720 			MH = gmt_get_M_hidden (M_obj);
5721 			if (gmtapi_adjust_grdpadding (G_obj->header, GMT_no_pad))
5722 				gmt_grd_pad_on (GMT, G_obj, GMT_no_pad);	/* Adjust pad */
5723 			/* This method requires the output data to be a gmt_grdfloat matrix - otherwise we should be DUPLICATING.
5724 			   This distinction is set in GMT_Open_VirtualFile */
5725 			gmtapi_grdheader_to_matrixinfo (GMT, G_obj->header, M_obj);	/* Populate an array with GRD header information */
5726 			M_obj->shape = GMT_IS_ROW_FORMAT;	/* Because it is a direct GMT gmt_grdfloat grid */
5727 			if (S_obj->resource) {
5728 				GMT_Report (API, GMT_MSG_INFORMATION, "Memcpy grid data to user memory location\n");
5729 #ifdef DOUBLE_PRECISION_GRID
5730 				gmt_M_memcpy (M_obj->data.f8, G_obj->data, G_obj->header->nm, double);
5731 #else
5732 				gmt_M_memcpy (M_obj->data.f4, G_obj->data, G_obj->header->nm, float);
5733 #endif
5734 			}
5735 			else {
5736 				GMT_Report (API, GMT_MSG_INFORMATION, "Referencing grid data to user memory location\n");
5737 #ifdef DOUBLE_PRECISION_GRID
5738 				M_obj->data.f8 = G_obj->data;
5739 #else
5740 				M_obj->data.f4 = G_obj->data;
5741 #endif
5742 			}
5743 			MH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
5744 			S_obj->resource = M_obj;	/* Set resource pointer to the matrix */
5745 			break;
5746 
5747 		default:
5748 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export grids\n");
5749 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
5750 			break;
5751 	}
5752 
5753 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as written (unless we only updated header) */
5754 
5755 	return (GMT_NOERROR);
5756 }
5757 
GMT_Put_Levels(void * V_API,struct GMT_CUBE * C,double * levels,uint64_t n_levels)5758 int GMT_Put_Levels (void *V_API, struct GMT_CUBE *C, double *levels, uint64_t n_levels) {
5759 	/* Duplicate and assign a level array to the cube for its 3rd dimension coordinates */
5760 	struct GMT_CUBE_HIDDEN *CU;
5761 	struct GMTAPI_CTRL *API = NULL;
5762 
5763 	/* Check for NULL and void arguments */
5764 	if (V_API == NULL) return_error (API, GMT_NOT_A_SESSION);
5765 	if (levels == NULL) return_error (API, GMT_PTR_IS_NULL);
5766 	if (n_levels == 0) return_error (API, GMT_DIM_TOO_SMALL);
5767 	if (C == NULL) return_error (API, GMT_PTR_IS_NULL);
5768 	if (C->z) return_error (API, GMT_PTR_NOT_NULL);
5769 	if (C->header == NULL) return_error (API, GMT_PTR_IS_NULL);
5770 	if (C->header->n_bands > 0) {	/* If set then it better match */
5771 		if ((uint64_t)C->header->n_bands < n_levels) return_error (API, GMT_DIM_TOO_SMALL);
5772 		if ((uint64_t)C->header->n_bands > n_levels) return_error (API, GMT_DIM_TOO_LARGE);
5773 	}
5774 	if ((CU = gmt_get_U_hidden (C)) == NULL) return_error (API, GMT_PTR_IS_NULL);
5775 	API = gmtapi_get_api_ptr (V_API);
5776 	if ((C->z = gmt_duplicate_array (API->GMT, levels, n_levels)) == NULL) return_error (API, GMT_MEMORY_ERROR);
5777 	CU->xyz_alloc_mode[GMT_Z] = GMT_ALLOC_INTERNALLY;	/* Since allocated by GMT */
5778 	C->header->n_bands = (uint32_t)n_levels;
5779 
5780 	return (GMT_NOERROR);
5781 }
5782 
5783 #ifdef FORTRAN_API
GMT_Put_Levels_(struct GMT_CUBE * C,double * level,uint64_t * n)5784 int GMT_Put_Levels_ (struct GMT_CUBE *C, double *level, uint64_t *n) {
5785 	/* Fortran version: We pass the global GMT_FORTRAN structure */
5786 	return (GMT_Put_Levels (GMT_FORTRAN, C, level, *n));
5787 }
5788 #endif
5789 
5790 /*! . */
gmtapi_import_cube(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_CUBE * cube)5791 GMT_LOCAL struct GMT_CUBE * gmtapi_import_cube (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_CUBE *cube) {
5792 	/* Handles the reading of a 3-D data cube given in one of several ways.
5793 	 * Get the entire cube:
5794  	 * 	mode = GMT_CONTAINER_AND_DATA reads both header and cube;
5795 	 * Get a subset of the cube:  Call gmtapi_import_cube twice:
5796 	 * 	1. first with mode = GMT_CONTAINER_ONLY which reads header only.  Then, pass
5797 	 *	   the new S_obj-> wesn to match your desired subregion
5798 	 *	2. 2nd with mode = GMT_DATA_ONLY, which reads cube based on header's settings
5799 	 * If the cube->data array is NULL it will be allocated for you.
5800 	 */
5801 
5802 	char file[PATH_MAX] = {""};
5803 	int item, new_item, new_ID;
5804 	bool done = true;
5805  	uint64_t row, col, kol, row_out, i0, i1, j0, j1, k0, k1, ij, ij_orig;
5806 	uint64_t n_layers = 0, k, n_layers_used, here;
5807 	unsigned int both_set = (GMT_CONTAINER_ONLY | GMT_DATA_ONLY);
5808 	unsigned int method, start_over_method = 0;
5809 	double dx, dy, d;
5810 	double *level = NULL, z_min, z_max, w_range[2] = {0.0, 0.0};
5811 	p_func_uint64_t GMT_2D_to_index = NULL;
5812 	struct GMT_CUBE *U_obj = NULL, *U_orig = NULL;
5813 	struct GMT_GRID *G = NULL;
5814 	struct GMT_MATRIX *M_obj = NULL;
5815 	struct GMT_MATRIX_HIDDEN *MH = NULL;
5816 	struct GMT_CUBE_HIDDEN *UH = NULL;
5817 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
5818 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
5819 	struct GMT_CTRL *GMT = API->GMT;
5820 	GMT_getfunction api_get_val = NULL;
5821 
5822 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_cube: Passed ID = %d and mode = %d\n", object_ID, mode);
5823 
5824 	if ((item = gmtlib_validate_id (API, GMT_IS_CUBE, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET) return_null (API, API->error);
5825 
5826 	S_obj = API->object[item];		/* Current data object */
5827 	if (S_obj->status != GMT_IS_UNUSED && S_obj->method == GMT_IS_FILE && !(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Already read this file before, so fail unless overridden by mode */
5828 	if ((mode & both_set) == both_set) mode -= both_set;	/* Allow users to have set GMT_CONTAINER_ONLY | GMT_DATA_ONLY; reset to GMT_CONTAINER_AND_DATA */
5829 	if ((mode & GMT_CONTAINER_ONLY) && S_obj->region && S_obj->method == GMT_IS_FILE) {
5830 		GMT_Report (API, GMT_MSG_ERROR, "Cannot request a subset when just inquiring about the cube header\n");
5831 		return_null (API, GMT_SUBSET_NOT_ALLOWED);
5832 	}
5833 
5834 	if (S_obj->region && cube) {	/* See if this is really a subset or just the same region as the cube */
5835 		if (cube->header->wesn[XLO] == S_obj->wesn[XLO] && cube->header->wesn[XHI] == S_obj->wesn[XHI] && \
5836 			cube->header->wesn[YLO] == S_obj->wesn[YLO] && cube->header->wesn[YHI] == S_obj->wesn[YHI] && \
5837 			cube->z_range[0] == S_obj->wesn[ZLO] && cube->z_range[1] == S_obj->wesn[ZHI])
5838 				S_obj->region = false;
5839 	}
5840 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use since may be MATRIX or VECTOR masquerading as GRID */
5841 
5842 start_over_import_cube:		/* We may get here if we cannot honor a GMT_IS_REFERENCE from below */
5843 
5844 	switch (method) {
5845 		/* Status: This case is fully tested and operational */
5846 		case GMT_IS_FILE:	/* Name of a cube file on disk */
5847 			/* When source is an actual file we place the cube container into the S_obj->resource slot; no new object required */
5848 			/* If we need to read the header etc then we based this on the first layer in the cube */
5849 			if ((mode & GMT_DATA_ONLY) == 0) {	/* Get the cube header information */
5850 				char cube_layer[GMT_LEN64] = {""}, z_name[GMT_GRID_UNIT_LEN80] = {""}, *nc_z_named = NULL, *the_file = NULL;
5851 				/* Got a single 3-D cube netCDF name, possibly selecting a specific variable via ?<name> */
5852 				the_file = strdup (S_obj->filename);		/* Duplicate filename since we may change it */
5853 				nc_z_named = strchr (the_file, '?');	/* Maybe given a specific variable? */
5854 				if (nc_z_named) {	/* Gave a specific layer. Keep variable name and truncate the filename */
5855 					strcpy (cube_layer, &nc_z_named[1]);	/* Place variable name in cube_layer string */
5856 					nc_z_named[0] = '\0';	/* Chop off layer name for now */
5857 				}
5858 				if (gmt_nc_read_cube_info (GMT, the_file, w_range, &n_layers, &level, z_name)) {	/* Learn the basics about the cube */
5859 					GMT_Report (API, GMT_MSG_ERROR, "gmtapi_import_cube: Unable to examine cube %s.\n", the_file);
5860 					gmt_M_str_free (the_file);
5861 					return_null (API, GMT_RUNTIME_ERROR);
5862 				}
5863 				sprintf (file, "%s?%s[0]", the_file, cube_layer);	/* Read cube header from the first layer in the cube */
5864 				/* Read the first layer grid */
5865 				if ((G = GMT_Read_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, NULL, file, NULL)) == NULL) {
5866 					gmt_M_str_free (the_file);
5867 					return_null (API, GMT_RUNTIME_ERROR);
5868 				}
5869 				/* Allocate a data cube structure and fill in the file information */
5870 				U_obj = gmtlib_create_cube (GMT);
5871 				gmt_copy_gridheader (GMT, U_obj->header, G->header);
5872 				if (level) {	/* Got an array of levels from the 3-D grid */
5873 					U_obj->z_range[0] = level[0];	U_obj->z_range[1] = level[n_layers-1];
5874 					if (n_layers < 3 || !gmtlib_var_inc (level, n_layers))	/* Equidistant layering */
5875 						U_obj->z_inc = level[1] - level[0];	/* Since they are all the same */
5876 					U_obj->z = level;	/* Let C be the owner of this array from now on */
5877 					UH = gmt_get_U_hidden (U_obj);
5878 					UH->xyz_alloc_mode[GMT_Z] = GMT_ALLOC_INTERNALLY;
5879 				}
5880 				U_obj->header->n_bands = n_layers;
5881 				U_obj->mode = mode;
5882 				if (nc_z_named) strncpy (U_obj->name, cube_layer, GMT_GRID_VARNAME_LEN80);	/* Remember this name if given */
5883 				strncpy (U_obj->units, z_name, GMT_GRID_UNIT_LEN80);	/* Place the cube's z-unit (for z-dimension) */
5884 				HH = gmt_get_H_hidden (U_obj->header);
5885 				strncpy (HH->name, the_file, GMT_GRID_NAME_LEN256);	/* Filename minus any specified variable */
5886 				if (nc_z_named) nc_z_named[0] = '?';	/* Restore layer name in file name */
5887 				gmt_M_str_free (the_file);
5888 				if (GMT_Destroy_Data (API, &G)) {	/* Must use GMT_Destroy_Data since G was registered in GMT_Read_Data */
5889 					gmtlib_free_cube (GMT, &U_obj, true);
5890 					return_null (API, GMT_RUNTIME_ERROR);
5891 				}
5892 			}
5893 			else
5894 				U_obj = cube;	/* We are working on a cube already allocated */
5895 			S_obj->resource = U_obj;	/* Set resource pointer to the cube */
5896 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read cube */
5897 			if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
5898 
5899 			/* Here we have the cube header and possibly the levels */
5900 			HH = gmt_get_H_hidden (U_obj->header);
5901 
5902 			/* Determine which layers we want to read */
5903 			k0 = 0;	k1 = U_obj->header->n_bands - 1;	/* All layers selected */
5904 			if (S_obj->region && S_obj->wesn[ZHI] > S_obj->wesn[ZLO]) {	/* Want a subset of layers */
5905 				if (U_obj->z == NULL) {
5906 					GMT_Report (API, GMT_MSG_ERROR, "gmtapi_import_cube: No layer level array available in GMT_IS_CUBE.\n");
5907 					gmtlib_free_cube (GMT, &U_obj, true);
5908 					return_null (API, GMT_PTR_IS_NULL);
5909 				}
5910 				else if (gmt_get_active_layers (GMT, U_obj, &(S_obj->wesn[ZLO]), &k0, &k1) == 0) {
5911 					gmtlib_free_cube (GMT, &U_obj, true);
5912 					return_null (API, GMT_PTR_IS_NULL);
5913 				}
5914 			}
5915 			n_layers_used = k1 - k0 + 1;	/* Total number of layers actually to be read */
5916 			if (n_layers_used == 0) {
5917 				GMT_Report (API, GMT_MSG_ERROR, "gmtapi_import_cube: No layers selected from GMT_IS_CUBE.\n");
5918 				gmtlib_free_cube (GMT, &U_obj, true);
5919 				return_null (API, GMT_DIM_TOO_SMALL);
5920 			}
5921 
5922 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading cube from file %s\n", S_obj->filename);
5923 			for (k = k0; k <= k1; k++) {	/* Read the required layers into individual grid structures */
5924 				/* Get the k'th layer from 3D cube possibly via a selected variable name */
5925 				sprintf (file, "%s?%s[%" PRIu64 "]", HH->name, U_obj->name, k);
5926 				/* Read in the layer as a temporary grid */
5927 				if ((G = GMT_Read_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_GRID_ALL, S_obj->wesn, file, NULL)) == NULL) {
5928 					GMT_Report (API, GMT_MSG_ERROR, "gmtapi_import_cube: Unable to read layer %" PRIu64 " from file %s.\n", k, file);
5929 					gmtlib_free_cube (GMT, &U_obj, true);
5930 					return_null (API, GMT_GRID_READ_ERROR);
5931 				}
5932 				if (U_obj->data == NULL) {	/* Update grid header (due to possible subsets) and allocate cube the first time */
5933 					n_layers = U_obj->header->n_bands;	/* Remember full number of layers before overwriting in the next line */
5934 					if (S_obj->region) gmt_copy_gridheader (GMT, U_obj->header, G->header);	/* Since subset can have changed dims and ranges */
5935 					U_obj->header->n_bands = n_layers_used;	/* New number of layers */
5936 					U_obj->z_range[0] = U_obj->z[k0];
5937 					U_obj->z_range[1] = U_obj->z[k1];
5938 					if (k0) {	/* Eliminate levels not included and shrink length of array */
5939 						memmove (U_obj->z, &U_obj->z[k0], n_layers_used * sizeof(double));
5940 						gmt_M_memset (&U_obj->z[n_layers_used], n_layers-n_layers_used, double);
5941 						U_obj->z = gmt_M_memory (API->GMT, U_obj->z, n_layers_used, double);
5942 					}
5943 					/* Allocate cube data (note: each layer has padding) */
5944 					U_obj->data = gmt_M_memory_aligned (API->GMT, NULL, U_obj->header->size * n_layers_used, gmt_grdfloat);
5945 					z_min = U_obj->header->z_min;	/* Initialize cube min/max values based on this first layer */
5946 					z_max = U_obj->header->z_max;
5947 					here = 0;	/* Initialize offset into k'th layer */
5948 				}
5949 				else {	/* Here we update min/max for subsequent layers read */
5950 					if (G->header->z_min < z_min) z_min = G->header->z_min;
5951 					if (G->header->z_max > z_max) z_max = G->header->z_max;
5952 				}
5953 				/* Place this layer in the cube */
5954 				gmt_M_memcpy (&U_obj->data[here], G->data, U_obj->header->size, gmt_grdfloat);
5955 				here += U_obj->header->size;	/* Advance the offset */
5956 				if (GMT_Destroy_Data (API, &G)) {	/* Must eliminate this registered resource */
5957 					gmtlib_free_cube (GMT, &U_obj, true);
5958 					return_null (API, GMT_RUNTIME_ERROR);
5959 				}
5960 			}
5961 			/* Update cube min/max */
5962 			U_obj->header->z_min = z_min;
5963 			U_obj->header->z_max = z_max;
5964 			/* Set BCs per layer */
5965 			gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
5966 			if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_IN), "Cube memory"))
5967 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5968 			break;
5969 
5970 	 	case GMT_IS_DUPLICATE:	/* GMT cube and header in a GMT_CUBE container object. */
5971 			/* Must duplicate the cube container from S_obj->resource and hence a new object is required */
5972 			if ((U_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
5973 			if (cube == NULL) {	/* Only allocate when not already allocated */
5974 				if (mode & GMT_DATA_ONLY) return_null (API, GMT_NO_GRDHEADER);		/* For mode & GMT_DATA_ONLY cube must already be allocated */
5975 				if ((U_obj = GMT_Duplicate_Data (API, GMT_IS_GRID, GMT_DUPLICATE_NONE, U_orig)) == NULL)
5976 					return_null (API, GMT_MEMORY_ERROR);
5977 			}
5978 			else
5979 				U_obj = cube;	/* We are passing in a cube already */
5980 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read cube */
5981             if (mode & GMT_CONTAINER_ONLY) break;	/* Just needed the header, get out of here */
5982 			/* Here we will read cube data. */
5983 			/* To get a subset we use region that is not NULL or contain 0/0/0/0/0/0.
5984 			 * Otherwise we use everything passed in */
5985 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating cube data from GMT_CUBE memory location\n");
5986 			if (!U_obj->data) {	/* Array is not allocated, do so now. We only expect header (and possibly subset w/e/s/n/z0/z1) to have been set correctly */
5987 				U_obj->header->size = gmtapi_set_grdarray_size (GMT, U_obj->header, mode, S_obj->wesn);	/* Get x/y array dimension only, which may include padding */
5988 				U_obj->data = gmt_M_memory_aligned (GMT, NULL, U_obj->header->size * U_obj->header->n_bands, gmt_grdfloat);
5989 			}
5990 			UH = gmt_get_U_hidden (U_obj);
5991 			UH->alloc_mode = GMT_ALLOC_INTERNALLY;
5992 			if (!S_obj->region && gmt_grd_pad_status (GMT, U_obj->header, GMT->current.io.pad)) {	/* Want an exact copy with no subset and same padding */
5993 				gmt_M_memcpy (U_obj->data, U_orig->data, U_orig->header->size * U_orig->header->n_bands, gmt_grdfloat);
5994 				gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
5995 				if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_IN), "Cube memory"))
5996 					return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
5997 				break;		/* Done with this cube */
5998 			}
5999 			/* Here we need to do more work: Either extract subset or add/change padding, or both. */
6000 			/* Get start/stop row/cols for subset (or the entire domain) */
6001 			/* dx,dy are needed when the cube is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
6002 			dx = U_obj->header->inc[GMT_X] * U_obj->header->xy_off;	dy = U_obj->header->inc[GMT_Y] * U_obj->header->xy_off;
6003 			j1 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YLO]+dy, U_orig->header);
6004 			j0 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YHI]-dy, U_orig->header);
6005 			i0 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XLO]+dx, U_orig->header);
6006 			i1 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XHI]-dx, U_orig->header);
6007 			(void) gmt_get_active_layers (GMT, U_orig, &(S_obj->wesn[ZLO]), &k0, &k1);
6008 			gmt_M_memcpy (U_obj->header->pad, GMT->current.io.pad, 4, int);	/* Set desired padding */
6009 			gmt_M_memcpy (U_obj->header->wesn, S_obj->wesn, 4U, double);	/* Update the cube header region to match subset request */
6010 			gmt_M_memcpy (U_obj->z_range, &(S_obj->wesn[ZLO]), 2U, double);	/* Update the cube range to match subset request */
6011 			gmt_set_grddim (GMT, U_obj->header);	/* Adjust all dimensions accordingly before accessing the cube for output */
6012 			U_obj->header->n_bands = k1 - k0 + 1;
6013 			/* get stats */
6014 			HH = gmt_get_H_hidden (U_obj->header);
6015 			U_obj->header->z_min = DBL_MAX;
6016 			U_obj->header->z_max = -DBL_MAX;
6017 			HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
6018 			for (k = k0; k <= k1; k++) {
6019 				for (row = j0, row_out = 0; row <= j1; row++, row_out++) {
6020 					ij = gmt_M_ijp (U_obj->header, row_out, 0) + (k - k0) * U_obj->header->size;	/* Position in output cube at start of current row */
6021 					for (col = i0; col <= i1; col++, ij++) {
6022 						kol = col % U_orig->header->n_columns;
6023 						ij_orig = gmt_M_ijp (U_orig->header, row, kol) + k * U_orig->header->size;	/* Position of this (row,col) in original cube organization */
6024 						U_obj->data[ij] = U_orig->data[ij_orig];
6025 						if (gmt_M_is_fnan (U_obj->data[ij]))
6026 							HH->has_NaNs = GMT_GRID_HAS_NANS;
6027 						else {
6028 							U_obj->header->z_min = MIN (U_obj->header->z_min, U_obj->data[ij]);
6029 							U_obj->header->z_max = MAX (U_obj->header->z_max, U_obj->data[ij]);
6030 						}
6031 					}
6032 				}
6033 			}
6034 			gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
6035 			if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_IN), "Cube memory"))
6036 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
6037 			break;
6038 
6039 	 	case GMT_IS_REFERENCE:	/* GMT cube and header in a GMT_CUBE container object by reference [NOT SURE ABOUT THIS] */
6040 			if (S_obj->region) return_null (API, GMT_SUBSET_NOT_ALLOWED);
6041 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing cube data from GMT_CUBE memory location\n");
6042 			if ((U_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
6043 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read cube */
6044 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_cube: Change alloc mode\n");
6045 			UH = gmt_get_U_hidden (U_obj);
6046 			S_obj->alloc_mode = UH->alloc_mode;
6047 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_cube: Check pad\n");
6048 			gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
6049 			if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_IN), "Cube memory"))
6050 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
6051 			if (gmtapi_adjust_grdpadding (U_obj->header, GMT->current.io.pad)) {
6052 				GMT_Report (API, GMT_MSG_INFORMATION, "Reference cube must have standard padding\n");
6053 				return_null (API, GMT_PADDING_NOT_ALLOWED);	/* Set boundary conditions */
6054 			}
6055 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_cube: Return from GMT_IS_REFERENCE\n");
6056 			break;
6057 
6058 		/* Status: This case is fully tested and operational */
6059 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* The user's 3-D cube matrix of some sort, + info in the matrix header */
6060 			/* Must create a cube container from matrix info S_obj->resource and hence a new object is required */
6061 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
6062 			if (cube == NULL) {	/* Only allocate when not already allocated, and only get container */
6063 				uint64_t dim[3] = {M_obj->n_columns, M_obj->n_rows, M_obj->n_layers};
6064 				if ((U_obj = GMT_Create_Data (API, GMT_IS_CUBE, GMT_IS_VOLUME, GMT_CONTAINER_ONLY, dim, M_obj->range, M_obj->inc, M_obj->registration, GMT_NOTSET, NULL)) == NULL)
6065 					return_null (API, GMT_MEMORY_ERROR);
6066 			}
6067 			else
6068 				U_obj = cube;
6069 			if ((new_ID = gmtapi_get_object (API, GMT_IS_CUBE, U_obj)) == GMT_NOTSET)
6070 				return_null (API, GMT_OBJECT_NOT_FOUND);
6071 			if ((new_item = gmtlib_validate_id (API, GMT_IS_CUBE, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
6072 				return_null (API, GMT_OBJECT_NOT_FOUND);
6073 			API->object[new_item]->method = S_obj->method;
6074 			UH = gmt_get_U_hidden (U_obj);
6075 			HH = gmt_get_H_hidden (U_obj->header);
6076 			U_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
6077 			UH->alloc_mode = GMT_ALLOC_INTERNALLY;
6078 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read cube */
6079 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
6080 				return_null (API, GMT_WRONG_MATRIX_SHAPE);
6081 			if ((api_get_val = gmtapi_select_get_function (API, M_obj->type)) == NULL)
6082 				return_null (API, GMT_NOT_A_VALID_TYPE);
6083 			HH = gmt_get_H_hidden (U_obj->header);
6084 
6085 			if (! (mode & GMT_DATA_ONLY)) {	/* Must first init header and copy the header information from the matrix header */
6086 				gmtapi_matrixinfo_to_grdheader (GMT, U_obj->header, M_obj);	/* Populate a GRD header structure */
6087 				/* Must get the full zmin/max range since not provided by the matrix header */
6088 					U_obj->header->z_min = +DBL_MAX;
6089 					U_obj->header->z_max = -DBL_MAX;
6090 					HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
6091 					for (k = 0; k < M_obj->n_layers; k++) {
6092 						for (row = 0; row < M_obj->n_rows; row++) {
6093 							for (col = 0; col < M_obj->n_columns; col++) {
6094 							ij_orig = GMT_2D_to_index (row, col, M_obj->dim) + k * M_obj->size;
6095 							ij_orig = GMT_2D_to_index (row, col, M_obj->dim);
6096 							api_get_val (&(M_obj->data), ij_orig, &d);
6097 							if (gmt_M_is_dnan (d))
6098 								HH->has_NaNs = GMT_GRID_HAS_NANS;
6099 							else {
6100 								U_obj->header->z_min = MIN (U_obj->header->z_min, (gmt_grdfloat)d);
6101 								U_obj->header->z_max = MAX (U_obj->header->z_max, (gmt_grdfloat)d);
6102 							}
6103 						}
6104 					}
6105 				}
6106 				if (mode & GMT_CONTAINER_ONLY)	/* Just needed the header */
6107 					break;	/* Done for now */
6108 			}
6109 
6110 			GMT_Report (API, GMT_MSG_INFORMATION, "Importing cube data from user matrix memory location\n");
6111 
6112 			/* Get start/stop row/cols for subset (or the entire domain) */
6113 			/* dx,dy are needed when the cube is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
6114 			k0 = 0;	k1 = M_obj->n_layers - 1;
6115 			if (!S_obj->region || gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {	/* Easy, get the whole enchilada */
6116 				j0 = i0 = 0;
6117 				j1 = U_obj->header->n_rows    - 1;	/* Minus 1 since we loop up to and including below */
6118 				i1 = U_obj->header->n_columns - 1;
6119 			}
6120 			else {	/* Want a subset */
6121 				dx = U_obj->header->inc[GMT_X] * U_obj->header->xy_off;	dy = U_obj->header->inc[GMT_Y] * U_obj->header->xy_off;
6122 				if (gmt_M_is_geographic (GMT, GMT_IN)) { /* Must first wrap S_obj->wesn to fit the data if necessary */
6123 					if (S_obj->wesn[XLO] > U_obj->header->wesn[XHI]) { /* Must first wrap U_obj->header->wesn west to fit the data */
6124 						U_obj->header->wesn[XLO] += 360.0;	U_obj->header->wesn[XHI] += 360.0;
6125 					}
6126 					else if (S_obj->wesn[XHI] < U_obj->header->wesn[XLO]) { /* Must first wrap G_obj->header->wesn east to fit the data */
6127 						U_obj->header->wesn[XLO] -= 360.0;	U_obj->header->wesn[XHI] -= 360.0;
6128 					}
6129 				}
6130 				j1 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YLO]+dy, U_obj->header);
6131 				j0 = (unsigned int)gmt_M_grd_y_to_row (GMT, S_obj->wesn[YHI]-dy, U_obj->header);
6132 				i0 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XLO]+dx, U_obj->header);
6133 				i1 = (unsigned int)gmt_M_grd_x_to_col (GMT, S_obj->wesn[XHI]-dx, U_obj->header);
6134 				while (k0 < k1 && S_obj->wesn[ZLO] > (M_obj->range[ZLO] + k0 * M_obj->inc[GMT_Z])) k0++;	/* Set first layer */
6135 				while (k1 && S_obj->wesn[ZHI] > (M_obj->range[ZHI] - (M_obj->n_layers-k1 - 1) * M_obj->inc[GMT_Z])) k1++;	/* Set last layer */
6136 				gmt_M_memcpy (U_obj->header->wesn, S_obj->wesn, 4U, double);	/* Update the cube header region to match subset request */
6137 				gmt_set_grddim (GMT, U_obj->header);	/* Adjust all dimensions accordingly before allocating space */
6138 				gmt_M_memcpy (U_obj->z_range, &(S_obj->wesn[ZLO]), 2U, double);	/* Update the cube header range to match subset request */
6139 				U_obj->header->n_bands = k1 - k0 + 1;
6140 			}
6141 			if (U_obj->data) {	/* This is an error - there cannot be a data pointer yet */
6142 				GMT_Report (API, GMT_MSG_ERROR, "U->data is not NULL when memory allocation is about to happen\n");
6143 				return_null (API, GMT_PTR_IS_NULL);
6144 			}
6145 			else
6146 				U_obj->data = gmt_M_memory_aligned (GMT, NULL, U_obj->header->size * U_obj->header->n_bands, gmt_grdfloat);
6147 
6148 			here = 0;
6149 			for (k = k0; k <= k1; k++) {
6150 				for (row = j0, row_out = 0; row <= j1; row++, row_out++) {
6151 					ij = gmt_M_ijp (U_obj->header, row_out, 0) + here;	/* Position in output cube at start of current row */
6152 					for (col = i0; col <= i1; col++, ij++) {
6153 						kol = col % M_obj->n_columns;
6154 						ij_orig = GMT_2D_to_index (row, kol, M_obj->dim) + k * M_obj->size;	/* Position of this (row,col) in input matrix organization */
6155 						api_get_val (&(M_obj->data), ij_orig, &d);	/* Get the next item from the matrix */
6156 						U_obj->data[ij] = (gmt_grdfloat)d;
6157 						if (gmt_M_is_dnan (d))
6158 							HH->has_NaNs = GMT_GRID_HAS_NANS;
6159 						else {
6160 							U_obj->header->z_min = MIN (U_obj->header->z_min, (gmt_grdfloat)d);
6161 							U_obj->header->z_max = MAX (U_obj->header->z_max, (gmt_grdfloat)d);
6162 						}
6163 					}
6164 				}
6165 				here += U_obj->header->size;
6166 			}
6167 			if (gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {
6168 				/* Global cubes passed via matrix are not rotated to fit the desired global region, so we need to correct the wesn for this cube to match the matrix */
6169 				gmt_M_memcpy (U_obj->header->wesn, M_obj->range, 4U, double);
6170 			}
6171 			gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
6172 			if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_IN), "Cube memory"))
6173 				return_null (API, GMT_GRID_BC_ERROR);	/* Set boundary conditions */
6174 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
6175 			API->object[new_item]->actual_family = GMT_IS_CUBE;	/* Done reading from matrix */
6176 			if (start_over_method) API->object[new_item]->method = start_over_method;	/* We changed our mind from reference to duplicate due to region */
6177 			UH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
6178 			break;
6179 
6180 	 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:	/* The user's 3-D matrix of some sort, + info in the args [NOT YET FULLY TESTED] */
6181 			/* Getting a matrix info S_obj->resource. Create cube header and then pass the cube pointer via the matrix pointer */
6182 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
6183 			if (mode & GMT_GRID_NEEDS_PAD1 || mode & GMT_GRID_NEEDS_PAD2) {	/* Cannot do this by reference, switch to duplication */
6184 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Cube via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE due to a padding requirement - method has been switched\n");
6185 				goto start_over_import_cube;
6186 			}
6187 			/* Determine if it is possible to use the matrix given the region selected and the fact we chose GMT_IS_REFERENCE. This test will
6188 			 * only kick in after we allocate the U_obj and come back the second time (after getting header) since otherwise S_obj->wesn is not set yet */
6189 			if (!(!S_obj->region ||
6190 				(S_obj->wesn[XLO] >= M_obj->range[XLO] && S_obj->wesn[XHI] <= M_obj->range[XHI] && S_obj->wesn[YLO] >= M_obj->range[YLO] && S_obj->wesn[YHI] <= M_obj->range[YHI]) ||
6191 				gmt_whole_earth (GMT, M_obj->range, S_obj->wesn))) {	/* Cannot do this by reference, switch to duplication */
6192 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Cube subset selection via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE - method has been switched\n");
6193 				goto start_over_import_cube;
6194 			}
6195 			/* This method requires the input data to be a GMT_GRD_FORMAT matrix - otherwise we should be DUPLICATING */
6196 			if (!(M_obj->shape == GMT_IS_ROW_FORMAT && M_obj->type == GMT_GRDFLOAT && (mode & GMT_GRID_IS_COMPLEX_MASK) == 0)) {
6197 				start_over_method = gmtapi_switch_method (API, S_obj, &method, "Cube via a user matrix requires method GMT_IS_DUPLICATE instead of GMT_IS_REFERENCE due to incompatible data type for a cube - method has been switched\n");
6198 				goto start_over_import_cube;
6199 			}
6200 
6201 			if (cube == NULL) {	/* Only allocate when not already allocated.  Note cannot have pad since input matrix wont have one */
6202 				uint64_t dim[3] = {M_obj->n_rows, M_obj->n_columns, M_obj->n_layers};
6203 				if ((U_obj = GMT_Create_Data (API, GMT_IS_CUBE, GMT_IS_VOLUME, GMT_CONTAINER_ONLY, dim, M_obj->range, M_obj->inc, M_obj->registration, 0, NULL)) == NULL)
6204 					return_null (API, GMT_MEMORY_ERROR);
6205 			}
6206 			else
6207 				U_obj = cube;
6208 			HH = gmt_get_H_hidden (U_obj->header);
6209 			U_obj->header->complex_mode = (mode & GMT_GRID_IS_COMPLEX_MASK);	/* Set the complex mode */
6210 			done = (mode & GMT_CONTAINER_ONLY) ? false : true;	/* Not done until we read cube */
6211 			if (! (mode & GMT_DATA_ONLY)) {
6212 				gmtapi_matrixinfo_to_grdheader (GMT, U_obj->header, M_obj);	/* Populate a GRD header structure */
6213 				/* Temporarily set data pointer for convenience; removed later */
6214 #ifdef DOUBLE_PRECISION_GRID
6215 				U_obj->data = M_obj->data.f8;
6216 #else
6217 				U_obj->data = M_obj->data.f4;
6218 #endif
6219 				U_obj->header->z_min = +DBL_MAX;
6220 				U_obj->header->z_max = -DBL_MAX;
6221 				HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
6222 				here = 0;
6223 				for (k = 0; k < M_obj->n_layers; k++) {
6224 					for (row = 0; row < M_obj->n_rows; row++) {
6225 						ij = gmt_M_ijp (U_obj->header, row, 0) + here;
6226 						for (col = 0; col < M_obj->n_columns; col++, ij++) {
6227 							if (gmt_M_is_fnan (U_obj->data[ij]))
6228 								HH->has_NaNs = GMT_GRID_HAS_NANS;
6229 							else {
6230 								U_obj->header->z_min = MIN (U_obj->header->z_min, U_obj->data[ij]);
6231 								U_obj->header->z_max = MAX (U_obj->header->z_max, U_obj->data[ij]);
6232 							}
6233 						}
6234 					}
6235 					here += U_obj->header->size;
6236 				}
6237 				U_obj->data = NULL;	/* Since data are not requested yet */
6238 				if (mode & GMT_CONTAINER_ONLY)	/* Just needed the header but had to set zmin/zmax first */
6239 					break;
6240 			}
6241 			if ((new_ID = gmtapi_get_object (API, GMT_IS_CUBE, U_obj)) == GMT_NOTSET)
6242 				return_null (API, GMT_OBJECT_NOT_FOUND);
6243 			if ((new_item = gmtlib_validate_id (API, GMT_IS_CUBE, new_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
6244 				return_null (API, GMT_OBJECT_NOT_FOUND);
6245 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing cube data from user memory location\n");
6246 #ifdef DOUBLE_PRECISION_GRID
6247 			U_obj->data = M_obj->data.f8;
6248 #else
6249 			U_obj->data = M_obj->data.f4;
6250 #endif
6251 			UH = gmt_get_U_hidden (U_obj);
6252 			MH = gmt_get_M_hidden (M_obj);
6253 			S_obj->alloc_mode = MH->alloc_mode;	/* Pass on alloc_mode of matrix */
6254 			UH->alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Since we cannot have both M and G try to free */
6255 			API->object[new_item]->resource = U_obj;
6256 			API->object[new_item]->status = GMT_IS_USED;	/* Mark as read */
6257 			UH->alloc_level = API->object[new_item]->alloc_level;	/* Since allocated here */
6258 			if (gmt_whole_earth (GMT, M_obj->range, S_obj->wesn)) {
6259 				/* Global cubes passed via matrix are not rotated to fit the desired global region, so we need to correct the wesn for this cube to match the matrix */
6260 				gmt_M_memcpy (U_obj->header->wesn, M_obj->range, 4U, double);
6261 			}
6262 			else if (S_obj->region) {	/* Possibly adjust the pad so inner region matches wesn */
6263 				if (S_obj->reset_pad) {	/* First undo a prior sub-region used with this memory cube */
6264 					gmtapi_contract_headerpad (GMT, U_obj->header, S_obj->orig_pad, S_obj->orig_wesn);
6265 					S_obj->reset_pad = 0;
6266 				}
6267 				if (gmtapi_expand_headerpad (GMT, U_obj->header, S_obj->wesn, S_obj->orig_pad, S_obj->orig_wesn)) {
6268 					S_obj->reset_pad = 1;
6269 					gmtapi_update_cube_minmax (API->GMT, U_obj);	/* Update z-range */
6270 				}
6271 			}
6272 			break;
6273 
6274 		default:
6275 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import cube\n");
6276 			return_null (API, GMT_NOT_A_VALID_METHOD);
6277 			break;
6278 	}
6279 
6280 	if ((mode & GMT_CONTAINER_ONLY) == 0) {	/* Also allocate and initialize the x and y vectors unless already present  */
6281 		struct GMT_CUBE_HIDDEN *GU = gmt_get_U_hidden (U_obj);
6282 		if (U_obj->x == NULL) {
6283 			GU->xyz_alloc_mode[GMT_X] = GMT_ALLOC_INTERNALLY;
6284 			if (GMT->current.io.nc_xarray)	/* Got variable x-array and asked to used this instead */
6285 				U_obj->x = GMT->current.io.nc_xarray, GMT->current.io.nc_xarray = NULL;
6286 			else
6287 				U_obj->x = gmtapi_cube_coord (API, GMT_X, U_obj);	/* Get array of x coordinates */
6288 		}
6289 		if (U_obj->y == NULL) {
6290 			GU->xyz_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
6291 			if (GMT->current.io.nc_yarray)	/* Got variable y-array and asked to used this instead */
6292 				U_obj->y = GMT->current.io.nc_yarray, GMT->current.io.nc_yarray = NULL;
6293 			else
6294 				U_obj->y = gmtapi_cube_coord (API, GMT_Y, U_obj);	/* Get array of y coordinates */
6295 		}
6296 	}
6297 
6298 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as read (unless we just got the header) */
6299 
6300 	return (U_obj);	/* Pass back out what we have so far */
6301 }
6302 
6303 /*! Writes out a single cube to destination */
gmtapi_export_cube(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_CUBE * U_obj)6304 GMT_LOCAL int gmtapi_export_cube (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_CUBE *U_obj) {
6305 	int item, error;
6306 	bool done = true;
6307 	unsigned int method;
6308 	uint64_t row, col, i0, i1, j0, j1, k0, k1, ij, ijp, ij_orig;
6309 	uint64_t k, here = 0;
6310 	size_t size;
6311 	double dx, dy;
6312 	p_func_uint64_t GMT_2D_to_index = NULL;
6313 	GMT_putfunction api_put_val = NULL;
6314 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
6315 	struct GMT_CUBE *U_copy = NULL;
6316 	struct GMT_MATRIX *M_obj = NULL;
6317 	struct GMT_MATRIX_HIDDEN *MH = NULL;
6318 	struct GMT_CUBE_HIDDEN *UH = gmt_get_U_hidden (U_obj), *UH2 = NULL;
6319 	struct GMT_CTRL *GMT = API->GMT;
6320 
6321 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_cube: Passed ID = %d and mode = %d\n", object_ID, mode);
6322 
6323 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
6324 	if (U_obj->data == NULL && !(mode & GMT_CONTAINER_ONLY)) return (gmtlib_report_error (API, GMT_PTR_IS_NULL));
6325 	if ((item = gmtlib_validate_id (API, GMT_IS_CUBE, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
6326 
6327 	S_obj = API->object[item];	/* The current object whose data we will export */
6328 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET))
6329 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));	/* Only allow writing of a data set once, unless overridden by mode */
6330 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
6331 	if (S_obj->region) {	/* See if this is really a subset or just the same region as the cube */
6332 		if (U_obj->header->wesn[XLO] == S_obj->wesn[XLO] && U_obj->header->wesn[XHI] == S_obj->wesn[XHI] && \
6333 			U_obj->header->wesn[YLO] == S_obj->wesn[YLO] && U_obj->header->wesn[YHI] == S_obj->wesn[YHI] && \
6334 			U_obj->z_range[0] == S_obj->wesn[ZLO] && U_obj->z_range[1] == S_obj->wesn[ZHI])
6335 				S_obj->region = false;
6336 	}
6337 	if (mode & GMT_DATA_IS_GEO) gmt_set_geographic (GMT, GMT_OUT);	/* From API to tell cube is geographic */
6338 	gmtapi_cube_set_units (GMT, U_obj);	/* Ensure unit strings are set, regardless of destination */
6339 
6340 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use since may be MATRIX or VECTOR masquerading as GRID */
6341 	switch (method) {
6342 		case GMT_IS_FILE:	/* Name of a cube file to write to disk */
6343 			if (mode & GMT_CONTAINER_ONLY) {	/* Update header structure only */
6344 				GMT_Report (API, GMT_MSG_INFORMATION, "Updating cube header for file %s not implemented\n", S_obj->filename);
6345 				return (gmtlib_report_error (API, GMT_RUNTIME_ERROR));
6346 			}
6347 			else {
6348 				GMT_Report (API, GMT_MSG_INFORMATION, "Writing cube to file %s\n", S_obj->filename);
6349 				if (gmt_nc_write_cube (GMT, U_obj, S_obj->wesn, S_obj->filename) != GMT_NOERROR)
6350 					return (gmtlib_report_error (API, API->error));
6351 				done = true;
6352 			}
6353 			break;
6354 
6355 	 	case GMT_IS_DUPLICATE:	/* Duplicate GMT cube and header to a GMT_CUBE container object. Subset allowed */
6356 			if (S_obj->resource) return (gmtlib_report_error (API, GMT_PTR_NOT_NULL));	/* The output resource pointer must be NULL */
6357 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
6358 			if (gmtapi_adjust_grdpadding (U_obj->header, GMT->current.io.pad)) {
6359 				GMT_Report (API, GMT_MSG_INFORMATION, "Reference cube must have standard padding\n");
6360 				gmtlib_report_error (API, GMT_PADDING_NOT_ALLOWED);
6361 			}
6362 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating cube data to GMT_GRID memory location\n");
6363 			if (!S_obj->region) {	/* No subset, possibly same padding */
6364 				U_copy = gmtlib_duplicate_cube (API->GMT, U_obj, GMT_DUPLICATE_DATA);
6365 				UH2 = gmt_get_U_hidden (U_copy);
6366 				UH2->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
6367 				gmt_BC_init (GMT, U_copy->header);	/* Initialize cube interpolation and boundary condition parameters */
6368 				if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_copy, GMT_OUT), "Cube memory")) return (gmtlib_report_error (API, GMT_GRID_BC_ERROR));	/* Set boundary conditions */
6369 				S_obj->resource = U_copy;	/* Set resource pointer to the cube */
6370 				break;		/* Done with this cube */
6371 			}
6372 			/* Here we need to extract subset, and possibly change padding. */
6373 			/* Get start/stop row/cols for subset (or the entire domain) */
6374 			U_copy = gmtlib_create_cube (GMT);
6375 			UH2 = gmt_get_U_hidden (U_copy);
6376 			UH2->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
6377 			gmt_copy_gridheader (GMT, U_copy->header, U_obj->header);
6378 			gmt_M_memcpy (U_copy->header->wesn, S_obj->wesn, 4, double);
6379 			gmt_M_memcpy (U_copy->z_range, &(S_obj->wesn[ZLO]), 2U, double);	/* Update the cube range to match subset request */
6380 			/* dx,dy are needed when the cube is pixel-registered as the w/e/s/n bounds are off by 0.5 {dx,dy} relative to node coordinates */
6381 			dx = U_obj->header->inc[GMT_X] * U_obj->header->xy_off;	dy = U_obj->header->inc[GMT_Y] * U_obj->header->xy_off;
6382 			j1 = (unsigned int) gmt_M_grd_y_to_row (GMT, U_obj->header->wesn[YLO]+dy, U_obj->header);
6383 			j0 = (unsigned int) gmt_M_grd_y_to_row (GMT, U_obj->header->wesn[YHI]-dy, U_obj->header);
6384 			i0 = (unsigned int) gmt_M_grd_x_to_col (GMT, U_obj->header->wesn[XLO]+dx, U_obj->header);
6385 			i1 = (unsigned int) gmt_M_grd_x_to_col (GMT, U_obj->header->wesn[XHI]-dx, U_obj->header);
6386 			(void) gmt_get_active_layers (GMT, U_obj, &(S_obj->wesn[ZLO]), &k0, &k1);
6387 			gmt_M_memcpy (U_obj->header->pad, GMT->current.io.pad, 4, int);		/* Set desired padding */
6388 			U_copy->header->size = gmtapi_set_grdarray_size (GMT, U_obj->header, mode, S_obj->wesn);	/* Get array dimension only, which may include padding */
6389 			U_copy->data = gmt_M_memory_aligned (GMT, NULL, U_copy->header->size, gmt_grdfloat);
6390 			U_copy->header->z_min = DBL_MAX;	U_copy->header->z_max = -DBL_MAX;	/* Must set vmin/vmax since we are not writing to file */
6391 			U_copy->header->n_bands = k1 - k0 + 1;
6392 			for (k = k0; k <= k1; k++) {
6393 				for (row = j0; row <= j1; row++) {
6394 					for (col = i0; col <= i1; col++, ij++) {
6395 						ij_orig = gmt_M_ijp (U_obj->header, row, col) + (k - k0) * U_obj->header->size;	/* Position of this (row,col) in original cube organization */
6396 						ij = gmt_M_ijp (U_copy->header, row, col) + k * U_copy->header->size;	/* Position of this (row,col) in output cube organization */
6397 						U_copy->data[ij] = U_obj->data[ij_orig];
6398 						if (gmt_M_is_fnan (U_copy->data[ij])) continue;
6399 						/* Update z_min, z_max */
6400 						U_copy->header->z_min = MIN (U_copy->header->z_min, (double)U_copy->data[ij]);
6401 						U_copy->header->z_max = MAX (U_copy->header->z_max, (double)U_copy->data[ij]);
6402 					}
6403 				}
6404 				}
6405 			S_obj->resource = U_copy;	/* Set resource pointer to the cube */
6406 			break;
6407 
6408 	 	case GMT_IS_REFERENCE:	/* GMT cube and header in a GMT_CUBE container object - just pass the reference */
6409 			if (S_obj->region) return (gmtlib_report_error (API, GMT_SUBSET_NOT_ALLOWED));
6410 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
6411 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing cube data to GMT_CUBE memory location\n");
6412 			gmt_cube_vminmax (GMT, U_obj->header, U_obj->data);	/* Must set cube's vmin/vmax since we are not writing to file */
6413 			gmt_BC_init (GMT, U_obj->header);	/* Initialize cube interpolation and boundary condition parameters */
6414 			if (gmt_M_err_pass (GMT, gmt_cube_BC_set (GMT, U_obj, GMT_OUT), "Cube memory")) return (gmtlib_report_error (API, GMT_GRID_BC_ERROR));	/* Set boundary conditions */
6415 			S_obj->resource = U_obj;	/* Set resource pointer to the cube */
6416 			UH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
6417 			break;
6418 
6419 	 	case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* The user's 3-D matrix of some sort, + info in the args [NOT FULLY TESTED] */
6420 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
6421 			if (S_obj->resource) {	/* The output resource pointer already exist for matrix */
6422 				M_obj = gmtapi_get_matrix_data (S_obj->resource);
6423 				if (M_obj->n_rows < U_obj->header->n_rows || M_obj->n_columns < U_obj->header->n_columns || M_obj->n_layers < U_obj->header->n_bands)
6424 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
6425 			}
6426 			else {	/* Must allocate stuff */
6427 		 		M_obj = gmtlib_create_matrix (API->GMT, U_obj->header->n_bands, GMT_IS_OUTPUT, 0);
6428 				M_obj->type = S_obj->type;
6429 			}
6430 			MH = gmt_get_M_hidden (M_obj);
6431 			gmtapi_grdheader_to_matrixinfo (GMT, U_obj->header, M_obj);	/* Populate an array with GRD header information */
6432 			gmt_M_memcpy (&(M_obj->range[ZLO]), U_obj->z_range, 2U, double);	/* Update the cube range to match subset request */
6433 			M_obj->inc[GMT_Z] = U_obj->z_inc;
6434 			M_obj->dim = (M_obj->shape == GMT_IS_ROW_FORMAT) ? M_obj->n_columns : M_obj->n_rows;	/* Matrix layout order */
6435 			GMT_Report (API, GMT_MSG_INFORMATION, "Exporting cube data to user memory location\n");
6436 			if (S_obj->resource == NULL) {	/* Must allocate output */
6437 				size = gmt_M_get_nm (GMT, U_obj->header->n_columns, U_obj->header->n_rows) * M_obj->n_layers;
6438 				if ((error = gmtlib_alloc_univector (GMT, &(M_obj->data), M_obj->type, size)) != GMT_NOERROR) return (error);
6439 				MH->alloc_mode = GMT_ALLOC_INTERNALLY;
6440 			}
6441 			if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, M_obj->shape, GMT_GRID_IS_REAL)) == NULL)
6442 				return (gmtlib_report_error (API, GMT_WRONG_MATRIX_SHAPE));
6443 			if ((api_put_val = gmtapi_select_put_function (API, M_obj->type)) == NULL)
6444 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_TYPE));
6445 			size = gmt_M_get_nm (GMT, M_obj->n_columns, M_obj->n_rows);
6446 			for (k = 0; k < U_obj->header->n_bands; k++) {
6447 				gmt_M_grd_loop (GMT, U_obj, row, col, ijp) {
6448 					ij = GMT_2D_to_index (row, col, M_obj->dim) + k * size;
6449 					api_put_val (&(M_obj->data), ij, (double)U_obj->data[ijp+here]);
6450 				}
6451 				here += U_obj->header->size;
6452 			}
6453 			MH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
6454 			S_obj->resource = M_obj;	/* Set resource pointer to the matrix */
6455 			break;
6456 
6457 	 	case GMT_IS_REFERENCE|GMT_VIA_MATRIX:	/* Write to a user matrix of type gmt_grdfloat */
6458 			if (mode & GMT_CONTAINER_ONLY) return (gmtlib_report_error (API, GMT_NOT_A_VALID_MODE));
6459 			if (mode & GMT_GRID_IS_COMPLEX_MASK)	/* Cannot do a complex cube this way */
6460 				return (gmtlib_report_error (API, GMT_NOT_A_VALID_IO_ACCESS));
6461 			if (S_obj->resource) {	/* The output resource pointer already exist for matrix */
6462 				M_obj = gmtapi_get_matrix_data (S_obj->resource);
6463 				if (M_obj->n_rows < U_obj->header->n_rows || M_obj->n_columns < U_obj->header->n_columns || M_obj->n_layers < U_obj->header->n_bands)
6464 					return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
6465 				assert (M_obj->type == GMT_GRDFLOAT);	/* That is the whole point of getting here, no? */
6466 			}
6467 			else {	/* Must allocate stuff */
6468 		 		M_obj = gmtlib_create_matrix (API->GMT, U_obj->header->n_bands, GMT_IS_OUTPUT, 1);
6469 				M_obj->type = GMT_GRDFLOAT;	/* A cube is always gmt_grdfloat */
6470 			}
6471 			MH = gmt_get_M_hidden (M_obj);
6472 			if (gmtapi_adjust_grdpadding (U_obj->header, GMT_no_pad))
6473 				gmt_cube_pad_off (GMT, U_obj);	/* Remove pad */
6474 			/* This method requires the output data to be a gmt_grdfloat matrix - otherwise we should be DUPLICATING.
6475 			   This distinction is set in GMT_Open_VirtualFile */
6476 			gmtapi_grdheader_to_matrixinfo (GMT, U_obj->header, M_obj);	/* Populate an array with GRD header information */
6477 			gmt_M_memcpy (&(M_obj->range[ZLO]), U_obj->z_range, 2U, double);	/* Update the cube range to match subset request */
6478 			M_obj->inc[GMT_Z] = U_obj->z_inc;
6479 			M_obj->shape = GMT_IS_ROW_FORMAT;	/* Because it is a direct GMT gmt_grdfloat cube */
6480 			if (S_obj->resource) {
6481 				GMT_Report (API, GMT_MSG_INFORMATION, "Memcpy cube data to user memory location\n");
6482 #ifdef DOUBLE_PRECISION_GRID
6483 				gmt_M_memcpy (M_obj->data.f8, U_obj->data, U_obj->header->nm * U_obj->header->n_bands, double);
6484 #else
6485 				gmt_M_memcpy (M_obj->data.f4, U_obj->data, U_obj->header->nm * U_obj->header->n_bands, float);
6486 #endif
6487 			}
6488 			else {
6489 				GMT_Report (API, GMT_MSG_INFORMATION, "Referencing cube data to user memory location\n");
6490 #ifdef DOUBLE_PRECISION_GRID
6491 				M_obj->data.f8 = U_obj->data;
6492 #else
6493 				M_obj->data.f4 = U_obj->data;
6494 #endif
6495 			}
6496 			MH->alloc_level = S_obj->alloc_level;	/* Since we are passing it up to the caller */
6497 			S_obj->resource = M_obj;	/* Set resource pointer to the matrix */
6498 			break;
6499 
6500 		default:
6501 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export cubes\n");
6502 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
6503 			break;
6504 	}
6505 
6506 	if (done) S_obj->status = GMT_IS_USED;	/* Mark as written (unless we only updated header) */
6507 
6508 	return (GMT_NOERROR);
6509 }
6510 
6511 
gmtapi_read_matrix(struct GMT_CTRL * GMT,void * source,unsigned int src_type,unsigned int mode)6512 GMT_LOCAL struct GMT_MATRIX * gmtapi_read_matrix (struct GMT_CTRL *GMT, void *source, unsigned int src_type, unsigned int mode) {
6513 	/* We read the MATRIX from fp [or stdin].
6514 	 * src_type can be GMT_IS_[FILE|STREAM|FDESC]
6515 	 * Notes: mode is not used yet.  We only do ascii file for now - later need to deal with -b, if needed.
6516 	 */
6517 
6518 	bool close_file = false, first = true, add_first_segheader = false, in_header_section = true;
6519 	unsigned int pos;
6520 	int error = 0;
6521 	uint64_t row = 0, col, ij, n_col, nt_alloc = 0, nh_alloc = 0, n_headers = 0, dim[4] = {0, 0, 0, GMT->current.setting.export_type};
6522 	char M_file[PATH_MAX] = {""}, line[GMT_BUFSIZ] = {""};
6523 	char **text = NULL, **header = NULL;
6524 	FILE *fp = NULL;
6525 	struct GMT_MATRIX *M = NULL;
6526 	GMT_putfunction api_put_val = NULL;
6527 	p_func_uint64_t GMT_2D_to_index = NULL;
6528 	gmt_M_unused(mode);
6529 
6530 	if (src_type == GMT_IS_FILE && !source) src_type = GMT_IS_STREAM;	/* No filename given, default to stdin */
6531 
6532 	if (src_type == GMT_IS_FILE) {	/* dest is a file name */
6533 		strncpy (M_file, source, PATH_MAX-1);
6534 		if ((fp = gmt_fopen (GMT, M_file, GMT->current.io.r_mode)) == NULL) {
6535 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open Matrix file %s\n", M_file);
6536 			return_null (GMT->parent, GMT_ERROR_ON_FOPEN);
6537 		}
6538 		close_file = true;	/* We only close files we have opened here */
6539 	}
6540 	else if (src_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
6541 		fp = (FILE *)source;
6542 		if (fp == NULL) fp = GMT->session.std[GMT_IN];	/* Default destination */
6543 		if (fp == GMT->session.std[GMT_IN])
6544 			strcpy (M_file, "<stdin>");
6545 		else
6546 			strcpy (M_file, "<input stream>");
6547 	}
6548 	else if (src_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
6549 		int *fd = source;
6550 		if (fd && (fp = fdopen (*fd, "r")) == NULL) {
6551 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert Matrix file descriptor %d to stream in gmtapi_read_matrix\n", *fd);
6552 			return_null (GMT->parent, GMT_ERROR_ON_FDOPEN);
6553 		}
6554 		if (fd == NULL) fp = GMT->session.std[GMT_IN];	/* Default destination */
6555 		if (fp == GMT->session.std[GMT_IN])
6556 			strcpy (M_file, "<stdin>");
6557 		else
6558 			strcpy (M_file, "<input file descriptor>");
6559 		close_file = true;	/* since fdopen allocates space */
6560 	}
6561 	else {
6562 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtapi_read_matrix\n", src_type);
6563 		return_null (GMT->parent, GMT_NOT_A_VALID_METHOD);
6564 	}
6565 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Read Matrix from %s\n", M_file);
6566 
6567 	while (!error && fgets (line, GMT_BUFSIZ, fp)) {
6568 		gmt_chop (line);	/* Remove linefeeds */
6569 		if (strchr (GMT->current.setting.io_head_marker_in, line[0])) {
6570 			if (in_header_section) {
6571 				if (nh_alloc <= n_headers) header = gmt_M_memory (GMT, NULL, nh_alloc += GMT_TINY_CHUNK, char *);
6572 				header[n_headers++] = strdup (line);
6573 			}
6574 			continue;
6575 		}
6576 		in_header_section = false;
6577 		if (line[0] == '>') {
6578 			if (first) {	/* Have not allocated yet so just skip that row for now and deal with it later */
6579 				first = false;
6580 				add_first_segheader = true;
6581 			}
6582 			else {	/* Already allocated so place NaNs as segment header */
6583 				gmt_prep_tmp_arrays (GMT, GMT_IN, row, dim[0]);	/* Init or reallocate tmp vectors */
6584 				for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][row] = GMT->session.d_NaN;
6585 			}
6586 		}
6587 		else {	/* Regular data record */
6588 			if (dim[0] == 0)	/* First time we must establish how many columns */
6589 				dim[0] = gmtlib_conv_text2datarec (GMT, line, GMT_BUFSIZ, GMT->current.io.curr_rec, &pos);
6590 			if ((n_col = gmtlib_conv_text2datarec (GMT, line, dim[0], GMT->current.io.curr_rec, &pos)) != dim[0]) {
6591 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Matrix record %" PRIu64 " only had %" PRIu64 " columns but %" PRIu64 " was expected.  Record skipped\n", row, n_col, dim[0]);
6592 				continue;
6593 			}
6594 			gmt_prep_tmp_arrays (GMT, GMT_IN, row, dim[0]);	/* Init or reallocate tmp vectors */
6595 			for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][row] = GMT->current.io.curr_rec[col];
6596 			if (line[pos]) {	/* Deal with trailing text */
6597 				if (nt_alloc <= row) text = gmt_M_memory (GMT, NULL, nt_alloc += GMT_INITIAL_MEM_ROW_ALLOC, char **);
6598 				text[row] = strdup (&line[pos]);
6599 			}
6600 		}
6601 		row++;
6602 	}
6603 	/* Possibly restore the missing first segment header */
6604 	if (add_first_segheader) for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][0] = GMT->session.d_NaN;
6605 	dim[1] = row;	/* Allocate all vectors using current type setting in the defaults [GMT_DOUBLE] */
6606 	if ((M = GMT_Create_Data (GMT->parent, GMT_IS_MATRIX, GMT_IS_POINT, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) {
6607 		if (close_file) fclose (fp);
6608 		return_null (GMT->parent, GMT_MEMORY_ERROR);
6609 	}
6610 	if ((api_put_val = gmtapi_select_put_function (GMT->parent, M->type)) == NULL)	/* Get correct put function given data type */
6611 		return_null (GMT->parent, GMT_NOT_A_VALID_TYPE);
6612 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (GMT->parent, M->shape, GMT_GRID_IS_REAL)) == NULL)	/* Get ij index function */
6613 		return_null (GMT->parent, GMT_WRONG_MATRIX_SHAPE);
6614 	for (col = 0; col < M->n_columns; col++) {
6615 		for (row = 0; row < M->n_rows; row++) {
6616 			ij = GMT_2D_to_index (row, col, M->dim);	/* Index into the user data matrix depends on layout (M->shape) */
6617 			api_put_val (&(M->data), ij, GMT->hidden.mem_coord[col][row]);
6618 		}
6619 	}
6620 	M->size = dim[GMT_X] * dim[GMT_Y];
6621 	/* Set Default range and inc to reflect dim, with inc = 1 */
6622 	M->range[XHI] = dim[GMT_X] - 1.0;
6623 	M->range[YHI] = dim[GMT_Y] - 1.0;
6624 	M->inc[GMT_X] = M->inc[GMT_Y] = 1.0;
6625 
6626 	if (text) {	/* Attach the trailing text to the vector */
6627 		struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M);
6628 		if (nt_alloc > row) text = gmt_M_memory (GMT, text, row, char **);
6629 		GMT_Put_Strings (GMT->parent, GMT_IS_MATRIX, M, text);
6630 		MH->alloc_mode_text = GMT_ALLOC_INTERNALLY;	/* Override since it is allocated internally in GMT */
6631 	}
6632 	if (n_headers) {	/* Pass out the header records as well */
6633 		if (nh_alloc > n_headers) header = gmt_M_memory (GMT, header, n_headers, char *);
6634 		M->header = header;
6635 		M->n_headers = n_headers;
6636 	}
6637 
6638 	if (close_file) gmt_fclose (GMT, fp);
6639 	return (M);
6640 }
6641 
gmtapi_grid2matrix(struct GMTAPI_CTRL * API,struct GMT_GRID * In,struct GMT_MATRIX * Out)6642 GMT_LOCAL void *gmtapi_grid2matrix (struct GMTAPI_CTRL *API, struct GMT_GRID *In, struct GMT_MATRIX *Out) {
6643 	bool alloc = (Out == NULL);
6644 	uint64_t row, col, ij, ij_M;
6645 	double d;
6646 	GMT_putfunction api_put_val = NULL;
6647 	p_func_uint64_t GMT_2D_to_index = NULL;
6648 
6649 	if (alloc) Out = gmtlib_create_matrix (API->GMT, 1U, GMT_OUT, 0);
6650 
6651 	gmtapi_grdheader_to_matrixinfo (API->GMT, In->header, Out);
6652 	if (alloc) {	/* Allocate the matrix itself */
6653 		int error;
6654 		Out->type = API->GMT->current.setting.export_type;
6655 		Out->registration = In->header->registration;
6656 		Out->shape = GMT_IS_ROW_FORMAT;	/* For now */
6657 		Out->dim = (Out->shape == GMT_IS_ROW_FORMAT) ? Out->n_columns : Out->n_rows;	/* Matrix layout order */
6658 
6659 		if ((error = gmtlib_alloc_univector (API->GMT, &(Out->data), Out->type, Out->n_rows * Out->n_columns)) != GMT_NOERROR) {
6660 			gmt_M_free (API->GMT, Out);
6661 			return_null (API, error);
6662 		}
6663 	}
6664 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, Out->shape, GMT_GRID_IS_REAL)) == NULL) {
6665 		if (alloc) gmt_M_free (API->GMT, Out);
6666 		return_null (API, GMT_WRONG_MATRIX_SHAPE);
6667 	}
6668 	if ((api_put_val = gmtapi_select_put_function (API, Out->type)) == NULL) {
6669 		if (alloc) gmt_M_free (API->GMT, Out);
6670 		return_null (API, GMT_NOT_A_VALID_TYPE);
6671 	}
6672 
6673 	gmt_M_grd_loop (API->GMT, In, row, col, ij) {
6674 		d = In->data[ij];
6675 		ij_M = GMT_2D_to_index (row, col, Out->dim);
6676 		api_put_val (&(Out->data), ij_M, d);
6677 	}
6678 
6679 	return Out;
6680 }
6681 
gmtapi_matrix2grid(struct GMTAPI_CTRL * API,struct GMT_MATRIX * In,struct GMT_GRID * Out)6682 GMT_LOCAL void *gmtapi_matrix2grid (struct GMTAPI_CTRL *API, struct GMT_MATRIX *In, struct GMT_GRID *Out) {
6683 	bool alloc = (Out == NULL);
6684 	uint64_t row, col, ij, ij_M;
6685 	double d;
6686 	GMT_getfunction api_get_val = NULL;
6687 	p_func_uint64_t GMT_2D_to_index = NULL;
6688 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
6689 
6690 	if (alloc) Out = gmt_create_grid (API->GMT);
6691 
6692 	gmtapi_matrixinfo_to_grdheader (API->GMT, Out->header, In);
6693 	if (alloc) {	/* Allocate the grid itself */
6694 		int error;
6695 		gmt_set_grddim (API->GMT, Out->header);	/* Set all dimensions */
6696 		if ((Out->data = gmt_M_memory (API->GMT, NULL, Out->header->size, gmt_grdfloat)) == NULL) {
6697 			gmt_M_free (API->GMT, Out);
6698 			return_null (API, API->error);
6699 		}
6700 		if ((error = gmtapi_alloc_grid_xy (API, Out)) != GMT_NOERROR) {
6701 			gmt_M_free (API->GMT, Out);
6702 			return_null (API, error);	/* Allocation error */
6703 		}
6704 	}
6705 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, In->shape, GMT_GRID_IS_REAL)) == NULL) {
6706 		if (alloc) gmt_M_free (API->GMT, Out);
6707 		return_null (API, GMT_WRONG_MATRIX_SHAPE);
6708 	}
6709 	if ((api_get_val = gmtapi_select_get_function (API, In->type)) == NULL) {
6710 		if (alloc) gmt_M_free (API->GMT, Out);
6711 		return_null (API, GMT_NOT_A_VALID_TYPE);
6712 	}
6713 
6714 	HH = gmt_get_H_hidden (Out->header);
6715 	Out->header->z_min = DBL_MAX;
6716 	Out->header->z_max = -DBL_MAX;
6717 	HH->has_NaNs = GMT_GRID_NO_NANS;	/* We are about to check for NaNs and if none are found we retain 1, else 2 */
6718 	gmt_M_grd_loop (API->GMT, Out, row, col, ij) {
6719 		ij_M = GMT_2D_to_index (row, col, In->dim);
6720 		api_get_val (&(In->data), ij_M, &d);
6721 		Out->data[ij] = (gmt_grdfloat)d;
6722 		if (gmt_M_is_fnan (Out->data[ij]))
6723 			HH->has_NaNs = GMT_GRID_HAS_NANS;
6724 		else {
6725 			Out->header->z_min = MIN (Out->header->z_min, Out->data[ij]);
6726 			Out->header->z_max = MAX (Out->header->z_max, Out->data[ij]);
6727 		}
6728 	}
6729 
6730 	return Out;
6731 }
6732 
6733 /*! . */
gmtapi_import_matrix(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode)6734 GMT_LOCAL struct GMT_MATRIX *gmtapi_import_matrix (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode) {
6735 	/* Does the actual work of loading in a GMT matrix. This could either be from a grid file or a 2-D table. */
6736 	int item;
6737 	unsigned int kind;
6738 	struct GMT_MATRIX *M_obj = NULL, *M_orig = NULL;
6739 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
6740 	struct GMT_CTRL *GMT = API->GMT;
6741 
6742 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_matrix: Passed ID = %d and mode = %d\n", object_ID, mode);
6743 
6744 	if (object_ID == GMT_NOTSET) return_null (API, GMT_NO_INPUT);
6745 	if ((item = gmtlib_validate_id (API, GMT_IS_MATRIX, object_ID, GMT_IN, GMTAPI_OPTION_INPUT)) == GMT_NOTSET)
6746 		return_null (API, API->error);
6747 
6748 	S_obj = API->object[item];	/* Use S_obj as shorthand */
6749 	if (S_obj->status != GMT_IS_UNUSED) { /* Already read this resource before; are we allowed to re-read? */
6750 		if (S_obj->method == GMT_IS_STREAM || S_obj->method == GMT_IS_FDESC)
6751 			return_null (API, GMT_READ_ONCE); /* Not allowed to re-read streams */
6752 		if (!(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Not authorized to re-read */
6753 	}
6754 
6755 	/* Passed sanity and allowed to read */
6756 
6757 	switch (S_obj->method) {	/* File, array, stream etc ? */
6758 		case GMT_IS_FILE:
6759 			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
6760 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading MATRIX from %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
6761 			if (S_obj->geometry == GMT_IS_SURFACE) {	/* Read a grid file and convert to MATRIX */
6762 				struct GMT_GRID *G = NULL;
6763 				if ((G = gmtapi_import_grid (API, object_ID, mode, NULL)) == NULL)
6764 					return_null (API, GMT_DATA_READ_ERROR);
6765 				M_obj = gmtapi_grid2matrix (API, G, NULL);	/* Convert the grid to a matrix */
6766 				if (gmtapi_destroy_grid (API, &G))
6767 					return_null (API, GMT_DATA_READ_ERROR);
6768 			}
6769 			else if ((M_obj = gmtapi_read_matrix (GMT, S_obj->filename, S_obj->method, mode)) == NULL)	/* Read a 2-D table */
6770 				return_null (API, GMT_DATA_READ_ERROR);
6771 			S_obj->resource = M_obj;		/* Retain pointer to the allocated data so we use garbage collection later */
6772 			break;
6773 		case GMT_IS_STREAM:
6774  			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
6775 			kind = (S_obj->fp == GMT->session.std[GMT_IN]) ? 0 : 1;	/* Used for message: 0 if stdin, 1 otherwise for user pointer */
6776 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading MATRIX from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
6777 			if ((M_obj = gmtapi_read_matrix (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
6778 				return_null (API, GMT_DATA_READ_ERROR);
6779 			S_obj->resource = M_obj;		/* Retain pointer to the allocated data so we use garbage collection later */
6780 			break;
6781 		case GMT_IS_FDESC:
6782 			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
6783 			kind = (*((int *)S_obj->fp) == GMT_IN) ? 0 : 1;	/* Used for message: 0 if stdin, 1 otherwise for user pointer */
6784 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading MATRIX from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
6785 			if ((M_obj = gmtapi_read_matrix (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
6786 				return_null (API, GMT_CPT_READ_ERROR);
6787 			S_obj->resource = M_obj;		/* Retain pointer to the allocated data so we use garbage collection later */
6788 			break;
6789 		case GMT_IS_DUPLICATE:	/* Duplicate the input MATRIX */
6790 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating MATRIX from MATRIX memory location\n");
6791 			if ((M_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
6792 			if ((M_obj = GMT_Duplicate_Data (API, GMT_IS_MATRIX, mode, M_orig)))
6793 				return_null (API, GMT_MEMORY_ERROR);
6794 			break;
6795 		case GMT_IS_REFERENCE:	/* Just pass memory location, so nothing is allocated */
6796 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing MATRIX from MATRIX memory location\n");
6797 			if ((M_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
6798 			break;
6799 		default:	/* Barking up the wrong tree here... */
6800 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import MATRIX\n");
6801 			return_null (API, GMT_NOT_A_VALID_METHOD);
6802 			break;
6803 	}
6804 	S_obj->status = GMT_IS_USED;	/* Mark as read */
6805 	return (M_obj);	/* Pass back the vector */
6806 }
6807 
gmtapi_write_matrix(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,unsigned int mode,struct GMT_MATRIX * M)6808 GMT_LOCAL int gmtapi_write_matrix (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, unsigned int mode, struct GMT_MATRIX *M) {
6809 	/* We write the MATRIX to fp [or stdout].
6810 	 * dest_type can be GMT_IS_[FILE|STREAM|FDESC]
6811 	 * mode is not used yet.
6812 	 */
6813 
6814 	bool close_file = false, append = false, was;
6815 	uint64_t row, col, ij;
6816 	unsigned int hdr;
6817 	char M_file[PATH_MAX] = {""};
6818 	static char *msg1[2] = {"Writing", "Appending"};
6819 	FILE *fp = NULL;
6820 	p_func_uint64_t GMT_2D_to_index = NULL;
6821 	GMT_getfunction api_get_val = NULL;
6822 	gmt_M_unused(mode);
6823 
6824 	if (dest_type == GMT_IS_FILE && !dest) dest_type = GMT_IS_STREAM;	/* No filename given, default to stdout */
6825 
6826 	if (dest_type == GMT_IS_FILE) {	/* dest is a file name */
6827 		static char *msg2[2] = {"create", "append to"};
6828 		strncpy (M_file, dest, PATH_MAX-1);
6829 		append = (M_file[0] == '>');	/* Want to append to existing file */
6830 		if ((fp = fopen (&M_file[append], (append) ? "a" : "w")) == NULL) {
6831 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot %s Matrix file %s\n", msg2[append], &M_file[append]);
6832 			return (GMT_ERROR_ON_FOPEN);
6833 		}
6834 		close_file = true;	/* We only close files we have opened here */
6835 	}
6836 	else if (dest_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
6837 		fp = (FILE *)dest;
6838 		if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
6839 		if (fp == GMT->session.std[GMT_OUT])
6840 			strcpy (M_file, "<stdout>");
6841 		else
6842 			strcpy (M_file, "<output stream>");
6843 	}
6844 	else if (dest_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
6845 		int *fd = dest;
6846 		if (fd && (fp = fdopen (*fd, "w")) == NULL) {
6847 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert Matrix file descriptor %d to stream in gmtapi_write_matrix\n", *fd);
6848 			return (GMT_ERROR_ON_FDOPEN);
6849 		}
6850 		if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
6851 		if (fp == GMT->session.std[GMT_OUT])
6852 			strcpy (M_file, "<stdout>");
6853 		else
6854 			strcpy (M_file, "<output file descriptor>");
6855 		close_file = true;	/* since fdopen allocates space */
6856 	}
6857 	else {
6858 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtapi_write_matrix\n", dest_type);
6859 		return (GMT_NOT_A_VALID_METHOD);
6860 	}
6861 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s Matrix to %s\n", msg1[append], &M_file[append]);
6862 
6863 	/* Set index and put-value functions */
6864 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (GMT->parent, M->shape, GMT_GRID_IS_REAL)) == NULL) {
6865 		if (close_file) fclose (fp);
6866 		return (GMT_WRONG_MATRIX_SHAPE);
6867 	}
6868 	if ((api_get_val = gmtapi_select_get_function (GMT->parent, M->type)) == NULL) {
6869 		if (close_file) fclose (fp);
6870 		return (GMT_NOT_A_VALID_TYPE);
6871 	}
6872 
6873 
6874 	/* Start writing Matrix to fp */
6875 
6876 	if (M->n_headers) {	/* Make sure we enable header records to be written */
6877 		was = GMT->current.setting.io_header[GMT_OUT];
6878 		GMT->current.setting.io_header[GMT_OUT] = true;
6879 	}
6880 	for (hdr = 0; hdr < M->n_headers; hdr++)
6881 		gmtlib_write_tableheader (GMT, fp, M->header[hdr]);
6882 
6883 	for (row = 0; row < M->n_rows; row++) {
6884 		for (col = 0; col < M->n_columns; col++) {
6885 			ij = GMT_2D_to_index (row, col, M->dim);	/* Index into the user data matrix depends on layout (M->shape) */
6886 			api_get_val (&(M->data), ij, &(GMT->current.io.curr_rec[col]));
6887 		}
6888 		if (gmtapi_bin_input_memory (GMT, M->n_columns, M->n_columns) < 0)	/* Segment header found, finish the segment we worked on and goto next */
6889 			gmt_write_segmentheader (GMT, fp, M->n_columns);
6890 		else {	/* Format an ASCII output record */
6891 			fprintf (fp, GMT->current.setting.format_float_out, GMT->current.io.curr_rec[0]);
6892 			for (col = 1; col < M->n_columns; col++) {
6893 				fprintf (fp, "%s", GMT->current.setting.io_col_separator);
6894 				fprintf (fp, GMT->current.setting.format_float_out, GMT->current.io.curr_rec[col]);
6895 			}
6896 			if (M->text && M->text[row])
6897 				fprintf (fp, "%s%s", GMT->current.setting.io_col_separator, M->text[row]);
6898 			fprintf (fp, "\n");
6899 		}
6900 	}
6901 	if (M->n_headers) GMT->current.setting.io_header[GMT_OUT] = was;  /* Revert to the original setting */
6902 
6903 	if (close_file) fclose (fp);
6904 	return (GMT_NOERROR);
6905 }
6906 
6907 /*! . */
gmtapi_export_matrix(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_MATRIX * M_obj)6908 GMT_LOCAL int gmtapi_export_matrix (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_MATRIX *M_obj) {
6909 	/* Does the actual work of writing out the specified Matrix to a destination.  Only FILE supported for testing.
6910 	 * The mode not used yet.
6911 	 */
6912 	int item, error;
6913 	unsigned int kind;
6914 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
6915 	struct GMT_CTRL *GMT = API->GMT;
6916 
6917 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_matrix: Passed ID = %d and mode = %d\n", object_ID, mode);
6918 
6919 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
6920 	if ((item = gmtlib_validate_id (API, GMT_IS_MATRIX, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
6921 
6922 	S_obj = API->object[item];	/* This is the API object for the output destination */
6923 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET)) {	/* Only allow writing of a data set once, unless we override by resetting the mode */
6924 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));
6925 	}
6926 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
6927 
6928 	/* Passed sanity and allowed to write */
6929 
6930 	switch (S_obj->method) {	/* File, array, stream etc ? */
6931 		case GMT_IS_FILE:
6932 			/* gmtapi_write_matrix will report where it is writing from if level is GMT_MSG_INFORMATION */
6933 			GMT_Report (API, GMT_MSG_INFORMATION, "Write MATRIX to %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
6934 			if (S_obj->geometry == GMT_IS_SURFACE) {	/* Must convert matrix to grid then write to file */
6935 				struct GMT_GRID *G;
6936 				G = gmtapi_matrix2grid (API, M_obj, NULL);	/* Convert the matrix to a grid */
6937 				error = gmtapi_export_grid (API, object_ID, mode, G);
6938 				if (gmtapi_destroy_grid (API, &G))
6939 					return (gmtlib_report_error (API, GMT_DATA_READ_ERROR));
6940 			}
6941 			else if ((error = gmtapi_write_matrix (GMT, S_obj->filename, S_obj->method, mode, M_obj))) return (gmtlib_report_error (API, error));
6942 			break;
6943 	 	case GMT_IS_STREAM:
6944 			/* gmtapi_write_matrix will report where it is writing from if level is GMT_MSG_INFORMATION */
6945 			kind = (S_obj->fp == GMT->session.std[GMT_OUT]) ? 0 : 1;	/* For message only: 0 if stdout, 1 otherwise for user pointer */
6946 			GMT_Report (API, GMT_MSG_INFORMATION, "Write MATRIX to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
6947 			if ((error = gmtapi_write_matrix (GMT, S_obj->fp, S_obj->method, mode, M_obj))) return (gmtlib_report_error (API, error));
6948 			break;
6949 	 	case GMT_IS_FDESC:
6950 			/* gmtapi_write_matrix will report where it is writing from if level is GMT_MSG_INFORMATION */
6951 			kind = (*((int *)S_obj->fp) == GMT_OUT) ? 0 : 1;	/* For message only: 0 if stdout, 1 otherwise for user pointer */
6952 			GMT_Report (API, GMT_MSG_INFORMATION, "Write MATRIX to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
6953 			if ((error = gmtapi_write_matrix (GMT, S_obj->fp, S_obj->method, mode, M_obj))) return (gmtlib_report_error (API, error));
6954 			break;
6955 		default:
6956 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export MATRIX\n");
6957 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
6958 			break;
6959 	}
6960 	S_obj->status = GMT_IS_USED;	/* Mark as written */
6961 
6962 	return GMT_NOERROR;
6963 }
6964 
gmtapi_write_vector(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,unsigned int mode,struct GMT_VECTOR * V)6965 GMT_LOCAL int gmtapi_write_vector (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, unsigned int mode, struct GMT_VECTOR *V) {
6966 	/* We write the VECTOR to fp [or stdout].
6967 	 * dest_type can be GMT_IS_[FILE|STREAM|FDESC]
6968 	 * mode is not used yet.
6969 	 */
6970 
6971 	bool close_file = false, append = false, was;
6972 	uint64_t row, col;
6973 	unsigned int hdr;
6974 	char V_file[PATH_MAX] = {""};
6975 	static char *msg1[2] = {"Writing", "Appending"};
6976 	FILE *fp = NULL;
6977 	GMT_getfunction *api_get_val = NULL;
6978 	gmt_M_unused(mode);
6979 
6980 	if (V == NULL) {
6981 		GMT_Report(GMT->parent, GMT_MSG_ERROR, "GMTAPI: gmtapi_write_vector passed a NULL pointer *V\n");
6982 		return GMT_NOTSET;
6983 	}
6984 	if (dest_type == GMT_IS_FILE && !dest) dest_type = GMT_IS_STREAM;	/* No filename given, default to stdout */
6985 
6986 	if (dest_type == GMT_IS_FILE) {	/* dest is a file name */
6987 		static char *msg2[2] = {"create", "append to"};
6988 		strncpy (V_file, dest, PATH_MAX-1);
6989 		append = (V_file[0] == '>');	/* Want to append to existing file */
6990 		if ((fp = fopen (&V_file[append], (append) ? "a" : "w")) == NULL) {
6991 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot %s Vector file %s\n", msg2[append], &V_file[append]);
6992 			return (GMT_ERROR_ON_FOPEN);
6993 		}
6994 		close_file = true;	/* We only close files we have opened here */
6995 	}
6996 	else if (dest_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
6997 		fp = (FILE *)dest;
6998 		if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
6999 		if (fp == GMT->session.std[GMT_OUT])
7000 			strcpy (V_file, "<stdout>");
7001 		else
7002 			strcpy (V_file, "<output stream>");
7003 	}
7004 	else if (dest_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
7005 		int *fd = dest;
7006 		if (fd && (fp = fdopen (*fd, "a")) == NULL) {
7007 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert Vector file descriptor %d to stream in gmtapi_write_vector\n", *fd);
7008 			return (GMT_ERROR_ON_FDOPEN);
7009 		}
7010 		if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
7011 		if (fp == GMT->session.std[GMT_OUT])
7012 			strcpy (V_file, "<stdout>");
7013 		else
7014 			strcpy (V_file, "<output file descriptor>");
7015 		close_file = true;	/* since fdopen allocates space */
7016 	}
7017 	else {
7018 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtapi_write_vector\n", dest_type);
7019 		return (GMT_NOT_A_VALID_METHOD);
7020 	}
7021 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s Vector to %s\n", msg1[append], &V_file[append]);
7022 
7023 	/* Set get function per vector column */
7024 	api_get_val = gmt_M_memory (GMT, NULL, V->n_columns, GMT_getfunction);
7025 	for (col = 0; col < V->n_columns; col++) {
7026 		if ((api_get_val[col] = gmtapi_select_get_function (GMT->parent, V->type[col])) == NULL) {
7027 			gmt_M_free (GMT, api_get_val);
7028 			return (GMT_NOT_A_VALID_TYPE);
7029 		}
7030 	}
7031 
7032 	/* Start writing Vector to fp */
7033 
7034 	if (V->n_headers) {	/* Make sure we enable header records to be written */
7035 		was = GMT->current.setting.io_header[GMT_OUT];
7036 		GMT->current.setting.io_header[GMT_OUT] = true;
7037 	}
7038 	for (hdr = 0; hdr < V->n_headers; hdr++)
7039 		gmtlib_write_tableheader (GMT, fp, V->header[hdr]);
7040 
7041 	for (row = 0; row < V->n_rows; row++) {
7042 		for (col = 0; col < V->n_columns; col++)
7043 			api_get_val[col] (&(V->data[col]), row, &(GMT->current.io.curr_rec[col]));
7044 		if (gmtapi_bin_input_memory (GMT, V->n_columns, V->n_columns) < 0)	/* Segment header found, finish the segment we worked on and goto next */
7045 			gmt_write_segmentheader (GMT, fp, V->n_columns);
7046 		else {	/* Format an ASCII record for output */
7047 			gmt_ascii_output_col (GMT, fp, GMT->current.io.curr_rec[0], 0);
7048 			for (col = 1; col < V->n_columns; col++) {
7049 				fprintf (fp, "%s", GMT->current.setting.io_col_separator);
7050 				gmt_ascii_output_col (GMT, fp, GMT->current.io.curr_rec[col], col);
7051 			}
7052 			if (V->text && V->text[row])
7053 				fprintf (fp, "%s%s", GMT->current.setting.io_col_separator, V->text[row]);
7054 			fprintf (fp, "\n");
7055 		}
7056 	}
7057 	gmt_M_free (GMT, api_get_val);
7058 
7059 	if (close_file) fclose (fp);
7060 	if (V->n_headers) GMT->current.setting.io_header[GMT_OUT] = was;  /* Revert to the original setting */
7061 
7062 	return (GMT_NOERROR);
7063 }
7064 
7065 /*! . */
gmtapi_export_vector(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode,struct GMT_VECTOR * V_obj)7066 GMT_LOCAL int gmtapi_export_vector (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode, struct GMT_VECTOR *V_obj) {
7067 	/* Does the actual work of writing out the specified Matrix to a destination.  Only FILE supported for testing.
7068 	 * The mode not used yet.
7069 	 */
7070 	int item, error;
7071 	unsigned int kind;
7072 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7073 	struct GMT_CTRL *GMT = API->GMT;
7074 
7075 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_vector: Passed ID = %d and mode = %d\n", object_ID, mode);
7076 
7077 	if (object_ID == GMT_NOTSET) return (gmtlib_report_error (API, GMT_OUTPUT_NOT_SET));
7078 	if ((item = gmtlib_validate_id (API, GMT_IS_VECTOR, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
7079 
7080 	S_obj = API->object[item];	/* This is the API object for the output destination */
7081 	if (S_obj->status != GMT_IS_UNUSED && !(mode & GMT_IO_RESET)) {	/* Only allow writing of a data set once, unless we override by resetting the mode */
7082 		return (gmtlib_report_error (API, GMT_WRITTEN_ONCE));
7083 	}
7084 	if (mode & GMT_IO_RESET) mode -= GMT_IO_RESET;
7085 
7086 	/* Passed sanity and allowed to write */
7087 
7088 	switch (S_obj->method) {	/* File, array, stream etc ? */
7089 		case GMT_IS_FILE:
7090 			/* gmtapi_write_vector will report where it is writing from if level is GMT_MSG_INFORMATION */
7091 			GMT_Report (API, GMT_MSG_INFORMATION, "Write VECTOR to %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
7092 			if ((error = gmtapi_write_vector (GMT, S_obj->filename, S_obj->method, mode, V_obj))) return (gmtlib_report_error (API, error));
7093 			break;
7094 	 	case GMT_IS_STREAM:
7095 			/* gmtapi_write_vector will report where it is writing from if level is GMT_MSG_INFORMATION */
7096 			kind = (S_obj->fp == GMT->session.std[GMT_OUT]) ? 0 : 1;	/* For message only: 0 if stdout, 1 otherwise for user pointer */
7097 			GMT_Report (API, GMT_MSG_INFORMATION, "Write VECTOR to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
7098 			if ((error = gmtapi_write_vector (GMT, S_obj->fp, S_obj->method, mode, V_obj))) return (gmtlib_report_error (API, error));
7099 			break;
7100 	 	case GMT_IS_FDESC:
7101 			/* gmtapi_write_vector will report where it is writing from if level is GMT_MSG_INFORMATION */
7102 			kind = (*((int *)S_obj->fp) == GMT_OUT) ? 0 : 1;	/* For message only: 0 if stdout, 1 otherwise for user pointer */
7103 			GMT_Report (API, GMT_MSG_INFORMATION, "Write VECTOR to %s %s output stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
7104 			if ((error = gmtapi_write_vector (GMT, S_obj->fp, S_obj->method, mode, V_obj))) return (gmtlib_report_error (API, error));
7105 			break;
7106 		default:
7107 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to export VECTOR\n");
7108 			return (gmtlib_report_error (API, GMT_NOT_A_VALID_METHOD));
7109 			break;
7110 	}
7111 	S_obj->status = GMT_IS_USED;	/* Mark as written */
7112 
7113 	return GMT_NOERROR;
7114 }
7115 
gmtapi_read_vector(struct GMT_CTRL * GMT,void * source,unsigned int src_type,unsigned int mode)7116 GMT_LOCAL struct GMT_VECTOR *gmtapi_read_vector (struct GMT_CTRL *GMT, void *source, unsigned int src_type, unsigned int mode) {
7117 	/* We read the VECTOR from fp [or stdin].
7118 	 * src_type can be GMT_IS_[FILE|STREAM|FDESC]
7119 	 * mode is not used yet.  We only do ascii file for now - later need to deal with -b
7120 	 */
7121 
7122 	bool close_file = false, first = true, add_first_segheader = false, in_header_section = true;
7123 	unsigned int pos;
7124 	uint64_t nt_alloc = 0, nh_alloc = 0, n_headers = 0, row = 0, n_col, col, dim[GMT_DIM_SIZE] = {0, 0, GMT->current.setting.export_type, 0};
7125 	char V_file[PATH_MAX] = {""};
7126 	char line[GMT_BUFSIZ] = {""};
7127 	char **text = NULL, **header = NULL;
7128 	FILE *fp = NULL;
7129 	struct GMT_VECTOR *V = NULL;
7130 	GMT_putfunction api_put_val = NULL;
7131 	gmt_M_unused(mode);
7132 
7133 	if (src_type == GMT_IS_FILE && !source) src_type = GMT_IS_STREAM;	/* No filename given, default to stdin */
7134 
7135 	if (src_type == GMT_IS_FILE) {	/* dest is a file name */
7136 		strncpy (V_file, source, PATH_MAX-1);
7137 		if ((fp = gmt_fopen (GMT, V_file, "r")) == NULL) {
7138 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open Vector file %s\n", V_file);
7139 			return_null (GMT->parent, GMT_ERROR_ON_FOPEN);
7140 		}
7141 		close_file = true;	/* We only close files we have opened here */
7142 	}
7143 	else if (src_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
7144 		fp = (FILE *)source;
7145 		if (fp == NULL) fp = GMT->session.std[GMT_IN];	/* Default destination */
7146 		if (fp == GMT->session.std[GMT_IN])
7147 			strcpy (V_file, "<stdin>");
7148 		else
7149 			strcpy (V_file, "<input stream>");
7150 	}
7151 	else if (src_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
7152 		int *fd = source;
7153 		if (fd && (fp = fdopen (*fd, "r")) == NULL) {
7154 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert Vector file descriptor %d to stream in gmtapi_read_vector\n", *fd);
7155 			return_null (GMT->parent, GMT_ERROR_ON_FDOPEN);
7156 		}
7157 		if (fd == NULL) fp = GMT->session.std[GMT_IN];	/* Default destination */
7158 		if (fp == GMT->session.std[GMT_IN])
7159 			strcpy (V_file, "<stdin>");
7160 		else
7161 			strcpy (V_file, "<input file descriptor>");
7162 		close_file = true;	/* since fdopen allocates space */
7163 	}
7164 	else {
7165 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtapi_read_vector\n", src_type);
7166 		return_null (GMT->parent, GMT_NOT_A_VALID_METHOD);
7167 	}
7168 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Read Vector from %s\n", V_file);
7169 
7170 	while (fgets (line, GMT_BUFSIZ, fp)) {
7171 		gmt_chop (line);	/* Remove linefeeds */
7172 		if (strchr (GMT->current.setting.io_head_marker_in, line[0])) {
7173 			if (in_header_section) {
7174 				if (nh_alloc <= n_headers) header = gmt_M_memory (GMT, NULL, nh_alloc += GMT_TINY_CHUNK, char *);
7175 				header[n_headers++] = strdup (line);
7176 			}
7177 			continue;
7178 		}
7179 		in_header_section = false;
7180 		if (line[0] == '>') {
7181 			if (first) {	/* Have not allocated yet so just skip that row for now */
7182 				first = false;
7183 				add_first_segheader = true;
7184 			}
7185 			else {	/* Already allocated so place NaNs */
7186 				gmt_prep_tmp_arrays (GMT, GMT_IN, row, dim[0]);	/* Init or reallocate tmp vectors */
7187 				for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][row] = GMT->session.d_NaN;
7188 			}
7189 		}
7190 		else {	/* Regular data record */
7191 			if (dim[0] == 0)	/* First time we must extablish how many columns */
7192 				dim[0] = gmtlib_conv_text2datarec (GMT, line, GMT_BUFSIZ, GMT->current.io.curr_rec, &pos);
7193 			if ((n_col = gmtlib_conv_text2datarec (GMT, line, dim[0], GMT->current.io.curr_rec, &pos)) != dim[0]) {
7194 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Vector record %" PRIu64 " only had %" PRIu64 " columns but %" PRIu64 " was expected.  Record skipped\n", row, n_col, dim[0]);
7195 				continue;
7196 			}
7197 			gmt_prep_tmp_arrays (GMT, GMT_IN, row, dim[0]);	/* Init or reallocate tmp vectors */
7198 			for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][row] = GMT->current.io.curr_rec[col];
7199 			if (line[pos]) {	/* Deal with trailing text */
7200 				if (nt_alloc <= row) text = gmt_M_memory (GMT, NULL, nt_alloc += GMT_INITIAL_MEM_ROW_ALLOC, char **);
7201 				text[row] = strdup (&line[pos]);
7202 			}
7203 		}
7204 		row++;
7205 	}
7206 	if (add_first_segheader) for (col = 0; col < dim[0]; col++) GMT->hidden.mem_coord[col][0] = GMT->session.d_NaN;
7207 	dim[1] = row;	/* Allocate all vectors using current type setting in the defaults [GMT_DOUBLE] */
7208 	if ((V = GMT_Create_Data (GMT->parent, GMT_IS_VECTOR, GMT_IS_POINT, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) {
7209 		if (close_file) gmt_fclose (GMT, fp);
7210 		return_null (GMT->parent, GMT_MEMORY_ERROR);
7211 	}
7212 	for (col = 0; col < V->n_columns; col++) {
7213 		if ((api_put_val = gmtapi_select_put_function (GMT->parent, V->type[col])) == NULL)
7214 			return_null (GMT->parent, GMT_NOT_A_VALID_TYPE);
7215 		for (row = 0; row < V->n_rows; row++)
7216 			api_put_val (&(V->data[col]), row, GMT->hidden.mem_coord[col][row]);
7217 	}
7218 
7219 	if (text) {	/* Attach the trailing text to the vector */
7220 		struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V);
7221 		if (nt_alloc > row) text = gmt_M_memory (GMT, text, row, char **);
7222 		GMT_Put_Strings (GMT->parent, GMT_IS_VECTOR, V, text);
7223 		VH->alloc_mode_text = GMT_ALLOC_INTERNALLY;	/* Override since it is allocated internally in GMT */
7224 	}
7225 	if (n_headers) {	/* Pass out the header records as well */
7226 		if (nh_alloc > n_headers) header = gmt_M_memory (GMT, header, n_headers, char *);
7227 		V->header = header;
7228 		V->n_headers = n_headers;
7229 	}
7230 
7231 	if (close_file) gmt_fclose (GMT, fp);
7232 	return (V);
7233 }
7234 
7235 /*! . */
gmtapi_import_vector(struct GMTAPI_CTRL * API,int object_ID,unsigned int mode)7236 GMT_LOCAL struct GMT_VECTOR * gmtapi_import_vector (struct GMTAPI_CTRL *API, int object_ID, unsigned int mode) {
7237 	/* Does the actual work of loading in a GMT vector table. */
7238 	int item;
7239 	unsigned int kind;
7240 	struct GMT_VECTOR *V_obj = NULL, *V_orig = NULL;
7241 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7242 	struct GMT_CTRL *GMT = API->GMT;
7243 
7244 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_import_vector: Passed ID = %d and mode = %d\n", object_ID, mode);
7245 
7246 	if (object_ID == GMT_NOTSET) return_null (API, GMT_NO_INPUT);
7247 	if ((item = gmtlib_validate_id (API, GMT_IS_VECTOR, object_ID, GMT_IN, GMTAPI_OPTION_INPUT)) == GMT_NOTSET)
7248 		return_null (API, API->error);
7249 
7250 	S_obj = API->object[item];	/* Use S_obj as shorthand */
7251 	if (S_obj->status != GMT_IS_UNUSED) { /* Already read this resource before; are we allowed to re-read? */
7252 		if (S_obj->method == GMT_IS_STREAM || S_obj->method == GMT_IS_FDESC)
7253 			return_null (API, GMT_READ_ONCE); /* Not allowed to re-read streams */
7254 		if (!(mode & GMT_IO_RESET)) return_null (API, GMT_READ_ONCE);	/* Not authorized to re-read */
7255 	}
7256 
7257 	/* Passed sanity and allowed to read */
7258 
7259 	switch (S_obj->method) {	/* File, array, stream etc ? */
7260 		case GMT_IS_FILE:
7261 			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
7262 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading VECTOR from %s %s\n", gmtapi_method (S_obj->method), S_obj->filename);
7263 			if ((V_obj = gmtapi_read_vector (GMT, S_obj->filename, S_obj->method, mode)) == NULL)
7264 				return_null (API, GMT_DATA_READ_ERROR);
7265 			S_obj->resource = V_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
7266 			break;
7267 		case GMT_IS_STREAM:
7268  			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
7269 			kind = (S_obj->fp == GMT->session.std[GMT_IN]) ? 0 : 1;	/* For message only: 0 if stdin, 1 otherwise for user pointer */
7270 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading VECTOR from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
7271 			if ((V_obj = gmtapi_read_vector (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
7272 				return_null (API, GMT_DATA_READ_ERROR);
7273 			S_obj->resource = V_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
7274 			break;
7275 		case GMT_IS_FDESC:
7276 			/* gmtapi_read_vector will report where it is reading from if level is GMT_MSG_INFORMATION */
7277 			kind = (*((int *)S_obj->fp) == GMT_IN) ? 0 : 1;	/* For message only: 0 if stdin, 1 otherwise for user pointer */
7278 			GMT_Report (API, GMT_MSG_INFORMATION, "Reading VECTOR from %s %s stream\n", gmtapi_method (S_obj->method), GMT_stream[kind]);
7279 			if ((V_obj = gmtapi_read_vector (GMT, S_obj->fp, S_obj->method, mode)) == NULL)
7280 				return_null (API, GMT_CPT_READ_ERROR);
7281 			S_obj->resource = V_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
7282 			break;
7283 		case GMT_IS_DUPLICATE:	/* Duplicate the input VECTOR */
7284 			GMT_Report (API, GMT_MSG_INFORMATION, "Duplicating VECTOR from VECTOR memory location\n");
7285 			if ((V_orig = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
7286 			if ((V_obj = GMT_Duplicate_Data (API, GMT_IS_VECTOR, mode, V_orig)))
7287 				return_null (API, GMT_MEMORY_ERROR);
7288 			break;
7289 		case GMT_IS_REFERENCE:	/* Just pass memory location, so nothing is allocated */
7290 			GMT_Report (API, GMT_MSG_INFORMATION, "Referencing VECTOR from VECTOR memory location\n");
7291 			if ((V_obj = S_obj->resource) == NULL) return_null (API, GMT_PTR_IS_NULL);
7292 			break;
7293 		default:	/* Barking up the wrong tree here... */
7294 			GMT_Report (API, GMT_MSG_ERROR, "Wrong method used to import VECTOR\n");
7295 			return_null (API, GMT_NOT_A_VALID_METHOD);
7296 			break;
7297 	}
7298 	S_obj->status = GMT_IS_USED;	/* Mark as read */
7299 	return (V_obj);	/* Pass back the vector */
7300 }
7301 
7302 /*! . */
gmtapi_import_data(struct GMTAPI_CTRL * API,enum GMT_enum_family family,int object_ID,unsigned int mode,void * data)7303 GMT_LOCAL void * gmtapi_import_data (struct GMTAPI_CTRL *API, enum GMT_enum_family family, int object_ID, unsigned int mode, void *data) {
7304 
7305 	/* Function that will import the data object referred to by the object_ID (or all registered inputs if object_ID == GMT_NOTSET).
7306 	 * This is a wrapper functions for CPT, Dataset, Grid, Image and PostScript imports; see the specific functions
7307 	 * for details on the arguments, in particular the mode setting (or see the GMT API documentation).
7308 	 */
7309 	int item, flag = GMT_NOTSET;
7310 	void *new_obj = NULL;
7311 
7312 	if (API == NULL) return_null (API, GMT_NOT_A_SESSION);			/* GMT_Create_Session has not been called */
7313 	if (!API->registered[GMT_IN]) return_null (API, GMT_NO_INPUT);		/* No sources registered yet */
7314 
7315 	/* Get information about this resource first */
7316 	if (multiple_files_ok (family)) {
7317 		flag = (API->module_input) ? GMTAPI_MODULE_INPUT : GMTAPI_OPTION_INPUT;
7318 	}
7319 	if ((item = gmtlib_validate_id (API, family, object_ID, GMT_IN, flag)) == GMT_NOTSET) return_null (API, API->error);
7320 
7321 	switch (family) {
7322 		case GMT_IS_PALETTE:
7323 			new_obj = gmtapi_import_palette (API, object_ID, mode);		/* Try to import a CPT */
7324 			break;
7325 		case GMT_IS_DATASET:
7326 			new_obj = gmtapi_import_dataset (API, object_ID, mode);		/* Try to import data tables */
7327 			break;
7328 		case GMT_IS_GRID:
7329 			new_obj = gmtapi_import_grid (API, object_ID, mode, data);	/* Try to import a grid */
7330 			break;
7331 		case GMT_IS_IMAGE:
7332 			new_obj = gmtapi_import_image (API, object_ID, mode, data);	/* Try to import an image */
7333 			break;
7334 		case GMT_IS_CUBE:
7335 			new_obj = gmtapi_import_cube (API, object_ID, mode, data);	/* Try to import a 3-D cube */
7336 			break;
7337 		case GMT_IS_MATRIX:
7338 			new_obj = gmtapi_import_matrix (API, object_ID, mode);		/* Try to import a matrix */
7339 			break;
7340 		case GMT_IS_VECTOR:
7341 			new_obj = gmtapi_import_vector (API, object_ID, mode);		/* Try to import a vector */
7342 			break;
7343 		case GMT_IS_POSTSCRIPT:
7344 			new_obj = gmtapi_import_postscript (API, object_ID, mode);		/* Try to import PS */
7345 			break;
7346 		default:
7347 			API->error = GMT_NOT_A_VALID_FAMILY;
7348 			break;
7349 	}
7350 	if (new_obj == NULL) return_null (API, API->error);	/* Return NULL as something went wrong */
7351 	return (new_obj);	/* Successful, return pointer */
7352 }
7353 
7354 /*! . */
gmtapi_get_data(void * V_API,int object_ID,unsigned int mode,void * data)7355 GMT_LOCAL void * gmtapi_get_data (void *V_API, int object_ID, unsigned int mode, void *data) {
7356 	/* Function to import registered data sources directly into program memory as a set (not record-by-record).
7357 	 * data is pointer to an existing grid container when we read a grid in two steps, otherwise use NULL.
7358 	 * ID is the registered resource from which to import.
7359 	 * Return: Pointer to data container, or NULL if there were errors (passed back via API->error).
7360 	 */
7361 	int item, family, flag = GMT_NOTSET;
7362 	bool was_enabled;
7363 	void *new_obj = NULL;
7364 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7365 	struct GMTAPI_CTRL *API = NULL;
7366 
7367 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
7368 
7369 	/* Determine the item in the object list that matches this ID and direction */
7370 	API = gmtapi_get_api_ptr (V_API);
7371 	API->error = GMT_NOERROR;
7372 	if (object_ID == GMT_NOTSET) {	/* Must pick up the family from the shelf */
7373 		family = API->shelf;
7374 		API->shelf = GMT_NOTSET;
7375 		if (multiple_files_ok(family)) flag = (API->module_input) ? GMTAPI_MODULE_INPUT : GMTAPI_OPTION_INPUT;
7376 	}
7377 	else
7378 		family = GMT_NOTSET;
7379 	if ((item = gmtlib_validate_id (API, family, object_ID, GMT_IN, flag)) == GMT_NOTSET) {
7380 		return_null (API, API->error);
7381 	}
7382 
7383 	was_enabled = API->io_enabled[GMT_IN];
7384 	if (!was_enabled && gmtapi_begin_io (API, GMT_IN) != GMT_NOERROR) {	/* Enables data input if not already set and sets access mode */
7385 		return_null (API, API->error);
7386 	}
7387 	S_obj = API->object[item];	/* Short-hand */
7388 	S_obj->selected = true;	/* Make sure it the requested data set is selected */
7389 
7390 	/* OK, try to do the importing */
7391 	if ((new_obj = gmtapi_import_data (API, S_obj->family, object_ID, mode, data)) == NULL) {
7392 		return_null (API, API->error);
7393 	}
7394 
7395 	if (!was_enabled && GMT_End_IO (API, GMT_IN, 0) != GMT_NOERROR) {	/* Disables data input if we had to set it in this function */
7396 		return_null (API, API->error);
7397 	}
7398 #ifdef DEBUG
7399 	gmtapi_set_object (API, S_obj);
7400 	//gmtapi_list_objects (API, "gmtapi_get_data");
7401 #endif
7402 	return (new_obj);		/* Return pointer to the data container */
7403 }
7404 
gmtapi_reconsider_messenger(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S_obj)7405 GMT_LOCAL void gmtapi_reconsider_messenger (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S_obj) {
7406 	/* A messenger is a dummy container with no memory allocated that is there to tell a
7407 	 * module that it can be deleted to make space for an actual container with output data.
7408 	 * However, for MATRIX and VECTOR output we will need to check if user supplied actual
7409 	 * output memory.  For this to be true we need (a) non-NULL vectors/matrix and (b) known
7410 	 * dimension(s).  If we pass those tests then we set the messenger flag to false.
7411 	 */
7412 	gmt_M_unused(API);
7413 	if (S_obj->messenger == false) return;	/* Nothing to ponder */
7414 	if (S_obj->actual_family == GMT_IS_VECTOR) {	/* Examine a vector container */
7415 		struct GMT_VECTOR *V = S_obj->resource;
7416 		if (V == NULL) return;
7417 		if (V->n_rows == 0) return;
7418 		for (unsigned int col = 0; col < V->n_columns; col++)
7419 			if (V->data[col].f8 == NULL) return;	/* Any of the actual members could be used here */
7420 	}
7421 	else if (S_obj->actual_family == GMT_IS_MATRIX) {	/* Examine a matrix container */
7422 		struct GMT_MATRIX *M = S_obj->resource;
7423 		if (M == NULL) return;
7424 		if (M->n_rows == 0 || M->n_columns == 0) return;
7425 		if (M->data.f8 == NULL) return;	/* Any of the actual members could be used here */
7426 	}
7427 	else	/* Wrong container */
7428 		return;
7429 	/* Here we need to shoot the messenger */
7430 	S_obj->messenger = false;
7431 }
7432 
7433 /*! . */
gmtapi_export_data(struct GMTAPI_CTRL * API,enum GMT_enum_family family,int object_ID,unsigned int mode,void * data)7434 GMT_LOCAL int gmtapi_export_data (struct GMTAPI_CTRL *API, enum GMT_enum_family family, int object_ID, unsigned int mode, void *data) {
7435 	/* Function that will export the single data object referred to by the object_ID as registered by GMT_Register_IO.
7436 	 */
7437 	int error, item;
7438 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7439 
7440 	if (API == NULL) return (GMT_NOT_A_SESSION);	/* GMT_Create_Session has not been called */
7441 	if (data == NULL) return (GMT_PTR_IS_NULL);		/* Got a NULL data pointer */
7442 	if (!API->registered[GMT_OUT]) return (gmtlib_report_error (API, GMT_NO_OUTPUT));		/* No destination registered yet */
7443 
7444 	/* Get information about this resource first */
7445 	if ((item = gmtlib_validate_id (API, family, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
7446 
7447 	S_obj = API->object[item];	/* The current object we are trying to export */
7448 	/* The case where object_ID is not set but a virtual (memory) file is found is a special case: we must supply the correct object_ID */
7449 	if (object_ID == GMT_NOTSET && item && S_obj->method != GMT_IS_FILE)
7450 		object_ID = S_obj->ID;	/* Found virtual file; set actual object_ID */
7451 
7452 	/* Check if this is a container passed from the outside to capture output */
7453 	gmtapi_reconsider_messenger (API, S_obj);	/* This may set S_obj->messenger to false in some cases */
7454 	if (S_obj->messenger && S_obj->resource) {	/* Need to destroy the dummy container before passing data out */
7455 		error = gmtapi_destroy_data_ptr (API, S_obj->actual_family, S_obj->resource);	/* Do the dirty deed */
7456 		if (error) return error;
7457 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_export_data: Messenger dummy output container for object %d [item %d] freed and set resource=data=NULL\n", S_obj->ID, item);
7458 		S_obj->resource  = NULL;	/* Since we now have nothing */
7459 		S_obj->messenger = false;	/* OK, now clean for output */
7460 	}
7461 
7462 #ifdef DEBUG
7463 	//gmtapi_list_objects (API, "gmtapi_export_data-in");
7464 #endif
7465 	/* PW Note: Important that any exporter needing to create memory to hold an output
7466 	 * that will be returned to the caller: Never create/duplicate with the API functions
7467 	 * as these add memory registrations and thus leads to duplicate entries in the objects
7468 	 * table.  Symptoms of this are memory junk back in the calling program because two objects
7469 	 * have a pointer to the same memory and one of them is destroyed, messing up the other.
7470 	 * Use the gmtlib functions like gmt_duplicate_grid, etc for these purposes herein. */
7471 
7472 	switch (family) {
7473 		case GMT_IS_PALETTE:	/* Export a CPT */
7474 			error = gmtapi_export_palette (API, object_ID, mode, data);
7475 			break;
7476 		case GMT_IS_DATASET:	/* Export a Data set */
7477 			error = gmtapi_export_dataset (API, object_ID, mode, data);
7478 			break;
7479 		case GMT_IS_GRID:	/* Export a GMT grid */
7480 			error = gmtapi_export_grid (API, object_ID, mode, data);
7481 			break;
7482 		case GMT_IS_IMAGE:	/* Export a GMT image */
7483 			error = gmtapi_export_image (API, object_ID, mode, data);
7484 			break;
7485 		case GMT_IS_CUBE:	/* Export a GMT cube */
7486 			error = gmtapi_export_cube (API, object_ID, mode, data);
7487 			break;
7488 		case GMT_IS_POSTSCRIPT:	/* Export PS */
7489 			error = gmtapi_export_postscript (API, object_ID, mode, data);
7490 			break;
7491 		case GMT_IS_MATRIX:	/* Export MATRIX */
7492 			error = gmtapi_export_matrix (API, object_ID, mode, data);
7493 			break;
7494 		case GMT_IS_VECTOR:	/* Export VECTOR */
7495 			error = gmtapi_export_vector (API, object_ID, mode, data);
7496 			break;
7497 		default:
7498 			error = GMT_NOT_A_VALID_FAMILY;
7499 			break;
7500 	}
7501 #ifdef DEBUG
7502 	//gmtapi_list_objects (API, "gmtapi_export_data-out");
7503 #endif
7504 	return (gmtlib_report_error (API, error));	/* Return status */
7505 }
7506 
7507 /*! . */
gmtapi_put_data(void * V_API,int object_ID,unsigned int mode,void * data)7508 GMT_LOCAL int gmtapi_put_data (void *V_API, int object_ID, unsigned int mode, void *data) {
7509 	/* Function to write data directly from program memory as a set (not record-by-record).
7510 	 * We can combine the <register resource - export resource > sequence in
7511 	 * one combined function.  See GMT_Register_IO for details on arguments.
7512 	 * Here, *data is the pointer to the data object to save (CPT, dataset, Grid)
7513 	 * ID is the registered destination.
7514 	 * While only one output destination is allowed, for DATASETS one can
7515 	 * have the tables and even segments be written to individual files (see the mode
7516 	 * description in the documentation for how to enable this feature.)
7517 	 * Return: false if all is well, true if there was an error (and set API->error).
7518 	 */
7519 	int item;
7520 	bool was_enabled;
7521 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7522 	struct GMTAPI_CTRL *API = NULL;
7523 
7524 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
7525 	if (data == NULL) return_error (V_API, GMT_PTR_IS_NULL);
7526 	API = gmtapi_get_api_ptr (V_API);
7527 	API->error = GMT_NOERROR;
7528 
7529 	/* Determine the item in the object list that matches this ID and direction */
7530 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) return_error (API, API->error);
7531 
7532 	was_enabled = API->io_enabled[GMT_OUT];
7533 	if (!was_enabled && gmtapi_begin_io (API, GMT_OUT) != GMT_NOERROR) {	/* Enables data output if not already set and sets access mode */
7534 		return_error (API, API->error);
7535 	}
7536 	S_obj = API->object[item];	/* The current object we are trying to export */
7537 	if (gmtapi_export_data (API, S_obj->family, object_ID, mode, data) != GMT_NOERROR) return_error (API, API->error);
7538 
7539 	if (!was_enabled && GMT_End_IO (API, GMT_OUT, 0) != GMT_NOERROR) {	/* Disables data output if we had to set it in this function */
7540 		return_error (API, API->error);
7541 	}
7542 #ifdef DEBUG
7543 	gmtapi_set_object (API, S_obj);
7544 	//gmtapi_list_objects (API, "gmtapi_put_data");
7545 #endif
7546 	return (GMT_NOERROR);	/* No error encountered */
7547 }
7548 
7549 /*! See if this file has already been registered and used.  If so, do not add it again */
gmtapi_not_used(struct GMTAPI_CTRL * API,char * name)7550 GMT_LOCAL bool gmtapi_not_used (struct GMTAPI_CTRL *API, char *name) {
7551 	unsigned int item = 0;
7552 	bool not_used = true;
7553 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7554 	while (item < API->n_objects && not_used) {
7555 		if ((S_obj = API->object[item]) == NULL) continue;	/* Skip NULLs */
7556 			if (S_obj->direction == GMT_IN && S_obj->status != GMT_IS_UNUSED && S_obj->filename && !strcmp (S_obj->filename, name))
7557 			/* Used resource with same name */
7558 			not_used = false;	/* Got item with same name, but used */
7559 		else
7560 			item++;	/* No, keep looking */
7561 	}
7562 	return (not_used);
7563 }
7564 
7565 /*! . */
gmtapi_init_import(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int geometry,unsigned int mode,struct GMT_OPTION * head)7566 GMT_LOCAL int gmtapi_init_import (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int geometry, unsigned int mode, struct GMT_OPTION *head) {
7567 	/* Handle registration of data files given with option arguments and/or stdin as input sources.
7568 	 * These are the possible actions taken:
7569 	 * 1. If (mode | GMT_ADD_FILES_IF_NONE) is true and NO resources have previously been registered, then we scan the option list for files (option == '<' (input)).
7570 	 *    For each file found we register the item as a resource.
7571 	 * 2. If (mode | GMT_ADD_FILES_ALWAYS) is true then we always scan the option list for files (option == '<' (input)).
7572 	 *    For each file found we register the item as a resource.
7573 	 * 3. If (mode & GMT_ADD_STDIO_IF_NONE) is true we will register stdin as an input source only if there are NO input items registered.
7574 	 * 4. If (mode & GMT_ADD_STDIO_ALWAYS) is true we will register stdin as an input source, regardless of other items already registered.
7575 	 */
7576 
7577 	int object_ID, first_ID = GMT_NOTSET, item;
7578  	unsigned int n_reg = 0;
7579 	struct GMT_OPTION *current = NULL;
7580 	double *wesn = NULL;
7581 
7582 	API->error = GMT_NOERROR;
7583 
7584 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_import: Passed family = %s and geometry = %s\n", GMT_family[family], GMT_geometry[gmtapi_gmtry(geometry)]);
7585 
7586 	if (mode & GMT_ADD_EXISTING)
7587 		n_reg = gmtapi_add_existing (API, family, geometry, GMT_IN, &first_ID);
7588 
7589 	if ((mode & GMT_ADD_FILES_ALWAYS) || ((mode & GMT_ADD_FILES_IF_NONE))) {	/* Wish to register all command-line file args as sources */
7590 		current = head;
7591 		while (current) {	/* Loop over the list and look for input files */
7592 			if (current->option == GMT_OPT_INFILE && gmtapi_not_used (API, current->arg)) {	/* File given, register it if has not already been used */
7593 				if (geometry == GMT_IS_SURFACE) {	/* Grids and images may require a subset */
7594 					if (API->GMT->common.R.active[RSET]) {	/* Global subset may have been specified (it might also match the grid/image domain) */
7595 						wesn = gmt_M_memory (API->GMT, NULL, 4U, double);
7596 						gmt_M_memcpy (wesn, API->GMT->common.R.wesn, 4U, double);
7597 					}
7598 				}
7599 				if ((object_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_FILE, geometry, GMT_IN, wesn, current->arg)) == GMT_NOTSET) {
7600 					gmt_M_free (API->GMT, wesn);
7601 					return_value (API, API->error, GMT_NOTSET);	/* Failure to register */
7602 				}
7603 				n_reg++;	/* Count of new items registered */
7604 				gmt_M_free (API->GMT, wesn);
7605 				if (first_ID == GMT_NOTSET) first_ID = object_ID;	/* Found our first ID */
7606 				if ((item = gmtlib_validate_id (API, family, object_ID, GMT_IN, GMTAPI_MODULE_INPUT)) == GMT_NOTSET)
7607 					return_value (API, API->error, GMT_NOTSET);	/* Some internal error... */
7608 				API->object[item]->selected = true;	/* We will use this variable to find the files to read later */
7609 			}
7610 			current = current->next;	/* Go to next option */
7611 		}
7612 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_import: Added %d new sources\n", n_reg);
7613 	}
7614 
7615 	/* Note that n_reg can have changed if we added file args above */
7616 
7617 	if ((mode & GMT_ADD_STDIO_ALWAYS) || ((mode & GMT_ADD_STDIO_IF_NONE) && n_reg == 0)) {	/* Wish to register stdin pointer as a source */
7618 		if ((object_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_STREAM, geometry, GMT_IN, NULL, API->GMT->session.std[GMT_IN])) == GMT_NOTSET)
7619 			return_value (API, API->error, GMT_NOTSET);	/* Failure to register stdin */
7620 		n_reg++;		/* Add the single item */
7621 		if (first_ID == GMT_NOTSET) first_ID = object_ID;	/* Found our first ID */
7622 		if ((item = gmtlib_validate_id (API, family, object_ID, GMT_IN, GMTAPI_MODULE_INPUT)) == GMT_NOTSET)
7623 			return_value (API, API->error, GMT_NOTSET);	/* Some internal error... */
7624 		API->object[item]->selected = true;	/* We will use this variable to find stdin to read from later */
7625 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_import: Added stdin to registered sources\n");
7626 	}
7627 	if (geometry == GMT_IS_TEXT)
7628 		API->GMT->current.io.trailing_text[GMT_IN] = true;
7629 	return (first_ID);
7630 }
7631 
7632 /*! . */
gmtapi_init_export(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int geometry,unsigned int mode,struct GMT_OPTION * head)7633 GMT_LOCAL int gmtapi_init_export (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int geometry, unsigned int mode, struct GMT_OPTION *head) {
7634 	/* Handle registration of output file given with option arguments and/or stdout as output destinations.
7635 	 * Only a single output may be considered.  These are the possible actions taken:
7636 	 * 1. If (mode | GMT_ADD_FILES_IF_NONE) is true and NO destinations have previously been registered,
7637 	 *    then we scan the option list for files (option == '>' (output)).
7638 	 *    Only one file can be registered as a destination; finding more than one results in an error.
7639 	 * 2. If (mode | GMT_ADD_FILES_ALWAYS) is true then we always scan the option list for files (option == '>' (output)).
7640 	 *    Only one file can be registered as a destination; finding more than one results in an error.
7641 	 * 3. If (mode & GMT_ADD_STDIO_IF_NONE) is true we will register stdout as the only destination if there is NO output item registered.
7642 	 * 4. If (mode & GMT_ADD_STDIO_ALWAYS) is true we will register stdout as an destination,
7643 	 *    and give error if other output items have already been registered.
7644 	 */
7645 
7646 	unsigned int n_reg = 0;
7647 	int object_ID = GMT_NOTSET, item;
7648 	struct GMT_OPTION *current = NULL, *out_item = NULL;
7649 
7650 	API->error = GMT_NOERROR;
7651 
7652 	GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_export: Passed family = %s and geometry = %s\n", GMT_family[family], GMT_geometry[gmtapi_gmtry(geometry)]);
7653 
7654 	if (mode & GMT_ADD_EXISTING)
7655 		n_reg = gmtapi_add_existing (API, family, geometry, GMT_OUT, &object_ID);
7656 	if (n_reg > 1) return_value (API, GMT_ONLY_ONE_ALLOWED, GMT_NOTSET);	/* Only one output allowed */
7657 
7658 	if ((mode & GMT_ADD_FILES_ALWAYS) || (mode & GMT_ADD_FILES_IF_NONE)) {	/* Wish to register a single output file arg as destination */
7659 		current = head;
7660 		while (current) {	/* Loop over the list and look for output files */
7661 			if (current->option == GMT_OPT_OUTFILE) {	/* Output file given */
7662 				n_reg++;	/* Count it */
7663 				out_item = current;	/* Remember which one it was for later */
7664 			}
7665 			current = current->next;				/* Go to next option */
7666 		}
7667 		if (n_reg > 1) return_value (API, GMT_ONLY_ONE_ALLOWED, GMT_NOTSET);	/* Only one output allowed */
7668 
7669 		if (n_reg == 1 && out_item) {	/* Register the single output file found above */
7670 			if ((object_ID = GMT_Register_IO (API, family, GMT_IS_FILE, geometry, GMT_OUT, NULL, out_item->arg)) == GMT_NOTSET)
7671 				return_value (API, API->error, GMT_NOTSET);	/* Failure to register */
7672 			if ((item = gmtlib_validate_id (API, family, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
7673 				return_value (API, API->error, GMT_NOTSET);	/* Some internal error... */
7674 			API->object[item]->selected = true;
7675 			GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_export: Added 1 new destination\n");
7676 		}
7677 	}
7678 	/* Note that n_reg may have changed if we added file arg */
7679 
7680 	if ((mode & GMT_ADD_STDIO_ALWAYS) && n_reg == 1)
7681 		return_value (API, GMT_ONLY_ONE_ALLOWED, GMT_NOTSET);	/* Only one output destination allowed at once */
7682 
7683 	if (n_reg == 0 && ((mode & GMT_ADD_STDIO_ALWAYS) || (mode & GMT_ADD_STDIO_IF_NONE))) {	/* Wish to register stdout pointer as a destination */
7684 		if ((object_ID = GMT_Register_IO (API, family, GMT_IS_STREAM, geometry, GMT_OUT, NULL, API->GMT->session.std[GMT_OUT])) == GMT_NOTSET)
7685 			return_value (API, API->error, GMT_NOTSET);	/* Failure to register stdout?*/
7686 		if ((item = gmtlib_validate_id (API, family, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
7687 			return_value (API, API->error, GMT_NOTSET);	/* Some internal error... */
7688 		API->object[item]->selected = true;
7689 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_init_export: Added stdout to registered destinations\n");
7690 		n_reg = 1;	/* Only have one item */
7691 	}
7692 	if (n_reg == 0) return_value (API, GMT_OUTPUT_NOT_SET, GMT_NOTSET);	/* No output set */
7693 	return (object_ID);
7694 }
7695 
7696 /*! . */
gmtapi_destroy_image(struct GMTAPI_CTRL * API,struct GMT_IMAGE ** I_obj)7697 GMT_LOCAL int gmtapi_destroy_image (struct GMTAPI_CTRL *API, struct GMT_IMAGE **I_obj) {
7698 	/* Delete the given image resource */
7699 	struct GMT_IMAGE_HIDDEN  *IH = NULL;
7700 	if (!(*I_obj)) {	/* Probably not a good sign */
7701 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_destroy_image: Passed NULL pointer - skipped\n");
7702 		return (GMT_PTR_IS_NULL);
7703 	}
7704 	IH = gmt_get_I_hidden (*I_obj);
7705 	if (IH->alloc_level != API->GMT->hidden.func_level) return (GMT_FREE_WRONG_LEVEL);	/* Not the right level */
7706 
7707 	gmtlib_free_image (API->GMT, I_obj, true);
7708 	return GMT_NOERROR;
7709 }
7710 
7711 /*! . */
gmtapi_make_dataobject(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int method,unsigned int geometry,void * resource,unsigned int direction)7712 GMT_LOCAL struct GMTAPI_DATA_OBJECT * gmtapi_make_dataobject (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int method, unsigned int geometry, void *resource, unsigned int direction) {
7713 	/* Simply the creation and initialization of this DATA_OBJECT structure */
7714 	struct GMTAPI_DATA_OBJECT *S_obj = gmt_M_memory (API->GMT, NULL, 1, struct GMTAPI_DATA_OBJECT);
7715 
7716 	S_obj->family    = S_obj->actual_family = family;	/* At creation we are all equal */
7717 	S_obj->method    = method;
7718 	S_obj->geometry  = geometry;
7719 	S_obj->resource  = resource;
7720 	S_obj->direction = direction;
7721 
7722 	return (S_obj);
7723 }
7724 
7725 /*! . */
gmtapi_colors2cpt(struct GMTAPI_CTRL * API,char ** str,unsigned int * mode)7726 GMT_LOCAL int gmtapi_colors2cpt (struct GMTAPI_CTRL *API, char **str, unsigned int *mode) {
7727 	/* Take comma-separated color entries given in lieu of a file and build a linear, discrete CPT.
7728 	 * This may be converted to a continuous CPT if -Z is used by makecpt/grd2cpt.
7729 	 * We check if a color is valid then write the given entries verbatim to the temp file.
7730 	 * Returns GMT_NOTSET on error, 0 if no CPT is created (str presumably held a CPT name) and 1 otherwise.
7731 	*/
7732 	unsigned int pos = 0, z = 0;
7733 	char *pch = NULL, color[GMT_LEN256] = {""}, tmp_file[GMT_LEN64] = "";
7734 	double rgb[4] = {0.0, 0.0, 0.0, 0.0};
7735 	FILE *fp = NULL;
7736 
7737 	if (!(pch = strchr (*str, ','))) {	/* No comma so presumably a regular CPT name, but check for single color entry */
7738 		bool gray = true;
7739 		size_t k;
7740 		const size_t s_length = strlen(*str);
7741 		 /* Since "gray" is both a master CPT and a shade we must let the CPT take precedence */
7742 		if (!strcmp (*str, "gray"))
7743 			return (0);
7744 		/* Because gmtlib_is_color cannot uniquely determine what a single number is, check for that separately first. */
7745 		for (k = 0; gray && k < s_length; k++)
7746 			if (!isdigit ((*str)[k])) gray = false;	/* Not just a bunch of integers */
7747 		if (gray) {	/* Must also rule out temporary files like 14334.cpt since the GMT_CPT_EXTENSION is not present */
7748 			snprintf (tmp_file, GMT_LEN64, "%s%s", *str, GMT_CPT_EXTENSION);	/* Try this as a filename */
7749 			if (!gmt_access (API->GMT, tmp_file, F_OK))
7750 				return 0;	/* Probably a process id temp file like 13223.cpt */
7751 		}
7752 		if (!gray && !gmtlib_is_color (API->GMT, *str))	/* Not a single color/shade, skip */
7753 			return (0);
7754 	}
7755 
7756 	/* OK, here we need to create the temporary palette file */
7757 	snprintf (tmp_file, GMT_LEN64, "gmtapi_colors2cpt_%d.cpt", (int)getpid());
7758 	if ((fp = fopen (tmp_file, "w")) == NULL) {
7759 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s file for writing\n", tmp_file);
7760 		return (GMT_NOTSET);
7761 	}
7762 	fprintf (fp, "# COLOR_LIST\n");	/* Flag that we are building a CPT from a list of discrete colors */
7763 
7764 	if ((*mode) & GMT_CPT_CONTINUOUS) {	/* Make a continuous cpt from the colors */
7765 		char last_color[GMT_LEN256] = {""};
7766 		if (!gmt_strtok (*str, ",", &pos, last_color)) {	/* Get 1st color entry */
7767 			GMT_Report (API, GMT_MSG_ERROR, "Unable to find 1st color entry in: %s\n", *str);
7768 			fclose (fp);
7769 			return (GMT_NOTSET);
7770 		}
7771 		if (gmt_getrgb (API->GMT, last_color, rgb)) {
7772 			GMT_Report (API, GMT_MSG_ERROR, "Badly formatted color entry: %s\n", color);
7773 			fclose (fp);
7774 			return (GMT_NOTSET);
7775 		}
7776 		while (gmt_strtok (*str, ",", &pos, color)) {	/* Get color entries */
7777 			if (gmt_getrgb (API->GMT, color, rgb)) {
7778 				GMT_Report (API, GMT_MSG_ERROR, "Badly formatted color entry: %s\n", color);
7779 				fclose (fp);
7780 				return (GMT_NOTSET);
7781 			}
7782 			fprintf (fp, "%d\t%s\t%d\t%s\n", z, last_color, z+1, color);
7783 			strncpy (last_color, color, GMT_LEN256-1);
7784 			z++;	/* Increment z-slice values */
7785 		}
7786 		*mode -= GMT_CPT_CONTINUOUS;	/* Served its purpose */
7787 		if (z == 0) {	/* Needed at least two colors to specify a ramp */
7788 			GMT_Report (API, GMT_MSG_ERROR, "Cannot make a continuous color ramp from a single color: %s\n", *str);
7789 			fclose (fp);
7790 			return (GMT_NOTSET);
7791 		}
7792 	}
7793 	else {
7794 		while (gmt_strtok (*str, ",", &pos, color)) {	/* Get color entries */
7795 			if (gmt_getrgb (API->GMT, color, rgb)) {
7796 				GMT_Report (API, GMT_MSG_ERROR, "Badly formatted color entry: %s\n", color);
7797 				fclose (fp);
7798 				return (GMT_NOTSET);
7799 			}
7800 			fprintf (fp, "%d\t%s\t%d\t%s\n", z, color, z+1, color);
7801 			z++;	/* Increment z-slice values */
7802 		}
7803 	}
7804 	fclose (fp);
7805 
7806 	GMT_Report (API, GMT_MSG_DEBUG, "Converted %s to CPT %s\n", *str, tmp_file);
7807 
7808 	gmt_M_str_free (*str);		/* Because it was allocated with strdup */
7809 	*str = strdup (tmp_file);	/* Pass out the temp file name instead */
7810 
7811 	return (1);	/* We replaced the name */
7812 }
7813 
7814 /*! . */
gmtapi_destroy_coord(struct GMTAPI_CTRL * API,double ** ptr)7815 GMT_LOCAL int gmtapi_destroy_coord (struct GMTAPI_CTRL *API, double **ptr) {
7816 	gmt_M_free (API->GMT, *ptr);
7817 	return GMT_NOERROR;
7818 }
7819 
7820 /*! Also called in gmt_init.c and prototyped in gmt_internals.h: */
gmtlib_garbage_collection(struct GMTAPI_CTRL * API,int level)7821 void gmtlib_garbage_collection (struct GMTAPI_CTRL *API, int level) {
7822 	/* gmtlib_garbage_collection frees all registered memory associated with the
7823 	 * current module level or for the entire session if level == GMT_NOTSET (-1). */
7824 
7825 	unsigned int i, j, n_free = 0, u_level = 0;
7826 	int error = GMT_NOERROR;
7827 	void *address = NULL;
7828 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7829 
7830 	if (API->n_objects == 0) return;	/* Nothing to do */
7831 
7832 #ifdef DEBUG
7833 	gmtapi_list_objects (API, "GMTAPI_Garbage_Collection entry");
7834 #endif
7835 	/* Free memory allocated during data registration (e.g., via GMT_Get|Put_Data).
7836 	 * Because gmtlib_unregister_io will delete an object and shuffle
7837 	 * the API->object array, reducing API->n_objects by one we must
7838 	 * be aware that API->n_objects changes in the loop below, hence the while loop */
7839 
7840 	i = n_free = 0;
7841 	if (level != GMT_NOTSET) u_level = level;
7842 	while (i < API->n_objects) {	/* While there are more objects to consider */
7843 		S_obj = API->object[i];	/* Shorthand for the the current object */
7844 		if (!S_obj) {		/* Skip empty object [NOTE: Should not happen?] */
7845 			GMT_Report (API, GMT_MSG_WARNING, "gmtlib_garbage_collection found empty object number %d [Bug?]\n", i++);
7846 			continue;
7847 		}
7848 		if (!(level == GMT_NOTSET || S_obj->alloc_level == u_level)) {	/* Not the right module level (or not end of session yet) */
7849 			if (S_obj->reset_pad && S_obj->no_longer_owner == false) {	/* Temporarily changed pad to access a sub-region of a memory grid - now reset this if still the owner */
7850 				address = S_obj->resource;	/* Try to get the data object */
7851 				gmtapi_contract_pad (API->GMT, address, S_obj->actual_family, S_obj->orig_pad, S_obj->orig_wesn);
7852 				S_obj->reset_pad = 0;
7853 			}
7854 			i++;	continue;
7855 		}
7856 		if (S_obj->resource == NULL) {	/* No memory to free (probably freed earlier); handle trashing of empty object after this loop */
7857 			i++;	continue;
7858 		}
7859 		if (level != GMT_NOTSET && S_obj->no_longer_owner) {	/* No memory to free since we passed it on; just NULL the pointers */
7860 			S_obj->resource = NULL;				/* Since other objects own the data now */
7861 			S_obj->alloc_level = u_level;			/* To ensure it will be Unregistered below */
7862 			S_obj->alloc_mode = GMT_ALLOC_INTERNALLY;	/* To ensure it will be Unregistered below */
7863 			i++;	continue;
7864 		}
7865 		/* Here we will try to free the memory pointed to by S_obj->resource|data */
7866 		GMT_Report (API, GMT_MSG_DEBUG, "gmtlib_garbage_collection: Destroying object: C=%d A=%d ID=%d W=%s F=%s M=%s S=%s P=%" PRIxS " N=%s\n",
7867 			S_obj->close_file, S_obj->alloc_mode, S_obj->ID, GMT_direction[S_obj->direction],
7868 			GMT_family[S_obj->family], gmtapi_method (S_obj->method), GMT_status[S_obj->status&2],
7869 			(size_t)S_obj->resource, S_obj->filename);
7870 		if (S_obj->resource) {
7871 			address = S_obj->resource;	/* Keep a record of what the address was (since S_obj->resource will be set to NULL when freed) */
7872 			error = gmtapi_destroy_data_ptr (API, S_obj->actual_family, API->object[i]->resource);	/* Do the dirty deed */
7873 		}
7874 
7875 		if (error < 0) {	/* Failed to destroy this memory; that cannot be a good thing... */
7876 			GMT_Report (API, GMT_MSG_WARNING, "gmtlib_garbage_collection failed to destroy memory for object % d [Bug?]\n", i++);
7877 			/* Skip it for now; but this is possibly a fatal error [Bug]? */
7878 		}
7879 		else  {	/* Successfully freed.  See if this address occurs more than once (e.g., both for in and output); if so just set repeated data pointer to NULL */
7880 			S_obj->resource = NULL;
7881 			for (j = i; j < API->n_objects; j++) {
7882 				if (API->object[j]->resource == address)
7883 					API->object[j]->resource = NULL;	/* Yes, set to NULL so we don't try to free twice */
7884 			}
7885 			n_free++;	/* Number of freed n_objects; do not increment i since GMT_Destroy_Data shuffled the array */
7886 		}
7887 		i++;	/* Go to next object */
7888 	}
7889  	if (n_free) GMT_Report (API, GMT_MSG_DEBUG, "GMTAPI_Garbage_Collection freed %d memory objects\n", n_free);
7890 
7891 	/* Deallocate all remaining objects associated with NULL pointers (e.g., rec-by-rec i/o or those set to NULL above) set during this module (or session) */
7892 	i = 0;
7893 	while (i < API->n_objects) {	/* While there are more objects to consider */
7894 		S_obj = API->object[i];	/* Shorthand for the the current object */
7895 		if (S_obj && (level == GMT_NOTSET || (S_obj->alloc_level == u_level)))	/* Yes, this object was added at this level, get rid of it; do not increment i */
7896 			gmtlib_unregister_io (API, (int)S_obj->ID, (unsigned int)GMT_NOTSET);	/* This shuffles the object array and reduces n_objects */
7897 		else
7898 			i++;	/* Was allocated higher up, leave alone and go to next */
7899 	}
7900 #ifdef DEBUG
7901 	gmtapi_list_objects (API, "GMTAPI_Garbage_Collection exit");
7902 #endif
7903 }
7904 
7905 /*! Determine if file contains a netCDF directive to a specific variable, e.g., table.nc?time[2] */
gmtapi_file_with_netcdf_directive(struct GMTAPI_CTRL * API,const char * file)7906 GMT_LOCAL bool gmtapi_file_with_netcdf_directive (struct GMTAPI_CTRL *API, const char *file) {
7907 	char *duplicate = NULL, *c = NULL;
7908 	if (!strchr (file, '?')) return false;  /* No question mark found */
7909 	duplicate = strdup (file);              /* Found a ?, duplicate this const char string and chop off the end */
7910 	c = strchr (duplicate, '?');            /* Locate the location of ? again */
7911 	if (c) c[0] = '\0';                     /* Chop off text from ? onwards */
7912 	if (gmt_access (API->GMT, duplicate, F_OK)) {	/* No such file, presumably */
7913 		free (duplicate);
7914 		return false;
7915 	}
7916 	else {	/* Since the file exist we know it is a netCDF directive */
7917 		free (duplicate);
7918 		return true;
7919 	}
7920 }
7921 
7922 /* Several lower-level API function are needed in a few other gmt_*.c library codes and are thus NOT local.
7923  * They are listed here and declared via EXTERN_MSC where they occur:
7924  *   gmtlib_report_error
7925  *   gmtlib_validate_id
7926  *   gmtlib_unregister_io
7927  *   gmtlib_count_objects
7928  *   gmtlib_close_grd
7929  *   gmtlib_create_header_item
7930  * If DEBUG is defined then these two are also accessible:
7931  *   gmtapi_list_objects
7932  *   gmtapi_set_object
7933  */
7934 
7935 
7936 /*! ===>  Error message reporting */
7937 
gmtlib_report_error(void * V_API,int error)7938 int gmtlib_report_error (void *V_API, int error) {
7939 	/* Write error message to log or stderr, then return error code back.
7940  	 * All functions can call this, even if API has not been initialized. */
7941 	FILE *fp = NULL;
7942 	bool report;
7943 	char message[GMT_LEN256];
7944 	struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
7945 
7946 	report = (API) ? API->error != API->last_error : true;
7947 	if (report && error != GMT_NOERROR) {	/* Report error */
7948 		if (!API || !API->GMT || (fp = API->GMT->session.std[GMT_ERR]) == NULL) fp = stderr;
7949 		if (API && API->session_tag) {
7950 			snprintf (message, GMT_LEN256, "[Session %s (%d)]: Error returned from GMT API: %s (%d)\n",
7951 				API->session_tag, API->session_ID, gmt_api_error_string[error], error);
7952 			GMT_Message (API, GMT_TIME_NONE, message);
7953 			if (API->log_level) fflush (fp);	/* Flush the latest message to file in case of crash */
7954 		}
7955 		else
7956 			fprintf (fp, "Error returned from GMT API: %s (%d)\n", gmt_api_error_string[error], error);
7957 	}
7958 	if (API) API->last_error = API->error, API->error = error;	/* Update API error value if API exists */
7959 	return (error);
7960 }
7961 
7962 /*! . */
gmtlib_validate_id(struct GMTAPI_CTRL * API,int family,int object_ID,int direction,int module_input)7963 int gmtlib_validate_id (struct GMTAPI_CTRL *API, int family, int object_ID, int direction, int module_input) {
7964 	/* Checks to see if the given object_ID is listed and of the right direction.  If so
7965  	 * we return the item number; otherwise return GMT_NOTSET and set API->error to the error code.
7966 	 * Note: int arguments MAY be GMT_NOTSET, hence we use signed ints.  If object_ID == GMT_NOTSET
7967 	 * then we only look for DATASETS.  Note: module_input controls if we are being very specific
7968 	 * about the type of input resource.  There are module inputs and option inputs. We have:
7969 	 * module_input = GMT_NOTSET [-1]:	Do not use the resource's module_input status in determining the next ID.
7970 	 * module_input = GMTAPI_OPTION_INPUT [0]:	Only validate resources with module_input = false.
7971 	 * module_input = GMTAPI_MODULE_INPUT [1]:	Only validate resources with module_input = true.
7972 	 * Finally, since we allow vectors and matrices to masquerade as DATASETs we check for this and
7973 	 * re-baptize such objects to become GMT_IS_DATASETs. */
7974 	unsigned int i;
7975 	int item;
7976 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
7977 
7978 	API->error = GMT_NOERROR;
7979 
7980 	 /* Search for the object in the active list.  However, if object_ID == GMT_NOTSET we pick the first in that direction */
7981 
7982 	for (i = 0, item = GMT_NOTSET; item == GMT_NOTSET && i < API->n_objects; i++) {
7983 		S_obj = API->object[i];	/* Shorthand only */
7984 		if (!S_obj) continue;	/* Empty object, skip */
7985 		if (direction != GMT_NOTSET && (int)S_obj->direction != direction) continue;	/* Not the requested direction */
7986 		if (direction == GMT_IN && S_obj->status != GMT_IS_UNUSED && object_ID == GMT_NOTSET) continue;	/* Already used this input object once */
7987 		/* Preliminary checks passed, no look at family */
7988 		//if (!(family == GMT_NOTSET || (int)S_obj->family == family)) {		/* Not the required data type; check for exceptions... */
7989 		if (family != GMT_NOTSET) {		/* Was specific about the family. */
7990 			if (family == GMT_IS_GRID && S_obj->actual_family == GMT_IS_MATRIX && S_obj->family != GMT_IS_DATASET)
7991 				S_obj->family = GMT_IS_GRID;	/* Matrix masquerading as grids is valid. Change the family here. */
7992 			else if (family == GMT_IS_DATASET && (S_obj->actual_family == GMT_IS_VECTOR || S_obj->actual_family == GMT_IS_MATRIX) && !(S_obj->family == GMT_IS_GRID || S_obj->family == GMT_IS_IMAGE))
7993 				S_obj->family = GMT_IS_DATASET;	/* Vectors or Matrix masquerading as dataset are valid. Change their family here. */
7994 			else if (family != S_obj->family)	/* We don't like your kind */
7995 				continue;
7996 		}
7997 		if (object_ID == GMT_NOTSET && (int)S_obj->direction == direction) item = i;	/* Pick the first object with the specified direction */
7998 		if (object_ID == GMT_NOTSET && !(S_obj->family == GMT_IS_DATASET)) continue;	/* Must be data/text-set */
7999 		else if (direction == GMT_NOTSET && (int)S_obj->ID == object_ID) item = i;	/* Pick the requested object regardless of direction */
8000 		else if ((int)S_obj->ID == object_ID) item = i;					/* Pick the requested object */
8001 		if (item != GMT_NOTSET && direction == GMT_IN && module_input != GMT_NOTSET) {		/* Must check that object's module_input status matches */
8002 			bool status = (module_input == GMTAPI_MODULE_INPUT) ? true : false;
8003 			if (status != S_obj->module_input) item = GMT_NOTSET;	/* Not the right type of input resource */
8004 		}
8005 	}
8006 	if (item == GMT_NOTSET) return_value (API, GMT_NOT_A_VALID_ID, GMT_NOTSET);		/* No such object found */
8007 
8008 	/* OK, we found the object; is it the right kind (input or output)? */
8009 	if (direction != GMT_NOTSET && (int)(API->object[item]->direction) != direction) {
8010 		/* Passing an input object but it is listed as output, or vice versa */
8011 		if (direction == GMT_IN)  return_value (API, GMT_NOT_INPUT_OBJECT, GMT_NOTSET);
8012 		if (direction == GMT_OUT) return_value (API, GMT_NOT_OUTPUT_OBJECT, GMT_NOTSET);
8013 	}
8014 	/* Here we have been successful in finding the right object */
8015 	return (item);
8016 }
8017 
8018 /*! . */
gmtlib_unregister_io(struct GMTAPI_CTRL * API,int object_ID,unsigned int direction)8019 int gmtlib_unregister_io (struct GMTAPI_CTRL *API, int object_ID, unsigned int direction) {
8020 	/* Remove specified object ID from active list of objects */
8021 	int s_item;
8022 	unsigned item;
8023 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
8024 
8025 	if (API == NULL) return (GMT_NOT_A_SESSION);		/* GMT_Create_Session has not been called */
8026 	if (API->n_objects == 0) return (gmtlib_report_error (API, GMT_NO_RESOURCES));	/* There are no known resources yet */
8027 
8028 	/* Check if this is a valid ID and matches the direction */
8029 	if ((s_item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, direction, GMT_NOTSET)) == GMT_NOTSET) return (gmtlib_report_error (API, API->error));
8030 
8031 	/* OK, now it is safe to remove the object; item >= 0 */
8032 
8033 	item = s_item;
8034 	S_obj = API->object[item];	/* Short-hand */
8035 	GMT_Report (API, GMT_MSG_DEBUG, "gmtlib_unregister_io: Unregistering object no %d [n_objects = %d]\n", S_obj->ID, API->n_objects-1);
8036  	if (S_obj->resource) GMT_Report (API, GMT_MSG_DEBUG, "gmtlib_unregister_io: Object no %d has non-NULL resource pointer\n", S_obj->ID);
8037 
8038 	if (S_obj->method == GMT_IS_FILE) gmt_M_str_free (S_obj->filename);	/* Free any strdup-allocated filenames */
8039 	gmt_M_free (API->GMT, S_obj);		/* Free the current data object */
8040 	API->n_objects--;				/* Tally of how many data sets are left */
8041 	while (item < API->n_objects) {
8042 		API->object[item] = API->object[item+1];	/* Shuffle pointers down one entry */
8043 		item++;
8044 	}
8045 
8046 	/* All active resources are found consecutively from 0 to (API->n_objects-1); those with status == 0 (GMT_IS_UNUSED) are available for use. */
8047 	return GMT_NOERROR;
8048 }
8049 
8050 /*! . */
gmtlib_count_objects(struct GMTAPI_CTRL * API,enum GMT_enum_family family,unsigned int geometry,unsigned int direction,int * first_ID)8051 unsigned int gmtlib_count_objects (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int geometry, unsigned int direction, int *first_ID) {
8052 	/* Count how many data sets of the given family are currently registered and unused for the given direction (GMT_IN|GMT_OUT).
8053  	 * Also return the ID of the first unused data object for the given direction, geometry, and family (GMT_NOTSET if not found).
8054 	 */
8055 	unsigned int i, n;
8056 
8057 	*first_ID = GMT_NOTSET;	/* Not found yet */
8058 	for (i = n = 0; i < API->n_objects; i++) {
8059 		if (!API->object[i]) continue;	 /* A freed object, skip it */
8060 		if (API->object[i]->direction != (enum GMT_enum_std)direction) continue;	  /* Wrong direction */
8061 		if (API->object[i]->geometry  != (enum GMT_enum_geometry)geometry) continue;	  /* Wrong geometry */
8062 		if (API->object[i]->status    != GMT_IS_UNUSED) continue; /* Already used */
8063 		if (family != API->object[i]->family) continue;		  /* Wrong data type */
8064 		n++;	/* Found one that satisfied requirements */
8065 		if (*first_ID == GMT_NOTSET) *first_ID = API->object[i]->ID;
8066 	}
8067 	return (n);
8068 }
8069 
8070 /*! . */
gmtlib_create_header_item(struct GMTAPI_CTRL * API,unsigned int mode,void * arg)8071 char * gmtlib_create_header_item (struct GMTAPI_CTRL *API, unsigned int mode, void *arg) {
8072 	size_t lim;
8073 	char *txt = (mode & GMT_COMMENT_IS_OPTION) ? GMT_Create_Cmd (API, arg) : (char *)arg;
8074 	static char buffer[GMT_BUFSIZ];
8075 	gmt_M_memset (buffer, GMT_BUFSIZ, char);
8076 	if (mode & GMT_COMMENT_IS_TITLE) strcat (buffer, "  Title :");
8077 	if (mode & GMT_COMMENT_IS_COMMAND) {
8078 		strcat (buffer, " Command : ");
8079 		if (strlen(API->GMT->init.module_name) < 500)		/* 500, just to shut up a Coverity issue */
8080 			strcat (buffer, API->GMT->init.module_name);
8081 		strcat (buffer, " ");
8082 	}
8083     if (mode & GMT_COMMENT_IS_REMARK) strcat (buffer, " Remark : ");
8084 	if (mode & GMT_COMMENT_IS_MULTISEG) strcat (buffer, "> ");
8085 	lim = GMT_BUFSIZ - strlen (buffer) - 1;	/* Max characters left */
8086 	strncat (buffer, txt, lim);
8087 	if (mode & GMT_COMMENT_IS_OPTION) gmt_M_free (API->GMT, txt);
8088 	return (buffer);
8089 }
8090 
8091 /*! . */
gmtlib_close_grd(struct GMT_CTRL * GMT,struct GMT_GRID * G)8092 void gmtlib_close_grd (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
8093 	struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (G);
8094 	struct GMT_GRID_ROWBYROW *R = gmtapi_get_rbr_ptr (GH->extra);	/* Shorthand to row-by-row book-keeping structure */
8095 	gmt_M_free (GMT, R->v_row);
8096 	if (GMT->session.grdformat[G->header->type][0] == 'c' || GMT->session.grdformat[G->header->type][0] == 'n')
8097 		gmt_nc_close (GMT, R->fid);
8098 	else
8099 		gmt_fclose (GMT, R->fp);
8100 	gmt_M_free (GMT, GH->extra);
8101 }
8102 
8103 /*========================================================================================================
8104  *          HERE ARE THE PUBLIC GMT API UTILITY FUNCTIONS, WITH THEIR FORTRAN BINDINGS
8105  *========================================================================================================
8106  */
8107 
8108 /*! ===>  Create a new GMT Session */
8109 
GMT_Create_Session(const char * session,unsigned int pad,unsigned int mode,int (* print_func)(FILE *,const char *))8110 void * GMT_Create_Session (const char *session, unsigned int pad, unsigned int mode, int (*print_func) (FILE *, const char *)) {
8111 	/* Initializes the GMT API for a new session. This is typically called once in a program,
8112 	 * but programs that manage many threads might call it several times to create as many
8113 	 * sessions as needed. [Note: There is of yet no thread support built into the GMT API
8114 	 * but you could still manage many sessions at once].
8115 	 * The session argument is a textstring used when reporting errors or messages from activity
8116 	 *   originating within this session.
8117 	 * Pad sets the default number or rows/cols used for grid padding.  GMT uses 2; users of
8118 	 *   the API may wish to use 0 if they have no need for BCs, etc.
8119 	 * The mode argument is a bitflag that controls a few things [0, or GMT_SESSION_NORMAL]:
8120 	 *   bit 1 (GMT_SESSION_NOEXIT)   means call return and not exit when returning from an error.
8121 	 *   bit 2 (GMT_SESSION_EXTERNAL) means we are called by an external API (e.g., MATLAB, Python).
8122 	 *   bit 3 (GMT_SESSION_COLMAJOR) means the external API uses column-major format (e.g., MATLAB, Fortran) [Default is row-major, i.e., C/C++, Python]
8123 	 *   bit 4 (GMT_SESSION_LOGERRORS) means we redirect stderr to a log file whose name is the session string + log.
8124 	 *   We reserve the right to add future flags.
8125 	 * We return the pointer to the allocated API structure.
8126 	 * If any error occurs we report the error, set the error code via API->error, and return NULL.
8127 	 * We terminate each session with a call to GMT_Destroy_Session.
8128 	 */
8129 
8130 	struct GMTAPI_CTRL *API = NULL;
8131 	size_t len;
8132 	static char *unknown = "unknown";
8133 	char *dir = NULL;
8134 	/* Determine the width of the current terminal */
8135 #ifdef WIN32
8136 	CONSOLE_SCREEN_BUFFER_INFO csbi;
8137     GetConsoleScreenBufferInfo (GetStdHandle(STD_ERROR_HANDLE), &csbi);
8138     int n_columns = csbi.srWindow.Right;
8139 #else
8140 	struct winsize w;
8141 	int err = 0;
8142     if ((err = ioctl (STDERR_FILENO, TIOCGWINSZ, &w))) {
8143  		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Create_Session: Unable to get terminal width via ioctl, err = %d\n", err);
8144     }
8145     int n_columns = w.ws_col;
8146 #endif
8147 
8148 	if ((API = calloc (1, sizeof (struct GMTAPI_CTRL))) == NULL) return_null (NULL, GMT_MEMORY_ERROR);	/* Failed to allocate the structure */
8149 	API->verbose = (mode >> 16);	/* Pick up any -V settings from gmt.c */
8150 	API->pad = pad;		/* Preserve the default pad value for this session */
8151 	API->print_func = (print_func == NULL) ? gmtapi_print_func : print_func;	/* Pointer to the print function to use in GMT_Message|Report */
8152 	API->do_not_exit = mode & GMT_SESSION_NOEXIT;	/* Deprecated, we no longer call exit anywhere in the API (gmt_api.c) */
8153     API->external = (mode & GMT_SESSION_EXTERNAL) ? 1 : 0;  /* if false|0 then we don't list read and write as modules */
8154     if (API->external && mode & GMT_SESSION_NOGDALCLOSE) API->external = 2;  /* Avoid calling GDALDestroyDriverManager */
8155 	API->shape = (mode & GMT_SESSION_COLMAJOR) ? GMT_IS_COL_FORMAT : GMT_IS_ROW_FORMAT;		/* if set then we must use column-major format [row-major] */
8156 	API->runmode = mode & GMT_SESSION_RUNMODE;		/* If nonzero we set up modern GMT run-mode, else classic */
8157 	API->no_history = mode & GMT_SESSION_NOHISTORY;		/* If nonzero we disable the gmt.history mechanism (shorthands) entirely */
8158 	if (API->internal) API->leave_grid_scaled = 1;	/* Do NOT undo grid scaling after write since modules do not reuse grids we save some CPU */
8159 	if (session) {	/* Pick up a tag for this session */
8160 		char *tmptag = strdup (session);
8161 		API->session_tag = strdup (basename (tmptag));	/* Only used in reporting and error messages */
8162 		gmt_M_str_free (tmptag);
8163 	}
8164 
8165 	if ((API->message = calloc (GMT_MSGSIZ, sizeof (char))) == NULL) {	/* Failed to allocate the message string */
8166 	 	gmt_M_str_free (API);	/* Not gmt_M_free since this item was allocated before GMT was initialized */
8167 		return_null (NULL, GMT_MEMORY_ERROR);
8168 	}
8169 	/* Ensure Windows terminal can handle UTF-8 characters */
8170 #ifdef WIN32
8171 	SetConsoleOutputCP (CP_UTF8);
8172 #endif
8173 	API->terminal_width = (n_columns > 24) ? n_columns : 100;	/* Character width of current terminal [100] */
8174 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Create_Session: Terminal width = %d\n", API->terminal_width);
8175 	/* Set temp directory used by GMT */
8176 
8177 #ifdef WIN32
8178 	if ((dir = getenv ("TEMP")))	/* Standard Windows temp directory designation */
8179 		API->tmp_dir = strdup (dir);
8180 	/* If not found we leave it NULL */
8181 #else
8182 	if ((dir = getenv ("TMPDIR")))	/* Alternate tmp dir for *nix */
8183 		API->tmp_dir = strdup (dir);
8184 	else	/* Set standard temporary directory under *nix */
8185 		API->tmp_dir = strdup ("/tmp");
8186 #endif
8187 	if ((len = strlen (API->tmp_dir)) > 2 && API->tmp_dir[len-1] == '/') API->tmp_dir[len-1] = '\0';	/* Chop off trailing slash */
8188 	API->session_name = gmtapi_get_ppid (API);		/* Save session name for the rest of the session */
8189 
8190 	/* gmt_begin initializes, among other things, the settings in the user's (or the system's) gmt.conf file */
8191 	if (gmt_begin (API, session, pad) == NULL) {		/* Initializing GMT and PSL machinery failed */
8192 		gmt_M_str_free (API);	/* Free API */
8193 		return_null (API, GMT_MEMORY_ERROR);
8194 	}
8195 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Create_Session initialized GMT structure\n");
8196 
8197 	if (mode & GMT_SESSION_LOGERRORS) {	/* Want to redirect errors to a log file */
8198 		char file[PATH_MAX] = {""};
8199 		FILE *fp = NULL;
8200 		if (API->session_tag == NULL) {
8201 			GMT_Report (API, GMT_MSG_DEBUG, "Must pass a session tag to be used for error log file name\n");
8202 			return_null (API, GMT_ARG_IS_NULL);
8203 		}
8204 		snprintf (file, PATH_MAX, "%s.log", API->session_tag);
8205 		if ((fp = fopen (file, "w")) == NULL) {
8206 			GMT_Report (API, GMT_MSG_DEBUG, "Unable to open error log file %s\n", file);
8207 			return_null (API, GMT_ERROR_ON_FOPEN);
8208 		}
8209 		API->GMT->session.std[GMT_ERR] = fp;	/* Set the error fp pointer */
8210 		API->log_level = GMT_LOG_SET;
8211 	}
8212 
8213 	API->n_cores = gmtlib_get_num_processors();	/* Get number of available CPU cores */
8214 	GMTAPI_index_function = gmtapi_get_index_from_TRS;	/* Default grid node lookup */
8215 
8216 	/* Allocate memory to keep track of registered data resources */
8217 
8218 	API->n_objects_alloc = GMT_SMALL_CHUNK;	/* Start small; this may grow as more resources are registered */
8219 	API->object = gmt_M_memory (API->GMT, NULL, API->n_objects_alloc, struct GMTAPI_DATA_OBJECT *);
8220 
8221 	/* Set the unique Session parameters */
8222 
8223 	API->session_ID = GMTAPI_session_counter++;		/* Guarantees each session ID will be unique and sequential from 0 up */
8224 	if (session)
8225 		API->GMT->init.module_name = API->session_tag;	/* So non-modules can report name of program, */
8226 	else
8227 		API->GMT->init.module_name = unknown; /* or unknown */
8228 
8229 	if (gmtapi_init_sharedlibs (API) < 0)				/* Count how many shared libraries we should know about, and get their names and paths */
8230 		return_null (API, GMT_RUNTIME_ERROR);
8231 
8232 	return (API);	/* Pass the structure back out */
8233 }
8234 
8235 #ifdef FORTRAN_API
8236 /* Fortran binding [THESE MAY CHANGE ONCE WE ACTUALLY TRY TO USE THESE] */
GMT_Create_Session_(const char * tag,unsigned int * pad,unsigned int * mode,void * print,int len)8237 struct GMTAPI_CTRL * GMT_Create_Session_ (const char *tag, unsigned int *pad, unsigned int *mode, void *print, int len) {
8238 	/* Fortran version: We pass the hidden global GMT_FORTRAN structure */
8239 	return (GMT_Create_Session (tag, *pad, *mode, print));
8240 }
8241 #endif
8242 
8243 /*! ===>  Destroy a registered GMT Session */
8244 
GMT_Destroy_Session(void * V_API)8245 int GMT_Destroy_Session (void *V_API) {
8246 	/* GMT_Destroy_Session terminates the information for the specified session and frees all memory.
8247 	 * Returns false if all is well and true if there were errors. */
8248 
8249 	unsigned int i;
8250 	char *module = NULL;
8251 	struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
8252 
8253 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);	/* GMT_Create_Session has not been called */
8254 	API->error = GMT_NOERROR;
8255 
8256 	GMT_Report (API, GMT_MSG_DEBUG, "Entering GMT_Destroy_Session\n");
8257 	module = strdup (API->GMT->init.module_name);	/* Need a copy as the pointer to static memory in library will close soon */
8258 	gmtlib_garbage_collection (API, GMT_NOTSET);	/* Free any remaining memory from data registration during the session */
8259 	gmtapi_free_sharedlibs (API);			/* Close shared libraries and free list */
8260 	API->GMT->init.module_name = module;		/* So GMT_Report will function after GMT_SUPPL_LIB_NAME.so shut down */
8261 
8262 	/* Deallocate all remaining objects associated with NULL pointers (e.g., rec-by-rec i/o) */
8263 	for (i = 0; i < API->n_objects; i++) gmtlib_unregister_io (API, (int)API->object[i]->ID, (unsigned int)GMT_NOTSET);
8264 	gmt_M_free (API->GMT, API->object);
8265 	if (API->GMT->session.std[GMT_ERR] != stderr)	/* Close the error log fp pointer */
8266 		fclose (API->GMT->session.std[GMT_ERR]);
8267 	gmt_end (API->GMT);	/* Terminate GMT machinery */
8268 	gmt_M_str_free (API->session_tag);
8269 	gmt_M_str_free (API->session_name);
8270 	gmt_M_str_free (API->tmp_dir);
8271 	gmt_M_str_free (API->session_dir);
8272 	gmt_M_str_free (API->message);
8273 	gmt_M_memset (API, 1U, struct GMTAPI_CTRL);	/* Wipe it clean first */
8274  	gmt_M_str_free (API);	/* Not gmt_M_free since this item was allocated before GMT was initialized */
8275  	gmt_M_str_free (module);
8276 
8277 	return (GMT_NOERROR);
8278 }
8279 
8280 #ifdef FORTRAN_API
GMT_Destroy_Session_()8281 int GMT_Destroy_Session_ () {
8282 	/* Fortran version: We pass the hidden global GMT_FORTRAN structure */
8283 	return (GMT_Destroy_Session (GMT_FORTRAN));
8284 }
8285 #endif
8286 
8287 /*! . */
gmtapi_debug_geometry_code(unsigned int geometry)8288 GMT_LOCAL char gmtapi_debug_geometry_code (unsigned int geometry) {
8289 	char c;
8290 	switch (geometry) {
8291 		case GMT_IS_POINT:	 c = 'T'; break;
8292 		case GMT_IS_LINE:	 c = 'L'; break;
8293 		case GMT_IS_POLY:	 c = 'P'; break;
8294 		case GMT_IS_LP:		 c = 'C'; break;
8295 		case GMT_IS_PLP:	 c = 'A'; break;
8296 		case GMT_IS_SURFACE: c = 'G'; break;
8297 		case GMT_IS_VOLUME:	 c = 'U'; break;
8298 		case GMT_IS_NONE:	 c = 'N'; break;
8299 		case GMT_IS_TEXT:	 c = 'X'; break;
8300 		default:	 		 c = '-'; break;
8301 	}
8302 	return c;
8303 }
8304 
8305 /*! . */
gmtapi_encode_id(struct GMTAPI_CTRL * API,unsigned int module_input,unsigned int direction,unsigned int family,unsigned int actual_family,unsigned int geometry,unsigned int messenger,int object_ID,char * filename)8306 GMT_LOCAL int gmtapi_encode_id (struct GMTAPI_CTRL *API, unsigned int module_input, unsigned int direction, unsigned int family, unsigned int actual_family, unsigned int geometry, unsigned int messenger, int object_ID, char *filename) {
8307 	/* Creates a virtual filename with the embedded object information .  Space for up to GMT_VF_LEN characters in filename must exist.
8308 	 * Name template: @GMTAPI@-S-D-F-A-G-M-###### where # is the 6-digit integer object code.  Total length is 27 chars (GMTAPI_MEMFILE_LEN)
8309 	 * S stands for P(rimary) or S(econdary) input or output object (command line is primary, files via options are secondary).
8310 	 * D stands for Direction and is either I(n) or O(ut).
8311 	 * F stands for Family and is one of D(ataset), G(rid), I(mage), C(PT), X(PostScript), M(atrix), V(ector), U(cube)-(undefined).
8312 	 * A stands for Actual Family and is one of D, G, I, C, X, M, V, and U as well.
8313 	 *   Actual family may differ from family if a Dataset is actually passed as a Matrix, for instance.
8314 	 * G stands for Geometry and is one of (poin)T, L(ine), P(olygon), C(Line|Polygon), A(Point|Line|Polygon), G(rid), V(olume), N(one), X(text), or -(ndefined).
8315 	 * M stands for Messenger and is either Y(es) or N(o).
8316 	 * Limitation:  object_ID must be <= GMTAPI_MAX_ID */
8317 
8318 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);	/* GMT_Create_Session has not been called */
8319 	if (!filename) return_error (API, GMT_MEMORY_ERROR);		/* Oops, cannot write to that variable */
8320 	if (object_ID <= GMT_NOTSET) return_error (API, GMT_NOT_A_VALID_ID);	/* ID is not set yet */
8321 	if (object_ID > GMTAPI_MAX_ID) return_error (API, GMT_ID_TOO_LARGE);	/* ID is too large to fit in %06d format below */
8322 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (API, GMT_NOT_A_VALID_DIRECTION);
8323 	if (!gmtapi_valid_input_family (family))  return_error (API, GMT_NOT_A_VALID_FAMILY);
8324 	if (!gmtapi_valid_actual_family (actual_family))  return_error (API, GMT_NOT_A_VALID_FAMILY);
8325 	if (gmtapi_validate_geometry (API, family, geometry)) return_error (API, GMT_BAD_GEOMETRY);
8326 	if (!(messenger == 0 || messenger == 1)) return_error (API, GMT_RUNTIME_ERROR);
8327 	if (module_input) module_input = 1;	/* It may be GMT_VIA_MODULE_INPUT but here we want just 0 or 1 */
8328 
8329 	gmt_M_memset (filename, GMT_VF_LEN, char);	/* Wipe any trace of previous text */
8330 	sprintf (filename, "@GMTAPI@-%c-%c-%s-%s-%c-%c-%06d", (module_input) ? 'P' : 'S', (direction == GMT_IN) ? 'I' : 'O', GMT_family_abbrev[family], GMT_family_abbrev[actual_family], gmtapi_debug_geometry_code (geometry), (messenger) ? 'Y' : 'N', object_ID);
8331 	GMT_Report (API, GMT_MSG_DEBUG, "VirtualFile name created: %s\n", filename);
8332 
8333 	return_error (API, GMT_NOERROR);	/* No error encountered */
8334 }
8335 
8336 /* Data registration: The main reason for data registration is the following:
8337  * Unlike GMT 4, GMT 5 may be used as modules by another calling program.  In
8338  * that case, the input data file may not be a file but a memory location (i.e.,
8339  * a data array).  To allow the program to pass such information we needed a
8340  * way to abstract things so that the modules have no idea of where things are
8341  * coming from (and were output is going as well).
8342  * The API session maintains a single linked linear list of data objects; these
8343  * objects contain information about all the data resources (sources and destinations)
8344  * that it has been told about.  Because GMT programs (hence the GMT modules) must
8345  * be able to find data from stdin, command line files, and command options (e.g.,
8346  * -Gmyfile.txt) we must be flexible in how things are done.
8347  *
8348  * Source registration is done in one of several ways:
8349  *  1. Call GMT_Register_IO directly and specify the source.  The specifics about the
8350  *     source will be stored in a new data object which is added to the linked list.
8351  *     This is what top-level programs must do to allow a GMT module to read via a
8352  *     memory location.
8353  *  2. Give file names via the option list (this is what happens when stand-alone
8354  *     GMT programs process the command line argv list).  Depending on the GMT module,
8355  *     the module will call GMT_Init_IO to scan for such option arguments and then add
8356  *     each file found as a new data object.
8357  *  3. Again, depending on the GMT module, if no unused resources are found, the module
8358  *     will, via GMT_Init_IO, add stdin as an input resource.  This can be in addition
8359  *     to any other registered sources, but most often it is added because no other
8360  *     sources were found.
8361  *
8362  * The lower-level GMT i/o machinery will handle complications such as 0 (stdin), 1, or
8363  * many data files so that the modules themselves simply read the next record with
8364  * GMT_Get_Record until EOF (as if there is only one input source).  Modules that need
8365  * to store all the data in memory for further processing will call gmtapi_get_data instead,
8366  * which will return a single entity (grid, dataset, cpt, etc).
8367  *
8368  * Destination registration is done in the same way, with the exception that for most
8369  * modules (those processing data tables, at least), only one output destination (e.g., file)
8370  * can be specified.  However, data sets such as tables with segments can, via mode
8371  * options, be specified to be written to separate table files or even segment files.
8372  * The actual writing is done by lower-level functions so that the GMT modules are simply
8373  * calling gmtapi_put_data (all in one go).  For record-by-record output the modules will use
8374  * GMT_Put_Record.  This keeps data i/o in the modules uniform and simple across GMT.
8375  */
8376 
8377  /*! . */
GMT_Register_IO(void * V_API,unsigned int family,unsigned int method,unsigned int geometry,unsigned int direction,double wesn[],void * resource)8378 int GMT_Register_IO (void *V_API, unsigned int family, unsigned int method, unsigned int geometry, unsigned int direction, double wesn[], void *resource) {
8379 	/* Adds a new data object to the list of registered objects and returns a unique object ID.
8380 	 * Arguments are as listed for api_Register_Im|Export (); see those for details.
8381 	 * During the registration we make sure files exist and are readable.
8382 	 *
8383 	 * if direction == GMT_IN:
8384 	 * A program uses this routine to pass information about input data to GMT.
8385 	 * family:	Specifies the data type we are trying to import; select one of 6 families:
8386 	 *   GMT_IS_PALETTE:	A GMT_PALETTE structure:
8387 	 *   GMT_IS_DATASET:	A GMT_DATASET structure:
8388 	 *   GMT_IS_GRID:	A GMT_GRID structure:
8389 	 *   GMT_IS_IMAGE:	A GMT_IMAGE structure:
8390 	 *   GMT_IS_CUBE:		A GMT_CUBE structure:
8391 	 *   GMT_IS_POSTSCRIPT:		A GMT_POSTSCRIPT structure:
8392 	 * method:	Specifies by what method we will import this data set:
8393 	 *   GMT_IS_FILE:	A file name is given via input.  The program will read data from this file
8394 	 *   GMT_IS_STREAM:	A file pointer to an open file is passed via input. --"--
8395 	 *   GMT_IS_FDESC:	A file descriptor to an open file is passed via input. --"--
8396 	 *   GMT_IS_DUPLICATE:	A pointer to a data set to be copied
8397 	 *   GMT_IS_REFERENCE:	A pointer to a data set to be passed as is [we may reallocate sizes only if GMT-allocated]
8398 	 * The following approaches can be added to the method for all but CPT:
8399 	 *   GMT_VIA_MATRIX:	A 2-D user matrix is passed via input as a source for copying.
8400 	 *			The GMT_MATRIX structure must have parameters filled out.
8401 	 *   GMT_VIA_VECTOR:	An array of user column vectors is passed via input as a source for copying.
8402 	 *			The GMT_VECTOR structure must have parameters filled out.
8403 	 * geometry:	One of GMT_IS_{TEXT|POINT|LINE|POLY|SURF} (the last for GMT grids)
8404 	 * input:	Pointer to the source filename, stream, handle, array position, etc.
8405 	 * wesn:	Grid subset defined by 4 doubles (or 6 for cubes); otherwise use NULL
8406 	 * RETURNED:	Unique ID assigned to this input resource, or GMT_NOTSET (-1) if error.
8407 	 *
8408 	 * An error status is returned if problems are encountered via API->error [GMT_NOERROR].
8409 	 *
8410 	 * GMT_IS_GRID & GMT_VIA_MATRIX: Since GMT internally uses floats in C arrangement, anything else will be converted to gmt_grdfloat.
8411 	 * GMT_IS_DATASET & GMT_VIA_MATRIX: Since GMT internally uses doubles in C arrangement, anything else will be converted to double.
8412 	 *
8413 	 * api_Register_Import will allocate and populate a GMTAPI_DATA_OBJECT structure which
8414 	 * is appended to the data list maintained by the GMTAPI_CTRL API structure.
8415 	 *
8416 	 * if direction == GMT_OUT:
8417 	 * The main program uses this routine to pass information about output data from GMT.
8418 	 * family:	Specifies the data type we are trying to export; select one of:
8419 	 *   GMT_IS_PALETTE:	A GMT_PALETTE structure:
8420 	 *   GMT_IS_DATASET:	A GMT_DATASET structure:
8421 	 *   GMT_IS_IMAGE:	A GMT_IMAGE structure:
8422 	 *   GMT_IS_GRID:	A GMT_GRID structure:
8423 	 *   GMT_IS_CUBE:	A GMT_CUBE structure:
8424 	 *   GMT_IS_POSTSCRIPT:	A GMT_POSTSCRIPT structure:
8425 	 * method:	Specifies by what method we will export this data set:
8426 	 *   GMT_IS_FILE:	A file name is given via output.  The program will write data to this file
8427 	 *   GMT_IS_STREAM:	A file pointer to an open file is passed via output. --"--
8428 	 *   GMT_IS_FDESC:	A file descriptor to an open file is passed via output. --"--
8429 	 *   GMT_IS_DUPLICATE:	A pointer to a data set to be copied
8430 	 *   GMT_IS_REFERENCE:	A pointer to a data set to be passed as is [we may reallocate sizes only if GMT-allocated]
8431 	 * geometry:	One of GMT_IS_{TEXT|POINT|LINE|POLY|SURFACE|VOLUME} (the last for GMT grids and cubes)
8432 	 * output:	Pointer to the destination filename, stream, handle, array position, etc.
8433 	 * wesn:	Grid/volume subset defined by 4/6 doubles; otherwise use NULL
8434 	 * RETURNED:	Unique ID assigned to this output resource, or GMT_NOTSET (-1) if error.
8435 	 *
8436 	 * An error status is returned if problems are encountered via API->error [GMT_NOERROR].
8437 	 *
8438 	 * api_Register_Export will allocate and populate a GMTAPI_DATA_OBJECT structure which
8439 	 * is appended to the data list maintained by the GMTAPI_CTRL API structure.
8440 	 */
8441 	int item, object_ID;
8442 	unsigned int module_input, mode = method & GMT_IO_RESET;	/* In case we wish to reuse this resource */
8443 	unsigned int first = 0;
8444 	char message[GMT_LEN256], *file = NULL;
8445 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
8446 	struct GMTAPI_CTRL *API = NULL;
8447 	struct GMT_CTRL *GMT = NULL;
8448 
8449 	if (V_API == NULL) return_value (V_API, GMT_NOT_A_SESSION, GMT_NOTSET);
8450 	API = gmtapi_get_api_ptr (V_API);
8451 	API->error = GMT_NOERROR;	/* Reset in case it has some previous error */
8452 	module_input = (family & GMT_VIA_MODULE_INPUT);	/* Are we registering a resource that is a module input? */
8453 	family -= module_input;
8454 	if (gmtapi_validate_geometry (API, family, geometry)) return_value (API, GMT_BAD_GEOMETRY, GMT_NOTSET);
8455 
8456 	if ((object_ID = gmtapi_is_registered (API, family, geometry, direction, mode, resource, resource)) != GMT_NOTSET) {	/* Registered before */
8457 		if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, direction, GMT_NOTSET)) == GMT_NOTSET) return_value (API, API->error, GMT_NOTSET);
8458 		S_obj = API->object[item];	/* Use S as shorthand */
8459 		if (module_input) S_obj->module_input = true;
8460 		if ((a_grid_or_image (family) || a_matrix_surface(family,geometry)) && !full_region (wesn)) {	/* Update the subset region if given (for grids/images only) */
8461 			gmt_M_memcpy (S_obj->wesn, wesn, 4, double);
8462 			S_obj->region = true;
8463 		}
8464 		else if (family == GMT_IS_CUBE && (!full_region (wesn) || (wesn && wesn[ZLO] != wesn[ZHI]))) {
8465 			gmt_M_memcpy (S_obj->wesn, wesn, 6, double);
8466 			S_obj->region = true;
8467 		}
8468 		return (object_ID);	/* Already registered so we are done */
8469 	}
8470 	method -= mode;	/* Remove GMT_IO_RESET if it was passed */
8471 	GMT = API->GMT;
8472 
8473 	switch (method) {	/* Consider CPT, data, text, and grids, accessed via a variety of methods */
8474 		case GMT_IS_FILE:	/* Registration via a single file name */
8475 			/* No, so presumably it is a regular file name */
8476 			file = strdup (resource);
8477 			if (direction == GMT_IN) {	/* For input we can check if the file exists and can be read. */
8478 				char *p = NULL;
8479 				bool not_url = true, is_plus_sign = false;
8480 				if (a_grid_or_image_or_cube (family) && !gmtlib_remote_file_is_tiled (API, file, NULL) && (p = strchr (file, '='))) *p = '\0';	/* Chop off any =<stuff> for grids and images so access can work */
8481 				else if (family == GMT_IS_IMAGE && (p = strchr (file, '+'))) {
8482 					char *c = strchr (file, '.');	/* The period before an extension */
8483 					 /* PW 1/30/2014: Protect images with band requests, e.g., my_image.jpg+b2 */
8484 					if (c && c < p && p[1] == 'b' && isdigit (p[2])) {
8485 						GMT_Report (API, GMT_MSG_DEBUG, "Truncating +b modifier for image filename %s\n", file);
8486 						*p = '\0';	/* Chop off any +b<band> for images at end of extension so access can work */
8487 						is_plus_sign = true;
8488 					}
8489 					else	/* Make sure p is NULL so we don't restore a character below */
8490 						p = NULL;
8491 				}
8492 				if (a_grid_or_image (family))	/* Only grid and images can be URLs so far */
8493 					not_url = !(gmtlib_found_url_for_gdal (file) || gmt_M_file_is_url (file));	/* true if neither special GDAL-remote files nor regular URLs */
8494 				first = gmt_download_file_if_not_found (API->GMT, file, 0);	/* Deal with downloadable GMT data sets first */
8495 				if (gmt_access (GMT, &file[first], F_OK) && not_url) {	/* For input we can check if the file exists (except if via Web) */
8496 					GMT_Report (API, GMT_MSG_ERROR, "File %s not found\n", &file[first]);
8497 					gmt_M_str_free (file);
8498 					return_value (API, GMT_FILE_NOT_FOUND, GMT_NOTSET);
8499 				}
8500 				if (gmt_access (GMT, &file[first], R_OK) && not_url) {	/* Found it but we cannot read. */
8501 					GMT_Report (API, GMT_MSG_ERROR, "Not permitted to read file %s\n", &file[first]);
8502 					gmt_M_str_free (file);
8503 					return_value (API, GMT_BAD_PERMISSION, GMT_NOTSET);
8504 				}
8505 				if (p) p[0] = (is_plus_sign) ? '+' : '=';	/* Restore the extensions */
8506 			}
8507 			else if (resource == NULL) {	/* No file given [should this mean stdin/stdout?] */
8508 				gmt_M_str_free (file);
8509 				return_value (API, GMT_OUTPUT_NOT_SET, GMT_NOTSET);
8510 			}
8511 			/* Create a new data object and initialize variables */
8512 			if ((S_obj = gmtapi_make_dataobject (API, family, method, geometry, NULL, direction)) == NULL) {
8513 				gmt_M_str_free (file);
8514 				return_value (API, GMT_MEMORY_ERROR, GMT_NOTSET);	/* No more memory */
8515 			}
8516 			if (strlen (resource))	/* Strip off any beginning of the name */
8517 				S_obj->filename = strdup (&file[first]);
8518 			gmt_M_str_free (file);
8519 			snprintf (message, GMT_LEN256, "Object ID %%d : Registered %s %s %s as an %s resource with geometry %s [n_objects = %%d]\n", GMT_family[family], gmtapi_method (method), S_obj->filename, GMT_direction[direction], GMT_geometry[gmtapi_gmtry(geometry)]);
8520 			break;
8521 
8522 		case GMT_IS_STREAM:	/* Methods that indirectly involve a file */
8523 		case GMT_IS_FDESC:
8524 			if (resource == NULL) {	/* No file given [should this mean stdin/stdout?] */
8525 				return_value (API, GMT_OUTPUT_NOT_SET, GMT_NOTSET);
8526 			}
8527 			if ((S_obj = gmtapi_make_dataobject (API, family, method, geometry, NULL, direction)) == NULL) {
8528 				return_value (API, GMT_MEMORY_ERROR, GMT_NOTSET);	/* No more memory */
8529 			}
8530 			S_obj->fp = resource;	/* Pass the stream of fdesc onward */
8531 			snprintf (message, GMT_LEN256, "Object ID %%d : Registered %s %s %" PRIxS " as an %s resource with geometry %s [n_objects = %%d]\n", GMT_family[family], gmtapi_method (method), (size_t)resource, GMT_direction[direction], GMT_geometry[gmtapi_gmtry(geometry)]);
8532 			break;
8533 
8534 		case GMT_IS_DUPLICATE:
8535 		case GMT_IS_REFERENCE:
8536 			if (direction == GMT_IN && resource == NULL) {
8537 				return_value (API, GMT_PTR_IS_NULL, GMT_NOTSET);	/* Input registration of memory takes a resource */
8538 			}
8539 			if ((S_obj = gmtapi_make_dataobject (API, family, method, geometry, resource, direction)) == NULL) {
8540 				return_value (API, GMT_MEMORY_ERROR, GMT_NOTSET);	/* No more memory */
8541 			}
8542 			snprintf (message, GMT_LEN256, "Object ID %%d : Registered %s %s %" PRIxS " as an %s resource with geometry %s [n_objects = %%d]\n", GMT_family[family], gmtapi_method (method), (size_t)resource, GMT_direction[direction], GMT_geometry[gmtapi_gmtry(geometry)]);
8543 			break;
8544 
8545 		case GMT_IS_COORD:	/* Internal registration of coordinate arrays so that GMT_Destroy_Data can free them */
8546 			if ((S_obj = gmtapi_make_dataobject (API, family, method, geometry, resource, direction)) == NULL) {
8547 				return_value (API, GMT_MEMORY_ERROR, GMT_NOTSET);	/* No more memory */
8548 			}
8549 			snprintf (message, GMT_LEN256, "Object ID %%d : Registered double array %" PRIxS " as an %s resource [n_objects = %%d]\n", (size_t)resource, GMT_direction[direction]);
8550 			break;
8551 		default:
8552 			GMT_Report (API, GMT_MSG_ERROR, "Failure in GMT_Register_IO (%s): Unrecognized method %d\n", GMT_direction[direction], method);
8553 			return_value (API, GMT_NOT_A_VALID_METHOD, GMT_NOTSET);
8554 			break;
8555 	}
8556 
8557 	if ((a_grid_or_image (family) || a_matrix_surface(family,geometry)) && !full_region (wesn)) {	/* Copy the subset region if it was given (for grids) */
8558 		gmt_M_memcpy (S_obj->wesn, wesn, 4, double);
8559 		S_obj->region = true;
8560 	}
8561 	else if (family == GMT_IS_CUBE && (!full_region (wesn) || (wesn && wesn[ZLO] != wesn[ZHI]))) {
8562 		gmt_M_memcpy (S_obj->wesn, wesn, 6, double);
8563 		S_obj->region = true;
8564 	}
8565 	else if (family == GMT_IS_DATASET && S_obj->actual_family == GMT_IS_DATASET) 	/* All datasets are double (this is informational only) */
8566 		S_obj->type = GMT_DOUBLE;
8567 	S_obj->alloc_level = GMT->hidden.func_level;	/* Object was allocated at this module nesting level */
8568 	if (module_input) S_obj->module_input = true;
8569 
8570 	/* Here S is not NULL and no errors have occurred (yet) */
8571 
8572 	if (direction == GMT_OUT && resource == NULL) S_obj->messenger = true;	/* Output messenger */
8573 	if (method != GMT_IS_COORD) API->registered[direction] = true;	/* We have at least registered one item */
8574 	object_ID = gmtapi_add_data_object (API, S_obj);
8575 	GMT_Report (API, GMT_MSG_DEBUG, message, object_ID, API->n_objects);
8576 #ifdef DEBUG
8577 	//gmtapi_list_objects (API, "GMT_Register_IO");
8578 #endif
8579 	return_value (API, API->error, object_ID);
8580 }
8581 
8582 #ifdef FORTRAN_API
GMT_Register_IO_(unsigned int * family,unsigned int * method,unsigned int * geometry,unsigned int * direction,double wesn[],void * input)8583 int GMT_Register_IO_ (unsigned int *family, unsigned int *method, unsigned int *geometry, unsigned int *direction, double wesn[], void *input) {
8584 	/* Fortran version: We pass the global GMT_FORTRAN structure */
8585 	return (GMT_Register_IO (GMT_FORTRAN, *family, *method, *geometry, *direction, wesn, input));
8586 }
8587 #endif
8588 
8589 
8590  /*! . */
GMT_Init_IO(void * V_API,unsigned int family,unsigned int geometry,unsigned int direction,unsigned int mode,unsigned int n_args,void * args)8591 int GMT_Init_IO (void *V_API, unsigned int family, unsigned int geometry, unsigned int direction, unsigned int mode, unsigned int n_args, void *args) {
8592 	/* Registers program option file arguments as sources/destinations for the current module.
8593 	 * All modules planning to use std* and/or command-line file args must call GMT_Init_IO to register these resources.
8594 	 * family:	The kind of data (GMT_IS_DATASET|CPT|GRID|IMAGE|PS)
8595 	 * geometry:	Either GMT_IS_NONE|TEXT|POINT|LINE|POLYGON|SURFACE
8596 	 * direction:	Either GMT_IN or GMT_OUT
8597 	 * mode:	Bitflags composed of 1 = add command line (option) files, 2 = add std* if no other input/output,
8598 	 *		4 = add std* regardless.  mode must be > 0.
8599 	 * n_args:	Either 0 if we pass linked option structs or argc if we pass argv[]
8600 	 * args:	Either linked list of program option arguments (n_args == 0) or char *argv[].
8601 	 *
8602 	 * Returns:	false if successful, true if error.
8603 	 */
8604 	int object_ID;	/* ID of first object [only for debug purposes - not used in this function; ignore -Wunused-but-set-variable warning */
8605 	struct GMT_OPTION *head = NULL;
8606 	struct GMTAPI_CTRL *API = NULL;
8607 
8608 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
8609 	API = gmtapi_get_api_ptr (V_API);
8610 	API->error = GMT_NOERROR;	/* Reset in case it has some previous error */
8611 	if (gmtapi_validate_geometry (API, family, geometry)) return_error (API, GMT_BAD_GEOMETRY);
8612 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (API, GMT_NOT_A_VALID_DIRECTION);
8613 	if (!((mode & GMT_ADD_FILES_IF_NONE) || (mode & GMT_ADD_FILES_ALWAYS) || (mode & GMT_ADD_STDIO_IF_NONE) || (mode & GMT_ADD_STDIO_ALWAYS) || (mode & GMT_ADD_EXISTING))) return_error (API, GMT_NOT_A_VALID_MODE);
8614 
8615 	if (n_args == 0) /* Passed the head of linked option structures */
8616 		head = args;
8617 	else		/* Passed argc, argv, likely from Fortran */
8618 		head = GMT_Create_Options (API, n_args, args);
8619 	gmtlib_io_banner (API->GMT, direction);	/* Message for binary i/o */
8620 	if (direction == GMT_IN)
8621 		object_ID = gmtapi_init_import (API, family, geometry, mode, head);
8622 	else
8623 		object_ID = gmtapi_init_export (API, family, geometry, mode, head);
8624 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Init_IO: Returned first %s object ID = %d\n", GMT_direction[direction], object_ID);
8625 	return (API->error);
8626 }
8627 
8628 #ifdef FORTRAN_API
GMT_Init_IO_(unsigned int * family,unsigned int * geometry,unsigned int * direction,unsigned int * mode,unsigned int * n_args,void * args)8629 int GMT_Init_IO_ (unsigned int *family, unsigned int *geometry, unsigned int *direction, unsigned int *mode, unsigned int *n_args, void *args) {
8630 	/* Fortran version: We pass the global GMT_FORTRAN structure */
8631 	return (GMT_Init_IO (GMT_FORTRAN, *family, *geometry, *direction, *mode, *n_args, args));
8632 }
8633 #endif
8634 
gmtapi_end_io_dataset(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S,unsigned int * item)8635 GMT_LOCAL int gmtapi_end_io_dataset (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S, unsigned int *item) {
8636 	/* These are the steps we must take to finalize a GMT_DATASET that was written to
8637 	 * record-by-record via GMT_Put_Record.  It needs to set number of records and set
8638 	 * the min/max per segments and table */
8639 	int check, object_ID;
8640 	int64_t *count = API->GMT->current.io.curr_pos[GMT_OUT];		/* Short-hand for counts of tbl, seg, rows */
8641 	struct GMT_DATASET *D = S->resource;
8642 	struct GMT_DATATABLE *T = NULL;
8643 	struct GMT_DATASET_HIDDEN *DH = NULL;
8644 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
8645 	if (D == NULL) {	/* No output records produced by module; just return an empty dataset with no rows instead of NULL */
8646 		unsigned int smode = (API->GMT->current.io.record_type[GMT_OUT] & GMT_WRITE_TEXT) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
8647 		D = gmtlib_create_dataset (API->GMT, 1, 1, 0, 0, S->geometry, smode, true);	/* 1 table, 1 segment; no cols or rows yet */
8648 		S->resource = D;
8649 	}
8650 
8651 	T = D->table[0];	/* Shorthand to the only table */
8652 	DH = gmt_get_DD_hidden (D);
8653 	TH = gmt_get_DT_hidden (T);
8654 	if (count[GMT_SEG] >= 0) {	/* Finalize segment allocations */
8655 		if (!T->segment[count[GMT_SEG]]) T->segment[count[GMT_SEG]] = gmt_get_segment (API->GMT, T->n_columns);
8656 		gmtlib_assign_segment (API->GMT, GMT_OUT, T->segment[count[GMT_SEG]], count[GMT_ROW], T->n_columns);	/* Allocate and place arrays into segment */
8657 		count[GMT_SEG]++;	/* Set final number of segments */
8658 		T->n_segments++;
8659 	}
8660 	if (count[GMT_SEG] < (int64_t)TH->n_alloc) {	/* Realloc final number of segments */
8661 		uint64_t s;
8662 		for (s = T->n_segments; s < TH->n_alloc; s++) {	/* Free the extra structures */
8663 			if (T->segment[s] == NULL) continue;
8664 			gmt_M_free (API->GMT, T->segment[s]->hidden);
8665 			gmt_M_free (API->GMT, T->segment[s]);
8666 		}
8667 		T->segment = gmt_M_memory (API->GMT, T->segment, T->n_segments, struct GMT_DATASEGMENT *);	/* Finalize pointer array */
8668 		TH->n_alloc = T->n_segments;	/* Update allocation count */
8669 	}
8670 	if (S->h_delay) {	/* Must do the first table headers now since we finally have allocated the table */
8671 		T->header = API->tmp_header;
8672 		T->n_headers = API->n_tmp_headers;
8673 		API->n_tmp_headers = 0;
8674 		API->tmp_header = NULL;
8675 		S->h_delay = false;
8676 	}
8677 	if (S->s_delay) {	/* Must do the first segment header now since we finally have allocated the table */
8678 		T->segment[0]->header = API->tmp_segmentheader;
8679 		API->tmp_segmentheader = NULL;
8680 		S->s_delay = false;
8681 	}
8682 	D->n_segments = T->n_segments;
8683 	gmt_set_dataset_minmax (API->GMT, D);	/* Update the min/max values for this dataset */
8684 	D->n_records = T->n_records = count[GMT_ROW];
8685 	DH->alloc_level = S->alloc_level;	/* Since we are passing it up to the caller */
8686 	/* Register this resource */
8687 	if ((object_ID = GMT_Register_IO (API, GMT_IS_DATASET, GMT_IS_REFERENCE, D->geometry, GMT_OUT, NULL, D)) == GMT_NOTSET)
8688 		return_error (API, API->error);		/* Failure to register */
8689 	if ((check = gmtlib_validate_id (API, GMT_IS_DATASET, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
8690 		return_error (API, API->error);		/* Failure to validate */
8691 	*item = (unsigned int) check;
8692 	return (GMT_NOERROR);
8693 }
8694 
gmtapi_end_io_matrix(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S,unsigned int * item)8695 GMT_LOCAL int gmtapi_end_io_matrix (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S, unsigned int *item) {
8696 	/* These are the steps we must take to finalize a GMT_MATRIX that was written to
8697 	 * record-by-record vai GMT_Put_Record.  It needs to set number of rows and possibly
8698 	 * add leading NaN-record(s) if there were segment headers at the beginning of file. */
8699 	int error = 0, check, object_ID;
8700 	struct GMT_MATRIX *M = S->resource;
8701 	struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M);
8702 	if (S->alloc_mode != GMT_ALLOC_EXTERNALLY && S->n_alloc != S->rec) {	/* Must finalize matrix memory */
8703 		size_t size = S->n_alloc = S->rec;
8704 		size *= M->n_columns;
8705 		if ((error = gmtlib_alloc_univector (API->GMT, &(M->data), M->type, size)) != GMT_NOERROR)
8706 			return_error (API, error);
8707 	}
8708 	MH->alloc_level = S->alloc_level;	/* Since we are passing it up to the caller */
8709 	if (S->h_delay) {	/* Must do the first table headers now since we finally have allocated the table */
8710 		M->header = API->tmp_header;
8711 		M->n_headers = API->n_tmp_headers;
8712 		API->n_tmp_headers = 0;
8713 		S->h_delay = false;
8714 	}
8715 	if (S->delay) {	/* Must place delayed NaN record(s) signifying segment header(s) */
8716 		GMT_putfunction api_put_val = gmtapi_select_put_function (API, M->type);
8717 		if (api_put_val == NULL) return_error (API, GMT_NOT_A_VALID_TYPE);
8718 		p_func_uint64_t GMT_2D_to_index = NULL;
8719 		uint64_t col, ij;
8720 		if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, GMT_IS_ROW_FORMAT, GMT_GRID_IS_REAL)) == NULL)	/* Can only do row-format until end of this function */
8721 			return_error (API, GMT_WRONG_MATRIX_SHAPE);
8722 		while (S->delay) {	/* Place delayed NaN-rows(s) up front */
8723 			S->delay--;
8724 			for (col = 0; col < M->n_columns; col++) {	/* Place the output items */
8725 				ij = GMT_2D_to_index (S->delay, col, M->dim);
8726 				api_put_val (&(M->data), ij, API->GMT->session.d_NaN);
8727 			}
8728 		}
8729 	}
8730 	if (M->shape == GMT_IS_COL_FORMAT) {	/* Oh no, must do a transpose in place */
8731 		GMT_Report (API, GMT_MSG_DEBUG, "gmtapi_end_io_matrix: Must transpose union matrix to GMT_IS_COL_FORMAT arrangement\n");
8732 		gmtlib_union_transpose (API->GMT, &(M->data), M->n_rows, M->n_columns, M->type);
8733 		M->dim = M->n_rows;	/* Since now it is in FORTRAN column format */
8734 	}
8735 	/* Register this resource */
8736 	if ((object_ID = GMT_Register_IO (API, GMT_IS_MATRIX, GMT_IS_REFERENCE, GMT_IS_SURFACE, GMT_OUT, NULL, M)) == GMT_NOTSET)
8737 		return_error (API, API->error);	/* Failure to register */
8738 	if ((check = gmtlib_validate_id (API, GMT_IS_MATRIX, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
8739 		return_error (API, API->error);	/* Failure to validate */
8740 	*item = (unsigned int) check;
8741 	return (GMT_NOERROR);
8742 }
8743 
gmtapi_end_io_vector(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S,unsigned int * item)8744 GMT_LOCAL int gmtapi_end_io_vector (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S, unsigned int *item) {
8745 	/* These are the steps we must take to finalize a GMT_VECTOR that was written to
8746 	 * record-by-record via GMT_Put_Record.  It needs to set number of rows and possibly
8747 	 * add leading NaN-record(s) if there were segment headers at the beginning of file. */
8748 	int error = 0, check, object_ID;
8749 	struct GMT_VECTOR *V = S->resource;
8750 	struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V);
8751 	if (S->alloc_mode != GMT_ALLOC_EXTERNALLY && S->n_alloc != S->rec) {	/* Must finalize memory */
8752 		S->n_alloc = S->rec;
8753 		if ((error = gmtlib_alloc_vectors (API->GMT, V, S->n_alloc)) != GMT_NOERROR)
8754 			return_error (API, error);
8755 	}
8756 	if ((object_ID = GMT_Register_IO (API, GMT_IS_VECTOR, GMT_IS_REFERENCE, S->geometry, GMT_OUT, NULL, V)) == GMT_NOTSET)
8757 		return_error (API, API->error);	/* Failure to register */
8758 	if ((check = gmtlib_validate_id (API, GMT_IS_VECTOR, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
8759 		return_error (API, API->error);	/* Failure to validate */
8760 	VH->alloc_level = S->alloc_level;	/* Since we are passing it up to the caller */
8761 	if (S->h_delay) {	/* Must do the first table headers now since we finally have allocated the table */
8762 		V->header = API->tmp_header;
8763 		V->n_headers = API->n_tmp_headers;
8764 		API->n_tmp_headers = 0;
8765 		S->h_delay = false;
8766 	}
8767 	if (S->delay) {	/* Must place delayed NaN record(s) signifying segment header(s) */
8768 		uint64_t col;
8769 		while (S->delay) {	/* Place delayed NaN-record(s) as leading rows */
8770 			S->delay--;
8771 			V->n_rows++;	/* Since could not be incremented before V was created */
8772 			for (col = 0; col < V->n_columns; col++)
8773 				API->current_put_V_val[col] (&(V->data[col]), S->delay, API->GMT->session.d_NaN);
8774 		}
8775 	}
8776 	gmt_M_free (API->GMT, API->current_put_V_val);
8777 	*item = (unsigned int) check;
8778 	return (GMT_NOERROR);
8779 }
8780 
8781 /*! . */
GMT_End_IO(void * V_API,unsigned int direction,unsigned int mode)8782 int GMT_End_IO (void *V_API, unsigned int direction, unsigned int mode) {
8783 	/* Terminates the i/o mechanism for either input or output (given by direction).
8784 	 * GMT_End_IO must be called after all data i/o is completed.
8785 	 * direction:	Either GMT_IN or GMT_OUT
8786 	 * mode:	Either GMT_IO_DONE (nothing), GMT_IO_RESET (let all resources be accessible again), or GMT_IO_UNREG (unreg all accessed resources).
8787 	 * NOTE: 	Mode not yet implemented until we see a use.
8788 	 * Returns:	false if successful, true if error.
8789 	 * For memory output we finalized the container, register it, sets the alloc_level to the calling entity
8790 	 * and pass the resource upwards.
8791 	 */
8792 	unsigned int item = 0;
8793 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
8794 	struct GMTAPI_CTRL *API = NULL;
8795 
8796 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
8797 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (V_API, GMT_NOT_A_VALID_DIRECTION);
8798 	if (mode > GMT_IO_UNREG) return_error (V_API, GMT_NOT_A_VALID_IO_MODE);
8799 
8800 	API = gmtapi_get_api_ptr (V_API);
8801 	gmtlib_free_ogr (API->GMT, &(API->GMT->current.io.OGR), 0);	/* Free segment-related array */
8802 	if (direction == GMT_OUT && API->io_mode[GMT_OUT] == GMTAPI_BY_REC) {
8803 		/* Finalize record-by-record output object dimensions */
8804 		S_obj = API->object[API->current_item[GMT_OUT]];	/* Shorthand for the data object we are working on */
8805 		if (S_obj) {
8806 			S_obj->status = GMT_IS_USED;	/* Done "writing" to this destination */
8807 			if ((S_obj->method == GMT_IS_DUPLICATE || S_obj->method == GMT_IS_REFERENCE)) {	/* Used GMT_Put_Record: Must now realloc dimensions given known sizes */
8808 				int error = GMT_NOERROR;	/* If all goes well */
8809 				if (S_obj->actual_family == GMT_IS_DATASET)			/* Dataset type */
8810 					error = gmtapi_end_io_dataset (API, S_obj, &item);
8811 				else if (S_obj->actual_family == GMT_IS_MATRIX)		/* Matrix type */
8812 					error = gmtapi_end_io_matrix (API, S_obj, &item);
8813 				else if (S_obj->actual_family == GMT_IS_VECTOR)		/* Vector type */
8814 					error = gmtapi_end_io_vector (API, S_obj, &item);
8815 				else	/* Should not get here... */
8816 					error = GMT_NOT_A_VALID_FAMILY;
8817 				if (error) return_error (API, error);	/* Failure to finalize */
8818 				API->object[item]->no_longer_owner = true;	/* Since we passed it via S_obj */
8819 			}
8820 			if (S_obj->close_file) {	/* Close any file that we opened earlier */
8821 				gmt_fclose (API->GMT, S_obj->fp);
8822 				S_obj->close_file = false;
8823 			}
8824 		}
8825 	}
8826 	else {	/* Input files were closed when we tried to go to next item */
8827 		if (API->current_get_V_val) gmt_M_free (API->GMT, API->current_get_V_val);
8828 	}
8829 	API->is_file = true;
8830 	API->io_enabled[direction] = false;	/* No longer OK to access resources or destinations */
8831 	API->current_rec[direction] = 0;	/* Reset count for next time */
8832 	for (item = 0; item < API->n_objects; item++) {	/* Deselect the used resources */
8833 		if (!API->object[item]) continue;	/* Skip empty object */
8834 		if (API->object[item]->direction != (enum GMT_enum_std)direction) continue;	/* Not the required direction */
8835 		if (API->object[item]->selected) API->object[item]->selected = false;	/* No longer a selected resource */
8836 	}
8837 
8838 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_End_IO: %s resource access is now disabled\n", GMT_direction[direction]);
8839 
8840 	return_error (V_API, GMT_NOERROR);	/* No error encountered */
8841 }
8842 
8843 #ifdef FORTRAN_API
GMT_End_IO_(unsigned int * direction,unsigned int * mode)8844 int GMT_End_IO_ (unsigned int *direction, unsigned int *mode) {
8845 	/* Fortran version: We pass the global GMT_FORTRAN structure */
8846 	return (GMT_End_IO (GMT_FORTRAN, *direction, *mode));
8847 }
8848 #endif
8849 
8850 /*! . */
GMT_Get_Status(void * V_API,unsigned int mode)8851 int GMT_Get_Status (void *V_API, unsigned int mode) {
8852 	/* Returns nonzero (true) or 0 (false) if the current io status
8853 	 * associated with record-by-record reading matches the
8854 	 * specified mode.  The modes are:
8855 	 * GMT_IO_TABLE_HEADER		: Is current record a table header?
8856 	 * GMT_IO_SEGMENT_HEADER	: Is current record a segment header?
8857 	 * GMT_IO_ANY_HEADER		: Is current record a header or segment header?
8858 	 * GMT_IO_MISMATCH		: Did current record result in a parsing error?
8859 	 * GMT_IO_EOF			: Did we reach end-of-file for entire data set(EOF)?
8860 	 * GMT_IO_NAN			: Did we encounter any NaNs in current record?
8861 	 * GMT_IO_GAP			: Did current record indicate a data gap?
8862 	 * GMT_IO_NEW_SEGMENT		: Is current record the start of a new segment (gap or header)
8863 	 * GMT_IO_LINE_BREAK		: Any sort of new line break (gap, headers, nan)
8864 	 * GMT_IO_FILE_BREAK		: Did we reach end-of-file for a single table (EOF)?
8865 	 * GMT_IO_DATA			: Is current record a data record (including nans)?
8866 	 */
8867 
8868 	struct GMTAPI_CTRL *API = NULL;
8869 	struct GMT_IO *IO = NULL;
8870 
8871 	if (V_API == NULL) return_value (V_API, GMT_NOT_A_SESSION, GMT_NOTSET);
8872 
8873 	API = gmtapi_get_api_ptr (V_API);
8874 	API->error = GMT_NOERROR;
8875 	IO = &(API->GMT->current.io);	/* Pointer to the GMT IO structure */
8876 	if (mode == GMT_IO_DATA_RECORD) return (IO->status == 0 || IO->status == GMT_IO_NAN);
8877 	return (IO->status & mode);
8878 }
8879 
8880 #ifdef FORTRAN_API
GMT_Get_Status_(unsigned int * mode)8881 int GMT_Get_Status_ (unsigned int *mode) {
8882 	/* Fortran version: We pass the global GMT_FORTRAN structure */
8883 	return (GMT_Get_Status (GMT_FORTRAN, *mode));
8884 }
8885 #endif
8886 
8887 /*! . */
gmtapi_get_id(void * V_API,unsigned int family,unsigned int direction,void * resource)8888 GMT_LOCAL int gmtapi_get_id (void *V_API, unsigned int family, unsigned int direction, void *resource) {
8889 	unsigned int i;
8890 	int item;
8891 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
8892 	struct GMTAPI_CTRL *API = NULL;
8893 
8894 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);	/* GMT_Create_Session has not been called */
8895 	API = gmtapi_get_api_ptr (V_API);
8896 	API->error = GMT_NOERROR;
8897 	for (i = 0, item = GMT_NOTSET; item == GMT_NOTSET && i < API->n_objects; i++) {
8898 		if ((S_obj = API->object[i]) == NULL) continue;	/* Empty object */
8899 		if (!S_obj->resource) continue;		/* Empty resource */
8900 		if (S_obj->family != (enum GMT_enum_family)family) {		/* Not the required data type, but check for exceptions */
8901 			if (family == GMT_IS_DATASET && (S_obj->family == GMT_IS_MATRIX || S_obj->family == GMT_IS_VECTOR))
8902 				S_obj->family = GMT_IS_DATASET;	/* Vectors or Matrix masquerading as dataset are valid. Change their family here. */
8903 			else
8904 				continue;
8905 		}
8906 		if (S_obj->direction != (enum GMT_enum_std)direction) continue;	/* Not the required direction */
8907 		if (S_obj->resource == resource) item = i;	/* Pick the requested object regardless of direction */
8908 	}
8909 	if (item == GMT_NOTSET) return_value (API, GMT_NOT_A_VALID_ID, GMT_NOTSET);	/* No such resource found */
8910 	return (S_obj->ID);
8911 }
8912 
gmtapi_matrix_data_conforms_to_grid(struct GMT_MATRIX * M)8913 GMT_LOCAL bool gmtapi_matrix_data_conforms_to_grid (struct GMT_MATRIX *M) {
8914 	/* Check if a matrix data array matches the form of a GMT grid (row-oriented floats) */
8915 	if (M->shape == GMT_IS_COL_FORMAT) return (false);	/* Must transpose */
8916 	if (M->data.f4 == NULL) return (false);	/* Having nothing means we must allocate */
8917 	return (M->type == GMT_GRDFLOAT);		/* Having gmt_grdfloat means we can use as is */
8918 }
8919 
gmtapi_matrix_data_conforms_to_dataset(struct GMT_MATRIX * M)8920 GMT_LOCAL bool gmtapi_matrix_data_conforms_to_dataset (struct GMT_MATRIX *M) {
8921 	/* Check if a matrix data array matches the form of a GMT dataset (columns of doubles) */
8922 	if (M->shape == GMT_IS_ROW_FORMAT) return (false);	/* Must transpose */
8923 	if (M->data.f8 == NULL) return (false);	/* Having nothing means we must allocate */
8924 	return (M->type == GMT_DOUBLE);			/* Having double means we can use as is */
8925 }
8926 
gmtapi_vector_data_conforms_to_dataset(struct GMTAPI_CTRL * API,struct GMT_VECTOR * V,enum GMT_enum_type type)8927 GMT_LOCAL bool gmtapi_vector_data_conforms_to_dataset (struct GMTAPI_CTRL *API, struct GMT_VECTOR *V, enum GMT_enum_type type) {
8928 	gmt_M_unused(API);
8929 	/* Check if the vector data arrays matches the form of a GMT dataset (columns of doubles) */
8930 	if (type != GMT_DOUBLE) {	/* Only doubles can be passed or memcpy directly */
8931 		if (V->n_columns == 0) return (false);	/* Having nothing yet means we must duplicate */
8932 		if (V->type == NULL) return (false);	/* Having nothing yet means we must duplicate */
8933 		if (V->data == NULL) return (false);	/* Having nothing yet means we must duplicate */
8934 	}
8935 	for (unsigned int col = 0; col < V->n_columns; col++) {
8936 		if (V->data[col].f8 == NULL) return (false);	/* Having nothing means we must duplicate */
8937 		if (V->type[col] != GMT_DOUBLE) return (false);	/* Not having double means must duplicate */
8938 	}
8939 	return true;	/* Seems OK */
8940 }
8941 
8942  /*! . */
gmtapi_separate_families(unsigned int * family)8943 GMT_LOCAL unsigned int gmtapi_separate_families (unsigned int *family) {
8944 	unsigned int actual_family;
8945 	if ((*family) & GMT_VIA_VECTOR) {	/* Must allocate a GMT_VECTOR despite family being something else (like DATASET) */
8946 		actual_family = GMT_IS_VECTOR;
8947 		(*family) -=  GMT_VIA_VECTOR;
8948 	}
8949 	else if ((*family) & GMT_VIA_MATRIX) {	/* Must allocate a GMT_MATRIX despite family being something else (like GRID) */
8950 		actual_family = GMT_IS_MATRIX;
8951 		(*family) -=  GMT_VIA_MATRIX;
8952 	}
8953 	else
8954 		actual_family = (*family);	/* It is what it says it is */
8955 	return actual_family;
8956 }
8957 
gmtapi_maybe_change_method_to_duplicate(struct GMTAPI_CTRL * API,struct GMTAPI_DATA_OBJECT * S_obj)8958 GMT_LOCAL void gmtapi_maybe_change_method_to_duplicate (struct GMTAPI_CTRL *API, struct GMTAPI_DATA_OBJECT *S_obj) {
8959 	/* We want to pass a matrix or set of vectors from the outside as a grid or as a dataset.
8960 	 * grid: If it is a float matrix in row-order layout then we can, else we must duplicate
8961 	 * dataset: If matrix or vector are in columns and they are all doubles then we can, else we must duplicate */
8962 	if (S_obj->actual_family == GMT_IS_MATRIX && S_obj->family == GMT_IS_GRID && !gmtapi_matrix_data_conforms_to_grid (S_obj->resource)) {
8963 		S_obj->method = GMT_IS_DUPLICATE;	/* Must duplicate this resource */
8964 		GMT_Report (API, GMT_MSG_INFORMATION, "GMT_Open_VirtualFile: Switch method to GMT_IS_DUPLICATE as input matrix is not compatible with a GMT gmt_grdfloat grid\n");
8965 	}
8966 	else if (S_obj->actual_family == GMT_IS_MATRIX && S_obj->family == GMT_IS_DATASET && !gmtapi_matrix_data_conforms_to_dataset (S_obj->resource)) {
8967 		S_obj->method = GMT_IS_DUPLICATE;	/* Must duplicate this resource */
8968 		GMT_Report (API, GMT_MSG_INFORMATION, "GMT_Open_VirtualFile: Switch method to GMT_IS_DUPLICATE as input matrix is not compatible with a GMT dataset\n");
8969 	}
8970 	else if (S_obj->actual_family == GMT_IS_VECTOR && S_obj->family == GMT_IS_GRID) {
8971 		S_obj->method = GMT_IS_DUPLICATE;	/* Must duplicate this resource */
8972 		GMT_Report (API, GMT_MSG_INFORMATION, "GMT_Open_VirtualFile: Switch method to GMT_IS_DUPLICATE as vectors are not compatible with a GMT grid\n");
8973 	}
8974     else if (S_obj->actual_family == GMT_IS_VECTOR && S_obj->family == GMT_IS_DATASET && !gmtapi_vector_data_conforms_to_dataset (API, S_obj->resource, S_obj->type)) {
8975         S_obj->method = GMT_IS_DUPLICATE;   /* Must duplicate this resource */
8976         GMT_Report (API, GMT_MSG_INFORMATION, "GMT_Open_VirtualFile: Switch method to GMT_IS_DUPLICATE as input vectors are not compatible with a GMT dataset\n");
8977     }
8978     else if (S_obj->actual_family == GMT_IS_DATASET && S_obj->family == GMT_IS_DATASET && API->GMT->common.i.select) {
8979         S_obj->method = GMT_IS_DUPLICATE;   /* Must duplicate this resource */
8980         GMT_Report (API, GMT_MSG_INFORMATION, "GMT_Open_VirtualFile: Switch method to GMT_IS_DUPLICATE as input dataset needs to be operated on to become output GMT dataset\n");
8981     }
8982 }
8983 
8984  /*! . */
GMT_Open_VirtualFile(void * V_API,unsigned int family,unsigned int geometry,unsigned int direction,void * data,char * name)8985 int GMT_Open_VirtualFile (void *V_API, unsigned int family, unsigned int geometry, unsigned int direction, void *data, char *name) {
8986 	/* Associate a virtual file with a data object for either reading or writing.
8987 	 * Family and geometry specifies the nature of the data to be read or written.
8988 	 * Direction is either GMT_IN or GMT_OUT and determines if we read or write.
8989 	 * Reading: data must point to a data container we wish to read from via a module.
8990 	 * Writing: data is either an existing output data container that the user created
8991 	 *  beforehand or it is NULL and we create an expanding output resource.
8992 	 * name is the name given to the virtual file and is returned. */
8993 	int object_ID = GMT_NOTSET, item_s = 0;
8994 	unsigned int item, orig_family, actual_family = 0, via_type = 0, messenger = 0, module_input, the_mode = GMT_IS_DUPLICATE;
8995 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
8996 	struct GMTAPI_CTRL *API = NULL;
8997 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
8998 	module_input = (family & GMT_VIA_MODULE_INPUT);	/* Are we registering a resource that is a module input? */
8999 	family -= module_input;
9000 	if (direction & GMT_IS_REFERENCE) {	/* Treat this memory as read-only */
9001 		direction -= GMT_IS_REFERENCE;
9002 		the_mode = GMT_IS_REFERENCE;
9003 	}
9004 	else if (direction & GMT_IS_DUPLICATE)	/* This is the default - just remove the mode flag */
9005 		direction -= GMT_IS_DUPLICATE;
9006 	if (!(direction == GMT_IN || direction == GMT_OUT)) return GMT_NOT_A_VALID_DIRECTION;
9007 	if (direction == GMT_IN && data == NULL) return GMT_PTR_IS_NULL;
9008 	if (name == NULL) return_error (V_API, GMT_PTR_IS_NULL);
9009 	orig_family = family;	/* In case we will call GMT_Create_Data later */
9010 	actual_family = gmtapi_separate_families (&family);	/* In case via have a VIA situation */
9011 	if (geometry >= GMT_VIA_CHAR) {
9012 		via_type = (geometry / 100);	/* via_type is 1 higher than GMT_CHAR */
9013 		geometry %= 100;
9014 	}
9015 	if (direction == GMT_IN  && !gmtapi_valid_input_family (family))  return GMT_NOT_A_VALID_FAMILY;
9016 	if (direction == GMT_OUT && !gmtapi_valid_output_family (family)) return GMT_NOT_A_VALID_FAMILY;
9017 	if (via_type && data && !gmtapi_valid_type (via_type-1)) return GMT_NOT_A_VALID_TYPE;	/* via type only valid if not passing any data but want vector or matrix */
9018 	if (actual_family != family && !gmtapi_valid_via_family (actual_family)) return GMT_NOT_A_VALID_FAMILY;
9019 	API = gmtapi_get_api_ptr (V_API);
9020 
9021 	if (data) {	/* Data container provided, see if registered */
9022 		for (item = 0; object_ID == GMT_NOTSET && item < API->n_objects; item++) {	/* Loop over all objects */
9023 			if (!API->object[item]) continue;	/* Skip freed objects */
9024 			if (API->object[item]->resource == data) object_ID = API->object[item]->ID;	/* Found a matching data pointer */
9025 		}
9026 		if (object_ID != GMT_NOTSET && (item_s = gmtapi_get_item (API, family, data)) == GMT_NOTSET) {	/* Not found in list */
9027 			return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find that item in the array despite finding its ID? */
9028 		}
9029 		S_obj = API->object[item_s];	/* Short-hand for later */
9030 		if (!(S_obj->family == family && S_obj->actual_family == actual_family))
9031 			 return GMT_NOT_A_VALID_FAMILY;
9032 	}
9033 	if (direction == GMT_IN) {	/* Set things up for reading */
9034 		/* See if this one is known to us already */
9035 		if (object_ID == GMT_NOTSET) {	/* Register data as a new object for reading [GMT_IN] and reset its status to unread */
9036 			if ((object_ID = GMT_Register_IO (API, family, the_mode|GMT_IO_RESET, geometry, GMT_IN, NULL, data)) == GMT_NOTSET)
9037 				return (API->error);
9038 			if ((item_s = gmtapi_get_item (API, family, data)) == GMT_NOTSET) {	/* Not found in list */
9039 				return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find that item in the array despite finding its ID? */
9040 			}
9041 			S_obj = API->object[item_s];	/* Short-hand for later */
9042 		}
9043 		else {	/* Found the object earlier; recycle the address and ensure it is a readable object */
9044 			if (S_obj->family != family || S_obj->actual_family != actual_family)
9045 				return_error (API, GMT_WRONG_FAMILY);	/* Mixup between what was created and what was passed in */
9046 			S_obj->status = 0;					/* Open for business */
9047 			S_obj->method = the_mode;			/* Now a memory resource */
9048 			S_obj->direction = GMT_IN;			/* Make sure it now is flagged for reading */
9049 		}
9050 		/* If the input a container masquerading as another then we may have to replace method GMT_IS_REFERENCE by GMT_IS_DUPLICATE if REFERENCE was specified */
9051 		if (S_obj->method == GMT_IS_REFERENCE) gmtapi_maybe_change_method_to_duplicate (API, S_obj);
9052 	}
9053 	else {	/* Set things up for writing */
9054 		if (data) {	/* Was provided an object to use */
9055 			if (object_ID == GMT_NOTSET) {	/* Register a new object for writing [GMT_OUT] and reset its status to unread */
9056 				if ((object_ID = GMT_Register_IO (API, orig_family, the_mode|GMT_IO_RESET, geometry, GMT_OUT, NULL, data)) == GMT_NOTSET)
9057 					return (API->error);
9058 				if ((item_s = gmtapi_get_item (API, family, data)) == GMT_NOTSET) {	/* Not found in list */
9059 					return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find that item in the array despite finding its ID? */
9060 				}
9061 				S_obj = API->object[item_s];	/* Short-hand for later */
9062 			}
9063 			else {	/* Here we have the item and can recycle the address */
9064 				S_obj->status = 0;				/* Open for business */
9065 				S_obj->method = the_mode;		/* Now a memory resource */
9066 				S_obj->direction = GMT_OUT;		/* Make sure it now is flagged for writing */
9067 			}
9068 		}
9069 		else {	/* New expanding output resource */
9070 			void *object = NULL;
9071 			/* GMT_Create_Data may return error code if there are issues with the values of family, or geometry */
9072 			/* Creating an empty object with mode & GMT_IS_OUTPUT means it is intended to hold output [GMT_OUT] from a module */
9073 			if ((object = GMT_Create_Data (API, orig_family, geometry, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL)
9074 				return (API->error);
9075 			/* Obtain the object's ID */
9076 			if ((object_ID = gmtapi_get_id (API, family, GMT_OUT, object)) == GMT_NOTSET)
9077 				return (API->error);
9078 			if ((item_s = gmtapi_get_item (API, family, object)) == GMT_NOTSET) {	/* Not found in list */
9079 				return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find that item in the array despite finding its ID? */
9080 			}
9081 			S_obj = API->object[item_s];	/* Short-hand for later */
9082 			S_obj->type = (via_type) ? via_type - 1 : API->GMT->current.setting.export_type;	/* Remember what output type we want */
9083 			S_obj->method = the_mode;	/* Now a memory resource */
9084 			messenger = 1;
9085 		}
9086 		/* If the output is a matrix masquerading as grid then it must be GMT_FLOAT, otherwise change to DUPLICATE if REFERENCE was specified */
9087 		if (S_obj->method == GMT_IS_REFERENCE) gmtapi_maybe_change_method_to_duplicate (API, S_obj);
9088 	}
9089 	S_obj->region = false;	/* No subset of anything is being considered here */
9090 	gmt_M_memset (S_obj->wesn, 6U, double);
9091 	/* Obtain the unique VirtualFile name */
9092 	if (gmtapi_encode_id (API, module_input, direction, family, actual_family, geometry, messenger, object_ID, name) != GMT_NOERROR)
9093 		return (API->error);
9094 	return GMT_NOERROR;
9095 }
9096 
9097 #ifdef FORTRAN_API
GMT_Open_VirtualFile_(unsigned int * family,unsigned int * geometry,unsigned int * direction,void * data,char * string,int len)9098 int GMT_Open_VirtualFile_ (unsigned int *family, unsigned int *geometry, unsigned int *direction, void *data, char *string, int len) {
9099 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9100 	return (GMT_Open_VirtualFile (GMT_FORTRAN, *family, *geometry, *direction, data, string));
9101 }
9102 #endif
9103 
GMT_Close_VirtualFile(void * V_API,const char * string)9104 int GMT_Close_VirtualFile (void *V_API, const char *string) {
9105 	/* Given a VirtualFile name, close it */
9106 	int object_ID, item;
9107 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
9108 	struct GMTAPI_CTRL *API = NULL;
9109 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
9110 	if (string == NULL) return_error (V_API, GMT_PTR_IS_NULL);
9111 	if ((object_ID = gmtapi_decode_id (string)) == GMT_NOTSET)
9112 		return_error (V_API, GMT_OBJECT_NOT_FOUND);
9113 	API = gmtapi_get_api_ptr (V_API);
9114 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET)
9115 		return_error (API, GMT_OBJECT_NOT_FOUND);
9116 	S_obj = API->object[item];	/* Short-hand */
9117 	if (S_obj->family != S_obj->actual_family)	/* Reset the un-masquerading that GMT_Open_VirtualFile did */
9118 		S_obj->family = S_obj->actual_family;
9119 	return GMT_NOERROR;
9120 }
9121 
9122 #ifdef FORTRAN_API
GMT_Close_VirtualFile_(unsigned int * family,char * string,int len)9123 int GMT_Close_VirtualFile_ (unsigned int *family, char *string, int len) {
9124 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9125 	return (GMT_Close_VirtualFile (GMT_FORTRAN, string));
9126 }
9127 #endif
9128 
GMT_Read_VirtualFile(void * V_API,const char * string)9129 void * GMT_Read_VirtualFile (void *V_API, const char *string) {
9130 	/* Given a VirtualFile name, retrieve the resulting object */
9131 	int object_ID;
9132 	void *object = NULL;
9133 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
9134 	if (string == NULL) return_null (V_API, GMT_PTR_IS_NULL);
9135 	if ((object_ID = gmtapi_decode_id (string)) == GMT_NOTSET)
9136 		return_null (V_API, GMT_OBJECT_NOT_FOUND);
9137 	if ((object = gmtapi_retrieve_data (V_API, object_ID)) == NULL)
9138 		return_null (V_API, GMT_OBJECT_NOT_FOUND);
9139 	return object;
9140 }
9141 
9142 #ifdef FORTRAN_API
GMT_Read_VirtualFile_(char * string,int len)9143 void * GMT_Read_VirtualFile_ (char *string, int len) {
9144 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9145 	return (GMT_Read_VirtualFile (GMT_FORTRAN, string));
9146 }
9147 #endif
9148 
GMT_Inquire_VirtualFile(void * V_API,const char * string)9149 int GMT_Inquire_VirtualFile (void *V_API, const char *string) {
9150 	/* Given a VirtualFile name, retrieve the family of the resulting object */
9151 	int object_ID, item;
9152 	struct GMTAPI_CTRL *API = NULL;
9153 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
9154 	if (string == NULL) return_error (V_API, GMT_PTR_IS_NULL);
9155 	if ((object_ID = gmtapi_decode_id (string)) == GMT_NOTSET)
9156 		return_error (V_API, GMT_OBJECT_NOT_FOUND);
9157 	if ((item = gmtlib_validate_id (V_API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET)
9158 		return_error (API, GMT_OBJECT_NOT_FOUND);
9159 	API = gmtapi_get_api_ptr (V_API);
9160 	return API->object[item]->family;
9161 }
9162 
9163 #ifdef FORTRAN_API
GMT_Inquire_VirtualFile_(char * string,int len)9164 int GMT_Inquire_VirtualFile_ (char *string, int len) {
9165 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9166 	return (GMT_Inquire_VirtualFile (GMT_FORTRAN, string));
9167 }
9168 #endif
9169 
9170  /*! . */
GMT_Init_VirtualFile(void * V_API,unsigned int mode,const char * name)9171 int GMT_Init_VirtualFile (void *V_API, unsigned int mode, const char *name) {
9172 	/* Reset a virtual file back to its original configuration so that it can be
9173 	 * repurposed for reading or writing again.
9174 	 */
9175 	int object_ID = GMT_NOTSET, item;
9176 	struct GMTAPI_DATA_OBJECT *S = NULL;
9177 	struct GMTAPI_CTRL *API = NULL;
9178 	gmt_M_unused (mode);
9179 
9180 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
9181 	if (name == NULL) return_error (V_API, GMT_PTR_IS_NULL);
9182 	API = gmtapi_get_api_ptr (V_API);
9183 	if ((object_ID = gmtapi_decode_id (name)) == GMT_NOTSET) return (GMT_OBJECT_NOT_FOUND);	/* Not a registered resource */
9184 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET)
9185 		return_error (API, GMT_OBJECT_NOT_FOUND);
9186 	S = API->object[item];	/* Short-hand pointer */
9187 	S->rec = 0;	/* Start at first record */
9188 	S->delay = 0;	/* No Nan-fuckery yet */
9189 	S->s_delay = S->h_delay = false;	/* No header issues yet */
9190 	S->status = GMT_IS_UNUSED;
9191 	S->selected = true;
9192 	return GMT_NOERROR;
9193 }
9194 
9195 #ifdef FORTRAN_API
GMT_Init_VirtualFile_(unsigned int mode,char * string,int len)9196 int GMT_Init_VirtualFile_ (unsigned int mode, char *string, int len) {
9197 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9198 	return (GMT_Init_VirtualFile (GMT_FORTRAN, mode, string));
9199 }
9200 #endif
9201 
gmtapi_is_passable(struct GMTAPI_DATA_OBJECT * S_obj,unsigned int family)9202 GMT_LOCAL bool gmtapi_is_passable (struct GMTAPI_DATA_OBJECT *S_obj, unsigned int family) {
9203 	if (family != (unsigned int)S_obj->actual_family) return false;	/* Cannot deal with masquerading containers */
9204 	if (S_obj->resource == NULL) return false;	/* Certainly cannot pass this guy */
9205 	if (S_obj->method != GMT_IS_REFERENCE) return false;	/* Only references can be passed */
9206 	if (S_obj->family == GMT_IS_GRID) {
9207 		struct GMT_GRID *G = gmtapi_get_grid_data (S_obj->resource);
9208 		return (G->data == NULL) ? false : true;
9209 	}
9210 	if (S_obj->family == GMT_IS_IMAGE) {
9211 		struct GMT_IMAGE *I = gmtapi_get_image_data (S_obj->resource);
9212 		return (I->data == NULL) ? false : true;
9213 	}
9214 	return true; /* True to its word, otherwise we fall through and read the data */
9215 }
9216 
9217 /* Simple macro to tell us if this file (which we know is a memory file when called) is an output file */
9218 #define gmtapi_M_is_output(file) (file[GMTAPI_OBJECT_DIR_START] == 'O')
9219 
9220 /*! . */
GMT_Read_Data(void * V_API,unsigned int family,unsigned int method,unsigned int geometry,unsigned int mode,double wesn[],const char * infile,void * data)9221 void * GMT_Read_Data (void *V_API, unsigned int family, unsigned int method, unsigned int geometry, unsigned int mode, double wesn[], const char *infile, void *data) {
9222 	/* Function to read data files directly into program memory as a set (not record-by-record).
9223 	 * We can combine the <register resource - import resource > sequence in
9224 	 * one combined function.  See GMT_Register_IO for details on arguments.
9225 	 * data is pointer to an existing grid container when we read a grid in two steps, otherwise it must be NULL.
9226 	 * Case 1: infile != NULL: Register input as the source and import data.
9227 	 * Case 2: infile == NULL: Register stdin as the source and import data.
9228 	 * Case 3: geometry == 0: Loop over all previously registered AND unread sources and combine as virtual dataset [DATASET only]
9229 	 * Case 4: family is GRID|IMAGE and method = GMT_DATA_ONLY: Just find already registered resource
9230 	 * Return: Pointer to data container, or NULL if there were errors (passed back via API->error).
9231 	 */
9232 	int in_ID = GMT_NOTSET, item = GMT_NOTSET;
9233 	unsigned int module_input = 0;
9234 	bool just_get_data, reset, reg_here = false;
9235 	void *new_obj = NULL;
9236 	char *input = NULL;
9237 	struct GMTAPI_CTRL *API = NULL;
9238 
9239 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
9240 	API = gmtapi_get_api_ptr (V_API);
9241 	API->error = GMT_NOERROR;
9242 	if (infile) input = strdup (infile);
9243 	just_get_data = (gmt_M_file_is_memory (input));     /* A regular GMT resource passed via memory */
9244 	if (just_get_data && gmtapi_M_is_output (input)) {  /* A virtual output file created elsewhere, retrieve and we are done */
9245 		gmt_M_str_free (input);
9246 		return (GMT_Read_VirtualFile (API, infile));
9247 	}
9248 	reset = (mode & GMT_IO_RESET);	/* We want to reset resource as unread after reading it */
9249 	if (reset) mode -= GMT_IO_RESET;
9250 	module_input = (family & GMT_VIA_MODULE_INPUT);	/* Are we reading a resource that should be considered a module input? */
9251 	family -= module_input;
9252 	API->module_input = (module_input) ? true : false;
9253 	if (a_grid_or_image_or_cube (family)) {	/* Further checks on the data argument */
9254 		if ((mode & GMT_DATA_ONLY) && data == NULL) {
9255 			free (input);
9256 			return_null (V_API, GMT_PTR_IS_NULL);
9257 		}
9258 		if ((mode & GMT_CONTAINER_ONLY) && data != NULL) {
9259 			free (input);
9260 			return_null (V_API, GMT_PTR_NOT_NULL);
9261 		}
9262 	}
9263 
9264 	if (!gmt_M_file_is_remote (infile) && !gmt_M_file_is_url(infile) && infile && strpbrk (infile, "*?[]") && !gmtapi_file_with_netcdf_directive (API, infile)) {
9265 		/* Gave a wildcard filename */
9266 		uint64_t n_files;
9267 		unsigned int k;
9268 		char **filelist = NULL;
9269 		if (!multiple_files_ok (family)) {
9270 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Data: Wildcards only allowed for DATASET. "
9271 			                                 "Use GMT_Read_Group to read groups of other data types\n");
9272 			free (input);
9273 			return_null (API, GMT_ONLY_ONE_ALLOWED);
9274 		}
9275 		if ((n_files = gmtlib_glob_list (API->GMT, infile, &filelist)) == 0) {
9276 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Data: Expansion of \"%s\" gave no results\n", infile);
9277 			free (input);
9278 			return_null (API, GMT_OBJECT_NOT_FOUND);
9279 		}
9280 		API->shelf = family;	/* Save which one it is so we know in gmtapi_get_data */
9281 		API->module_input = true;	/* Since we are passing NULL as file name we must loop over registered resources */
9282 		for (k = 0; k < n_files; k++) {
9283 			if ((in_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_FILE, geometry, GMT_IN, NULL, filelist[k])) == GMT_NOTSET) {
9284 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Data: Could not register file for input: \n", filelist[k]);
9285 				gmt_M_str_free (input);
9286 				gmt_free_list (API->GMT, filelist, n_files);	/* Free the file list */
9287 				return_null (API, API->error);
9288 			}
9289 			if ((item = gmtlib_validate_id (API, family, in_ID, GMT_IN, GMTAPI_MODULE_INPUT)) == GMT_NOTSET) {
9290 				gmt_M_str_free (input);
9291 				gmt_free_list (API->GMT, filelist, n_files);	/* Free the file list */
9292 				return_null (API, API->error);	/* Some internal error... */
9293 			}
9294 			API->object[item]->selected = true;
9295 		}
9296 		gmt_free_list (API->GMT, filelist, n_files);	/* Free the file list */
9297 		in_ID = GMT_NOTSET;
9298 	}
9299 	else if (a_grid_or_image (family) && (mode & GMT_DATA_ONLY)) {	/* Case 4: Already registered when we obtained header, find object ID */
9300 		if ((in_ID = gmtapi_is_registered (API, family, geometry, GMT_IN, mode, input, data)) == GMT_NOTSET) {
9301 			if (input) gmt_M_str_free (input);
9302 			return_null (API, GMT_OBJECT_NOT_FOUND);	/* Could not find it */
9303 		}
9304 		if (!full_region (wesn) || (family == GMT_IS_CUBE && wesn && wesn[ZLO] != wesn[ZHI])) {	/* Must update subset selection */
9305 			int item;
9306 			if ((item = gmtlib_validate_id (API, family, in_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET) {
9307 				if (input) gmt_M_str_free (input);
9308 				return_null (API, API->error);
9309 			}
9310 			if (family == GMT_IS_CUBE)
9311 				gmt_M_memcpy (API->object[item]->wesn, wesn, 6, double);
9312 			else
9313 				gmt_M_memcpy (API->object[item]->wesn, wesn, 4, double);
9314 			API->object[item]->region = true;
9315 		}
9316 	}
9317 	else if (input) {	/* Case 1: Load from a single input, given source. Register it first. */
9318 		unsigned int first = 0;
9319 		/* Must handle special case when a list of colors are given instead of a CPT name.  We make a temp CPT from the colors */
9320 		if (family == GMT_IS_PALETTE && !just_get_data) { /* CPTs must be handled differently since the master files live in share/cpt and filename is missing .cpt */
9321 			int c_err = 0;
9322 			char CPT_file[PATH_MAX] = {""}, *file = NULL, *m = NULL, *f = NULL;
9323 			if (input[0] == '@') first = gmt_download_file_if_not_found (API->GMT, input, 0);	/* Deal with downloadable CPTs */
9324 			file = strdup (&input[first]);
9325 			if ((c_err = gmtapi_colors2cpt (API, &file, &mode)) < 0) { /* Maybe converted colors to new CPT */
9326 				gmt_M_str_free (input);
9327 				gmt_M_str_free (file);
9328 				return_null (API, GMT_CPT_READ_ERROR);	/* Failed in the conversion */
9329 			}
9330 			else if (c_err == 0) {	/* Regular cpt (master or local), append .cpt and set path */
9331 				bool is_cpt_master = gmt_is_cpt_master (API->GMT, file);
9332 				char *q = NULL;
9333 
9334 				/* Need to check for CPT filename modifiers */
9335 				if ((f = gmt_strrstr (file, GMT_CPT_EXTENSION)))
9336 					m = gmtlib_last_valid_file_modifier (API, f, GMT_CPTFILE_MODIFIERS);
9337 				else
9338 					m = gmtlib_last_valid_file_modifier (API, file, GMT_CPTFILE_MODIFIERS);
9339 
9340 				if (m) {	/* Got one or more valid CPT file modifiers */
9341 					if ((q = gmtlib_cptfile_unitscale (API, m))) q[0] = '\0';    /* Truncate modifier after processing the unit */
9342 					if (m[0] && (q = strstr (m, "+h"))) q[0] = '\0';    /* Truncate +h modifier (checking for m[0] since the line above could leave it blank) */
9343 				}
9344 				if (is_cpt_master)	/* Master: Append extension and supply path */
9345 					gmt_getsharepath (API->GMT, "cpt", file, GMT_CPT_EXTENSION, CPT_file, R_OK);
9346 				else if (!gmt_getdatapath (API->GMT, file, CPT_file, R_OK)) {	/* Use name.cpt as is but look for it */
9347 					GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Data: File not found: %s\n", file);
9348 					gmt_M_str_free (input);
9349 					return_null (API, GMT_FILE_NOT_FOUND);	/* Failed to find the file anywhere */
9350 				}
9351 				if (m && q) {q[0] = '+'; strncat (CPT_file, q, PATH_MAX-1);}	/* Add back the z modifiers */
9352 			}
9353 			else	/* Got color list, now a temp CPT instead */
9354 				strncpy (CPT_file, file, PATH_MAX-1);
9355 			gmt_M_str_free (file);	/* Free temp CPT name */
9356 			if ((in_ID = GMT_Register_IO (API, family, method, geometry, GMT_IN, wesn, CPT_file)) == GMT_NOTSET) {
9357 				gmt_M_str_free (input);
9358 				return_null (API, API->error);
9359 			}
9360 		}
9361 		else {	/* Not a CPT file but could be remote */
9362 			int k_data;
9363 			char file[PATH_MAX] = {""};
9364 			if (API->remote_info == NULL && !API->GMT->current.io.internet_error && input[0] == '@' && !gmt_M_file_is_memory (input)) {
9365 				/* Maybe using the API without a module call first so server has not been refreshed yet */
9366 				gmt_refresh_server (API);
9367 			}
9368 			gmt_set_unspecified_remote_registration (API, &input);	/* Same, this call otherwise only happens with modules */
9369 			first = gmt_download_file_if_not_found (API->GMT, input, 0);	/* Deal with downloadable GMT data sets first */
9370 			strncpy (file, &input[first], PATH_MAX-1);
9371 			if ((k_data = gmt_remote_no_extension (API, input)) != GMT_NOTSET)	/* A remote @earth_relief_xxm|s grid without extension */
9372 				strcat (file, API->remote_info[k_data].ext);	/* Must supply the .extension */
9373 			if ((in_ID = GMT_Register_IO (API, family|module_input, method, geometry, GMT_IN, wesn, file)) == GMT_NOTSET) {
9374 				gmt_M_str_free (input);
9375 				return_null (API, API->error);
9376 			}
9377 		}
9378 		reg_here = true;
9379 	}
9380 	else if (input == NULL && geometry) {	/* Case 2: Load from stdin.  Register stdin first */
9381 		if ((in_ID = GMT_Register_IO (API, family|module_input, GMT_IS_STREAM, geometry, GMT_IN, wesn, API->GMT->session.std[GMT_IN])) == GMT_NOTSET) {
9382 			gmt_M_str_free (input);
9383 			return_null (API, API->error);	/* Failure to register std??? */
9384 		}
9385 		reg_here = true;
9386 	}
9387 	else {	/* Case 3: input == NULL && geometry == 0, so use all previously registered sources (unless already used). */
9388 		if (!multiple_files_ok (family)) {
9389 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Data: Multiple input resources only allowed for DATASET.");
9390 			return_null (API, GMT_ONLY_ONE_ALLOWED);	/* Virtual source only applies to data and text tables */
9391 		}
9392 		API->shelf = family;	/* Save which one it is so we know in gmtapi_get_data */
9393 		API->module_input = true;	/* Since we are passing NULL as file name we must loop over registered resources */
9394 	}
9395 	if (just_get_data) {
9396 		struct GMTAPI_DATA_OBJECT *S_obj = NULL;
9397 		if ((item = gmtlib_validate_id (API, GMT_NOTSET, in_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET) {
9398 			gmt_M_str_free (input);
9399 			return_null (API, API->error);
9400 		}
9401 		S_obj = API->object[item];	/* Current object */
9402 		/* Try to catch a matrix or vector masquerading as dataset by examining the object's actual family  */
9403 		if (gmtapi_is_passable (S_obj, family)) {	/* True to its word, otherwise we fall through and read the data */
9404 #ifdef DEBUG
9405 			gmtapi_set_object (API, S_obj);
9406 #endif
9407 			if (reset) S_obj->status = 0;	/* Reset  to unread */
9408 			return (gmtapi_pass_object (API, S_obj, family, mode, wesn));
9409 		}
9410 	}
9411 
9412 	/* OK, try to do the importing */
9413 	if (in_ID != GMT_NOTSET) {	/* Make sure we select the item we just registered */
9414 		if ((item = gmtlib_validate_id (API, GMT_NOTSET, in_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET) {
9415 			gmt_M_str_free (input);
9416 			return_null (API, API->error);
9417 		}
9418 		API->object[item]->selected = true;	/* Make sure the item we want is now selected */
9419 	}
9420 	if ((new_obj = gmtapi_get_data (API, in_ID, mode, data)) == NULL) {
9421 		if (reg_here) gmtlib_unregister_io (API, in_ID, GMT_IN);	/* Since reading failed */
9422 		gmt_M_str_free (input);	/* Done with this variable) */
9423 		return_null (API, API->error);
9424 	}
9425 	if (reset) API->object[item]->status = 0;	/* Reset  to unread */
9426 	gmt_M_str_free (input);	/* Done with this variable) */
9427 	API->module_input = false;	/* Reset to normal */
9428 
9429 #ifdef DEBUG
9430 	gmtapi_list_objects (API, "GMT_Read_Data");
9431 #endif
9432 
9433 	return (new_obj);		/* Return pointer to the data container */
9434 }
9435 
9436 #ifdef FORTRAN_API
GMT_Read_Data_(unsigned int * family,unsigned int * method,unsigned int * geometry,unsigned int * mode,double * wesn,char * input,void * data,int len)9437 void * GMT_Read_Data_ (unsigned int *family, unsigned int *method, unsigned int *geometry, unsigned int *mode, double *wesn, char *input, void *data, int len) {
9438 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9439 	return (GMT_Read_Data (GMT_FORTRAN, *family, *method, *geometry, *mode, wesn, input, data));
9440 }
9441 #endif
9442 
9443 /*! . */
GMT_Read_Group(void * V_API,unsigned int family,unsigned int method,unsigned int geometry,unsigned int mode,double wesn[],void * sources,unsigned int * n_items,void * data)9444 void * GMT_Read_Group (void *V_API, unsigned int family, unsigned int method, unsigned int geometry, unsigned int mode, double wesn[], void *sources, unsigned int *n_items, void *data) {
9445 	/* Function to read a group of data files directly into program memory given an array of objects.
9446 	 * data is pointer to an existing array of grid container when we read a grid in two steps, otherwise use NULL.
9447 	 * *n_items = 0: sources is a character string with wildcard-specification for file names.
9448 	 * *n_items > 0: sources is an array of *n_items character strings with filenames.
9449 	 * If n_items == NULL then it means 0 but we do not return back the number of items.
9450 	 * Note: For DATASET you can also use wildcard expressions in GMT_Read_Data but there we combine then into one data|test-set.
9451 	 * Return: Pointer to array of data container, or NULL if there were errors (passed back via API->error).
9452 	 */
9453 	bool free_list = false;
9454 	unsigned int n_files, k;
9455 	char **file = NULL, *pattern = NULL;
9456 	struct GMTAPI_CTRL *API = NULL;
9457 	void **object = NULL;
9458 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
9459 
9460 	API = gmtapi_get_api_ptr (V_API);
9461 	API->error = GMT_NOERROR;
9462 
9463 	if (data && !a_grid_or_image (family)) {
9464 		GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Group: data pointer must be NULL except for GRID and IMAGE\n");
9465 		return_null (API, GMT_PTR_NOT_NULL);
9466 	}
9467 	if (n_items && *n_items > 0) {	/* Gave list of files */
9468 		n_files = *n_items;
9469 		file = (char **)sources;
9470 	}
9471 	else {	/* Gave wildcard expression(s) */
9472 		pattern = (void *)sources;
9473 		if ((n_files = (unsigned int)gmtlib_glob_list (API->GMT, pattern, &file)) == 0) {
9474 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Group: Expansion of \"%s\" gave no results\n", pattern);
9475 			return_null (API, GMT_OBJECT_NOT_FOUND);
9476 		}
9477 		free_list = true;
9478 	}
9479 	/* Reuse data or allocate empty array of containers */
9480 	object = (data == NULL) ? gmtapi_alloc_object_array (API, n_files, family) : data;
9481 	for (k = 0; k < n_files; k++) {
9482 		if ((object[k] = GMT_Read_Data (API, family, method, geometry, mode, wesn, file[k], object[k])) == NULL)
9483 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Read_Group: Reading of %s failed, returning NULL\n", file[k]);
9484 	}
9485 	if (free_list) {	/* Free the file list we created above and optionally return back how many we found */
9486 		gmt_free_list (API->GMT, file, n_files);
9487 		if (n_items) *n_items = n_files;	/* Return how many items we allocated, if n_items is not NULL */
9488 	}
9489 	return (object);	/* Return pointer to the data containers */
9490 }
9491 
9492 #ifdef FORTRAN_API
GMT_Read_Group_(unsigned int * family,unsigned int * method,unsigned int * geometry,unsigned int * mode,double * wesn,void * sources,unsigned int * n_items,void * data)9493 void * GMT_Read_Group_ (unsigned int *family, unsigned int *method, unsigned int *geometry, unsigned int *mode, double *wesn, void *sources, unsigned int *n_items, void *data) {
9494 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9495 	return (GMT_Read_Group (GMT_FORTRAN, *family, *method, *geometry, *mode, wesn, sources, n_items, data));
9496 }
9497 #endif
9498 
9499 /*! . */
GMT_Duplicate_Data(void * V_API,unsigned int family,unsigned int mode,void * data)9500 void * GMT_Duplicate_Data (void *V_API, unsigned int family, unsigned int mode, void *data) {
9501 	/* Create an duplicate container of the requested kind and optionally allocate space
9502 	 * or duplicate content.
9503 	 * The known families are GMT_IS_{DATASET,GRID,PALETTE,IMAGE,POSTSCRIPT}.
9504  	 * Pass mode as one of GMT_DUPLICATE_{NONE|ALLOC|DATA} to just duplicate the
9505 	 * container and header structures, allocate space of same dimensions as original,
9506 	 * or allocate space and duplicate contents.  For GMT_IS_{DATA|TEXT}SET you may add
9507 	 * the modifiers GMT_ALLOC_VERTICAL or GMT_ALLOC_HORIZONTAL. Also, for GMT_IS_DATASET
9508 	 * you can manipulate the incoming data->dim to overwrite the number of items allocated.
9509 	 * [By default we follow the dimensions of the incoming data].
9510 	 *
9511 	 * Return: Pointer to new resource, or NULL if an error (set via API->error).
9512 	 */
9513 
9514 	int object_ID, item;
9515 	unsigned int geometry = 0U, pmode = 0U;
9516 	void *new_obj = NULL;
9517 	struct GMTAPI_CTRL *API = NULL;
9518 	struct GMT_CTRL *GMT = NULL;
9519 
9520 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
9521 	if (data == NULL)  return_null (V_API, GMT_PTR_IS_NULL);
9522 	API = gmtapi_get_api_ptr (V_API);
9523 	API->error = GMT_NOERROR;
9524 	GMT = API->GMT;
9525 
9526 	switch (family) {	/* dataset, cpt, text, grid , image, vector, matrix */
9527 		case GMT_IS_GRID:	/* GMT grid, allocate header and possibly data array */
9528 			new_obj = gmt_duplicate_grid (GMT, data, mode);
9529 			geometry = GMT_IS_SURFACE;
9530 			break;
9531 		case GMT_IS_IMAGE:	/* GMT image, allocate header but not data array */
9532 			new_obj = gmtlib_duplicate_image (GMT, data, mode);
9533 			geometry = GMT_IS_SURFACE;
9534 			break;
9535 		case GMT_IS_DATASET:	/* GMT dataset, allocate the requested tables, segments, rows, and columns */
9536 			pmode = (mode & (GMT_ALLOC_VERTICAL + GMT_ALLOC_HORIZONTAL));	/* Just isolate any special allocation modes */
9537 			mode -= pmode;	/* Remove the hor/ver flags from the rest of mode */
9538 			if (mode == GMT_DUPLICATE_DATA)
9539 				new_obj = gmt_duplicate_dataset (GMT, data, pmode, &geometry);
9540 			else if (mode == GMT_DUPLICATE_ALLOC) {	/* Allocate data set of same size, possibly modulated by Din->dim (of > 0) and pmode */
9541 				struct GMT_DATASET *Din = gmtapi_get_dataset_data (data);	/* We know this is a GMT_DATASET pointer */
9542 				struct GMT_DATASET_HIDDEN *DH = gmt_get_DD_hidden (Din);
9543 				new_obj = gmt_alloc_dataset (GMT, data, DH->dim[GMT_ROW], DH->dim[GMT_COL], pmode);
9544 				geometry = Din->geometry;
9545 				gmt_M_memset (DH->dim, 4U, uint64_t);	/* Reset alloc dimensions */
9546 			}
9547 			else {	/* Just want a dataset structure */
9548 				struct GMT_DATASET *Din = gmtapi_get_dataset_data (data);	/* We know this is a GMT_DATASET pointer */
9549 				new_obj = gmt_get_dataset (GMT);
9550 				geometry = Din->geometry;
9551 			}
9552 			break;
9553 		case GMT_IS_PALETTE:	/* GMT CPT, allocate one with space for dim[0] color entries */
9554 			new_obj = gmtlib_duplicate_palette (GMT, data, 0);
9555 			geometry = GMT_IS_NONE;
9556 			break;
9557 		case GMT_IS_POSTSCRIPT:	/* GMT PS, allocate one with space for the original */
9558 			new_obj = gmtlib_duplicate_ps (GMT, data, 0);
9559 			geometry = GMT_IS_NONE;
9560 			break;
9561 		case GMT_IS_CUBE:	/* GMT cube, allocate header and possibly data array */
9562 			new_obj = gmtlib_duplicate_cube (GMT, data, mode);
9563 			geometry = GMT_IS_VOLUME;
9564 			break;
9565 		case GMT_IS_MATRIX:	/* GMT MATRIX */
9566 			new_obj = gmtlib_duplicate_matrix (GMT, data, mode);
9567 			geometry = GMT_IS_NONE;
9568 			break;
9569 		case GMT_IS_VECTOR:	/* GMT VECTOR */
9570 			new_obj = gmtlib_duplicate_vector (GMT, data, mode);
9571 			geometry = GMT_IS_NONE;
9572 			break;
9573 		default:
9574 			API->error = GMT_NOT_A_VALID_FAMILY;
9575 			break;
9576 	}
9577 	if (API->error) return_null (API, API->error);
9578 
9579 	/* Now register this dataset so it can be deleted by GMT_Destroy_Data */
9580 	if ((object_ID = GMT_Register_IO (API, family, GMT_IS_REFERENCE, geometry, GMT_IN, NULL, new_obj)) == GMT_NOTSET)
9581 		return_null (API, API->error);	/* Failure to register */
9582 	if ((item = gmtlib_validate_id (API, family, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
9583 		return_null (API, API->error);
9584 	API->object[item]->geometry = geometry;	/* Ensure same geometry */
9585 	API->object[item]->resource = new_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
9586 
9587 	GMT_Report (API, GMT_MSG_DEBUG, "Successfully duplicated a %s\n", GMT_family[family]);
9588 #ifdef DEBUG
9589 	gmtapi_list_objects (API, "GMT_Duplicate_Data");
9590 #endif
9591 
9592 	return (new_obj);
9593 }
9594 
9595 #ifdef FORTRAN_API
GMT_Duplicate_Data_(unsigned int * family,unsigned int * mode,void * data)9596 void * GMT_Duplicate_Data_ (unsigned int *family,  unsigned int *mode, void *data) {
9597 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9598 	return (GMT_Duplicate_Data (GMT_FORTRAN, *family, *mode, data));
9599 }
9600 #endif
9601 
9602 /*! . */
GMT_Write_Data(void * V_API,unsigned int family,unsigned int method,unsigned int geometry,unsigned int mode,double wesn[],const char * outfile,void * data)9603 int GMT_Write_Data (void *V_API, unsigned int family, unsigned int method, unsigned int geometry, unsigned int mode, double wesn[], const char *outfile, void *data) {
9604 	/* Function to write data directly from program memory as a set (not record-by-record).
9605 	 * We can combine the <register resource - export resource > sequence in
9606 	 * one combined function.  See GMT_Register_IO for details on arguments.
9607 	 * Here, *data is the pointer to the data object to save (CPT, dataset, Grid)
9608 	 * Case 1: outfile != NULL: Register this as the destination and export data.
9609 	 * Case 2: outfile == NULL: Register stdout as the destination and export data.
9610 	 * Case 3: geometry == 0: Use a previously registered single destination.
9611 	 * While only one output destination is allowed, for DATASETS one can
9612 	 * have the tables and even segments be written to individual files (see the mode
9613 	 * description in the documentation for how to enable this feature.)
9614 	 * Return: false if all is well, true if there was an error (and set API->error).
9615 	 */
9616 	unsigned int n_reg;
9617 	int out_ID;
9618 	char *output = NULL;
9619 	struct GMTAPI_CTRL *API = NULL;
9620 
9621 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
9622 	if (data == NULL) return_error (V_API, GMT_PTR_IS_NULL);
9623 	API = gmtapi_get_api_ptr (V_API);
9624 	API->error = GMT_NOERROR;
9625 	if (outfile) output = strdup (outfile);
9626 
9627 	if (output) {	/* Case 1: Save to a single specified destination (file or memory).  Register it first. */
9628 		if ((out_ID = gmtapi_memory_registered (API, family, GMT_OUT, output)) != GMT_NOTSET) {
9629 			/* Output is a memory resource, passed via a @GMTAPI@-###### file name, and ###### is the out_ID.
9630 			   In this case we must make some further checks.  We need to find the API object that holds data.
9631 			   We do this below and get in_ID (the id of the data to write), while out_ID is the id of where
9632 			   things go (the output "memory").  Having the in_ID we get the array index in_item that matches
9633 			   this ID and of the correct family.  We set direction to GMT_NOTSET since otherwise we may be
9634 			   denied a hit as we don't really know what the direction is for in_ID.  Once in_item has been
9635 			   secured we transfer ownership of this data from the in_ID object to the out_ID object.  That
9636 			   way we avoid accidental premature freeing of the data object via the in_ID object since it now
9637 			   will live on via out_ID and outlive the current module.
9638 			    */
9639 			int in_ID = GMT_NOTSET,  in_item = GMT_NOTSET;
9640 			in_ID = gmtapi_get_object (API, family, data);	/* Get the object ID of the input source */
9641 			if (in_ID != GMT_NOTSET) in_item = gmtlib_validate_id (API, family, in_ID, GMT_NOTSET, GMT_NOTSET);	/* Get the item in the API array; pass dir = GMT_NOTSET to bypass status check */
9642 			if (in_item != GMT_NOTSET) {
9643 				int out_item = gmtlib_validate_id (API, GMT_NOTSET, out_ID, GMT_OUT, GMT_NOTSET);	/* Get the item in the API array; pass family = GMT_NOTSET to bypass status check */
9644 				GMT_Report (API, GMT_MSG_DEBUG, "GMT_Write_Data: Writing %s to memory object %d from object %d which transfers ownership\n", GMT_family[family], out_ID, in_ID);
9645 				if (API->object[out_item]->method == GMT_IS_REFERENCE) API->object[in_item]->no_longer_owner = true;	/* Since we have passed the content onto an output object */
9646 				if (!API->object[out_item]->filename) API->object[out_item]->filename = strdup (output);
9647 			}
9648 		}	/* else it is a regular file and we just register it and get the new out_ID needed below */
9649 		else if ((out_ID = GMT_Register_IO (API, family, method, geometry, GMT_OUT, wesn, output)) == GMT_NOTSET) {
9650 			gmt_M_str_free (output);	/* Done with this variable */
9651 			return_error (API, API->error);
9652 		}
9653 	}
9654 	else if (output == NULL && geometry) {	/* Case 2: Save to stdout.  Register stdout first. */
9655 		if (family == GMT_IS_GRID) return_error (API, GMT_STREAM_NOT_ALLOWED);	/* Cannot write grids to stream */
9656 		if ((out_ID = GMT_Register_IO (API, family, GMT_IS_STREAM, geometry, GMT_OUT, wesn, API->GMT->session.std[GMT_OUT])) == GMT_NOTSET) return_error (API, API->error);	/* Failure to register std??? */
9657 	}
9658 	else {	/* Case 3: output == NULL && geometry == 0, so use the previously registered destination */
9659 		if ((n_reg = gmtlib_count_objects (API, family, geometry, GMT_OUT, &out_ID)) != 1) {
9660 			gmt_M_str_free (output);	/* Done with this variable */
9661 			return_error (API, GMT_NO_OUTPUT);	/* There is no registered output */
9662 		}
9663 	}
9664 	gmt_M_str_free (output);	/* Done with this variable */
9665 	/* With out_ID in hand we can now put the data where it should go */
9666 	if (gmtapi_put_data (API, out_ID, mode, data) != GMT_NOERROR)
9667 		return_error (API, API->error);
9668 
9669 #ifdef DEBUG
9670 	gmtapi_list_objects (API, "GMT_Write_Data");
9671 #endif
9672 	return (GMT_NOERROR);	/* No error encountered */
9673 }
9674 
9675 #ifdef FORTRAN_API
GMT_Write_Data_(unsigned int * family,unsigned int * method,unsigned int * geometry,unsigned int * mode,double * wesn,char * output,void * data,int len)9676 int GMT_Write_Data_ (unsigned int *family, unsigned int *method, unsigned int *geometry, unsigned int *mode, double *wesn, char *output, void *data, int len) {
9677 	/* Fortran version: We pass the global GMT_FORTRAN structure */
9678 	return (GMT_Write_Data (GMT_FORTRAN, *family, *method, *geometry, *mode, wesn, output, data));
9679 }
9680 #endif
9681 
gmtapi_wind_to_next_datarecord(int64_t * count,struct GMT_DATASET * D,unsigned int mode)9682 static inline int gmtapi_wind_to_next_datarecord (int64_t *count, struct GMT_DATASET *D, unsigned int mode) {
9683 	/* Increment row, seg, tbl to next record and return current record status */
9684 	if (count[GMT_SEG] == GMT_NOTSET) {	/* Special flag to processes table header(s) */
9685 		if (count[GMTAPI_HDR_POS] < D->table[count[GMT_TBL]]->n_headers) {	/* Must first handle table headers */
9686 			count[GMTAPI_HDR_POS]++;	/* Increment counter for each one we return until done */
9687 			return GMT_IO_TABLE_HEADER;
9688 		}
9689 		/* Must be out of table headers - time for the segment header */
9690 		count[GMT_SEG] = count[GMT_ROW] = 0;
9691 		return GMT_IO_SEGMENT_HEADER;
9692 	}
9693 	if (count[GMT_ROW] == (int64_t)D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->n_rows) {	/* Previous record was last in segment, go to next */
9694 		count[GMT_SEG]++;	/* Next segment number */
9695 		count[GMT_ROW] = 0;
9696 		if (count[GMT_SEG] == (int64_t)D->table[count[GMT_TBL]]->n_segments) {		/* Also the end of a table ("file") */
9697 			count[GMT_TBL]++;	/* Next table number */
9698 			count[GMT_SEG] = GMT_NOTSET;	/* Reset to start at first segment in this table */
9699 			count[GMTAPI_HDR_POS] = 0;	/* Ready to process headers from next table */
9700 			if (count[GMT_TBL] == (int64_t)D->n_tables)	/* End of entire data set */
9701 				return GMT_IO_EOF;
9702 			/* Just end of a file */
9703 			if (mode & GMT_READ_FILEBREAK)	/* Return empty handed to indicate a break between files */
9704 				return GMT_IO_NEXT_FILE;
9705 		}
9706 		return GMT_IO_SEGMENT_HEADER;
9707 	}
9708 	/* No drama, here we have a data record just go to next row */
9709 	return GMT_IO_DATA_RECORD;
9710 }
9711 
9712 /*! . */
GMT_Set_Geometry(void * V_API,unsigned int direction,unsigned int geometry)9713 int GMT_Set_Geometry (void *V_API, unsigned int direction, unsigned int geometry) {
9714 	/* Sets the geometry of direction resource for record-by-record i/o.
9715 	 * This currently only applies to external interfaces receiving data via rec-by-rec writing.
9716 	 */
9717 	unsigned int method;
9718 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
9719 	struct GMTAPI_CTRL *API = NULL;
9720 
9721 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
9722 	API = gmtapi_get_api_ptr (V_API);
9723 	if (!API->io_enabled[GMT_OUT]) return_error (API, GMT_ACCESS_NOT_ENABLED);
9724 	API->error = GMT_NOERROR;
9725 
9726 	S_obj = API->object[API->current_item[direction]];	/* Shorthand for the data source we are working on */
9727 	if (S_obj == NULL) return_error (API, GMT_OBJECT_NOT_FOUND);	/* No such object */
9728 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use */
9729 	switch (method) {	/* File, array, stream etc ? */
9730 		case GMT_IS_DUPLICATE:
9731 		case GMT_IS_REFERENCE:
9732 			if (S_obj->family == GMT_IS_DATASET) {
9733 				struct GMT_DATASET *D_obj = gmtapi_get_dataset_data (S_obj->resource);
9734 				if (!D_obj)	/* Not allocated yet? */
9735 					GMT_Report (API, GMT_MSG_DEBUG, "GMTAPI: GMT_Set_Geometry called but no object available\n");
9736 				else
9737 					D_obj->geometry = geometry;
9738 			}
9739 			break;
9740 		default:	/* For all others there is no geometry requirement, so quietly skip */
9741 			break;
9742 	}
9743 	return GMT_NOERROR;
9744 }
9745 
9746 #ifdef FORTRAN_API
GMT_Set_Geometry_(unsigned int * direction,unsigned int * geometry)9747 int GMT_Set_Geometry_ (unsigned int *direction, unsigned int *geometry) {	/* Fortran version: We pass the global GMT_FORTRAN structure */
9748 	return (GMT_Set_Geometry (GMT_FORTRAN, *direction, *geometry));
9749 }
9750 #endif
9751 
gmtapi_get_record_fp_sub(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields,struct GMTAPI_DATA_OBJECT ** S_obj)9752 GMT_LOCAL void * gmtapi_get_record_fp_sub (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields, struct GMTAPI_DATA_OBJECT **S_obj) {
9753 	/* Gets next data record from current open stream */
9754 	int status;
9755 	struct GMTAPI_DATA_OBJECT *S = API->current_get_obj;
9756 	struct GMT_CTRL *GMT = API->GMT;
9757 	void *record = S->import (GMT, S->fp, &(S->n_expected_fields), &status);	/* Get that next record */
9758 	*n_fields = status;	/* Number of fields read */
9759 	S->n_columns = (uint64_t)status;	/* Number of fields read */
9760 
9761 	if (GMT->current.io.status & GMT_IO_EOF) {	/* Hit end-of-file in current file (but there may be many files in queue) */
9762 		S->status = GMT_IS_USED;	/* Mark this file object as read */
9763 		if (S->close_file) {	/* Close if it was a file that we opened earlier */
9764 			gmt_fclose (GMT, S->fp);
9765 			S->close_file = false;
9766 		}
9767 		/* Move on to next data source, if any */
9768 		if (gmtapi_next_data_object (API, S->family, GMT_IN) == EOF)	/* That was the last source, we are done */
9769 			*n_fields = EOF;				/* EOF is ONLY returned when we reach the end of the LAST data file */
9770 		else if (mode & GMT_READ_FILEBREAK) {			/* Return empty handed to indicate a break between files */
9771 			*n_fields = GMT_IO_NEXT_FILE;			/* We flag this situation with a special return value */
9772 			GMT->current.io.status = GMT_IO_NEXT_FILE;	/* And set the status accordingly */
9773 		}
9774 		else {	/* Get ready to read the next data file */
9775 			S = API->current_get_obj = API->object[API->current_item[GMT_IN]];	/* Shorthand for the next data source to work on */
9776 			API->get_next_record = true;			/* Since we haven't read the next record yet */
9777 			gmtapi_get_record_init (API);			/* Perform init steps on the new resource */
9778 		}
9779 		GMT->current.io.tbl_no++;				/* Update number of tables we have processed */
9780 	}
9781 	else
9782 		S->status = GMT_IS_USING;				/* Mark current object as being read */
9783 	*S_obj = S;
9784 	return record;
9785 }
9786 
api_get_record_fp(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields)9787 struct GMT_RECORD *api_get_record_fp (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields) {
9788 	/* Gets other data record from current open stream */
9789 	struct GMTAPI_DATA_OBJECT *S;
9790 	return (gmtapi_get_record_fp_sub (API, mode, n_fields, &S));
9791 }
9792 
api_get_record_fp_first(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields)9793 struct GMT_RECORD *api_get_record_fp_first (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields) {
9794 	/* Gets first data record from current open stream */
9795 	struct GMTAPI_DATA_OBJECT *S = NULL;
9796 	struct GMT_CTRL *GMT = API->GMT;
9797 	void *record = gmtapi_get_record_fp_sub (API, mode, n_fields, &S);
9798 
9799 	if (gmt_M_rec_is_data (GMT) && S->n_expected_fields != GMT_MAX_COLUMNS) {	/* Set the actual column count */
9800 		GMT->common.b.ncol[GMT_IN] = S->n_expected_fields;
9801 		API->api_get_record = api_get_record_fp;	/* From now on we can read just the record */
9802 	}
9803 	return record;
9804 }
9805 
api_get_record_matrix(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields)9806 struct GMT_RECORD *api_get_record_matrix (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields) {
9807 	/* Gets next data record from current matrix */
9808 	struct GMTAPI_DATA_OBJECT *S = API->current_get_obj;
9809 	struct GMT_CTRL *GMT = API->GMT;
9810 	struct GMT_RECORD *record;
9811 
9812 	if (S->rec >= S->n_rows) {	/* Our only way of knowing we are done is to quit when we reach the number of rows that was registered */
9813 		S->status = (API->allow_reuse) ? GMT_IS_UNUSED : GMT_IS_USED;	/* Mark as finished reading this guy unless we may reuse */
9814 		if (gmtapi_next_data_object (API, S->family, GMT_IN) == EOF) {	/* That was the last source, return */
9815 			*n_fields = EOF;				/* EOF is ONLY returned when we reach the end of the LAST data file */
9816 			GMT->current.io.status = GMT_IO_EOF;
9817 		}
9818 		else if (mode & GMT_READ_FILEBREAK) {			/* Return empty handed to indicate a break between files */
9819 			*n_fields = GMT_IO_NEXT_FILE;			/* We flag this situation with a special return value */
9820 			GMT->current.io.status = GMT_IO_NEXT_FILE;
9821 		}
9822 		else {	/* Get ready to read the next data file */
9823 			S = API->current_get_obj = API->object[API->current_item[GMT_IN]];	/* Shorthand for the next data source to work on */
9824 			API->get_next_record = true;	/* Since we haven't read the next record yet */
9825 		}
9826 		API->current_get_M = gmtapi_get_matrix_data (S->resource);
9827 		API->current_get_n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : S->n_columns;
9828 		if ((API->current_get_M_index = gmtapi_get_2d_to_index (API, API->current_get_M->shape, GMT_GRID_IS_REAL)) == NULL)
9829 			return NULL;
9830 		if ((API->current_get_M_val = gmtapi_select_get_function (API, API->current_get_M->type)) == NULL)
9831 			return NULL;
9832 		record = NULL;
9833 	}
9834 	else {	/* Read from the current resource */
9835 		struct GMT_MATRIX *M = API->current_get_M;
9836 		unsigned int col, n_use, col_pos_out, col_pos_in;
9837 		uint64_t ij;
9838 		int status;
9839 		S->status = GMT_IS_USING;				/* Mark as being read */
9840 		n_use = gmtapi_n_cols_needed_for_gaps (GMT, S->n_columns);
9841 		gmtapi_update_prev_rec (GMT, n_use);
9842 
9843 		for (col = 0; col < API->current_get_n_columns; col++) {
9844 			col_pos_out = gmtlib_pick_in_col_number (GMT, (unsigned int)col, &col_pos_in);
9845 			ij = API->current_get_M_index (S->rec, col_pos_in, M->dim);
9846 			API->current_get_M_val (&(M->data), ij, &(GMT->current.io.curr_rec[col_pos_out]));
9847 			GMT->current.io.curr_rec[col_pos_out] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col], GMT->current.io.curr_rec[col_pos_out]);
9848 		}
9849 		S->rec++;
9850 		if ((status = gmtapi_bin_input_memory (GMT, S->n_columns, n_use)) < 0) {	/* Process the data record */
9851 			if (status == GMTAPI_GOT_SEGGAP)	 /* Since we inserted a segment header we must revisit this record as first in next segment */
9852 				S->rec--, API->current_rec[GMT_IN]--;
9853 			record = NULL;
9854 		}
9855 		else {	/* Valid data record */
9856 			if (M->text)	/* Also have text as part of record */
9857 				strncpy (GMT->current.io.curr_trailing_text, M->text[S->rec-1], GMT_BUFSIZ-1);
9858 			record = &GMT->current.io.record;
9859 			*n_fields = (int)API->current_get_n_columns;
9860 		}
9861 	}
9862 	return (record);
9863 }
9864 
api_get_record_vector(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields)9865 struct GMT_RECORD *api_get_record_vector (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields) {
9866 	/* Gets next data record from current vector */
9867 	struct GMTAPI_DATA_OBJECT *S = API->current_get_obj;
9868 	struct GMT_CTRL *GMT = API->GMT;
9869 	struct GMT_RECORD *record;
9870 	uint64_t col;
9871 
9872 	if (S->rec == S->n_rows) {	/* Our only way of knowing we are done is to quit when we reach the number of rows that was registered */
9873 		S->status = (API->allow_reuse) ? GMT_IS_UNUSED : GMT_IS_USED;	/* Mark as finished reading this guy unless we may reuse */
9874 		if (gmtapi_next_data_object (API, S->family, GMT_IN) == EOF) {	/* That was the last source, return */
9875 			*n_fields = EOF;				/* EOF is ONLY returned when we reach the end of the LAST data file */
9876 			GMT->current.io.status = GMT_IO_EOF;
9877 		}
9878 		else if (mode & GMT_READ_FILEBREAK) {			/* Return empty handed to indicate a break between files */
9879 			*n_fields = GMT_IO_NEXT_FILE;			/* We flag this situation with a special return value */
9880 			GMT->current.io.status = GMT_IO_NEXT_FILE;
9881 		}
9882 		else {	/* Get ready to read the next data file */
9883 			S = API->current_get_obj = API->object[API->current_item[GMT_IN]];	/* Shorthand for the next data source to work on */
9884 			API->get_next_record = true;	/* Since we haven't read the next record yet */
9885 		}
9886 		API->current_get_V = gmtapi_get_vector_data (S->resource);
9887 		API->current_get_n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : S->n_columns;
9888 		API->current_get_V_val = gmt_M_memory (GMT, API->current_get_V_val, API->current_get_V->n_columns, GMT_getfunction);	/* Array of functions */
9889 		for (col = 0; col < API->current_get_V->n_columns; col++)	/* We know the number of columns from registration */
9890 			API->current_get_V_val[col] = gmtapi_select_get_function (API, API->current_get_V->type[col]);
9891 		record = NULL;
9892 	}
9893 	else {	/* Read from this resource */
9894 		struct GMT_VECTOR *V = API->current_get_V;
9895 		unsigned int n_use, col_pos_out, col_pos_in;
9896 		int status;
9897 		S->status = GMT_IS_USING;				/* Mark as being read */
9898 		n_use = gmtapi_n_cols_needed_for_gaps (GMT, S->n_columns);
9899 		gmtapi_update_prev_rec (GMT, n_use);
9900 
9901 		for (col = 0; col < API->current_get_n_columns; col++) {
9902 			col_pos_out = gmtlib_pick_in_col_number (GMT, (unsigned int)col, &col_pos_in);
9903 			API->current_get_V_val[col_pos_in] (&(V->data[col_pos_in]), S->rec, &(GMT->current.io.curr_rec[col_pos_out]));
9904 			GMT->current.io.curr_rec[col_pos_out] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col], GMT->current.io.curr_rec[col_pos_out]);
9905 		}
9906 
9907 		S->rec++;
9908 		if ((status = gmtapi_bin_input_memory (GMT, S->n_columns, n_use)) < 0) {	/* Process the data record */
9909 			if (status == GMTAPI_GOT_SEGGAP)	 /* Since we inserted a segment header we must revisit this record as first in next segment */
9910 				S->rec--, API->current_rec[GMT_IN]--;
9911 			record = NULL;
9912 		}
9913 		else {	/* Valid data record */
9914 			if (V->text)	/* Also have text as part of record */
9915 				strncpy (GMT->current.io.curr_trailing_text, V->text[S->rec-1], GMT_BUFSIZ-1);
9916 			record = &GMT->current.io.record;
9917 			*n_fields = (int)API->current_get_n_columns;
9918 		}
9919 	}
9920 	return record;
9921 }
9922 
gmtapi_get_record_dataset(struct GMTAPI_CTRL * API,unsigned int mode,int * n_fields)9923 GMT_LOCAL struct GMT_RECORD * gmtapi_get_record_dataset (struct GMTAPI_CTRL *API, unsigned int mode, int *n_fields) {
9924 	/* Gets next data record from current dataset */
9925 	struct GMTAPI_DATA_OBJECT *S = API->current_get_obj;
9926 	struct GMT_CTRL *GMT = API->GMT;
9927 	struct GMT_DATASET *D = API->current_get_D_set;	/* Get the current dataset */
9928 	struct GMT_RECORD *record = NULL;
9929 	int64_t *count = GMT->current.io.curr_pos[GMT_IN];	/* Shorthand used below */
9930 	unsigned int col, col_pos_in, col_pos_out;
9931 	int status = gmtapi_wind_to_next_datarecord (count, D, mode);	/* Get current record status and wind counters if needed */
9932 
9933 	switch (status) {
9934 		case GMT_IO_DATA_RECORD:	/* Got a data record */
9935 			S->status = GMT_IS_USING;		/* Mark this resource as currently being read */
9936 			for (col = 0; col < API->current_get_n_columns; col++) {	/* Copy from row to curr_rec */
9937 				col_pos_out = gmtlib_pick_in_col_number (GMT, (unsigned int)col, &col_pos_in);
9938 				GMT->current.io.curr_rec[col_pos_out] = D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->data[col_pos_in][count[GMT_ROW]];
9939 			}
9940 			if (D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->text && D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->text[count[GMT_ROW]])
9941 				strncpy (GMT->current.io.curr_trailing_text, D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->text[count[GMT_ROW]], GMT_BUFSIZ-1);
9942 			if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && API->current_get_n_columns >= 2) {
9943 				gmt_M_double_swap (GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]);	/* Got lat/lon instead of lon/lat */
9944 			}
9945 			record = &GMT->current.io.record;
9946 			GMT->common.b.ncol[GMT_IN] = API->current_get_n_columns;
9947 			*n_fields = (int)API->current_get_n_columns;
9948 			count[GMT_ROW]++;	/* Advance to next row for next time GMT_Get_Record is called */
9949 			break;
9950 		case GMT_IO_SEGMENT_HEADER:	/* Segment break */
9951 			if (D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->header)
9952 				strncpy (GMT->current.io.segment_header, D->table[count[GMT_TBL]]->segment[count[GMT_SEG]]->header, GMT_BUFSIZ-1);
9953 			else
9954 				GMT->current.io.segment_header[0] = '\0';	/* No header for this segment */
9955 			record = NULL;	/* No data record to return */
9956 			*n_fields = 0;
9957 			break;
9958 		case GMT_IO_TABLE_HEADER:	/* Table header(s) */
9959 			strncpy (GMT->current.io.curr_text, D->table[count[GMT_TBL]]->header[count[GMTAPI_HDR_POS]-1], GMT_BUFSIZ-1);
9960 			record = NULL;	/* No data record to return */
9961 			*n_fields = 0;
9962 			break;
9963 		case GMT_IO_NEXT_FILE:	/* End of a table but more tables to come */
9964 			record = NULL;	/* No data record to return */
9965 			*n_fields = GMT_IO_NEXT_FILE;
9966 			break;
9967 		case GMT_IO_EOF:	/* End of entire data set */
9968 			S->status = (API->allow_reuse) ? GMT_IS_UNUSED : GMT_IS_USED;	/* Mark as finished reading this guy unless we may reuse */
9969 			record = NULL;	/* No more to return anyway */
9970 			*n_fields = EOF;
9971 			break;
9972 	}
9973 	GMT->current.io.status = status;
9974 	return record;
9975 }
9976 
9977 /*! . */
gmtapi_get_record_init(struct GMTAPI_CTRL * API)9978 GMT_LOCAL void gmtapi_get_record_init (struct GMTAPI_CTRL *API) {
9979 	/* Initializes reading from current source. We must redo this after
9980 	 * selecting a new source since there is no guarantee that the sources
9981 	 * are all of the same kind. */
9982 
9983 	unsigned int method;
9984 	uint64_t col;
9985 	struct GMTAPI_DATA_OBJECT *S;
9986 	struct GMT_CTRL *GMT;
9987 
9988 	if (!API->io_enabled[GMT_IN]) {
9989 		API->error = GMT_ACCESS_NOT_ENABLED;
9990 		return;
9991 	}
9992 	API->error = GMT_NOERROR;
9993 	API->is_file = false;
9994 	S = API->current_get_obj;	/* Shorthand for the current data source we are working on */
9995 	GMT = API->GMT;			/* Shorthand for GMT access */
9996 	/* Reset to default association for current record's data and text pointers */
9997 	GMT->current.io.record.text = GMT->current.io.curr_trailing_text;
9998 	GMT->current.io.record.data = GMT->current.io.curr_rec;
9999 
10000 	method = gmtapi_set_method (S);	/* Get the actual method to use */
10001 	GMT->current.io.status = 0;	/* Initialize status to OK */
10002 	S->status = GMT_IS_USING;				/* Mark as being read */
10003 	switch (method) {
10004 		case GMT_IS_FILE:	/* File, stream, and fd are all the same for us, regardless of data or text input */
10005 	 	case GMT_IS_STREAM:
10006 	 	case GMT_IS_FDESC:
10007 			API->api_get_record = api_get_record_fp_first;
10008 			GMT->current.io.first_rec = true;
10009 			gmtlib_reset_input (GMT);	/* Go back to being agnostic about number of columns, etc. */
10010 			API->is_file = true;
10011 			break;
10012 
10013 		case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* Here we copy/read from a user memory location which is a matrix */
10014 		case GMT_IS_REFERENCE|GMT_VIA_MATRIX:
10015 			API->current_get_M = gmtapi_get_matrix_data (S->resource);
10016 			API->current_get_n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : S->n_columns;
10017 			if ((API->current_get_M_index = gmtapi_get_2d_to_index (API, API->current_get_M->shape, GMT_GRID_IS_REAL)) == NULL) {
10018 				GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_get_record_init called gmtapi_get_2d_to_index with wring shape\n");
10019 			}
10020 			API->current_get_M_val = gmtapi_select_get_function (API, API->current_get_M->type);
10021 			if (API->current_get_M->text == NULL) GMT->current.io.record.text = NULL;
10022 			API->api_get_record = api_get_record_matrix;
10023 			break;
10024 
10025 		 case GMT_IS_DUPLICATE|GMT_VIA_VECTOR:	/* Here we copy from a user memory location that points to an array of column vectors */
10026 		 case GMT_IS_REFERENCE|GMT_VIA_VECTOR:
10027 			API->current_get_n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : S->n_columns;
10028 			API->current_get_V = gmtapi_get_vector_data (S->resource);
10029 			API->current_get_V_val = gmt_M_memory (GMT, NULL, API->current_get_V->n_columns, GMT_getfunction);	/* Array of functions */
10030 			for (col = 0; col < API->current_get_V->n_columns; col++)	/* We know the number of columns from registration */
10031 				API->current_get_V_val[col] = gmtapi_select_get_function (API, API->current_get_V->type[col]);
10032 			API->api_get_record = api_get_record_vector;
10033 			if (API->current_get_V->text == NULL) GMT->current.io.record.text = NULL;
10034 			break;
10035 
10036 		case GMT_IS_DUPLICATE:	/* Only for datasets */
10037 		case GMT_IS_REFERENCE:	/* Only for datasets */
10038 			API->current_get_D_set = gmtapi_get_dataset_data (S->resource);	/* Get the right dataset */
10039 			API->current_get_n_columns = (GMT->common.i.select) ? GMT->common.i.n_cols : API->current_get_D_set->n_columns;
10040 			API->api_get_record = gmtapi_get_record_dataset;
10041 			if (!(API->current_get_D_set->type & GMT_READ_TEXT)) GMT->current.io.record.text = NULL;
10042 			break;
10043 		default:
10044 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_get_record_init called with illegal method\n");
10045 			break;
10046 	}
10047 }
10048 
GMT_Get_Record(void * V_API,unsigned int mode,int * retval)10049 void * GMT_Get_Record (void *V_API, unsigned int mode, int *retval) {
10050 	/* Retrieves the next data record from the virtual input source and
10051 	 * returns the number of columns found via *retval (unless retval == NULL).
10052 	 * If current record is a segment header then we return 0.
10053 	 * If we reach EOF then we return EOF.
10054 	 * mode is either GMT_READ_DATA (data columns), GMT_READ_TEXT (text string) or
10055 	 *	GMT_READ_MIXED (expect data but tolerate read errors).
10056 	 * Also, if (mode | GMT_READ_FILEBREAK) is true then we will return empty-handed
10057 	 *	when we get to the end of a file except the final file (which is EOF).
10058 	 *	The calling module can then take actions appropriate between data files.
10059 	 * The double array OR text string is returned via the pointer *record.
10060 	 * If not a data record we return NULL, and pass status via API->GMT->current.io.status.
10061 	 */
10062 
10063 	int n_fields;
10064 	struct GMTAPI_CTRL *API;
10065 	struct GMT_CTRL *GMT;
10066 	void *record;
10067 
10068 	/* Top level check of active session */
10069 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
10070 	/* Various initializations before reading */
10071 	API = gmtapi_get_api_ptr (V_API);
10072 	API->error = GMT_NOERROR;
10073 	if (retval) *retval = 0;
10074 	GMT = API->GMT;	/* Shorthand for GMT access */
10075 
10076 	do {	/* We do this until we can secure the next record or we run out of records (and return EOF) */
10077 		API->get_next_record = false;	/* We expect to read one data record and return */
10078 		GMT->current.io.status = 0;	/* Initialize status to OK */
10079 		record = API->api_get_record (API, mode, &n_fields);
10080 	} while (API->get_next_record);
10081 
10082 	if (!(n_fields == EOF || n_fields == GMT_IO_NEXT_FILE)) API->current_rec[GMT_IN]++;	/* Increase record count, unless EOF */
10083 
10084 	if (retval) *retval = n_fields;	/* Requested we return the number of fields found */
10085 	return (record);		/* Return pointer to current record */
10086 }
10087 
10088 #ifdef FORTRAN_API
GMT_Get_Record_(unsigned int * mode,int * status)10089 void * GMT_Get_Record_ (unsigned int *mode, int *status) {	/* Fortran version: We pass the global GMT_FORTRAN structure */
10090 	return (GMT_Get_Record (GMT_FORTRAN, *mode, status));
10091 }
10092 #endif
10093 
gmtapi_put_record_fp(struct GMTAPI_CTRL * API,unsigned int mode,struct GMT_RECORD * record)10094 GMT_LOCAL int gmtapi_put_record_fp (struct GMTAPI_CTRL *API, unsigned int mode, struct GMT_RECORD *record) {
10095 	/* Function to use for rec-by-rec output to stream */
10096 	int error = GMT_NOERROR;
10097 	char *s;
10098 	struct GMT_CTRL *GMT = API->GMT;		/* Short hand */
10099 	switch (mode) {
10100 		case GMT_WRITE_TABLE_HEADER:	/* Export a table header record; skip if binary */
10101 			s = (record) ? (char*) (record) : GMT->current.io.curr_text;	/* Default to last input record if NULL */
10102 			gmtlib_write_tableheader (GMT, API->current_fp, s);	error = 1;	/* Write one item */
10103 			break;
10104 		case GMT_WRITE_SEGMENT_HEADER:	/* Export a segment header record; write NaNs if binary  */
10105 			if (record) strncpy (GMT->current.io.segment_header, (char*) (record), GMT_BUFSIZ-1);	/* Default to last segment record if NULL */
10106 			gmt_write_segmentheader (GMT, API->current_fp, GMT->common.b.ncol[GMT_OUT]);	error = 1;	/* Write one item */
10107 			break;
10108 		case GMT_WRITE_DATA:		/* Export either a formatted ASCII data record or a binary record */
10109 			if (GMT->common.b.ncol[GMT_OUT] == UINT_MAX) GMT->common.b.ncol[GMT_OUT] = GMT->common.b.ncol[GMT_IN];
10110 			error = GMT->current.io.output (GMT, API->current_fp, GMT->common.b.ncol[GMT_OUT], record->data, record->text);
10111 			break;
10112 		case GMT_WRITE_TABLE_START:	/* Write title and command to start of file; skip if binary */
10113 			gmtlib_write_newheaders (GMT, API->current_fp, API->current_put_n_columns);	error = 1;	/* Write one item */
10114 			break;
10115 		default:
10116 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: GMT_Put_Record called with illegal mode %u\n", mode);
10117 			return_error (API, GMT_NOT_A_VALID_IO_MODE);
10118 			break;
10119 	}
10120 	return ((error) ? GMT_NOTSET : 0);
10121 }
10122 
gmtapi_put_record_dataset(struct GMTAPI_CTRL * API,unsigned int mode,struct GMT_RECORD * record)10123 GMT_LOCAL int gmtapi_put_record_dataset (struct GMTAPI_CTRL *API, unsigned int mode, struct GMT_RECORD *record) {
10124 	/* Function to use for rec-by-rec output to a memory dataset */
10125 	char *s = NULL;
10126 	double value;
10127 	struct GMT_DATATABLE *T = API->current_put_D_table;	/* Short hand */
10128 	struct GMT_DATATABLE_HIDDEN *TH = gmt_get_DT_hidden (T);
10129 	struct GMT_CTRL *GMT = API->GMT;		/* Short hand */
10130 	int64_t *count = GMT->current.io.curr_pos[GMT_OUT];	/* Short hand to counters for table (not used as == 0), segment, row */
10131 	uint64_t col;
10132 	switch (mode) {
10133 		case GMT_WRITE_TABLE_HEADER:	/* Export a table header record; skip if binary */
10134 			s = (record) ? (char *)record : GMT->current.io.curr_text;	/* Default to last input record if NULL */
10135 			/* Hook into table header list */
10136 			if (count[GMT_SEG] == GMT_NOTSET && strlen(s)) {	/* Only allow headers for first segment in a table */
10137 				T->header = gmt_M_memory (GMT, T->header, T->n_headers+1, char *);
10138 				T->header[T->n_headers++] = strdup (s);
10139 			}
10140 			break;
10141 		case GMT_WRITE_SEGMENT_HEADER:	/* Export a segment header record; write NaNs if binary  */
10142 			count[GMT_SEG]++;	/* Start of new segment */
10143 			if (count[GMT_SEG]) {	/* Must first copy over records for the previous segments; last (or only) segment will be done by GMT_End_IO */
10144 				if (!T->segment[count[GMT_SEG]-1]) T->segment[count[GMT_SEG]-1] = gmt_get_segment (API->GMT, T->n_columns);
10145 				gmtlib_assign_segment (GMT, GMT_OUT, T->segment[count[GMT_SEG]-1], count[GMT_ROW], T->n_columns);	/* Allocate and place arrays into previous segment */
10146 				count[GMT_ROW] = 0;	/* Reset for next segment */
10147 				T->n_segments++;
10148 			}
10149 			if (count[GMT_SEG] == (int64_t)TH->n_alloc) {	/* Extend but set new members to NULL */
10150 				size_t was = TH->n_alloc;
10151 				T->segment = gmt_M_malloc (GMT, T->segment, count[GMT_SEG], &TH->n_alloc, struct GMT_DATASEGMENT *);
10152 				gmt_M_memset (&T->segment[was], TH->n_alloc - was, struct GMT_DATASEGMENT *);
10153 			}
10154 			if (!T->segment[count[GMT_SEG]]) T->segment[count[GMT_SEG]] = gmt_get_segment (GMT, T->n_columns);
10155 			s = (record) ? (char *)record : GMT->current.io.segment_header;	/* Default to last segment header record if NULL */
10156 			if (s && strlen(s)) {	/* Found a segment header */
10157 				if (T->segment[count[GMT_SEG]]->header) gmt_M_str_free (T->segment[count[GMT_SEG]]->header);	/* Hm, better free the old guy before strdup'ing a new one */
10158 				T->segment[count[GMT_SEG]]->header = strdup (s);
10159 			}
10160 			break;
10161 		case GMT_WRITE_DATA:		/* Export a segment row */
10162 			if (gmt_skip_output (GMT, record->data, T->n_columns))	/* Record was skipped via -s[a|r] */
10163 				break;
10164 			if (count[GMT_SEG] == GMT_NOTSET) {	/* Most likely a file with one segment but no segment header */
10165 				GMT_Report (API, GMT_MSG_DEBUG, "GMTAPI: GMT_Put_Record (double) called before any segments declared\n");
10166 				count[GMT_SEG] = 0;
10167 			}
10168 			gmt_prep_tmp_arrays (GMT, GMT_OUT, count[GMT_ROW], T->n_columns);	/* Init or reallocate tmp read vectors */
10169 			for (col = 0; col < T->n_columns; col++) {
10170 				value = gmtapi_select_record_value (GMT, record->data, (unsigned int)col, (unsigned int)GMT->common.b.ncol[GMT_OUT]);
10171 				if (GMT->current.io.col_type[GMT_OUT][col] & GMT_IS_LON) gmt_lon_range_adjust (GMT->current.io.geo.range, &value);
10172 				GMT->hidden.mem_coord[col][count[GMT_ROW]] = value;
10173 			}
10174 			if (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && T->n_columns >= 2) {
10175 				gmt_M_double_swap (GMT->hidden.mem_coord[GMT_X][count[GMT_ROW]], GMT->hidden.mem_coord[GMT_Y][count[GMT_ROW]]);	/* Got lat/lon instead of lon/lat */
10176 			}
10177 
10178 			if (record->text && record->text[0])	/* Also write trailing text */
10179 				GMT->hidden.mem_txt[count[GMT_ROW]] = strdup (record->text);
10180 			count[GMT_ROW]++;	/* Increment rows in this segment */
10181 			break;
10182 		case GMT_WRITE_TABLE_START:	/* Write title and command to start of file; skip if binary */
10183 			break;	/* Ignore for this method */
10184 		default:
10185 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: GMT_Put_Record called with illegal mode %u\n", mode);
10186 			return_error (API, GMT_NOT_A_VALID_IO_MODE);
10187 			break;
10188 	}
10189 	return GMT_NOERROR;
10190 }
10191 
gmtapi_put_record_matrix(struct GMTAPI_CTRL * API,unsigned int mode,struct GMT_RECORD * record)10192 GMT_LOCAL int gmtapi_put_record_matrix (struct GMTAPI_CTRL *API, unsigned int mode, struct GMT_RECORD *record) {
10193 	/* Function to use for rec-by-rec output to a memory matrix */
10194 	int error = GMT_NOERROR;
10195 	struct GMT_MATRIX *M = API->current_put_M;
10196 	struct GMT_CTRL *GMT = API->GMT;		/* Short hand */
10197 	uint64_t col, kol, ij;
10198 	char *s = NULL;
10199 
10200 	switch (mode) {
10201 		case GMT_WRITE_TABLE_HEADER:	/* Export a table header record; skip if binary */
10202 			s = (record) ? (char *)record : GMT->current.io.curr_text;	/* Default to last input record if NULL */
10203 			/* Hook into matrix header list */
10204 			if (strlen(s)) {	/* Only allow headers for first segment in a table */
10205 				M->header = gmt_M_memory (GMT, M->header, M->n_headers+1, char *);
10206 				M->header[M->n_headers++] = strdup (s);
10207 			}
10208 			break;
10209 		case GMT_WRITE_SEGMENT_HEADER:	/* Segment header */
10210 			if (GMT->current.io.multi_segments[GMT_OUT]) {	/* Flag in data as NaNs in current_record (d) */
10211 				for (col = 0; col < M->n_columns; col++) {	/* Place the output items */
10212 					ij = API->current_put_M_index (API->current_put_obj->rec, col, M->dim);
10213 					API->current_put_M_val (&(M->data), ij, GMT->session.d_NaN);
10214 				}
10215 				M->n_rows++;			/* Since the NaN-record becomes an actual data record that encodes a segment break */
10216 			}
10217 			break;
10218 		case GMT_WRITE_DATA:	/* Data record */
10219 			if (!record) {
10220 				GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: gmtapi_put_record_matrix got a NULL data pointer for method GMT_WRITE_DATA\n");
10221 				error = GMT_NOTSET;
10222 			}
10223 			else {
10224 				if (gmt_skip_output (GMT, record->data, M->n_columns))	/* Record was skipped via -s[a|r] */
10225 					error = GMT_NOTSET;
10226 				else {
10227 					double value;
10228 					bool toggle = (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && M->n_columns >= 2);
10229 
10230 					for (col = 0; col < M->n_columns; col++) {	/* Place the output items */
10231 						if (col < 2 && toggle)	/* Deal with -: since we are writing to matrix memory and not file */
10232 							kol = 1 - col;
10233 						else
10234 							kol = col;
10235 						ij = API->current_put_M_index (API->current_put_obj->rec, kol, M->dim);
10236 						value = gmtapi_select_record_value (GMT, record->data, (unsigned int)col, (unsigned int)GMT->common.b.ncol[GMT_OUT]);
10237 						API->current_put_M_val (&(M->data), ij, value);
10238 					}
10239 					if (record->text)
10240 						M->text[API->current_put_obj->rec] = strdup (record->text);
10241 					M->n_rows++;
10242 				}
10243 			}
10244 			break;
10245 		default:
10246 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_put_record_matrix called with illegal mode %u\n", mode);
10247 			return_error (API, GMT_NOT_A_VALID_IO_MODE);
10248 			break;
10249 	}
10250 	if (!error) {	/* Only increment if we placed a record on the output */
10251 		API->current_rec[GMT_OUT]++;
10252 		API->current_put_obj->rec++;
10253 	}
10254 
10255 	if (API->current_put_obj->n_alloc && API->current_put_obj->rec == API->current_put_obj->n_alloc) {	/* Must allocate more memory for vectors or matrices */
10256 		API->current_put_obj->n_alloc <<= 1;
10257 		if ((API->current_put_obj->method == GMT_IS_DUPLICATE || API->current_put_obj->method == GMT_IS_REFERENCE) && API->current_put_obj->actual_family == GMT_IS_MATRIX) {
10258 			size_t size = API->current_put_obj->n_alloc * M->n_columns;	/* Only one layer in this context */
10259 			if ((error = gmtlib_alloc_univector (API->GMT, &(M->data), M->type, size)) != GMT_NOERROR) return (error);
10260 			if (M->text) M->text = gmt_M_memory (API->GMT, M->text, API->current_put_obj->n_alloc, char *);
10261 		}
10262 	}
10263 	return error;
10264 }
10265 
gmtapi_put_record_vector(struct GMTAPI_CTRL * API,unsigned int mode,struct GMT_RECORD * record)10266 GMT_LOCAL int gmtapi_put_record_vector (struct GMTAPI_CTRL *API, unsigned int mode, struct GMT_RECORD *record) {
10267 	/* Function to use for rec-by-rec output to a memory vector */
10268 	int error = GMT_NOERROR;
10269 	struct GMT_VECTOR *V = API->current_put_V;
10270 	struct GMT_CTRL *GMT = API->GMT;		/* Short hand */
10271 	uint64_t col, kol;
10272 	char *s = NULL;
10273 
10274 	switch (mode) {
10275 		case GMT_WRITE_TABLE_HEADER:	/* Export a table header record; skip if binary */
10276 			s = (record) ? (char *)record : GMT->current.io.curr_text;	/* Default to last input record if NULL */
10277 			/* Hook into vector header list */
10278 			if (strlen(s)) {	/* Only allow headers for first segment in a table */
10279 				V->header = gmt_M_memory (GMT, V->header, V->n_headers+1, char *);
10280 				V->header[V->n_headers++] = strdup (s);
10281 			}
10282 			break;
10283 		case GMT_WRITE_SEGMENT_HEADER: /* Segment header */
10284 			if (GMT->current.io.multi_segments[GMT_OUT]) {	/* Segment header - flag in data as NaNs */
10285 				for (col = 0; col < V->n_columns; col++)	/* Place the output items */
10286 					API->current_put_V_val[col] (&(V->data[col]), API->current_put_obj->rec, GMT->session.d_NaN);
10287 				V->n_rows++;		/* Same */
10288 			}
10289 			break;
10290 		case GMT_WRITE_DATA:	/* Data record */
10291 			if (!record) {
10292 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Put_Record passed a NULL data pointer for method GMT_IS_DATASET|VECTOR\n");
10293 				error = GMT_NOTSET;
10294 			}
10295 			else {
10296 				double value;
10297 				if (gmt_skip_output (GMT, record->data, V->n_columns))	/* Record was skipped via -s[a|r] */
10298 					error = GMT_NOTSET;
10299 				else {
10300 					bool toggle = (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && V->n_columns >= 2);
10301 					for (col = 0; col < V->n_columns; col++) {	/* Place the output items */
10302 						if (col < 2 && toggle)	/* Deal with -: since we are writing to matrix memory and not file */
10303 							kol = 1 - col;
10304 						else
10305 							kol = col;
10306 						value = gmtapi_select_record_value (GMT, record->data, (unsigned int)col, (unsigned int)GMT->common.b.ncol[GMT_OUT]);
10307 						API->current_put_V_val[kol] (&(V->data[kol]), API->current_put_obj->rec, value);
10308 					}
10309 					if (record->text)
10310 						V->text[API->current_put_obj->rec] = strdup (record->text);
10311 					V->n_rows++;	/* Note that API->current_rec[GMT_OUT] and API->current_put_obj->rec are incremented separately at end of function */
10312 				}
10313 			}
10314 			break;
10315 		default:
10316 			GMT_Report (API, GMT_MSG_ERROR, "GMTAPI: Internal error: gmtapi_put_record_vector called with illegal mode %u\n", mode);
10317 			return_error (API, GMT_NOT_A_VALID_IO_MODE);
10318 			break;
10319 	}
10320 
10321 	if (!error) {	/* Only increment if we placed a record on the output */
10322 		API->current_rec[GMT_OUT]++;
10323 		API->current_put_obj->rec++;
10324 	}
10325 
10326 	if (API->current_put_obj->n_alloc && API->current_put_obj->rec == API->current_put_obj->n_alloc) {	/* Must allocate more memory for vectors or matrices */
10327 		API->current_put_obj->n_alloc <<= 1;
10328 		if ((error = gmtlib_alloc_vectors (GMT, V, API->current_put_obj->n_alloc)) != GMT_NOERROR) return (error);
10329 		if (V->text) V->text = gmt_M_memory (API->GMT, V->text, API->current_put_obj->n_alloc, char *);
10330 	}
10331 	return error;
10332 }
10333 
10334 /*! . */
gmtapi_put_record_init(struct GMTAPI_CTRL * API,unsigned int mode,struct GMT_RECORD * record)10335 GMT_LOCAL int gmtapi_put_record_init (struct GMTAPI_CTRL *API, unsigned int mode, struct GMT_RECORD *record) {
10336 	/* Writes a single data record to destimation.
10337 	 * We use mode to signal the kind of record:
10338 	 *   GMT_WRITE_TABLE_HEADER: Write an ASCII table header
10339 	 *   GMT_WRITE_SEGMENT_HEADER: Write an ASCII or binary segment header
10340 	 *   GMT_WRITE_DATA:    Write an data record
10341 	 * For text: If record == NULL use internal current record or header.
10342 	 * Returns 0 if a record was written successfully (See what -s[r] can do).
10343 	 * If an error occurs we return GMT_NOTSET and set API->error.
10344 	 */
10345 	int error = 0;
10346 	unsigned int method;
10347 	uint64_t col;
10348 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
10349 	struct GMT_MATRIX *M_obj  = NULL;
10350 	struct GMT_VECTOR *V_obj  = NULL;
10351 	struct GMT_DATASET *D_obj = NULL;
10352 	struct GMT_MATRIX_HIDDEN *MH = NULL;
10353 	struct GMT_CTRL *GMT;
10354 
10355 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
10356 	GMT = API->GMT;		/* Short hand */
10357 	if (!API->io_enabled[GMT_OUT]) return_error (API, GMT_ACCESS_NOT_ENABLED);
10358 	API->error = GMT_NOERROR;
10359 
10360 	S_obj = API->object[API->current_item[GMT_OUT]];	/* Shorthand for the data source we are working on */
10361 	if (S_obj->status == GMT_IS_USED) return_error (API, GMT_WRITTEN_ONCE);	/* Only allow writing of a data set once [unless we reset status] */
10362 	method = gmtapi_set_method (S_obj);	/* Get the actual method to use */
10363 	API->current_put_obj = S_obj;
10364 
10365 	switch (method) {	/* File, array, stream etc ? */
10366 		case GMT_IS_FILE:
10367 	 	case GMT_IS_STREAM:
10368 	 	case GMT_IS_FDESC:
10369 			API->api_put_record = gmtapi_put_record_fp;
10370 			API->current_fp = S_obj->fp;
10371 			API->current_put_n_columns = S_obj->n_columns;
10372 			if (API->GMT->common.o.end || API->GMT->common.o.text)	/* Asked for unspecified last column on input (e.g., -i3,2,5:), supply the missing last column number */
10373 				gmtlib_reparse_o_option (API->GMT, (API->GMT->common.o.text) ? 0 : S_obj->n_columns);
10374 			error = gmtapi_put_record_fp (API, mode, record);
10375 			break;
10376 
10377 		case GMT_IS_DUPLICATE:	/* Fill in a DATASET structure with one table only */
10378 		case GMT_IS_REFERENCE:
10379 			D_obj = gmtapi_get_dataset_data (S_obj->resource);
10380 			if (!D_obj) {	/* First time allocation of the single output table */
10381 				unsigned int smode;
10382 				if (mode == GMT_WRITE_TABLE_HEADER) {	/* Cannot do this yet since we don't know sizes. Delay */
10383 					API->tmp_header = gmt_M_memory (GMT, API->tmp_header, API->n_tmp_headers+1, char *);
10384 					if (record) strncpy (GMT->current.io.curr_text, (char*) (record), GMT_BUFSIZ-1);	/* Default to last segment record if NULL */
10385 					API->tmp_header[API->n_tmp_headers++] = strdup (GMT->current.io.curr_text);
10386 					S_obj->h_delay = true;
10387 					S_obj->status = GMT_IS_USING;	/* Have started writing to this destination */
10388 					return GMT_NOERROR;
10389 				}
10390 				else if (mode == GMT_WRITE_SEGMENT_HEADER) {	/* Cannot do this yet since we don't know sizes. Delay */
10391 					if (API->tmp_segmentheader) gmt_M_str_free (API->tmp_segmentheader);	/* Can happen if empty segment is written */
10392 					if (record) strncpy (GMT->current.io.segment_header, (char*) (record), GMT_BUFSIZ-1);	/* Default to last segment record if NULL */
10393 					API->tmp_segmentheader = strdup (GMT->current.io.segment_header);
10394 					S_obj->s_delay = true;
10395 					S_obj->status = GMT_IS_USING;	/* Have started writing to this destination */
10396 					return GMT_NOERROR;
10397 				}
10398 				else if (record->data == NULL && record->text == NULL) {
10399 					GMT_Report (API, GMT_MSG_ERROR, "GMT_Put_Record give NULL record? - Must skip\n");
10400 					return GMT_NOERROR;
10401 				}
10402 				/* Ensure record_type[GMT_OUT] is set correctly given we now have a data record to examine */
10403 				if (record->text == NULL)
10404 					GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_DATA;
10405 				else if (record->data == NULL)
10406 					GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_TEXT;
10407 				else
10408 					GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_MIXED;
10409 				smode = (GMT->current.io.record_type[GMT_OUT] & GMT_WRITE_TEXT) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
10410 				D_obj = gmtlib_create_dataset (GMT, 1, GMT_TINY_CHUNK, 0, 0, S_obj->geometry, smode, true);	/* 1 table, alloc segments array; no cols or rows yet */
10411 				S_obj->resource = D_obj;	/* Save this pointer for next time we call GMT_Put_Record */
10412 				GMT->current.io.curr_pos[GMT_OUT][GMT_SEG] = GMT_NOTSET;	/* Start at seg = -1 and increment at first segment header */
10413 				col = (GMT->common.o.select) ? GMT->common.o.n_cols : GMT->common.b.ncol[GMT_OUT];	/* Number of columns needed to hold the data records */
10414 				if ((GMT->current.io.record_type[GMT_OUT] & GMT_WRITE_DATA) && col == 0) {	/* Still don't know # of columns */
10415 					if (GMT->common.b.ncol[GMT_IN] < GMT_MAX_COLUMNS) {	/* Hail Mary pass to input columns */
10416 						col = GMT->common.b.ncol[GMT_IN];	/* Set output cols to equal input cols since not set */
10417 						GMT_Report (API, GMT_MSG_DEBUG, "GMTAPI: GMT_Put_Record does not know the number of output columns - set to equal input at %d\n", (int)GMT->common.b.ncol[GMT_IN]);
10418 					}
10419 					else {
10420 						GMT_Report (API, GMT_MSG_DEBUG, "GMT_Put_Record does not know the number of columns - must abort!\n");
10421 						return_error (API, GMT_N_COLS_NOT_SET);
10422 					}
10423 				}
10424 				if (GMT->current.io.record_type[GMT_OUT] == GMT_WRITE_TEXT) col = 0;	/* Just to be safe rather than fucked */
10425 				D_obj->n_columns = D_obj->table[0]->n_columns = col;	/* The final actual output column number */
10426 				if (GMT->common.b.ncol[GMT_OUT] == 0) GMT->common.b.ncol[GMT_OUT] = col;
10427 			}
10428 			API->current_put_D_table = D_obj->table[0];	/* GMT_Put_Record only writes one table with one or more segments */
10429 			API->api_put_record = gmtapi_put_record_dataset;
10430 			error = gmtapi_put_record_dataset (API, mode, record);
10431 			break;
10432 
10433 		case GMT_IS_DUPLICATE|GMT_VIA_MATRIX:	/* Data matrix only */
10434 		case GMT_IS_REFERENCE|GMT_VIA_MATRIX:
10435 			/* At the first output record the output matrix has not been allocated.
10436 			 * So first we do that, then later we can increment its size when needed.
10437 			 * The realloc to final size takes place in GMT_End_IO. */
10438 			if (mode == GMT_WRITE_TABLE_HEADER) {	/* Cannot do this yet since we don't know sizes. Delay */
10439 				API->tmp_header = gmt_M_memory (GMT, API->tmp_header, API->n_tmp_headers+1, char *);
10440 				if (record) strncpy (GMT->current.io.curr_text, (char*) (record), GMT_BUFSIZ-1);	/* Default to last segment record if NULL */
10441 				API->tmp_header[API->n_tmp_headers++] = strdup (GMT->current.io.curr_text);
10442 				S_obj->h_delay = true;
10443 				S_obj->status = GMT_IS_USING;	/* Have started writing to this destination */
10444 				return GMT_NOERROR;
10445 			}
10446 			if (S_obj->n_rows && S_obj->rec >= S_obj->n_rows)
10447 				GMT_Report (API, GMT_MSG_WARNING, "GMTAPI: GMT_Put_Record exceeding limits on rows(?) - possible bug\n");
10448 			if (S_obj->resource == NULL) {	/* First time allocating space; S_obj->n_rows == S_obj->n_alloc == 0 */
10449 				size_t size;
10450 				col = (GMT->common.o.select) ? GMT->common.o.n_cols : GMT->common.b.ncol[GMT_OUT];	/* Number of columns needed to hold the data records */
10451 				if (col == 0 && mode == GMT_WRITE_SEGMENT_HEADER && GMT->current.io.multi_segments[GMT_OUT]) {
10452 					/* Cannot place the NaN records since we don't know the number of columns yet */
10453 					S_obj->delay++;
10454 					S_obj->rec++;					/* Since the NaN-record is an actual data record that encodes a segment break */
10455 					API->current_rec[GMT_OUT]++;	/* Since the NaN-record is an actual data record that encodes a segment break */
10456 					break;
10457 				}
10458 				size = S_obj->n_alloc = GMT_CHUNK;
10459 				M_obj = gmtlib_create_matrix (GMT, 1U, GMT_OUT, 0);
10460 				M_obj->type = S_obj->type;	/* Use selected data type for export */
10461 				M_obj->dim = M_obj->n_columns = col;	/* If COL_FORMAT the dim will change in end_io_matrix after transpose */
10462 				size *= M_obj->n_columns;	/* Size in bytes of the initial matrix allocation */
10463 				if ((error = gmtlib_alloc_univector (GMT, &(M_obj->data), M_obj->type, size)) != GMT_NOERROR) return (gmtlib_report_error (API, error));
10464 				if (record->text) M_obj->text = gmt_M_memory (GMT, NULL, S_obj->n_alloc, char *);
10465 				MH = gmt_get_M_hidden (M_obj);
10466 				S_obj->alloc_mode = MH->alloc_mode = GMT_ALLOC_INTERNALLY;
10467 				S_obj->resource = M_obj;	/* Save so we can get it next time */
10468 				M_obj->n_rows = S_obj->rec;	/* So we start on the same output record due to the delayed NaNs */
10469 			}
10470 			/* Place current matrix parameters in API */
10471 			API->current_put_M = M_obj;
10472 			if ((API->current_put_M_index = gmtapi_get_2d_to_index (API, GMT_IS_ROW_FORMAT, GMT_GRID_IS_REAL)) == NULL)	/* Since we cannot do col_format without knowing dimension - see end_io_matrix */
10473 				return GMT_WRONG_MATRIX_SHAPE;
10474 			if ((API->current_put_M_val = gmtapi_select_put_function (API, M_obj->type)) == NULL)
10475 				return GMT_NOT_A_VALID_TYPE;
10476 			API->api_put_record = gmtapi_put_record_matrix;
10477 			error = gmtapi_put_record_matrix (API, mode, record);
10478 			break;
10479 
10480 		case GMT_IS_DUPLICATE|GMT_VIA_VECTOR:	/* List of column arrays */
10481 		case GMT_IS_REFERENCE|GMT_VIA_VECTOR:
10482 			if (mode == GMT_WRITE_TABLE_HEADER) {	/* Cannot do this yet since we don't know sizes. Delay. */
10483 				API->tmp_header = gmt_M_memory (GMT, API->tmp_header, API->n_tmp_headers+1, char *);
10484 				if (record) strncpy (GMT->current.io.curr_text, (char*) (record), GMT_BUFSIZ-1);	/* Default to last segment record if NULL */
10485 				API->tmp_header[API->n_tmp_headers++] = strdup (GMT->current.io.curr_text);
10486 				S_obj->h_delay = true;
10487 				S_obj->status = GMT_IS_USING;	/* Have started writing to this destination */
10488 				return GMT_NOERROR;
10489 			}
10490 			if (S_obj->n_rows && S_obj->rec >= S_obj->n_rows)
10491 				GMT_Report (API, GMT_MSG_WARNING, "GMTAPI: GMT_Put_Record exceeding limits on rows(?) - possible bug\n");
10492 			if ((V_obj = S_obj->resource) == NULL) {	/* First time allocating space; S_obj->n_rows == S_obj->n_alloc == 0 */
10493 				col = (GMT->common.o.select) ? GMT->common.o.n_cols : GMT->common.b.ncol[GMT_OUT];	/* Number of columns needed to hold the data records */
10494 				if (col == 0 && mode == GMT_WRITE_SEGMENT_HEADER && GMT->current.io.multi_segments[GMT_OUT]) {
10495 					/* Cannot place the NaN records since we don't know the number of columns yet */
10496 					S_obj->delay++;
10497 					S_obj->rec++;					/* Since the NaN-record is an actual data record that encodes a segment break */
10498 					API->current_rec[GMT_OUT]++;	/* Since the NaN-record is an actual data record that encodes a segment break */
10499 					break;
10500 				}
10501 				S_obj->n_alloc = GMT_CHUNK;	/* Size in bytes of the initial matrix allocation */
10502 				if ((V_obj = gmt_create_vector (GMT, col, GMT_OUT)) == NULL)
10503 					return_error (API, GMT_MEMORY_ERROR);
10504 				for (col = 0; col < V_obj->n_columns; col++)	/* Set same export data type for all vectors */
10505 					V_obj->type[col] = GMT->current.setting.export_type;
10506 				if ((error = gmtlib_alloc_vectors (GMT, V_obj, S_obj->n_alloc)) != GMT_NOERROR) {
10507 					/* Have to free V_obj here */
10508 					return (gmtlib_report_error (API, error));
10509 				}
10510 				if (record->text) V_obj->text = gmt_M_memory (GMT, NULL, S_obj->n_alloc, char *);
10511 				S_obj->resource = V_obj;	/* Save so we can get it next time */
10512 			}
10513 			/* Place current vector parameters in API */
10514 			API->current_put_V = V_obj;
10515 			API->current_put_V_val = gmt_M_memory (GMT, NULL, V_obj->n_columns, GMT_putfunction);	/* Array of functions */
10516 			for (col = 0; col < V_obj->n_columns; col++) {	/* Assign the functions */
10517 				if ((API->current_put_V_val[col] = gmtapi_select_put_function (API, V_obj->type[col])) == NULL)
10518 					return GMT_NOT_A_VALID_TYPE;
10519 			}
10520 			API->api_put_record = gmtapi_put_record_vector;
10521 			error = gmtapi_put_record_vector (API, mode, record);
10522 			break;
10523 
10524 		default:
10525 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Put_Record called with illegal method\n");
10526 			return_error (API, GMT_NOT_A_VALID_METHOD);
10527 			break;
10528 	}
10529 
10530 	S_obj->status = GMT_IS_USING;	/* Have started writing to this destination */
10531 
10532 	return ((error) ? GMT_NOTSET : 0);
10533 }
10534 
10535 /*! . */
GMT_Put_Record(void * V_API,unsigned int mode,void * record)10536 int GMT_Put_Record (void *V_API, unsigned int mode, void *record) {
10537 	/* Writes a single data record to destimation.
10538 	 * We use mode to signal the kind of record:
10539 	 *   GMT_WRITE_TABLE_HEADER:   Write an ASCII table header
10540 	 *   GMT_WRITE_SEGMENT_HEADER: Write an ASCII or binary segment header
10541 	 *   GMT_WRITE_DATA:           Write an data record
10542 	 * For text: If record == NULL we use internal current record or header.
10543 	 * Returns GMT_NOERROR if a record was written successfully (See what -s[r] can do).
10544 	 * If an error occurs we return GMT_NOTSET and set API->error.
10545 	 *
10546 	 * GMT_Put_Record calls api_put_record is a pointer to various container-specific
10547 	 * output functions.  It is initialized to gmtapi_put_record_init by GMT_Begin_IO.
10548 	 * gmtapi_put_record_init initializes the machinery and assigns api_put_record. */
10549 
10550 	struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
10551 	return (API->api_put_record (API, mode, record));
10552 }
10553 
10554 #ifdef FORTRAN_API
GMT_Put_Record_(unsigned int * mode,void * record)10555 int GMT_Put_Record_ (unsigned int *mode, void *record) {
10556 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10557 	return (GMT_Put_Record (GMT_FORTRAN, *mode, record));
10558 }
10559 #endif
10560 
10561  /*! . */
GMT_Begin_IO(void * V_API,unsigned int family,unsigned int direction,unsigned int mode)10562 int GMT_Begin_IO (void *V_API, unsigned int family, unsigned int direction, unsigned int mode) {
10563 	/* Initializes the rec-by-rec i/o mechanism for either input or output (given by direction).
10564 	 * GMT_Begin_IO must be called before any data i/o is allowed.
10565 	 * family:	The family of data must be GMT_IS_DATASET.
10566 	 * direction:	Either GMT_IN or GMT_OUT.
10567 	 * mode:	Currently unused
10568 	 * Returns:	false if successful, true if error.
10569 	 */
10570 	int error, item;
10571 	struct GMTAPI_DATA_OBJECT *S_obj = NULL;
10572 	struct GMTAPI_CTRL *API = NULL;
10573 	struct GMT_CTRL *GMT = NULL;
10574 
10575 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
10576 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (V_API, GMT_NOT_A_VALID_DIRECTION);
10577 	if (!multiple_files_ok (family)) return_error (V_API, GMT_NOT_A_VALID_IO_ACCESS);
10578 	API = gmtapi_get_api_ptr (V_API);
10579 	API->error = GMT_NOERROR;	/* Reset in case it has some previous error */
10580 	if (!API->registered[direction]) GMT_Report (API, GMT_MSG_DEBUG, "GMT_Begin_IO: No %s resources registered\n", GMT_direction[direction]);
10581 	if (mode) GMT_Report (API, GMT_MSG_DEBUG, "GMT_Begin_IO: Mode value %u not considered (ignored)\n", mode);
10582 
10583 	GMT = API->GMT;
10584 	/* Must initialize record-by-record machinery for dataset */
10585 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Begin_IO: Initialize record-by-record access for %s\n", GMT_direction[direction]);
10586 	API->current_item[direction] = GMT_NOTSET;	/* gmtapi_next_data_object (below) will wind it to the first item >= 0 */
10587 	if ((error = gmtapi_next_data_object (API, family, direction))) return_error (API, GMT_NO_RESOURCES);	/* Something went bad */
10588 	item = API->current_item[direction];	/* Next item */
10589 	S_obj = API->object[item];	/* Short-hand for next object */
10590 	API->io_mode[direction] = GMTAPI_BY_REC;
10591 	API->io_enabled[direction] = true;	/* OK to access resources */
10592 	GMT->current.io.need_previous = (GMT->common.g.active || GMT->current.io.skip_duplicates);
10593 	GMT->current.io.ogr = GMT_OGR_UNKNOWN;
10594 	GMT->current.io.segment_header[0] = GMT->current.io.curr_text[0] = 0;
10595 	GMT->current.io.first_rec = true;
10596 	if (direction == GMT_OUT) {	/* Special checks for output */
10597 		if (S_obj->messenger && S_obj->resource) {	/* Need to destroy the dummy container before passing data out */
10598 			if ((error = gmtapi_destroy_data_ptr (API, S_obj->actual_family, S_obj->resource)))	/* Do the dirty deed */
10599 				return_error (API,error);
10600 			S_obj->resource  = NULL;	/* Since we now have nothing */
10601 			S_obj->messenger = false;	/* OK, now clean for output */
10602 		}
10603 		API->current_put_obj = S_obj;
10604 		API->api_put_record = gmtapi_put_record_init;
10605 		API->GMT->current.io.record_type[GMT_OUT] = API->GMT->current.io.record_type[GMT_IN];	/* Can be overruled by GMT_Set_Columns */
10606 		//if (header == GMT_HEADER_ON && !GMT->common.b.active[GMT_OUT]) GMT_Put_Record (API, GMT_WRITE_TABLE_START, NULL);	/* Write standard ASCII header block */
10607 		if (!GMT->common.o.active) GMT->current.io.trailing_text[GMT_OUT] = true;	/* Default reads and writes any trailing text */
10608 	}
10609 	else {	/* Special checks for input */
10610 		API->current_get_obj = S_obj;
10611 		if (!GMT->common.i.active) GMT->current.io.trailing_text[GMT_IN] = true;	/* Default reads and writes any trailing text */
10612 		gmtapi_get_record_init (API);
10613 	}
10614 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Begin_IO: %s resource access is now enabled [record-by-record]\n", GMT_direction[direction]);
10615 
10616 	return_error (V_API, GMT_NOERROR);	/* No error encountered */
10617 }
10618 
10619 #ifdef FORTRAN_API
GMT_Begin_IO_(unsigned int * family,unsigned int * direction,unsigned int * mode)10620 int GMT_Begin_IO_ (unsigned int *family, unsigned int *direction, unsigned int *mode) {
10621 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10622 	return (GMT_Begin_IO (GMT_FORTRAN, *family, *direction, *mode));
10623 }
10624 #endif
10625 
10626 /*! . */
GMT_Get_Row(void * V_API,int row_no,struct GMT_GRID * G,gmt_grdfloat * row)10627 int GMT_Get_Row (void *V_API, int row_no, struct GMT_GRID *G, gmt_grdfloat *row) {
10628 	/* Reads the entire row vector from the grdfile.
10629 	 * If row_no is NEGATIVE it is interpreted to mean that we just want to
10630 	 * fseek to the start of the abs(row_no) record and no reading takes place.
10631 	 * If R->auto_advance is false we must set R->start explicitly to row_no.
10632 	 * If R->auto_advance is true it reads the current row and advances R->row++.
10633 	 * In this case row_no is not actually used.
10634 	 */
10635 	unsigned int err;
10636  	unsigned int col;
10637 	struct GMTAPI_CTRL *API = NULL;
10638 	char *fmt = NULL;
10639 	struct GMT_GRID_HIDDEN *GH = NULL;
10640 	struct GMT_GRID_ROWBYROW *R = NULL;
10641 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
10642 	struct GMT_CTRL *GMT = NULL;
10643 
10644 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
10645 	API = gmtapi_get_api_ptr (V_API);
10646 	API->error = GMT_NOERROR;
10647 	GMT = API->GMT;
10648 	GH = gmt_get_G_hidden (G);
10649 	R = gmtapi_get_rbr_ptr (GH->extra);
10650 	HH = gmt_get_H_hidden (G->header);
10651 	fmt = GMT->session.grdformat[G->header->type];
10652 	if (fmt[0] == 'c') {		/* Get one NetCDF row, old format */
10653 		if (row_no < 0) {	/* Special seek instruction, then return */
10654 			R->row = abs (row_no);
10655 			R->start[0] = R->row * R->edge[0];
10656 			return (GMT_NOERROR);
10657 		}
10658 		else if (!R->auto_advance) {	/* Go to specified row and read it */
10659 			R->row = row_no;
10660 			R->start[0] = R->row * R->edge[0];
10661 		}
10662 		gmt_M_err_trap (gmt_nc_get_vara_grdfloat (R->fid, HH->z_id, R->start, R->edge, row));
10663 		if (R->auto_advance) R->start[0] += R->edge[0];	/* Advance to next row if auto */
10664 	}
10665 	else if (fmt[0] == 'n') {	/* Get one NetCDF row, COARDS-compliant format */
10666 		if (row_no < 0) {	/* Special seek instruction */
10667 			R->row = abs (row_no);
10668 			R->start[0] = HH->row_order == k_nc_start_north ? R->row : G->header->n_rows - 1 - R->row;
10669 			return (GMT_NOERROR);
10670 		}
10671 		else if (!R->auto_advance) {
10672 			R->row = row_no;
10673 			R->start[0] = HH->row_order == k_nc_start_north ? R->row : G->header->n_rows - 1 - R->row;
10674 		}
10675 		gmt_M_err_trap (gmt_nc_get_vara_grdfloat (R->fid, HH->z_id, R->start, R->edge, row));
10676 		if (R->auto_advance) R->start[0] -= HH->row_order;	/* Advance to next row if auto */
10677 	}
10678 	else {			/* Get a native binary row */
10679 		size_t n_items;
10680 		if (row_no < 0) {	/* Special seek instruction */
10681 			R->row = abs (row_no);
10682 			if (fseek (R->fp, (off_t)(GMT_GRID_HEADER_SIZE + R->row * R->n_byte), SEEK_SET)) return (GMT_GRDIO_SEEK_FAILED);
10683 			return (GMT_NOERROR);
10684 		}
10685 		R->row = row_no;
10686 		if (!R->auto_advance && fseek (R->fp, (off_t)(GMT_GRID_HEADER_SIZE + R->row * R->n_byte), SEEK_SET)) return (GMT_GRDIO_SEEK_FAILED);
10687 
10688 		n_items = G->header->n_columns;
10689 		if (fmt[1] == GMT_GRD_FORMAT) {	/* Binary gmt_grdfloat, no need to mess with decoding */
10690 			if (gmt_M_fread (row, R->size, n_items, R->fp) != n_items) return (GMT_GRDIO_READ_FAILED);	/* Get one row */
10691 		}
10692 		else {
10693 			if (gmt_M_fread (R->v_row, R->size, n_items, R->fp) != n_items) return (GMT_GRDIO_READ_FAILED);	/* Get one row */
10694 			for (col = 0; col < G->header->n_columns; col++)
10695 				row[col] = gmtlib_decode (GMT, R->v_row, col, fmt[1]);	/* Convert whatever to gmt_grdfloat */
10696 		}
10697 #ifdef DEBUG
10698 		R->pos = ftell (R->fp);	/* Update where we are */
10699 #endif
10700 	}
10701 	if (R->check) {	/* Replace NaN-marker with actual NaN */
10702 		for (col = 0; col < G->header->n_columns; col++)
10703 			if (row[col] == G->header->nan_value)
10704 				row[col] = GMT->session.f_NaN;
10705 	}
10706 	gmt_scale_and_offset_f (GMT, row, G->header->n_columns, G->header->z_scale_factor, G->header->z_add_offset);
10707 	if (R->auto_advance) R->row++;
10708 	return (GMT_NOERROR);
10709 }
10710 
10711 #ifdef FORTRAN_API
GMT_Get_Row_(int * rec_no,struct GMT_GRID * G,gmt_grdfloat * row)10712 int GMT_Get_Row_ (int *rec_no, struct GMT_GRID *G, gmt_grdfloat *row) {
10713 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10714 	return (GMT_Get_Row (GMT_FORTRAN, *rec_no, G, row));
10715 }
10716 #endif
10717 
10718 /*! . */
GMT_Put_Row(void * V_API,int rec_no,struct GMT_GRID * G,gmt_grdfloat * row)10719 int GMT_Put_Row (void *V_API, int rec_no, struct GMT_GRID *G, gmt_grdfloat *row) {
10720 	/* Writes the entire row vector to the grdfile.
10721 	 * If row_no is NEGATIVE it is interpreted to mean that we just want to
10722 	 * fseek to the start of the abs(row_no) record and no reading takes place.
10723 	 * If R->auto_advance is false we must set R->start explicitly to row_no.
10724 	 * If R->auto_advance is true it writes at the current row and advances R->row++.
10725 	 * In this case row_no is not actually used.
10726 	 */
10727 
10728 	unsigned int err;	/* Required by gmt_M_err_trap */
10729 	unsigned int col;
10730 	size_t n_items;
10731 	struct GMTAPI_CTRL *API = NULL;
10732 	char *fmt = NULL;
10733 	struct GMT_GRID_ROWBYROW *R = NULL;
10734 	struct GMT_GRID_HIDDEN *GH = NULL;
10735 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
10736 	struct GMT_CTRL *GMT = NULL;
10737 
10738 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
10739 	API = gmtapi_get_api_ptr (V_API);
10740 	API->error = GMT_NOERROR;
10741 	GMT = API->GMT;
10742 	GH = gmt_get_G_hidden (G);
10743 	R = gmtapi_get_rbr_ptr (GH->extra);
10744 	HH = gmt_get_H_hidden (G->header);
10745 	gmt_scale_and_offset_f (GMT, row, G->header->n_columns, G->header->z_scale_factor, G->header->z_add_offset);
10746 	if (R->check) {	/* Replace NaNs with special value */
10747 		for (col = 0; col < G->header->n_columns; col++)
10748 			if (gmt_M_is_fnan (row[col]))
10749 				row[col] = G->header->nan_value;
10750 	}
10751 
10752 	fmt = GMT->session.grdformat[G->header->type];
10753 	switch (fmt[0]) {
10754 		case 'c':
10755 			if (!R->auto_advance) R->start[0] = rec_no * R->edge[0];
10756 			gmt_M_err_trap (gmt_nc_put_vara_grdfloat (R->fid, HH->z_id, R->start, R->edge, row));
10757 			if (R->auto_advance) R->start[0] += R->edge[0];
10758 			break;
10759 		case 'n':
10760 			if (!R->auto_advance) {
10761 				HH->row_order = k_nc_start_north ? rec_no : (int)G->header->n_rows - 1 - rec_no;
10762 				R->start[0] = (size_t)HH->row_order;
10763 			}
10764 			gmt_M_err_trap (gmt_nc_put_vara_grdfloat (R->fid, HH->z_id, R->start, R->edge, row));
10765 			if (R->auto_advance) R->start[0] -= HH->row_order;
10766 			break;
10767 		default:
10768 			if (!R->auto_advance && fseek (R->fp, (off_t)(GMT_GRID_HEADER_SIZE + rec_no * R->n_byte), SEEK_SET)) return (GMT_GRDIO_SEEK_FAILED);
10769 			n_items = G->header->n_columns;
10770 			if (fmt[1] == GMT_GRD_FORMAT) {	/* Regular grdfloats */
10771 				if (gmt_M_fwrite (row, R->size, n_items, R->fp) < n_items) return (GMT_GRDIO_WRITE_FAILED);
10772 			}
10773 			else {
10774 				for (col = 0; col < G->header->n_columns; col++) gmtlib_encode (GMT, R->v_row, col, row[col], fmt[1]);
10775 				if (gmt_M_fwrite (R->v_row, R->size, n_items, R->fp) < n_items) return (GMT_GRDIO_WRITE_FAILED);
10776 			}
10777 			break;
10778 	}
10779 	if (R->auto_advance) R->row++;
10780 
10781 	return (GMT_NOERROR);
10782 }
10783 
10784 #ifdef FORTRAN_API
GMT_Put_Row_(int * rec_no,struct GMT_GRID * G,gmt_grdfloat * row)10785 int GMT_Put_Row_ (int *rec_no, struct GMT_GRID *G, gmt_grdfloat *row) {
10786 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10787 	return (GMT_Put_Row (GMT_FORTRAN, *rec_no, G, row));
10788 }
10789 #endif
10790 
gmtapi_ptrvoid(char ** p)10791 GMT_LOCAL char * gmtapi_ptrvoid (char ** p) { 	/* Handle as char ** just to determine if address is of a NULL pointer */
10792 	return *p;
10793 }
10794 
10795 /*! . */
GMT_Destroy_Data(void * V_API,void * object)10796 int GMT_Destroy_Data (void *V_API, void *object) {
10797 	/* Destroy a resource that is no longer needed.
10798 	 * Returns the error code.  If passed an object allocated at a higher level then
10799 	 * we quietly return no error.
10800 	 */
10801 	int error, item, object_ID = GMT_NOTSET;
10802 	enum GMT_enum_family family;
10803 	struct GMTAPI_CTRL *API = NULL;
10804 
10805 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);	/* This is a cardinal sin */
10806 	if (object == NULL) return_error (API, GMT_NOERROR);	/* Null address, quietly skip */
10807 	if (!gmtapi_ptrvoid(object)) return_error (API, GMT_NOERROR);	/* Null pointer, quietly skip */
10808 	API = gmtapi_get_api_ptr (V_API);		/* Now we need to get that API pointer to check further */
10809 	if ((object_ID = gmtapi_get_object_id_from_data_ptr (API, object)) == GMT_NOTSET) return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find the object in the list */
10810 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET) return_error (API, API->error);	/* Could not find that item */
10811 	family = API->object[item]->actual_family;
10812 
10813 	switch (family) {	/* Standard 6 families, plus matrix/vector and coordinates */
10814 		case GMT_IS_GRID:	/* GMT grid */
10815 			error = gmtapi_destroy_grid (API, object);
10816 			break;
10817 		case GMT_IS_DATASET:
10818 			error = gmtapi_destroy_dataset (API, object);
10819 			break;
10820 		case GMT_IS_PALETTE:
10821 			error = gmtapi_destroy_palette (API, object);
10822 			break;
10823 		case GMT_IS_IMAGE:
10824 			error = gmtapi_destroy_image (API, object);
10825 			break;
10826 		case GMT_IS_POSTSCRIPT:
10827 			error = gmtapi_destroy_postscript (API, object);
10828 			break;
10829 		case GMT_IS_CUBE:
10830 			error = gmtapi_destroy_cube (API, object);
10831 			break;
10832 
10833 		/* Also allow destroying of intermediate vector and matrix containers */
10834 		case GMT_IS_MATRIX:
10835 			error = gmtapi_destroy_matrix (API, object);
10836 			break;
10837 		case GMT_IS_VECTOR:
10838 			error = gmtapi_destroy_vector (API, object);
10839 			break;
10840 		case GMT_IS_COORD:
10841 			error = gmtapi_destroy_coord (API, object);
10842 			break;
10843 		default:
10844 			return_error (API, GMT_NOT_A_VALID_FAMILY);
10845 			break;
10846 	}
10847 	if (error == GMT_NOERROR) {	/* We successfully freed the items, now remove from IO list */
10848 		unsigned int j;
10849 		void *address = API->object[item]->resource;
10850 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Destroy_Data: freed memory for a %s for object %d\n", GMT_family[family], object_ID);
10851 		if ((error = gmtlib_unregister_io (API, object_ID, (unsigned int)GMT_NOTSET))) return_error (API, error);	/* Did not find object */
10852 		for (j = 0; j < API->n_objects; j++) {
10853 			if (API->object[j]->resource == address) API->object[j]->resource = NULL;	/* Set matching resources to NULL so we don't try to read from there again either */
10854 		}
10855 #ifdef DEBUG
10856 		gmtapi_list_objects (API, "GMT_Destroy_Data");
10857 #endif
10858 
10859 	}
10860 	else if (error != GMT_FREE_WRONG_LEVEL) {
10861 		/* Quietly ignore these errors: GMT_PTR_IS_NULL, GMT_FREE_EXTERNAL_NOT_ALLOWED as they are not considered errors here. */
10862 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Destroy_Data: Ignored warning %d for object %d\n", error, object_ID);
10863 	}
10864 	else
10865 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Destroy_Data: Skipped due to wrong level for object %d\n", object_ID);
10866 	return_error (API, GMT_NOERROR);
10867 }
10868 
10869 #ifdef FORTRAN_API
GMT_Destroy_Data_(void * object)10870 int GMT_Destroy_Data_ (void *object) {
10871 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10872 	return (GMT_Destroy_Data (GMT_FORTRAN, object));
10873 }
10874 #endif
10875 
10876 /*! . */
GMT_Free(void * V_API,void * ptr)10877 int GMT_Free (void *V_API, void *ptr) {
10878 	struct GMTAPI_CTRL *API = NULL;
10879 	void *address = NULL;
10880 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);	/* This is a cardinal sin */
10881 	if (ptr == NULL) return_error (V_API, GMT_NOERROR);	/* Null address, quietly skip */
10882 	if ((address = gmtapi_ptrvoid(ptr)) == NULL) return_error (V_API, GMT_NOERROR);	/* Null pointer, quietly skip */
10883 	API = gmtapi_get_api_ptr (V_API);		/* Now we need to get that API pointer to check further */
10884 	gmt_M_free (API->GMT, address);
10885 	return_error (API, GMT_NOERROR);
10886 }
10887 
10888 #ifdef FORTRAN_API
GMT_Free_(void * ptr)10889 int GMT_Free_ (void *ptr) {
10890 	/* Fortran version: We pass the global GMT_FORTRAN structure */
10891 	return (GMT_Free (GMT_FORTRAN, ptr));
10892 }
10893 #endif
10894 
gmtapi_destroy_grids(struct GMTAPI_CTRL * API,struct GMT_GRID *** obj,unsigned int n_items)10895 GMT_LOCAL int gmtapi_destroy_grids (struct GMTAPI_CTRL *API, struct GMT_GRID ***obj, unsigned int n_items) {
10896 	/* Used to destroy a group of grids read via GMT_Read_Group */
10897 	unsigned int k;
10898 	int error;
10899 	struct GMT_GRID **G = *obj;
10900 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &G[k]))) return_error (API, error);
10901 	gmt_M_free (API->GMT, G);	*obj = NULL;
10902 	return_error (API, GMT_NOERROR);
10903 }
10904 
gmtapi_destroy_datasets(struct GMTAPI_CTRL * API,struct GMT_DATASET *** obj,unsigned int n_items)10905 GMT_LOCAL int gmtapi_destroy_datasets (struct GMTAPI_CTRL *API, struct GMT_DATASET ***obj, unsigned int n_items) {
10906 	/* Used to destroy a group of datasets read via GMT_Read_Group */
10907 	unsigned int k;
10908 	int error;
10909 	struct GMT_DATASET **D = *obj;
10910 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &D[k]))) return_error (API, error);
10911 	gmt_M_free (API->GMT, D);	*obj = NULL;
10912 	return_error (API, GMT_NOERROR);
10913 }
10914 
gmtapi_destroy_images(struct GMTAPI_CTRL * API,struct GMT_IMAGE *** obj,unsigned int n_items)10915 GMT_LOCAL int gmtapi_destroy_images (struct GMTAPI_CTRL *API, struct GMT_IMAGE ***obj, unsigned int n_items) {
10916 	/* Used to destroy a group of images read via GMT_Read_Group */
10917 	unsigned int k;
10918 	int error;
10919 	struct GMT_IMAGE **I = *obj;
10920 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &I[k]))) return_error (API, error);
10921 	gmt_M_free (API->GMT, I);	*obj = NULL;
10922 	return_error (API, GMT_NOERROR);
10923 }
10924 
gmtapi_destroy_palettes(struct GMTAPI_CTRL * API,struct GMT_PALETTE *** obj,unsigned int n_items)10925 GMT_LOCAL int gmtapi_destroy_palettes (struct GMTAPI_CTRL *API, struct GMT_PALETTE ***obj, unsigned int n_items) {
10926 	unsigned int k;
10927 	int error;
10928 	struct GMT_PALETTE **C = *obj;
10929 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &C[k]))) return_error (API, error);
10930 	gmt_M_free (API->GMT, C);	*obj = NULL;
10931 	return_error (API, GMT_NOERROR);
10932 }
10933 
gmtapi_destroy_postscripts(struct GMTAPI_CTRL * API,struct GMT_POSTSCRIPT *** obj,unsigned int n_items)10934 GMT_LOCAL int gmtapi_destroy_postscripts (struct GMTAPI_CTRL *API, struct GMT_POSTSCRIPT ***obj, unsigned int n_items) {
10935 	/* Used to destroy a group of palettes read via GMT_Read_Group */
10936 	unsigned int k;
10937 	int error;
10938 	struct GMT_POSTSCRIPT **P = *obj;
10939 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &P[k]))) return_error (API, error);
10940 	gmt_M_free (API->GMT, P);	*obj = NULL;
10941 	return_error (API, GMT_NOERROR);
10942 }
10943 
gmtapi_destroy_cubes(struct GMTAPI_CTRL * API,struct GMT_CUBE *** obj,unsigned int n_items)10944 GMT_LOCAL int gmtapi_destroy_cubes (struct GMTAPI_CTRL *API, struct GMT_CUBE ***obj, unsigned int n_items) {
10945 	/* Used to destroy a group of grids read via GMT_Read_Group */
10946 	unsigned int k;
10947 	int error;
10948 	struct GMT_CUBE **U = *obj;
10949 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &U[k]))) return_error (API, error);
10950 	gmt_M_free (API->GMT, U);	*obj = NULL;
10951 	return_error (API, GMT_NOERROR);
10952 }
10953 
gmtapi_destroy_matrices(struct GMTAPI_CTRL * API,struct GMT_MATRIX *** obj,unsigned int n_items)10954 GMT_LOCAL int gmtapi_destroy_matrices (struct GMTAPI_CTRL *API, struct GMT_MATRIX ***obj, unsigned int n_items) {
10955 	/* Used to destroy a group of matrices read via GMT_Read_Group */
10956 	unsigned int k;
10957 	int error;
10958 	struct GMT_MATRIX **M = *obj;
10959 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &M[k]))) return_error (API, error);
10960 	gmt_M_free (API->GMT, M);	*obj = NULL;
10961 	return_error (API, GMT_NOERROR);
10962 }
10963 
gmtapi_destroy_vectors(struct GMTAPI_CTRL * API,struct GMT_VECTOR *** obj,unsigned int n_items)10964 GMT_LOCAL int gmtapi_destroy_vectors (struct GMTAPI_CTRL *API, struct GMT_VECTOR ***obj, unsigned int n_items) {
10965 	/* Used to destroy a group of vectors read via GMT_Read_Group */
10966 	unsigned int k;
10967 	int error;
10968 	struct GMT_VECTOR **V = *obj;
10969 	for (k = 0; k < n_items; k++) if ((error = GMT_Destroy_Data (API, &V[k]))) return_error (API, error);
10970 	gmt_M_free (API->GMT, V);	*obj = NULL;
10971 	return_error (API, GMT_NOERROR);
10972 }
10973 
gmtapi_void3_to_void2(void *** p)10974 GMT_LOCAL void ** gmtapi_void3_to_void2 (void ***p) { return (*p); }	/* To avoid warnings and troubles */
10975 
10976 /*! . */
GMT_Destroy_Group(void * V_API,void * object,unsigned int n_items)10977 int GMT_Destroy_Group (void *V_API, void *object, unsigned int n_items) {
10978 	/* Destroy an array of resources that are no longer needed.
10979 	 * Returns the error code.
10980 	 */
10981 	int error, object_ID, item;
10982 	void **ptr = NULL;
10983 	struct GMTAPI_CTRL *API = NULL;
10984 
10985 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);	/* This is a cardinal sin */
10986 	if (object == NULL) return (false);	/* Null address, quietly skip */
10987 	API = gmtapi_get_api_ptr (V_API);		/* Now we need to get that API pointer to check further */
10988 	ptr = gmtapi_void3_to_void2 (object);		/* Get the array of pointers */
10989 	if ((object_ID = gmtapi_get_object_id_from_data_ptr (API, ptr)) == GMT_NOTSET) return_error (API, GMT_OBJECT_NOT_FOUND);	/* Could not find the object in the list */
10990 	if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, GMT_NOTSET, GMT_NOTSET)) == GMT_NOTSET) return_error (API, API->error);	/* Could not find that item */
10991 	switch (API->object[item]->actual_family) {
10992 		case GMT_IS_GRID:       error = gmtapi_destroy_grids       (API, object, n_items); break;
10993 		case GMT_IS_DATASET:    error = gmtapi_destroy_datasets    (API, object, n_items); break;
10994 		case GMT_IS_IMAGE:      error = gmtapi_destroy_images      (API, object, n_items); break;
10995 		case GMT_IS_PALETTE:    error = gmtapi_destroy_palettes    (API, object, n_items); break;
10996 		case GMT_IS_CUBE:   error = gmtapi_destroy_cubes   (API, object, n_items); break;
10997 		case GMT_IS_POSTSCRIPT: error = gmtapi_destroy_postscripts (API, object, n_items); break;
10998 		case GMT_IS_MATRIX:     error = gmtapi_destroy_matrices    (API, object, n_items); break;
10999 		case GMT_IS_VECTOR:     error = gmtapi_destroy_vectors     (API, object, n_items); break;
11000 		default: return_error (API, GMT_NOT_A_VALID_FAMILY); break;
11001 	}
11002 	return_error (API, error);
11003 }
11004 
11005 #ifdef FORTRAN_API
GMT_Destroy_Group_(void * object,unsigned int * n_items)11006 int GMT_Destroy_Group_ (void *object, unsigned int *n_items) {
11007 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11008 	return (GMT_Destroy_Group (GMT_FORTRAN, object, *n_items));
11009 }
11010 #endif
11011 
11012 /*! . */
GMT_Create_Data(void * V_API,unsigned int family,unsigned int geometry,unsigned int mode,uint64_t dim[],double * range,double * inc,unsigned int registration,int pad,void * data)11013 void * GMT_Create_Data (void *V_API, unsigned int family, unsigned int geometry, unsigned int mode, uint64_t dim[], double *range, double *inc, unsigned int registration, int pad, void *data) {
11014 	/* Create an empty container of the requested kind and allocate space for content.
11015 	 * The known families are GMT_IS_{DATASET,GRID,PALETTE,IMAGE,POSTSCRIPT}, but we
11016 	 * also allow for creation of the containers for GMT_IS_{VECTOR,MATRIX}. Note
11017 	 * that for VECTOR|MATRIX we don't allocate space to hold data as it is the users
11018 	 * responsibility to hook their data pointers in.  The VECTOR allocates the array
11019 	 * of column vector type and data pointers.
11020 	 * Geometry should reflect the resource, e.g. GMT_IS_SURFACE for grid, etc.
11021 	 * There are two ways to define the dimensions needed to actually allocate memory:
11022 	 * (A) Via uint64_t dim[]:
11023 	 *     The dim array contains up to 4 dimensions for:
11024 	 *	   0: dim[GMT_TBL] = number of tables,
11025 	 *	   1: dim[GMT_SEG] = number of segments per table
11026 	 *	   2: dim[GMT_ROW] = number of rows per segment.
11027 	 *	   3: dim[GMT_COL] = number of columns per row.
11028 	 *     The dim array is ignored for CPTs.
11029 	 *     For GMT_IS_IMAGE & GMT_IS_MATRIX, par[GMT_Z] = GMT[2] holds the number of bands or layers (dim == NULL means just 1).
11030 	 *     For GMT_IS_GRID, GMT_IS_IMAGE, & GMT_IS_MATRIX: dim[0] holds the number of columns and dim[1] holds the number
11031 	 *         of rows; this implies that wesn = 0-<dim-1>, inc = 1, and registration is pixel-registration.
11032 	 *     For GMT_IS_VECTOR, dim[0] holds the number of columns, optionally dim[1] holds number of rows, if known, or 0.
11033 	 *	   dim[2] can hold the data type (GMT_DOUBLE, etc). If dim[1] > 0 then we allocate the rows.
11034 	 * (B) Via range, inc, registration:
11035 	 *     Convert user domain range, increments, and registration into dimensions
11036 	 *     for the container.  For grids and images we fill out the GMT_GRID_HEADER;
11037 	 *     for vectors and matrices we fill out their internal parameters.
11038 	 *     For complex grids pass registration + GMT_GRID_IS_COMPLEX_{REAL|IMAG}
11039 	 *     For GMT_IS_MATRIX and GMT_IS_IMAGE, dim[GMT_Z] = holds the number of layers or bands (dim == NULL means just 1),
11040 	 *     and dim[3] holds the data type (dim == NULL means GMT_DOUBLE).
11041 	 *     For GMT_IS_VECTOR, dim[GMT_Z] holds the data type (dim == NULL means GMT_DOUBLE).
11042 	 * pad sets the padding for grids and images, while for matrices it can be
11043 	 *     0 for the default row/col orientation
11044 	 *     1 for row-major format (C)
11045 	 *     2 for column major format (Fortran)
11046 	   pad is ignored for other resources.
11047 	 * Some default actions for grids:
11048 	 * range = NULL: Select current -R setting if present.
11049 	 * registration = GMT_NOTSET: Gridline unless -r is in effect.
11050 	 * Give -1 (GMT_NOTSET) to accept GMT default padding [2].
11051 	 *
11052 	 * For creating grids and images you can do it in one or two steps:
11053  	 * (A) Pass mode = GMT_CONTAINER_AND_DATA; this creates both header and allocates grid|image;
11054 	 * (B) Call GMT_Create_Data twice:
11055 	 * 	1. First with mode = GMT_CONTAINER_ONLY which creates header only
11056 	 *	   and computes the dimensions based on the other arguments.
11057 	 *	2. 2nd with mode = GMT_DATA_ONLY, which allocates the grid|image array
11058 	 *	   based on the dimensions already set.  This time you pass NULL/0
11059 	 *	   for dim, wesn, inc, registration, pad but let data be your grid|image
11060 	 *	   returned to you after step 1.
11061 	 *
11062 	 * By default, the created resource is consider an input resource (direction == GMT_IN).
11063 	 * However, for the interface containers GMT_VECTOR and GMT_MATRIX they will have their
11064 	 * direction set to GMT_OUT if the row-dimension is not set.
11065 	 *
11066 	 * For containers GMT_IS_DATASET, GMT_IS_MATRIX and GMT_IS VECTOR: If you add the constant
11067 	 * GMT_WITH_STRINGS to the mode it will allocate the corresponding arrays of string pointers.
11068 	 * You can then add actual strings in addition to data values.  Note: GMT will assume
11069 	 * the individual strings was allocated using functions like malloc or strdup and will
11070 	 * free them when the container goes out of scope.  If you don't want that to happen then
11071 	 * you must set those pointers to NULL beforehand.
11072 	 *
11073 	 * Return: Pointer to resource, or NULL if an error (set via API->error).
11074 	 */
11075 
11076 	int error = GMT_NOERROR;
11077 	int def_direction = GMT_IN;	/* Default direction is GMT_IN  */
11078 	unsigned int module_input, actual_family, i_mode;
11079 	uint64_t n_layers = 0, zero_dim[4] = {0, 0, 0, 0}, *this_dim = dim;
11080 	int64_t n_cols = 0;
11081 	bool already_registered = false, has_ID = false;
11082 	struct GMT_CUBE *C = NULL;
11083 	struct GMT_CUBE_HIDDEN *HU = NULL;
11084 	struct GMT_GRID *G = NULL;
11085 	void *new_obj = NULL;
11086 	struct GMTAPI_CTRL *API = NULL;
11087 
11088 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
11089 	API = gmtapi_get_api_ptr (V_API);
11090 	API->error = GMT_NOERROR;
11091 	i_mode = (family & GMT_IMAGE_ALPHA_LAYER);
11092 	family -= i_mode;
11093 	module_input = (family & GMT_VIA_MODULE_INPUT);	/* Are we creating a resource that is a module input? */
11094 	family -= module_input;
11095 	actual_family = gmtapi_separate_families (&family);
11096 
11097 	if (mode & GMT_IS_OUTPUT) {	/* Flagged to be an output container */
11098 		def_direction = GMT_OUT;	/* Set output as default direction*/
11099 		if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data pointer is not NULL */
11100 		if (dim && !(actual_family == GMT_IS_MATRIX || actual_family == GMT_IS_VECTOR))
11101 			return_null (API, GMT_PTR_NOT_NULL);	/* Error if dim pointer is not NULL except for matrix and vector */
11102 		if (this_dim == NULL) this_dim = zero_dim;	/* Provide dimensions set to zero */
11103 	}
11104 
11105 	if (mode & GMT_DATA_IS_GEO) gmt_set_geographic (API->GMT, def_direction);	/* From API to tell the data are geographic */
11106 
11107 	/* Below, data can only be non-NULL for Grids or Images passing back G or I to allocate the data array */
11108 
11109 	switch (actual_family) {	/* dataset, cpt, text, grid , image, vector, matrix */
11110 		case GMT_IS_GRID:	/* GMT grid, allocate header but not data array */
11111 			if (mode & GMT_WITH_STRINGS) return_null (API, GMT_NO_STRINGS_ALLOWED);	/* Error if given unsuitable mode */
11112 			if (mode & GMT_IS_OUTPUT || (mode & GMT_DATA_ONLY) == 0) {	/* Create new grid unless we only ask for data only */
11113 				if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data pointer is not NULL */
11114 	 			if ((new_obj = gmt_create_grid (API->GMT)) == NULL)
11115 	 				return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11116 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, pad);	/* Change the default pad; give -1 to leave as is */
11117 				if ((error = gmtapi_init_grid (API, NULL, this_dim, range, inc, registration, mode, def_direction, new_obj)))
11118 					return_null (API, error);
11119 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, API->pad);	/* Reset to the default pad */
11120 			}
11121 			else {	/* Already registered so has_ID must be false */
11122 				if (has_ID || (new_obj = data) == NULL)
11123 					return_null (API, GMT_PTR_IS_NULL);	/* Error if data pointer is NULL */
11124 				already_registered = true;
11125 			}
11126 			if (def_direction == GMT_IN && (mode & GMT_CONTAINER_ONLY) == 0) {	/* Allocate the grid array unless we asked for header only */
11127 				if ((error = gmtapi_alloc_grid (API->GMT, new_obj)) != GMT_NOERROR)
11128 					return_null (API, error);	/* Allocation error */
11129 				/* Also allocate and populate the x,y vectors */
11130 				if ((error = gmtapi_alloc_grid_xy (API, new_obj)) != GMT_NOERROR)
11131 					return_null (API, error);	/* Allocation error */
11132 			}
11133 			break;
11134 		case GMT_IS_IMAGE:	/* GMT image, allocate header but not data array */
11135 			if (mode & GMT_WITH_STRINGS) return_null (API, GMT_NO_STRINGS_ALLOWED);	/* Error if given unsuitable mode */
11136 			if (mode & GMT_IS_OUTPUT || (mode & GMT_DATA_ONLY) == 0) {	/* Create new image unless we only ask for data only */
11137 				if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11138 	 			if ((new_obj = gmtlib_create_image (API->GMT)) == NULL)
11139 	 				return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11140 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, pad);	/* Change the default pad; give -1 to leave as is */
11141 				if ((error = gmtapi_init_image (API, NULL, this_dim, range, inc, registration, mode, def_direction, new_obj)))
11142 					return_null (API, error);
11143 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, API->pad);	/* Reset to the default pad */
11144 			}
11145 			else {
11146 				if ((new_obj = data) == NULL)
11147 					return_null (API, GMT_PTR_IS_NULL);	/* Error if data is NULL */
11148 				already_registered = true;
11149 			}
11150 			if (def_direction == GMT_IN && (mode & GMT_CONTAINER_ONLY) == 0) {	/* Allocate the image array unless we asked for header only */
11151 				if ((error = gmtapi_alloc_image (API->GMT, dim, i_mode, new_obj)) != GMT_NOERROR)
11152 					return_null (API, error);	/* Allocation error */
11153 					/* Also allocate and populate the image x,y vectors */
11154 				if ((error = gmtapi_alloc_image_xy (API, new_obj)) != GMT_NOERROR)
11155 					return_null (API, error);	/* Allocation error */
11156 			}
11157 			break;
11158 		case GMT_IS_DATASET:	/* GMT dataset, allocate the requested tables, segments, rows, and columns */
11159 			if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11160 			if (this_dim[GMT_TBL] > UINT_MAX || this_dim[GMT_ROW] > UINT_MAX)
11161 				return_null (API, GMT_DIM_TOO_LARGE);
11162 			/* We basically create a blank slate(s), with n_tables, n_segments, and n_columns set [unless 0], but all n_rows == 0 (even if known; S->n_alloc has the lengths) */
11163 			if ((new_obj = gmtlib_create_dataset (API->GMT, this_dim[GMT_TBL], this_dim[GMT_SEG], this_dim[GMT_ROW], this_dim[GMT_COL], geometry, mode & GMT_WITH_STRINGS, false)) == NULL) {
11164 				return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11165 			}
11166 			else if (gmt_M_is_geographic (API->GMT, def_direction)) {	/* Got a geographic data set, set hidden flag */
11167 				struct GMT_DATASET *D = gmtapi_get_dataset_data (new_obj);
11168 				struct GMT_DATASET_HIDDEN *DH = gmt_get_DD_hidden (D);
11169 				DH->geographic = 1;
11170 			}
11171 			break;
11172 		case GMT_IS_PALETTE:	/* GMT CPT, allocate one with space for dim[0] color entries */
11173 			if (mode & GMT_WITH_STRINGS) return_null (API, GMT_NO_STRINGS_ALLOWED);	/* Error if given unsuitable mode */
11174 			if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11175 			/* If dim is NULL then we ask for 0 color entries as direction here is GMT_OUT for return to an external API */
11176 		 	if ((new_obj = gmtlib_create_palette (API->GMT, this_dim[0])) == NULL)
11177 		 		return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11178 			break;
11179 		case GMT_IS_POSTSCRIPT:	/* GMT PS struct, allocate one struct */
11180 			if (mode & GMT_WITH_STRINGS) return_null (API, GMT_NO_STRINGS_ALLOWED);	/* Error if given unsuitable mode */
11181 			if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11182 		 	if ((new_obj = gmtlib_create_ps (API->GMT, this_dim[0])) == NULL)
11183 		 		return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11184 			break;
11185 		case GMT_IS_MATRIX:	/* GMT matrix container, allocate one with the requested number of layers, rows & columns */
11186 			if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11187 			n_layers = (this_dim == NULL || (this_dim[GMTAPI_DIM_COL] == 0 && this_dim[GMTAPI_DIM_ROW] == 0)) ? 1U : this_dim[GMT_Z];	/* Only by specifying nx,ny dimension might there be > 1 layer */
11188 		 	new_obj = gmtlib_create_matrix (API->GMT, n_layers, def_direction, pad);
11189 			if ((API->error = gmtapi_init_matrix (API, this_dim, range, inc, registration, mode, def_direction, new_obj))) {	/* Failure, must free the object */
11190 				struct GMT_MATRIX *M = gmtapi_return_address (new_obj, GMT_IS_MATRIX);	/* Get pointer to resource */
11191 				gmtlib_free_matrix (API->GMT, &M, true);
11192 		 		return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11193 			}
11194 			break;
11195 		case GMT_IS_VECTOR:	/* GMT vector container, allocate one with the requested number of columns & rows */
11196 			if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data is not NULL */
11197 			n_cols = gmtapi_vector_ncols (dim, def_direction);
11198 			if (n_cols == GMT_NOTSET) return_null (API, GMT_N_COLS_NOT_SET);
11199 	 		new_obj = gmt_create_vector (API->GMT, n_cols, def_direction);
11200 			if (pad) GMT_Report (API, GMT_MSG_DEBUG, "Pad argument (%d) ignored in initialization of %s\n", pad, GMT_family[family]);
11201 			if ((API->error = gmtapi_init_vector (API, this_dim, range, inc, registration, mode, def_direction, new_obj))) {	/* Failure, must free the object */
11202 				struct GMT_VECTOR *V = gmtapi_return_address (new_obj, GMT_IS_VECTOR);	/* Get pointer to resource */
11203 				gmt_free_vector (API->GMT, &V, true);
11204 		 		return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11205 			}
11206 			break;
11207 		case GMT_IS_CUBE:
11208 			if (mode & GMT_WITH_STRINGS) return_null (API, GMT_NO_STRINGS_ALLOWED);	/* Error if given unsuitable mode */
11209 			if (mode & GMT_IS_OUTPUT || (mode & GMT_DATA_ONLY) == 0) {	/* Create new cube unless we only ask for data only */
11210 				if (data) return_null (API, GMT_PTR_NOT_NULL);	/* Error if data pointer is not NULL */
11211 	 			if ((C = gmtlib_create_cube (API->GMT)) == NULL)
11212 	 				return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11213 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, pad);	/* Change the default pad; give -1 to leave as is */
11214 				if ((G = gmt_create_grid (API->GMT)) == NULL)
11215 		 			return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11216 				if ((error = gmtapi_init_grid (API, NULL, this_dim, range, inc, registration, mode, def_direction, G)))
11217 					return_null (API, error);
11218 				if (pad != GMT_NOTSET) gmt_set_pad (API->GMT, API->pad);	/* Reset to the default pad */
11219 				if (def_direction == GMT_IN) {
11220 					if (range == NULL) return_null (API, GMT_PTR_IS_NULL);	/* Need at least the z-range for cubes */
11221 					gmt_copy_gridheader (API->GMT, C->header, G->header);
11222 					C->z_range[0] = range[ZLO];	C->z_range[1] = range[ZHI];
11223 					if (inc && inc[GMT_Z] > 0.0) {	/* Must make equidistant array, else we lave it as NULL to be set by calling module */
11224 						HU = gmt_get_U_hidden (C);
11225 						C->header->n_bands = gmt_make_equidistant_array (API->GMT, range[ZLO], range[ZHI], inc[GMT_Z], &(C->z));
11226 						C->z_inc = inc[GMT_Z];
11227 						HU->xyz_alloc_mode[GMT_Z] = GMT_ALLOC_INTERNALLY;
11228 					}
11229 				}
11230 			}
11231 			else {	/* Already registered so has_ID must be false */
11232 				if (has_ID || (C = data) == NULL)
11233 					return_null (API, GMT_PTR_IS_NULL);	/* Error if data pointer is NULL */
11234 				already_registered = true;
11235 			}
11236 			if (def_direction == GMT_IN && (mode & GMT_CONTAINER_ONLY) == 0) {	/* Allocate the grid array unless we asked for header only */
11237 				size_t chunk = C->header->size * ((size_t)C->header->n_bands);	/* Total memory needed for the entire cube */
11238 				if ((C->data = gmt_M_memory_aligned (API->GMT, NULL, chunk, gmt_grdfloat)) == NULL)
11239 					return_null (API, error);	/* Allocation error */
11240 				/* Also allocate and populate the x,y vectors */
11241 				if ((error = gmtapi_alloc_grid_xy (API, G)) != GMT_NOERROR)
11242 					return_null (API, error);	/* Allocation error */
11243 				C->x = G->x;	C->y = G->y;	/* Let these be the cube's from now on */
11244 				G->x = G->y = NULL;	/* No longer anything to do with G */
11245 				HU = gmt_get_U_hidden (C);
11246 				HU->xyz_alloc_mode[GMT_X] = HU->xyz_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
11247 				if (gmtapi_destroy_grid (API, &G))	/* Use this instead of GMT_Destroy_Data since G was local and never registered */
11248 		 			return_null (API, GMT_MEMORY_ERROR);	/* Allocation error */
11249 			}
11250 			new_obj = C;	/* Finally assign to new_obj */
11251 			break;
11252 
11253 		default:
11254  			return_null (API, GMT_NOT_A_VALID_FAMILY);
11255 			break;
11256 	}
11257 	assert (API->error == GMT_NOERROR);	/* All errors were dealt with so why this? */
11258 
11259 	if (!already_registered) {	/* Register this object so it can be deleted by GMT_Destroy_Data or gmtlib_garbage_collection */
11260 		enum GMT_enum_method method = (mode & GMT_IS_OUTPUT) ? GMT_IS_DUPLICATE : GMT_IS_REFERENCE;	/* Since it is a memory object */
11261 		int item = GMT_NOTSET, object_ID = GMT_NOTSET;
11262 		struct GMTAPI_DATA_OBJECT *S_obj = NULL;
11263 		if ((object_ID = GMT_Register_IO (API, actual_family|module_input, method, geometry, def_direction, range, new_obj)) == GMT_NOTSET)
11264 			return_null (API, API->error);	/* Failure to register */
11265 		if ((item = gmtlib_validate_id (API, actual_family, object_ID, def_direction, GMT_NOTSET)) == GMT_NOTSET)
11266 			return_null (API, API->error);
11267 		S_obj = API->object[item];		/* Short-hand notation */
11268 		API->object[item]->resource = new_obj;	/* Retain pointer to the allocated data so we use garbage collection later */
11269 		S_obj->actual_family = actual_family;
11270 		S_obj->family = family;
11271 		if (def_direction == GMT_OUT) S_obj->messenger = true;	/* We are passing a dummy container that should be destroyed before returning actual data */
11272 		if (family == actual_family)
11273 			GMT_Report (API, GMT_MSG_DEBUG, "Successfully created a new %s container\n", GMT_family[actual_family]);
11274 		else
11275 			GMT_Report (API, GMT_MSG_DEBUG, "Successfully created a new %s container to represent a %s\n", GMT_family[actual_family], GMT_family[family]);
11276 #ifdef DEBUG
11277 		gmtapi_set_object (API, S_obj);
11278 #endif
11279 	}
11280 	else
11281 		GMT_Report (API, GMT_MSG_DEBUG, "Successfully added data array to previously registered %s container\n", GMT_family[family]);
11282 #ifdef DEBUG
11283 	gmtapi_list_objects (API, "GMT_Create_Data");
11284 #endif
11285 
11286 	return (new_obj);
11287 }
11288 
11289 #ifdef FORTRAN_API
GMT_Create_Data_(unsigned int * family,unsigned int * geometry,unsigned int * mode,uint64_t * dim,double * range,double * inc,unsigned int * registration,int * pad,void * container)11290 void * GMT_Create_Data_ (unsigned int *family, unsigned int *geometry, unsigned int *mode, uint64_t *dim, double *range, double *inc, unsigned int *registration, int *pad, void *container) {
11291 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11292 	return (GMT_Create_Data (GMT_FORTRAN, *family, *geometry, *mode, dim, range, inc, *registration, *pad, container));
11293 }
11294 #endif
11295 
GMT_Get_Info(void * V_API,unsigned int family,void * data,unsigned int * geometry,uint64_t dim[],double * range,double * inc,unsigned int * registration,int * pad)11296 int GMT_Get_Info (void *V_API, unsigned int family, void *data, unsigned int *geometry, uint64_t dim[], double *range, double *inc, unsigned int *registration, int *pad) {
11297 	/* Return information for this object identified by family and data pointer.
11298 	 * The known families are GMT_IS_{DATASET,GRID,PALETTE,IMAGE,POSTSCRIPT,GMT_IS_CUBE,GMT_IS_{VECTOR,MATRIX}.
11299 	 * Not all output args may be set as it depends on the family, and any output argument that
11300 	 * is NULL is always skipped.  This function is mostly useful for applications where the containers
11301 	 * are not easily inspected directly, e.g., we are calling from another programming language.
11302 	 */
11303 
11304 	struct GMTAPI_CTRL *API = NULL;
11305 
11306 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
11307 	if (data == NULL) return_error (API, GMT_PTR_IS_NULL);	/* Error if data is NULL */
11308 	API = gmtapi_get_api_ptr (V_API);
11309 	API->error = GMT_NOERROR;
11310 
11311 	switch (family) {	/* dataset, cpt, text, grid , image, vector, matrix */
11312 		case GMT_IS_GRID:	/* GMT grid, allocate header but not data array */
11313 			{	/* Deal with a local grid pointer */
11314 				struct GMT_GRID *G = gmtapi_get_grid_data (data);
11315 				if (dim) { dim[GMT_X] = G->header->n_columns; dim[GMT_Y] = G->header->n_rows; }
11316 				if (range) gmt_M_memcpy (range, G->header->wesn, 4U, double);
11317 				if (inc) gmt_M_memcpy (inc, G->header->inc, 2U, double);
11318 				if (geometry) *geometry = GMT_IS_SURFACE;
11319 				if (registration) *registration = G->header->registration;
11320 				if (pad) {	/* Need to check they are all the same, if not return undefined or something */
11321 					unsigned int bad = 0, k;
11322 					for (k = XHI; k <= YHI; k++) if (G->header->pad[k] != G->header->pad[XLO]) bad++;
11323 					if (bad) {
11324 						GMT_Report (API, GMT_MSG_WARNING, "Grid sides have different padding, return pad as not set [-1]\n");
11325 						*pad = GMT_NOTSET;
11326 					}
11327 					else
11328 						*pad = G->header->pad[XLO];
11329 				}
11330 			}
11331 			break;
11332 		case GMT_IS_IMAGE:	/* GMT image, allocate header but not data array */
11333 			{	/* Deal with a local image pointer */
11334 				struct GMT_IMAGE *I = gmtapi_get_image_data (data);
11335 				if (dim) { dim[GMT_X] = I->header->n_columns; dim[GMT_Y] = I->header->n_rows; dim[GMT_Z] = I->header->n_bands; }
11336 				if (range) gmt_M_memcpy (range, I->header->wesn, 4U, double);
11337 				if (inc) gmt_M_memcpy (inc, I->header->inc, 2U, double);
11338 				if (geometry) *geometry = GMT_IS_IMAGE;
11339 				if (registration) *registration = I->header->registration;
11340 				if (pad) {	/* Need to check they are all the same, if not return undefined or something */
11341 					unsigned int bad = 0, k;
11342 					for (k = XHI; k <= YHI; k++) if (I->header->pad[k] != I->header->pad[XLO]) bad++;
11343 					if (bad) {
11344 						GMT_Report (API, GMT_MSG_WARNING, "Image sides have different padding, return pad as not set [-1]\n");
11345 						*pad = GMT_NOTSET;
11346 					}
11347 					else
11348 						*pad = I->header->pad[XLO];
11349 				}
11350 			}
11351 			break;
11352 		case GMT_IS_DATASET:	/* GMT dataset, allocate the requested tables, segments, rows, and columns */
11353 			{	/* Deal with a local image pointer */
11354 				struct GMT_DATASET *D = gmtapi_get_dataset_data (data);
11355 				if (dim) { dim[GMT_TBL] = D->n_tables; dim[GMT_SEG] = D->n_segments; dim[GMT_ROW] = D->n_records;  dim[GMT_COL] = D->n_columns; }
11356 				if (geometry) *geometry = D->geometry;
11357 			}
11358 			break;
11359 		case GMT_IS_PALETTE:	/* GMT CPT, allocate one with space for dim[0] color entries */
11360 			{	/* Deal with a local palette pointer */
11361 				struct GMT_PALETTE *P = gmtapi_get_palette_data (data);
11362 				if (dim) dim[0] = P->n_colors;
11363 				if (range) gmt_M_memcpy (range, P->minmax, 2U, double);
11364 				if (geometry) *geometry = GMT_IS_NONE;
11365 			}
11366 			break;
11367 		case GMT_IS_POSTSCRIPT:	/* GMT PS struct, allocate one struct */
11368 			{	/* Deal with a local PostScript pointer */
11369 				struct GMT_POSTSCRIPT *X = gmtapi_get_postscript_data (data);
11370 				if (dim) dim[0] = X->n_bytes;
11371 				if (geometry) *geometry = GMT_IS_NONE;
11372 			}
11373 			break;
11374 		case GMT_IS_CUBE:	/* GMT cube, allocate header but not data array */
11375 			{	/* Deal with a local grid pointer */
11376 				struct GMT_CUBE *U = gmtapi_get_cube_data (data);
11377 				if (dim) { dim[GMT_X] = U->header->n_columns; dim[GMT_Y] = U->header->n_rows; dim[GMT_Z] = U->header->n_bands; }
11378 				if (range) {
11379 					gmt_M_memcpy (range, U->header->wesn, 4U, double);
11380 					gmt_M_memcpy (&range[4], U->z_range, 2U, double);
11381 				}
11382 				if (inc) {
11383 					gmt_M_memcpy (inc, U->header->inc, 2U, double);
11384 					inc[GMT_Z] = U->z_inc;
11385 				}
11386 				if (geometry) *geometry = GMT_IS_VOLUME;
11387 				if (registration) *registration = U->header->registration;
11388 				if (pad) {	/* Need to check they are all the same, if not return undefined or something */
11389 					unsigned int bad = 0, k;
11390 					for (k = XHI; k <= YHI; k++) if (U->header->pad[k] != U->header->pad[XLO]) bad++;
11391 					if (bad) {
11392 						GMT_Report (API, GMT_MSG_WARNING, "Cube x/y sides have different padding, return pad as not set [-1]\n");
11393 						*pad = GMT_NOTSET;
11394 					}
11395 					else
11396 						*pad = U->header->pad[XLO];
11397 				}
11398 			}
11399 			break;
11400 		case GMT_IS_MATRIX:	/* GMT matrix container, allocate one with the requested number of layers, rows & columns */
11401 			{	/* Deal with a local matrix pointer */
11402 				struct GMT_MATRIX *M = gmtapi_get_matrix_data (data);
11403 				if (dim) { dim[GMT_X] = M->n_columns; dim[GMT_Y] = M->n_rows; dim[GMT_Z] = M->n_layers; }
11404 				if (range) gmt_M_memcpy (range, M->range, (M->n_layers > 1) ? 6U : 4U, double);
11405 				if (inc) gmt_M_memcpy (inc, M->inc, (M->n_layers > 1) ? 3U : 2U, double);
11406 				if (registration) *registration = M->registration;
11407 				if (geometry) *geometry = GMT_IS_SURFACE;
11408 			}
11409 			break;
11410 		case GMT_IS_VECTOR:	/* GMT vector container, allocate one with the requested number of columns & rows */
11411 			{	/* Deal with a local image pointer */
11412 				struct GMT_VECTOR *V = gmtapi_get_vector_data (data);
11413 				if (dim) { dim[GMT_X] = V->n_columns; dim[GMT_Y] = V->n_rows; }
11414 				if (range) gmt_M_memcpy (range, V->range, 2U, double);
11415 				if (registration) *registration = V->registration;
11416 				if (geometry) *geometry = GMT_IS_PLP;
11417 			}
11418 			break;
11419 		default:
11420  			return_error (API, GMT_NOT_A_VALID_FAMILY);
11421 			break;
11422 	}
11423 
11424 	return (API->error);
11425 }
11426 
11427 #ifdef FORTRAN_API
GMT_Get_Info_(unsigned int * family,void * container,unsigned int * geometry,uint64_t * dim,double * range,double * inc,unsigned int * registration,int * pad)11428 int GMT_Get_Info_ (unsigned int *family, void *container, unsigned int *geometry, uint64_t *dim, double *range, double *inc, unsigned int *registration, int *pad) {
11429 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11430 	return (GMT_Get_Info (GMT_FORTRAN, *family, container, geometry, dim, range, inc, registration, pad));
11431 }
11432 #endif
11433 
11434 /*! Convenience function to get grid or image node */
GMT_Get_Index(void * V_API,struct GMT_GRID_HEADER * header,int row,int col)11435 uint64_t GMT_Get_Index (void *V_API, struct GMT_GRID_HEADER *header, int row, int col) {
11436 	/* V_API not used but all API functions take V_API so no exceptions! */
11437 	gmt_M_unused(V_API);
11438 	return (GMTAPI_index_function (header, row, col, 0));
11439 }
11440 
11441 #ifdef FORTRAN_API
GMT_Get_Index_(void * h,int * row,int * col)11442 uint64_t GMT_Get_Index_ (void *h, int *row, int *col) {
11443 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11444 	return (GMT_Get_Index (GMT_FORTRAN, h, *row, *col));
11445 }
11446 #endif
11447 
11448 /*! Convenience function to get image layer node */
GMT_Get_Pixel(void * V_API,struct GMT_GRID_HEADER * header,int row,int col,int layer)11449 uint64_t GMT_Get_Pixel (void *V_API, struct GMT_GRID_HEADER *header, int row, int col, int layer) {
11450 	/* V_API not used but all API functions take V_API so no exceptions! */
11451 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
11452 	gmt_M_unused(V_API);
11453 	return (HH->index_function (header, row, col, layer));
11454 }
11455 
11456 #ifdef FORTRAN_API
GMT_Get_Pixel_(void * h,int * row,int * col,int * layer)11457 uint64_t GMT_Get_Pixel_ (void *h, int *row, int *col, int *layer) {
11458 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11459 	return (GMT_Get_Pixel (GMT_FORTRAN, h, *row, *col, *layer));
11460 }
11461 #endif
11462 
11463 /*! Convenience function to get cube node */
GMT_Get_Index3(void * V_API,struct GMT_GRID_HEADER * header,int row,int col,int layer)11464 uint64_t GMT_Get_Index3 (void *V_API, struct GMT_GRID_HEADER *header, int row, int col, int layer) {
11465 	/* V_API not used but all API functions take V_API so no exceptions! */
11466 	gmt_M_unused(V_API);
11467 	return (GMTAPI_index_function (header, row, col, 0) + layer * header->size);
11468 }
11469 
11470 #ifdef FORTRAN_API
GMT_Get_Index3_(void * h,int * row,int * col,int * layer)11471 uint64_t GMT_Get_Index3_ (void *h, int *row, int *col, int *layer) {
11472 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11473 	return (GMT_Get_Index3 (GMT_FORTRAN, h, *row, *col, *layer));
11474 }
11475 #endif
11476 
11477 /*! Specify image memory layout */
GMT_Set_Index(void * V_API,struct GMT_GRID_HEADER * header,char * code)11478 int GMT_Set_Index (void *V_API, struct GMT_GRID_HEADER *header, char *code) {
11479 	struct GMTAPI_CTRL *API = NULL;
11480 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
11481 	enum GMT_enum_family family;
11482 	unsigned int mode;
11483 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
11484 	API = gmtapi_get_api_ptr (V_API);
11485 	API->error = GMT_NOERROR;
11486 	mode = gmtapi_decode_layout (API, code, &family);
11487 	switch (family) {
11488 		case GMT_IS_GRID:
11489 			switch (mode) {
11490 				case 0:	/* Default scanline C grid */
11491 					HH->index_function = gmtapi_get_index_from_TRS;
11492 					break;
11493 				case 4:	/* Same for real component in complex grid */
11494 					HH->index_function = gmtapi_get_index_from_TRR;
11495 					break;
11496 				case 8:	/* Same for imag component in complex grid */
11497 					HH->index_function = gmtapi_get_index_from_TRI;
11498 					break;
11499 				default:
11500 					GMT_Report (API, GMT_MSG_ERROR, "Unrecognized mode for grid layout [%u]\n", mode);
11501 					API->error = GMT_NOT_A_VALID_MODULE;
11502 					break;
11503 			}
11504 			break;
11505 		case GMT_IS_IMAGE:
11506 			switch (mode) {
11507 				case 0:	/* band-interleaved layout */
11508 					HH->index_function = gmtapi_get_index_from_TRB;
11509 					break;
11510 				case 4:	/* pixel-interleaved layout */
11511 					HH->index_function = gmtapi_get_index_from_TRP;
11512 					break;
11513 				case 8:	/* line-interleaved layout */
11514 					HH->index_function = gmtapi_get_index_from_TRL;
11515 					break;
11516 				default:
11517 					GMT_Report (API, GMT_MSG_ERROR, "Unrecognized mode for image layout [%u]\n", mode);
11518 					API->error = GMT_NOT_A_VALID_MODULE;
11519 					break;
11520 			}
11521 			break;
11522 		default:
11523 			GMT_Report (API, GMT_MSG_ERROR, "Unrecognized family for gmtapi_decode_layout [%s]\n", code);
11524 			API->error = GMT_NOT_A_VALID_FAMILY;
11525 			break;
11526 	}
11527 	GMTAPI_index_function = HH->index_function;
11528 	return API->error;
11529 }
11530 
11531 #ifdef FORTRAN_API
GMT_Set_Index_(void * h,char * code,int len)11532 int GMT_Set_Index_ (void *h, char *code, int len) {
11533 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11534 	return (GMT_Set_Index (GMT_FORTRAN, h, code));
11535 }
11536 #endif
11537 
11538 /*! . */
GMT_Get_Coord(void * V_API,unsigned int family,unsigned int dim,void * container)11539 double * GMT_Get_Coord (void *V_API, unsigned int family, unsigned int dim, void *container) {
11540 	/* Return an array of coordinates for the nodes along the specified dimension.
11541 	 * For GMT_GRID and GMT_IMAGE, dim is either 0 (GMT_X) or 1 (GMT_Y) while for
11542 	 * GMT_MATRIX it may be 2 (GMT_Z), provided the matrix has more than 1 layer.
11543 	 * For GMT_VECTOR that was registered as equidistant it will return coordinates
11544 	 * along the single dimension.
11545 	 * Cannot be used on other resources (GMT_DATASET, GMT_PALETTE).
11546 	 */
11547 	int object_ID, item;
11548 	double *coord = NULL;
11549 	struct GMTAPI_CTRL *API = NULL;
11550 
11551 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
11552 	if (container == NULL) return_null (V_API, GMT_ARG_IS_NULL);
11553 	API = gmtapi_get_api_ptr (V_API);
11554 	API->error = GMT_NOERROR;
11555 
11556 	switch (family) {	/* grid, image, or matrix */
11557 		case GMT_IS_GRID:	/* GMT grid */
11558 			if (dim > GMT_Y) return_null (API, GMT_DIM_TOO_LARGE);
11559 			coord = gmtapi_grid_coord (API, dim, container);
11560 			break;
11561 		case GMT_IS_IMAGE:	/* GMT image */
11562 			if (dim > GMT_Y) return_null (API, GMT_DIM_TOO_LARGE);
11563 			coord = gmtapi_image_coord (API, dim, container);
11564 			break;
11565 		case GMT_IS_VECTOR:	/* GMT vector */
11566 			if (dim != GMT_Y) return_null (API, GMT_DIM_TOO_LARGE);
11567 			coord = gmtapi_vector_coord (API, dim, container);
11568 			break;
11569 		case GMT_IS_MATRIX:	/* GMT matrix */
11570 			if (dim > GMT_Z) return_null (API, GMT_DIM_TOO_LARGE);
11571 			coord = gmtapi_matrix_coord (API, dim, container);
11572 			break;
11573 		default:
11574 			return_null (API, GMT_NOT_A_VALID_FAMILY);
11575 			break;
11576 	}
11577 	/* We register the coordinate array so that GMT_Destroy_Data can free them later */
11578 	if ((object_ID = GMT_Register_IO (V_API, GMT_IS_COORD, GMT_IS_COORD, GMT_IS_NONE, GMT_IN, NULL, coord)) == GMT_NOTSET)
11579 		return_null (API, API->error);
11580 	if ((item = gmtlib_validate_id (API, GMT_IS_COORD, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
11581 		return_null (API, API->error);
11582 	API->object[item]->resource = coord;	/* Retain pointer to the allocated data so we use garbage collection later */
11583 	GMT_Report (API, GMT_MSG_DEBUG, "Successfully created a new coordinate array for %s\n", GMT_family[family]);
11584 
11585 	return (coord);
11586 }
11587 
11588 #ifdef FORTRAN_API
GMT_Get_Coord_(unsigned int * family,unsigned int * dim,void * container)11589 double * GMT_Get_Coord_ (unsigned int *family, unsigned int *dim, void *container) {
11590 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11591 	return (GMT_Get_Coord (GMT_FORTRAN, *family, *dim, container));
11592 }
11593 #endif
11594 
11595 /*! . */
GMT_Set_Comment(void * V_API,unsigned int family,unsigned int mode,void * arg,void * container)11596 int GMT_Set_Comment (void *V_API, unsigned int family, unsigned int mode, void *arg, void *container) {
11597 	/* Set new header comment or grid command|remark to container */
11598 
11599 	int error = GMT_NOERROR;
11600 	struct GMTAPI_CTRL *API = NULL;
11601 
11602 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
11603 	if (container == NULL) return_error (V_API, GMT_ARG_IS_NULL);
11604 	if (arg == NULL) return_error (V_API, GMT_ARG_IS_NULL);
11605 	API = gmtapi_get_api_ptr (V_API);
11606 
11607 	switch (family) {	/* grid, image, dataset, cpt, PS or matrix */
11608 		case GMT_IS_GRID:	/* GMT grid */
11609 			gmtapi_grid_comment (API, mode, arg, container);
11610 			break;
11611 		case GMT_IS_IMAGE:	/* GMT image */
11612 			gmtapi_image_comment (API, mode, arg, container);
11613 			break;
11614 		case GMT_IS_DATASET:	/* GMT dataset */
11615 			gmtapi_dataset_comment (API, mode, arg, container);
11616 			break;
11617 		case GMT_IS_PALETTE:	/* GMT CPT */
11618 			gmtapi_cpt_comment (API, mode, arg, container);
11619 			break;
11620 		case GMT_IS_POSTSCRIPT:		/* GMT PS */
11621 			gmtapi_ps_comment (API, mode, arg, container);
11622 			break;
11623 		case GMT_IS_CUBE:	/* GMT cube */
11624 			gmtapi_cube_comment (API, mode, arg, container);
11625 			break;
11626 		case GMT_IS_VECTOR:	/* GMT Vector [PW: Why do we need these?]*/
11627 			gmtapi_vector_comment (API, mode, arg, container);
11628 			break;
11629 		case GMT_IS_MATRIX:	/* GMT Vector */
11630 			gmtapi_matrix_comment (API, mode, arg, container);
11631 			break;
11632 		default:
11633 			error = GMT_NOT_A_VALID_FAMILY;
11634 			break;
11635 	}
11636 	return_error (API, error);
11637 }
11638 
11639 /* FFT Extension: Functions available to do FFT work within the API */
11640 
11641 /*! . */
GMT_FFT_Option(void * V_API,char option,unsigned int dim,const char * string)11642 unsigned int GMT_FFT_Option (void *V_API, char option, unsigned int dim, const char *string) {
11643 	/* For programs that needs to do either 1-D or 2-D FFT work */
11644 	unsigned int d1 = dim - 1;	/* Index into the info text strings below for 1-D (0) and 2-D (1) case */
11645 	char *data_type[2] = {"table", "grid"}, *dim_name[2] = {"<n_columns>", "<n_columns>/<n_rows>"}, *trend_type[2] = {"line", "plane"};
11646 	char *dim_ref[2] = {"dimension", "dimensions"}, *linear_type[2] = {"linear", "planar"};
11647     struct GMTAPI_CTRL *API = NULL;
11648 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
11649 	if (dim > 2) return_error (V_API, GMT_DIM_TOO_LARGE);
11650 	if (dim == 0) return_error (V_API, GMT_DIM_TOO_SMALL);
11651     API = gmtapi_get_api_ptr (V_API);
11652 	if (string && string[0] == ' ') GMT_Report (V_API, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
11653 	GMT_Usage (API, 1, "\n-%c%s", option, GMT_FFT_OPT);
11654 	GMT_Usage (API, -2, "Choose or inquire about suitable %s %s for %u-D FFT, and set modifiers.", data_type[d1], dim_ref[d1], dim);
11655 	GMT_Usage (API, -2, "%s Setting the FFT %s. Append a directive:", GMT_LINE_BULLET, dim_ref[d1]);
11656 	GMT_Usage (API, 3, "a: Select %s promising the most accurate results.", dim_ref[d1]);
11657 	GMT_Usage (API, 3, "f: Force the FFT to use the %s of the %s.", dim_ref[d1], data_type[d1]);
11658 	GMT_Usage (API, 3, "m: Select %s using the least work storage.", dim_ref[d1]);
11659 	GMT_Usage (API, 3, "r: Select %s promising the most rapid calculation.", dim_ref[d1]);
11660 	GMT_Usage (API, 3, "s: List Singleton's [1967] recommended %s, then exit.", dim_ref[d1]);
11661 	GMT_Usage (API, -2, "Alternatively, append %s to do FFT on array size %s (Must be >= %s size) "
11662 	   "[Default chooses %s >= %s %s to optimize speed and accuracy of the FFT.]", dim_name[d1], dim_name[d1], data_type[d1], dim_ref[d1], data_type[d1], dim_ref[d1]);
11663 	GMT_Usage (API, -2, "%s Append modifiers for removing a %s trend:", GMT_LINE_BULLET, linear_type[d1]);
11664 	GMT_Usage (API, 3, "+d Detrend data, i.e., remove best-fitting %s [Default].", trend_type[d1]);
11665 	GMT_Usage (API, 3, "+a Only remove mean value.");
11666 	GMT_Usage (API, 3, "+h Only remove mid value, i.e., 0.5 * (max + min).");
11667 	GMT_Usage (API, 3, "+l Leave data alone.");
11668 	GMT_Usage (API, -2, "%s If FFT %s > %s %s, data are extended via edge point symmetry "
11669 	   "and tapered to zero.  Several modifiers can be set to change this behavior:", GMT_LINE_BULLET, dim_ref[d1], data_type[d1], dim_ref[d1]);
11670 	GMT_Usage (API, 3, "+e Extend data via edge point symmetry [Default].");
11671 	GMT_Usage (API, 3, "+m Extend data via edge mirror symmetry.");
11672 	GMT_Usage (API, 3, "+n Do NOT extend data.");
11673 	GMT_Usage (API, 3, "+t Limit tapering to <width> %% of the extended margins [100]. "
11674 	   "If +n is also set then +t instead sets the boundary width of the interior "
11675 	  "%s margin to be tapered [0].", data_type[d1]);
11676 	GMT_Usage (API, -2, "%s Append modifiers for saving modified %s before or after the %u-D FFT is called:", GMT_LINE_BULLET, data_type[d1], dim);
11677 	GMT_Usage (API, 3, "+w Write the intermediate %s passed to FFT after detrending/extension/tapering. "
11678 	  "File name will have _<suffix> [tapered] inserted before file extension.", data_type[d1]);
11679 	GMT_Usage (API, 3, "+z Write raw complex spectrum to two separate %s files. "
11680 	   "File name will have _real/_imag inserted before the file extensions. "
11681 	   "Alternatively, append p to store polar forms, using _mag/_phase instead.", data_type[d1]);
11682 	GMT_Usage (API, -2, "%s Append modifiers for messages:", GMT_LINE_BULLET);
11683 	GMT_Usage (API, 3, "+v Report all suitable dimensions (except when -Nf is selected).");
11684 
11685 	return_error (V_API, GMT_NOERROR);
11686 }
11687 
11688 #ifdef FORTRAN_API
GMT_FFT_Option_(char * option,unsigned int * dim,const char * string,int * length)11689 unsigned int GMT_FFT_Option_ (char *option, unsigned int *dim, const char *string, int *length) {
11690 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11691 	return (GMT_FFT_Option (GMT_FORTRAN, *option, *dim, string));
11692 }
11693 #endif
11694 
11695 /* first 2 cols from table III of Singleton's paper on fft.... */
11696 #define N_SINGLETON_LIST	117
11697 static int Singleton_list[N_SINGLETON_LIST] = {
11698 	64,72,75,80,81,90,96,100,108,120,125,128,135,144,150,160,162,180,192,200,
11699 	216,225,240,243,250,256,270,288,300,320,324,360,375,384,400,405,432,450,480,
11700 	486,500,512,540,576,600,625,640,648,675,720,729,750,768,800,810,864,900,960,
11701 	972,1000,1024,1080,1125,1152,1200,1215,1250,1280,1296,1350,1440,1458,1500,
11702 	1536,1600,1620,1728,1800,1875,1920,1944,2000,2025,2048,2160,2187,2250,2304,
11703 	2400,2430,2500,2560,2592,2700,2880,2916,3000,3072,3125,3200,3240,3375,3456,
11704 	3600,3645,3750,3840,3888,4000,4096,4320,4374,4500,4608,4800,4860,5000};
11705 
gmtapi_fft_Singleton_list(struct GMTAPI_CTRL * API)11706 GMT_LOCAL void gmtapi_fft_Singleton_list (struct GMTAPI_CTRL *API) {
11707 	unsigned int k;
11708 	char message[GMT_LEN16] = {""};
11709 	GMT_Message (API, GMT_TIME_NONE, "\t\"Good\" numbers for FFT dimensions [Singleton, 1967]:\n");
11710 	for (k = 0; k < N_SINGLETON_LIST; k++) {
11711 		snprintf (message, GMT_LEN16, "\t%d", Singleton_list[k]);
11712 		if ((k+1) % 10 == 0 || k == (N_SINGLETON_LIST-1)) strcat (message, "\n");
11713 		GMT_Message (API, GMT_TIME_NONE, message);
11714 	}
11715 }
11716 
11717 /*! . */
GMT_FFT_Parse(void * V_API,char option,unsigned int dim,const char * args)11718 void * GMT_FFT_Parse (void *V_API, char option, unsigned int dim, const char *args) {
11719 	/* Parse the 1-D or 2-D FFT options such as -N in grdfft */
11720 	unsigned int n_errors = 0, pos = 0;
11721 	char p[GMT_BUFSIZ] = {""}, *c = NULL;
11722 	struct GMT_FFT_INFO *info = NULL;
11723 	struct GMTAPI_CTRL *API = NULL;
11724 
11725 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
11726 	if (args == NULL) return_null (V_API, GMT_ARG_IS_NULL);
11727 	if (dim == 0) return_null (V_API, GMT_DIM_TOO_SMALL);
11728 	if (dim > 2) return_null (V_API, GMT_DIM_TOO_LARGE);
11729 	API = gmtapi_get_api_ptr (V_API);
11730 	API->error = GMT_NOERROR;
11731 	info = gmt_M_memory (API->GMT, NULL, 1, struct GMT_FFT_INFO);
11732 	info->taper_width = -1.0;				/* Not set yet */
11733 	info->taper_mode = GMT_FFT_EXTEND_NOT_SET;		/* Not set yet */
11734 	info->trend_mode = GMT_FFT_REMOVE_NOT_SET;		/* Not set yet */
11735 	info->info_mode = GMT_FFT_UNSPECIFIED;			/* Not set yet */
11736 	info->suggest = GMT_FFT_N_SUGGEST;				/* Not yet set */
11737 
11738 	if ((c = strchr (args, '+'))) {	/* Handle modifiers */
11739 		while ((gmt_strtok (c, "+", &pos, p))) {
11740 			switch (p[0]) {
11741 				/* Detrending modifiers */
11742 				case 'a':  info->trend_mode = GMT_FFT_REMOVE_MEAN;  break;
11743 				case 'd':  info->trend_mode = GMT_FFT_REMOVE_TREND; break;
11744 				case 'h':  info->trend_mode = GMT_FFT_REMOVE_MID;   break;
11745 				case 'l':  info->trend_mode = GMT_FFT_REMOVE_NOTHING;  break;
11746 				/* Taper modifiers */
11747 				case 'e':  info->taper_mode = GMT_FFT_EXTEND_POINT_SYMMETRY; break;
11748 				case 'n':  info->taper_mode = GMT_FFT_EXTEND_NONE; break;
11749 				case 'm':  info->taper_mode = GMT_FFT_EXTEND_MIRROR_SYMMETRY; break;
11750 				case 't':	/* Set taper width */
11751 					if ((info->taper_width = atof (&p[1])) < 0.0) {
11752 						GMT_Report (API, GMT_MSG_ERROR, "Option -%c: Negative taper width given\n", option);
11753 						n_errors++;
11754 					}
11755 					break;
11756 				/* i/o modifiers */
11757 				case 'w':	/* Save FFT input; optionally append file suffix */
11758 					info->save[GMT_IN] = true;
11759 					if (p[1]) strncpy (info->suffix, &p[1], GMT_LEN64-1);
11760 					break;
11761 				case 'v':  info->verbose = true; break;	/* Report FFT suggestions */
11762 				case 'z': 	/* Save FFT output in two files; append p for polar form */
11763 					info->save[GMT_OUT] = true;
11764 					if (p[1] == 'p') info->polar = true;
11765 					break;
11766 				default:
11767 					GMT_Report (API, GMT_MSG_ERROR, "Option -%c: Unrecognized modifier +%s.\n", option, p);
11768 					n_errors++;
11769 					break;
11770 			}
11771 		}
11772 	}
11773 	if (info->taper_mode == GMT_FFT_EXTEND_NOT_SET)
11774 		info->taper_mode = GMT_FFT_EXTEND_POINT_SYMMETRY;	/* Default action is edge-point symmetry */
11775 	if (info->taper_mode == GMT_FFT_EXTEND_NONE) {
11776 		if (info->taper_width < 0.0) info->taper_width = 0.0;	/* No tapering unless specified */
11777 	}
11778 	if (info->taper_width < 0.0)
11779 		info->taper_width = 100.0;		/* Taper over entire margin strip by default */
11780 
11781 	switch (args[0]) {
11782 		case '\0': info->suggest = GMT_FFT_N_SUGGEST;  break;	/* Pick dimensions for the "best" solution */
11783 		case 'a': info->suggest = GMT_FFT_ACCURATE;  break;	/* Pick dimensions for most accurate solution */
11784 		case 'f': info->info_mode = GMT_FFT_FORCE; break;	/* Default is force actual grid dimensions */
11785 		case 'm': info->suggest = GMT_FFT_STORAGE;  break;	/* Pick dimensions for minimum storage */
11786 		case 'q': info->verbose = true; break;	/* No longer a mode.  Backwards compatibility; see +v instead */
11787 		case 'r': info->suggest = GMT_FFT_FAST;  break;	/* Pick dimensions for most rapid solution */
11788 		case 's': info->info_mode = GMT_FFT_LIST;  break;
11789 		default:
11790 			if (dim == 2U) {	/* 2-D */
11791 				pos = sscanf (args, "%d/%d", &info->n_columns, &info->n_rows);
11792 				if (pos == 1) info->n_rows = info->n_columns;
11793 			}
11794 			else {	/* 1-D */
11795 				pos = sscanf (args, "%d", &info->n_columns);
11796 				info->n_rows = 0;
11797 			}
11798 			if (pos) info->info_mode = GMT_FFT_SET;
11799 	}
11800 	if (info->suffix[0] == '\0') strncpy (info->suffix, "tapered", GMT_LEN64-1);	/* Default suffix */
11801 	info->set = true;	/* We parsed this option */
11802 	if (info->info_mode == GMT_FFT_SET) {
11803 		if (dim == 2U && (info->n_columns <= 0 || info->n_rows <= 0)) {
11804 			GMT_Report (API, GMT_MSG_ERROR, "Option -%c: n_columns and/or n_rows are <= 0\n", option);
11805 			n_errors++;
11806 		}
11807 		else if (dim == 1U && info->n_columns <= 0) {
11808 			GMT_Report (API, GMT_MSG_ERROR, "Option -%c: n_columns is <= 0\n", option);
11809 			n_errors++;
11810 		}
11811 	}
11812 	if (info->taper_mode == GMT_FFT_EXTEND_NONE && info->taper_width == 100.0) {
11813 		GMT_Report (API, GMT_MSG_ERROR, "Option -%c: +n requires +t with width << 100!\n", option);
11814 		n_errors++;
11815 	}
11816 	if (info->info_mode == GMT_FFT_LIST) {
11817 		gmtapi_fft_Singleton_list (API);
11818 	}
11819 	if (n_errors) {
11820 		gmt_M_free (API->GMT, info);
11821 		info = NULL;
11822 	}
11823 	return (info);
11824 }
11825 
11826 #ifdef FORTRAN_API
GMT_FFT_Parse_(char * option,unsigned int * dim,char * args,int * length)11827 void * GMT_FFT_Parse_ (char *option, unsigned int *dim, char *args, int *length) {
11828 	/* Fortran version: We pass the global GMT_FORTRAN structure */
11829 	return (GMT_FFT_Parse (GMT_FORTRAN, *option, *dim, args));
11830 }
11831 #endif
11832 
11833 /*! . */
gmtapi_fft_init_1d(struct GMTAPI_CTRL * API,struct GMT_DATASET * D,unsigned int mode,void * v_info)11834 GMT_LOCAL struct GMT_FFT_WAVENUMBER * gmtapi_fft_init_1d (struct GMTAPI_CTRL *API, struct GMT_DATASET *D, unsigned int mode, void *v_info) {
11835 	struct GMT_FFT_WAVENUMBER *K = NULL;
11836 	gmt_M_unused(API); gmt_M_unused(D); gmt_M_unused(mode); gmt_M_unused(v_info);
11837 
11838 #if 0	/* Have not finalized 1-D FFT usage in general; this will probably happen when we add gmtfft [1-D FFT equivalent to grdfft] */
11839 	unsigned n_cols = 1;
11840 	struct GMT_FFT_INFO *F = gmtapi_get_fftinfo_ptr (v_info);
11841 	/* Determine number of columns in [t] x [y] input */
11842 	if (mode & GMT_FFT_CROSS_SPEC) n_cols++;
11843 	if (Din->n_columns < n_cols) {
11844 		GMT_report (API, GMT_MSG_ERROR, "2 columns needed but only 1 provided\n");
11845 		return NULL;
11846 	}
11847 	cross = (n_cols == 2);
11848 	if (mode & GMT_FFT_DELTA) n_cols++;
11849 	delta_t = (mode & GMT_FFT_DELTA) ? F->delta_t : D->table[0]->segment[0]->data[0][1] - D->table[0]->segment[0]->data[0][0];
11850 	K->delta_kx = 2.0 * M_PI / (F->n_columns * delta_t);
11851 
11852 	GMT_table_detrend (C, D, F->trend_mode, K->coeff);	/* Detrend data, if requested */
11853 	gmt_table_taper (C, G, F);				/* Taper data, if requested */
11854 	K->dim = 1;	/* 1-D FFT */
11855 #endif
11856 	return (K);
11857 }
11858 
11859 /*! . */
gmtapi_fft_taper2d(struct GMT_CTRL * GMT,struct GMT_GRID * Grid,struct GMT_FFT_INFO * F)11860 GMT_LOCAL void gmtapi_fft_taper2d (struct GMT_CTRL *GMT, struct GMT_GRID *Grid, struct GMT_FFT_INFO *F) {
11861 	/* mode sets if and how tapering will be performed [see GMT_FFT_EXTEND_* constants].
11862 	 * width is relative width in percent of the margin that will be tapered [100]. */
11863 	int il1, ir1, il2, ir2, jb1, jb2, jt1, jt2, im, jm, j, end_i, end_j, min_i, min_j, one;
11864 	int i, i_data_start, j_data_start, mx, i_width, j_width, width_percent;
11865 	unsigned int ju, start_component = 0, stop_component = 0, component;
11866 	uint64_t off;
11867 	char *method[2] = {"edge-point", "mirror"}, *comp[2] = {"real", "imaginary"};
11868 	gmt_grdfloat *datac = Grid->data, scale, cos_wt;
11869 	double width;
11870 	struct GMT_GRID_HEADER *h = Grid->header;	/* For shorthand */
11871 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
11872 
11873 	width_percent = irint (F->taper_width);
11874 
11875 	if ((Grid->header->n_columns == F->n_columns && Grid->header->n_rows == F->n_rows) || F->taper_mode == GMT_FFT_EXTEND_NONE) {
11876 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Data and FFT dimensions are equal - no data extension will take place\n");
11877 		/* But there may still be interior tapering */
11878 		if (F->taper_mode != GMT_FFT_EXTEND_NONE) {	/* Nothing to do since no outside pad */
11879 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Data and FFT dimensions are equal - no tapering will be performed\n");
11880 			return;
11881 		}
11882 		if (F->taper_mode == GMT_FFT_EXTEND_NONE && width_percent == 100) {	/* No interior taper specified */
11883 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No interior tapering will be performed\n");
11884 			return;
11885 		}
11886 	}
11887 
11888 	if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
11889 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Demultiplexing complex grid before tapering can take place.\n");
11890 		gmt_grd_mux_demux (GMT, Grid->header, Grid->data, GMT_GRID_IS_SERIAL);
11891 	}
11892 
11893 	/* Note that if nx2 = nx+1 and ny2 = n_rows + 1, then this routine
11894 	 * will do nothing; thus a single row/column of zeros may be
11895 	 * added to the bottom/right of the input array and it cannot
11896 	 * be tapered.  But when (nx2 - nx)%2 == 1 or ditto for y,
11897 	 * this is zero anyway.  */
11898 
11899 	i_data_start = GMT->current.io.pad[XLO];	/* Some shorthands for readability */
11900 	j_data_start = GMT->current.io.pad[YHI];
11901 	mx = h->mx;
11902 	one = (F->taper_mode == GMT_FFT_EXTEND_NONE) ? 0 : 1;	/* 0 is the boundary point which we want to taper to 0 for the interior taper */
11903 
11904 	if (width_percent == 0) {
11905 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Tapering has been disabled via +t0\n");
11906 	}
11907 	if (width_percent == 100 && F->taper_mode == GMT_FFT_EXTEND_NONE) {	/* Means user set +n but did not specify +t<taper> as 100% is unreasonable for interior */
11908 		width_percent = 0;
11909 		width = 0.0;
11910 	}
11911 	else
11912 		width = F->taper_width / 100.0;	/* Was percent, now fraction */
11913 
11914 	if (F->taper_mode == GMT_FFT_EXTEND_NONE) {	/* No extension, just tapering inside the data grid */
11915 		i_width = irint (Grid->header->n_columns * width);	/* Interior columns over which tapering will take place */
11916 		j_width = irint (Grid->header->n_rows * width);	/* Extended rows over which tapering will take place */
11917 	}
11918 	else {	/* We wish to extend data into the margin pads between FFT grid and data grid */
11919 		i_width = irint (i_data_start * width);	/* Extended columns over which tapering will take place */
11920 		j_width = irint (j_data_start * width);	/* Extended rows over which tapering will take place */
11921 	}
11922 	if (i_width == 0 && j_width == 0) one = 1;	/* So we do nothing further down */
11923 
11924 	/* Determine how many complex components (1 or 2) to taper, and which one(s) */
11925 	start_component = (h->complex_mode & GMT_GRID_IS_COMPLEX_REAL) ? 0 : 1;
11926 	stop_component  = (h->complex_mode & GMT_GRID_IS_COMPLEX_IMAG) ? 1 : 0;
11927 
11928 	for (component = start_component; component <= stop_component; component++) {	/* Loop over 1 or 2 components */
11929 		off = component * h->size / 2;	/* offset to start of this component in grid */
11930 
11931 		/* First reflect about xmin and xmax, either point symmetric about edge point OR mirror symmetric */
11932 
11933 		if (F->taper_mode != GMT_FFT_EXTEND_NONE) {
11934 			for (im = 1; im <= i_width; im++) {
11935 				il1 = -im;	/* Outside xmin; left of edge 1  */
11936 				ir1 = im;	/* Inside xmin; right of edge 1  */
11937 				il2 = il1 + h->n_columns - 1;	/* Inside xmax; left of edge 2  */
11938 				ir2 = ir1 + h->n_columns - 1;	/* Outside xmax; right of edge 2  */
11939 				for (ju = 0; ju < h->n_rows; ju++) {
11940 					if (F->taper_mode == GMT_FFT_EXTEND_POINT_SYMMETRY) {
11941 						datac[gmt_M_ijp(h,ju,il1)+off] = 2.0f * datac[gmt_M_ijp(h,ju,0)+off]       - datac[gmt_M_ijp(h,ju,ir1)+off];
11942 						datac[gmt_M_ijp(h,ju,ir2)+off] = 2.0f * datac[gmt_M_ijp(h,ju,h->n_columns-1)+off] - datac[gmt_M_ijp(h,ju,il2)+off];
11943 					}
11944 					else {	/* Mirroring */
11945 						datac[gmt_M_ijp(h,ju,il1)+off] = datac[gmt_M_ijp(h,ju,ir1)+off];
11946 						datac[gmt_M_ijp(h,ju,ir2)+off] = datac[gmt_M_ijp(h,ju,il2)+off];
11947 					}
11948 				}
11949 			}
11950 		}
11951 
11952 		/* Next, reflect about ymin and ymax.
11953 		 * At the same time, since x has been reflected,
11954 		 * we can use these vals and taper on y edges */
11955 
11956 		scale = (gmt_grdfloat)(M_PI / (j_width + 1));	/* Full 2*pi over y taper range */
11957 		min_i = (F->taper_mode == GMT_FFT_EXTEND_NONE) ? 0 : -i_width;
11958 		end_i = (F->taper_mode == GMT_FFT_EXTEND_NONE) ? (int)Grid->header->n_columns : mx - i_width;
11959 		for (jm = one; jm <= j_width; jm++) {	/* Loop over width of strip to taper */
11960 			jb1 = -jm;	/* Outside ymin; bottom side of edge 1  */
11961 			jt1 = jm;	/* Inside ymin; top side of edge 1  */
11962 			jb2 = jb1 + h->n_rows - 1;	/* Inside ymax; bottom side of edge 2  */
11963 			jt2 = jt1 + h->n_rows - 1;	/* Outside ymax; bottom side of edge 2  */
11964 			cos_wt = 0.5f * (1.0f + cosf (jm * scale));
11965 			if (F->taper_mode == GMT_FFT_EXTEND_NONE) cos_wt = 1.0f - cos_wt;	/* Reverse weights for the interior */
11966 			for (i = min_i; i < end_i; i++) {
11967 				if (F->taper_mode == GMT_FFT_EXTEND_POINT_SYMMETRY) {
11968 					datac[gmt_M_ijp(h,jb1,i)+off] = cos_wt * (2.0f * datac[gmt_M_ijp(h,0,i)+off]       - datac[gmt_M_ijp(h,jt1,i)+off]);
11969 					datac[gmt_M_ijp(h,jt2,i)+off] = cos_wt * (2.0f * datac[gmt_M_ijp(h,h->n_rows-1,i)+off] - datac[gmt_M_ijp(h,jb2,i)+off]);
11970 				}
11971 				else if (F->taper_mode == GMT_FFT_EXTEND_MIRROR_SYMMETRY) {
11972 					datac[gmt_M_ijp(h,jb1,i)+off] = cos_wt * datac[gmt_M_ijp(h,jt1,i)+off];
11973 					datac[gmt_M_ijp(h,jt2,i)+off] = cos_wt * datac[gmt_M_ijp(h,jb2,i)+off];
11974 				}
11975 				else {	/* Interior tapering only */
11976 					datac[gmt_M_ijp(h,jt1,i)+off] *= cos_wt;
11977 					datac[gmt_M_ijp(h,jb2,i)+off] *= cos_wt;
11978 				}
11979 			}
11980 		}
11981 		/* Now, cos taper the x edges */
11982 		scale = (gmt_grdfloat)(M_PI / (i_width + 1));	/* Full 2*pi over x taper range */
11983 		end_j = (F->taper_mode == GMT_FFT_EXTEND_NONE) ? h->n_rows : h->my - j_data_start;
11984 		min_j = (F->taper_mode == GMT_FFT_EXTEND_NONE) ? 0 : -j_width;
11985 		for (im = one; im <= i_width; im++) {
11986 			il1 = -im;
11987 			ir1 = im;
11988 			il2 = il1 + h->n_columns - 1;
11989 			ir2 = ir1 + h->n_columns - 1;
11990 			cos_wt = (gmt_grdfloat)(0.5f * (1.0f + cosf (im * scale)));
11991 			if (F->taper_mode == GMT_FFT_EXTEND_NONE) cos_wt = 1.0f - cos_wt;	/* Switch to weights for the interior */
11992 			for (j = min_j; j < end_j; j++) {
11993 				if (F->taper_mode == GMT_FFT_EXTEND_NONE) {
11994 					datac[gmt_M_ijp(h,j,ir1)+off] *= cos_wt;
11995 					datac[gmt_M_ijp(h,j,il2)+off] *= cos_wt;
11996 				}
11997 				else {
11998 					datac[gmt_M_ijp(h,j,il1)+off] *= cos_wt;
11999 					datac[gmt_M_ijp(h,j,ir2)+off] *= cos_wt;
12000 				}
12001 			}
12002 		}
12003 
12004 		if (F->taper_mode == GMT_FFT_EXTEND_NONE)
12005 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid margin (%s component) tapered to zero over %d %% of data width and height\n", comp[component], width_percent);
12006 		else
12007 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid (%s component) extended via %s symmetry at all edges, then tapered to zero over %d %% of extended area\n", comp[component], method[F->taper_mode], width_percent);
12008 	}
12009 }
12010 
12011 /*! . */
gmtapi_fft_init_2d(struct GMTAPI_CTRL * API,struct GMT_GRID * G,unsigned int mode,void * v_info)12012 GMT_LOCAL struct GMT_FFT_WAVENUMBER * gmtapi_fft_init_2d (struct GMTAPI_CTRL *API, struct GMT_GRID *G, unsigned int mode, void *v_info) {
12013 	/* Initialize grid dimensions for FFT machinery and set up wavenumbers */
12014 	unsigned int k, factors[32];
12015 	uint64_t node;
12016 	size_t worksize;
12017 	bool stop;
12018 	double tdummy, edummy;
12019 	struct GMT_FFT_SUGGESTION fft_sug[GMT_FFT_N_SUGGEST];
12020 	struct GMT_FFT_INFO *F = NULL, *F_in = gmtapi_get_fftinfo_ptr (v_info);
12021 	struct GMT_FFT_WAVENUMBER *K = NULL;
12022 	struct GMT_GRID_HEADER_HIDDEN *HH;
12023 	struct GMT_CTRL *GMT = NULL;
12024 
12025 	if (API == NULL) return_null (API, GMT_NOT_A_SESSION);
12026 	if (G == NULL) return_null (API, GMT_ARG_IS_NULL);
12027 	HH = gmt_get_H_hidden (G->header);
12028 	GMT = API->GMT;
12029 	K = gmt_M_memory (GMT, NULL, 1, struct GMT_FFT_WAVENUMBER);
12030 
12031 	F = gmt_M_memory (GMT, NULL, 1, struct GMT_FFT_INFO);
12032 	if (F_in) {	/* User specified -N so default settings should take effect */
12033 		gmt_M_memcpy (F, F_in, 1, struct GMT_FFT_INFO);
12034 		if (F->K) GMT_Report (API, GMT_MSG_DEBUG, "F->K already set; investigate.\n");
12035 	}
12036 	if (!F->set || F->info_mode == GMT_FFT_UNSPECIFIED) {	/* User is accepting the default values of extend via edge-point symmetry over 100% of margin */
12037 		F->info_mode = GMT_FFT_EXTEND_POINT_SYMMETRY;
12038 		F->taper_width = 100.0;
12039 		if (!F->set) F->suggest = GMT_FFT_N_SUGGEST;
12040 		F->set = true;
12041 	}
12042 
12043 	/* Get dimensions as may be appropriate */
12044 	if (F->info_mode == GMT_FFT_SET) {	/* User specified the n_columns/n_rows dimensions */
12045 		if (F->n_columns < G->header->n_columns || F->n_rows < G->header->n_rows) {
12046 			GMT_Report (API, GMT_MSG_WARNING, "You specified a FFT n_columns/n_rows smaller than input grid.  Ignored.\n");
12047 			F->info_mode = GMT_FFT_EXTEND;
12048 		}
12049 	}
12050 
12051 	if (F->info_mode != GMT_FFT_SET) {	/* Either adjust, force, inquiery */
12052 		if (F->info_mode == GMT_FFT_FORCE) {
12053 			F->n_columns = G->header->n_columns;
12054 			F->n_rows = G->header->n_rows;
12055 			GMT_Report (API, GMT_MSG_INFORMATION, "Selected FFT dimensions == Grid dimensions.\n");
12056 		}
12057 		else {	/* Determine best FFT dimensions */
12058 			unsigned int pick;
12059 			char *mode[GMT_FFT_N_SUGGEST] = {"fastest", "most accurate", "least storage"};
12060 			gmtlib_suggest_fft_dim (GMT, G->header->n_columns, G->header->n_rows, fft_sug, (gmt_M_is_verbose (GMT, GMT_MSG_WARNING) || F->verbose));
12061 			if (F->suggest == GMT_FFT_N_SUGGEST) {	/* Must choose smallest of accurate and fast */
12062 				pick = (fft_sug[GMT_FFT_ACCURATE].totalbytes < fft_sug[GMT_FFT_FAST].totalbytes) ? GMT_FFT_ACCURATE : GMT_FFT_FAST;
12063 				GMT_Report (API, GMT_MSG_INFORMATION, "Selected FFT dimensions for the overall best solution (%s).\n", mode[pick]);
12064 			}
12065 			else {	/* Pick the one we selected up front */
12066 				pick = F->suggest;
12067 				GMT_Report (API, GMT_MSG_INFORMATION, "Selected FFT dimensions for the %s solution.\n", mode[pick]);
12068 			}
12069 			F->n_columns = fft_sug[pick].n_columns;
12070 			F->n_rows    = fft_sug[pick].n_rows;
12071 		}
12072 	}
12073 
12074 	/* Because we taper and reflect below we DO NOT want any BCs set since that code expects 2 BC rows/cols */
12075 	for (k = 0; k < 4; k++) HH->BC[k] = GMT_BC_IS_DATA;
12076 
12077 	/* Get here when F->n_columns and F->n_rows are set to the values we will use.  */
12078 
12079 	gmtlib_fourt_stats (GMT, F->n_columns, F->n_rows, factors, &edummy, &worksize, &tdummy);
12080 	GMT_Report (API, GMT_MSG_INFORMATION, "Grid dimensions (n_rows by n_columns): %d x %d\tFFT dimensions: %d x %d\n", G->header->n_rows, G->header->n_columns, F->n_rows, F->n_columns);
12081 
12082 	/* Put the data in the middle of the padded array */
12083 
12084 	GMT->current.io.pad[XLO] = (F->n_columns - G->header->n_columns) / 2;	/* zero if n_columns < G->header->n_columns+1  */
12085 	GMT->current.io.pad[YHI] = (F->n_rows - G->header->n_rows) / 2;
12086 	GMT->current.io.pad[XHI] = F->n_columns - G->header->n_columns - GMT->current.io.pad[XLO];
12087 	GMT->current.io.pad[YLO] = F->n_rows - G->header->n_rows - GMT->current.io.pad[YHI];
12088 
12089 	/* Precompute wavenumber increments and initialize the GMT_FFT machinery */
12090 
12091 	K->delta_kx = 2.0 * M_PI / (F->n_columns * G->header->inc[GMT_X]);
12092 	K->delta_ky = 2.0 * M_PI / (F->n_rows * G->header->inc[GMT_Y]);
12093 	K->nx2 = F->n_columns;	K->ny2 = F->n_rows;
12094 
12095 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Give delta_kx, delta_ky units of 2pi/meters via Flat Earth assumption  */
12096 		K->delta_kx /= (GMT->current.proj.DIST_M_PR_DEG * cosd (0.5 * (G->header->wesn[YLO] + G->header->wesn[YHI])));
12097 		K->delta_ky /= GMT->current.proj.DIST_M_PR_DEG;
12098 	}
12099 
12100 	gmt_fft_set_wave (GMT, GMT_FFT_K_IS_KR, K);	/* Initialize for use with radial wavenumbers */
12101 
12102 	F->K = K;	/* So that F can access information in K later */
12103 	K->info = F;	/* So K can have access to information in F later */
12104 
12105 	/* Read in the data or change pad to match the nx2/ny2 determined */
12106 
12107 	if (G->data) {	/* User already read the data, check padding and possibly extend it */
12108 		if (G->header->complex_mode == 0) {	/* Grid was not read in interleaved, must do so now */
12109 			/* Because of no realloc for aligned memory we must do it the hard way */
12110 			gmt_grdfloat *f = NULL;
12111 			size_t new_size = 2 * G->header->size;
12112 			GMT_Report (API, GMT_MSG_INFORMATION, "Must double memory and multiplex external grid before we can do FFT\n", HH->name);
12113 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Extend grid via copy onto larger memory-aligned grid\n");
12114 			f = gmt_M_memory_aligned (GMT, NULL, new_size, gmt_grdfloat);	/* New, larger grid size */
12115 			gmt_M_memcpy (f, G->data, G->header->size, gmt_grdfloat);	/* Copy over previous grid values */
12116 			gmt_M_free_aligned (GMT, G->data);			/* Free previous aligned grid memory */
12117 			G->data = f;						/* Attach the new, larger aligned memory */
12118 			G->header->complex_mode = GMT_GRID_IS_COMPLEX_REAL;	/* Flag as complex grid with real components only */
12119 			G->header->size = new_size;					/* Update the size of complex grid */
12120 		}
12121 		if (!(G->header->mx == F->n_columns && G->header->my == F->n_rows)) {	/* Must re-pad, possibly re-allocate the grid */
12122 			gmt_grd_pad_on (GMT, G, GMT->current.io.pad);
12123 		}
12124 	}
12125 	else {	/* Read the data into a grid of approved dimension */
12126 		G->header->mx = G->header->n_columns;	G->header->my = G->header->n_rows;	/* Undo misleading padding since we have not read the data yet and GMT pad has changed above */
12127 		if (GMT_Read_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_DATA_ONLY | mode, NULL, HH->name, G) == NULL)	/* Get data only */
12128 			return (NULL);
12129 	}
12130 #ifdef DEBUG
12131 	gmt_grd_dump (G->header, G->data, mode & GMT_GRID_IS_COMPLEX_MASK, "Read in FFT_Create");
12132 #endif
12133 	/* Make sure there are no NaNs in the grid - that is a fatal flaw */
12134 
12135 	for (node = 0, stop = false; !stop && node < G->header->size; node++) stop = gmt_M_is_fnan (G->data[node]);
12136 	if (stop) {
12137 		GMT_Report (API, GMT_MSG_ERROR, "Input grid %s contain NaNs, cannot do FFT!\n", HH->name);
12138 		return (NULL);
12139 	}
12140 
12141 	if (F->trend_mode == GMT_FFT_REMOVE_NOT_SET) F->trend_mode = GMT_FFT_REMOVE_NOTHING;	/* Delayed default */
12142 	gmt_grd_detrend (GMT, G, F->trend_mode, K->coeff);	/* Detrend data, if requested */
12143 #ifdef DEBUG
12144 	gmt_grd_dump (G->header, G->data, mode & GMT_GRID_IS_COMPLEX_MASK, "After detrend");
12145 #endif
12146 	gmtapi_fft_taper2d (GMT, G, F);				/* Taper data, if requested */
12147 #ifdef DEBUG
12148 	gmt_grd_dump (G->header, G->data, mode & GMT_GRID_IS_COMPLEX_MASK, "After Taper");
12149 #endif
12150 	K->dim = 2;	/* 2-D FFT */
12151 	return (K);
12152 }
12153 
12154 /*! . */
GMT_FFT_Create(void * V_API,void * X,unsigned int dim,unsigned int mode,void * v_info)12155 void * GMT_FFT_Create (void *V_API, void *X, unsigned int dim, unsigned int mode, void *v_info) {
12156 	/* Initialize 1-D or 2-D FFT machinery and set up wavenumbers */
12157 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
12158 	if (dim == 1) return (gmtapi_fft_init_1d (V_API, X, mode, v_info));
12159 	if (dim == 2) return (gmtapi_fft_init_2d (V_API, X, mode, v_info));
12160 	GMT_Report (V_API, GMT_MSG_ERROR, "GMT FFT only supports dimensions 1 and 2, not %u\n", dim);
12161 	return_null (V_API, (dim == 0) ? GMT_DIM_TOO_SMALL : GMT_DIM_TOO_LARGE);
12162 }
12163 
12164 #ifdef FORTRAN_API
GMT_FFT_Create_(void * X,unsigned int * dim,unsigned int * mode,void * v_info)12165 void * GMT_FFT_Create_ (void *X, unsigned int *dim, unsigned int *mode, void *v_info) {
12166 	/* Fortran version: We pass the global GMT_FORTRAN structure */
12167 	return (GMT_FFT_Create (GMT_FORTRAN, X, *dim, *mode, v_info));
12168 }
12169 #endif
12170 
12171 /*! . */
gmtapi_fft_1d(struct GMTAPI_CTRL * API,struct GMT_DATASET * D,int direction,unsigned int mode,struct GMT_FFT_WAVENUMBER * K)12172 GMT_LOCAL int gmtapi_fft_1d (struct GMTAPI_CTRL *API, struct GMT_DATASET *D, int direction, unsigned int mode, struct GMT_FFT_WAVENUMBER *K) {
12173 	/* The 1-D FFT operating on DATASET segments */
12174 	int status = 0;
12175 	uint64_t seg, row, tbl, last = 0, col = 0;
12176 	gmt_grdfloat *data = NULL;
12177 	struct GMT_DATASEGMENT *S = NULL;
12178 	gmt_M_unused(K);
12179 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
12180 	/* Not at all finished; will require gmtfft.c to be developed and tested */
12181 	for (tbl = 0; tbl < D->n_tables; tbl++) {
12182 		for (seg = 0; seg < D->table[tbl]->n_segments; seg++) {
12183 			S = D->table[tbl]->segment[seg];
12184 			if (S->n_rows > last) {	/* Extend array */
12185 				data = gmt_M_memory (API->GMT, data, S->n_rows, gmt_grdfloat);
12186 				last = S->n_rows;
12187 			}
12188 			for (row = 0; S->n_rows; row++) data[row] = (gmt_grdfloat)S->data[col][row];
12189 			status = GMT_FFT_1D (API, data, S->n_rows, direction, mode);
12190 			for (row = 0; S->n_rows; row++) S->data[col][row] = data[row];
12191 		}
12192 	}
12193 	gmt_M_free (API->GMT, data);
12194 	return (status);
12195 }
12196 
gmtapi_fft_file_name_with_suffix(struct GMT_CTRL * GMT,char * name,char * suffix)12197 GMT_LOCAL char * gmtapi_fft_file_name_with_suffix (struct GMT_CTRL *GMT, char *name, char *suffix) {
12198 	static char file[PATH_MAX] = {""};
12199 	uint64_t i, j;
12200 	size_t len;
12201 
12202 	if ((len = strlen (name)) == 0) {	/* Grids that are being created have no filename yet */
12203 		snprintf (file, PATH_MAX, "tmpgrid_%s.grd", suffix);
12204 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Created grid has no name to derive new names from; choose %s\n", file);
12205 		return (file);
12206 	}
12207 	for (i = len; i > 0 && !(name[i] == '/' || name[i] == '\\'); i--);	/* i points to 1st char in name after slash, or 0 if no leading dirs */
12208 	if (i) i++;	/* Move to 1st char after / */
12209 	for (j = len; j > 0 && name[j] != '.'; j--);	/* j points to period before extension, or it is 0 if no extension */
12210 	len = strlen (&name[i]);
12211 	strncpy (file, &name[i], PATH_MAX-1);		/* Make a full copy of filename without leading directories */
12212 	for (i = len; i > 0 && file[i] != '.'; i--);	/* i now points to period before extension in file, or it is 0 if no extension */
12213 	if (i) file[i] = '\0';	/* Truncate at the extension */
12214 	/* Determine length of new filename and make sure it fits */
12215 	len = strlen (file);
12216 	if (j) len += strlen (&name[j]);
12217 	len += (1 + strlen(suffix));
12218 	if ((GMT_BUFSIZ - len) > 0) {	/* Have enough space */
12219 		strcat (file, "_");
12220 		strcat (file, suffix);
12221 		if (j) strncat (file, &name[j], PATH_MAX-1);
12222 	}
12223 	else
12224 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "File name [ %s] way too long - trouble in gmtapi_fft_file_name_with_suffix\n", file);
12225 	return (file);
12226 }
12227 
gmtapi_fft_grd_save_taper(struct GMT_CTRL * GMT,struct GMT_GRID * Grid,char * suffix)12228 GMT_LOCAL void gmtapi_fft_grd_save_taper (struct GMT_CTRL *GMT, struct GMT_GRID *Grid, char *suffix) {
12229 	/* Write the intermediate grid that will be passed to the FFT to file.
12230 	 * This grid may have been a mean, mid-value, or plane removed, may
12231 	 * have data filled into an extended margin, and may have been taperer.
12232 	 * Normally, the complex grid will be in serial layout, but just in case
12233 	 * we check and add a demux step if required.  The FFT will also check
12234 	 * and multiplex the grid (again) if needed.
12235 	 */
12236 	unsigned int pad[4];
12237 	struct GMT_GRID_HEADER *save = gmt_get_header (GMT);
12238 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (Grid->header);
12239 	char *file = NULL;
12240 
12241 	if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
12242 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Demultiplexing complex grid before saving can take place.\n");
12243 		gmt_grd_mux_demux (GMT, Grid->header, Grid->data, GMT_GRID_IS_SERIAL);
12244 	}
12245 	gmt_copy_gridheader (GMT, save, Grid->header);
12246 	gmt_M_memcpy (pad, Grid->header->pad, 4U, unsigned int);		/* Save current pad, then set pad to zero */
12247 	/* Extend w/e/s/n to what it would be if the pad was not present */
12248 	Grid->header->wesn[XLO] -= Grid->header->pad[XLO] * Grid->header->inc[GMT_X];
12249 	Grid->header->wesn[XHI] += Grid->header->pad[XHI] * Grid->header->inc[GMT_X];
12250 	Grid->header->wesn[YLO] -= Grid->header->pad[YLO] * Grid->header->inc[GMT_Y];
12251 	Grid->header->wesn[YHI] += Grid->header->pad[YHI] * Grid->header->inc[GMT_Y];
12252 	gmt_M_memset (Grid->header->pad,   4U, unsigned int);	/* Set header pad to {0,0,0,0} */
12253 	gmt_M_memset (GMT->current.io.pad, 4U, unsigned int);	/* set GMT default pad to {0,0,0,0} */
12254 	gmt_set_grddim (GMT, Grid->header);	/* Recompute all dimensions */
12255 	if ((file = gmtapi_fft_file_name_with_suffix (GMT, HH->name, suffix)) == NULL) {
12256 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to get file name for file %s\n", HH->name);
12257 		return;
12258 	}
12259 
12260 	if (GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_DATA_ONLY | GMT_GRID_IS_COMPLEX_REAL, NULL, file, Grid) != GMT_NOERROR)
12261 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Intermediate detrended, extended, and tapered grid could not be written to %s\n", file);
12262 	else
12263 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Intermediate detrended, extended, and tapered grid written to %s\n", file);
12264 
12265 	gmt_copy_gridheader (GMT, Grid->header, save);	/* Restore original, including the original pad */
12266 	gmt_M_memcpy (GMT->current.io.pad, pad, 4U, unsigned int);	/* Restore GMT default pad */
12267 	gmt_M_free (GMT, save->hidden);
12268 	gmt_M_free (GMT, save);
12269 }
12270 
gmtapi_fft_grd_save_fft(struct GMT_CTRL * GMT,struct GMT_GRID * G,struct GMT_FFT_INFO * F)12271 GMT_LOCAL void gmtapi_fft_grd_save_fft (struct GMT_CTRL *GMT, struct GMT_GRID *G, struct GMT_FFT_INFO *F) {
12272 	/* Save the raw spectrum as two files (real,imag) or (mag,phase), depending on mode.
12273 	 * We must first do an "fftshift" operation as in Matlab, to put the 0 frequency
12274 	 * value in the center of the grid. */
12275 	uint64_t i_ij, o_ij,  offset;
12276 	int row_in, col_in, row_out, col_out, nx_2, ny_2;
12277 	size_t len;
12278 	unsigned int k, pad[4], mode, wmode[2] = {GMT_GRID_IS_COMPLEX_REAL, GMT_GRID_IS_COMPLEX_IMAG};
12279 	double wesn[6], inc[2];
12280 	gmt_grdfloat re, im, i_scale;
12281 	char *file = NULL, *suffix[2][2] = {{"real", "imag"}, {"mag", "phase"}};
12282 	struct GMT_GRID *Out = NULL;
12283 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
12284 	struct GMT_FFT_WAVENUMBER *K = F->K;
12285 
12286 	if (K == NULL) return;
12287 
12288 	mode = (F->polar) ? 1 : 0;
12289 
12290 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Write components of complex raw spectrum with file suffix %s and %s\n", suffix[mode][0], suffix[mode][1]);
12291 
12292 	if (HH->arrangement == GMT_GRID_IS_SERIAL) {
12293 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot save complex grid unless it is interleaved.\n");
12294 		return;
12295 	}
12296 	/* Prepare wavenumber domain limits and increments */
12297 	nx_2 = K->nx2 / 2;	ny_2 = K->ny2 / 2;
12298 	wesn[XLO] = -K->delta_kx * nx_2;	wesn[XHI] =  K->delta_kx * (nx_2 - 1);
12299 	wesn[YLO] = -K->delta_ky * (ny_2 - 1);	wesn[YHI] =  K->delta_ky * ny_2;
12300 	inc[GMT_X] = K->delta_kx;		inc[GMT_Y] = K->delta_ky;
12301 	gmt_M_memcpy (pad, GMT->current.io.pad, 4U, unsigned int);	/* Save current GMT pad */
12302 	for (k = 0; k < 4; k++) GMT->current.io.pad[k] = 0;		/* No pad is what we need for this application */
12303 
12304 	/* Set up and allocate the temporary grid which is always gridline registered. */
12305 	if ((Out = GMT_Create_Data (GMT->parent, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA | GMT_GRID_IS_COMPLEX_MASK,
12306 	                            NULL, wesn, inc, GMT_GRID_NODE_REG, 0, NULL)) == NULL) {	/* Note: 0 for pad since no BC work needed for this temporary grid */
12307 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to create complex output grid for %s\n", HH->name);
12308 		return;
12309 	}
12310 
12311 	strcpy (Out->header->x_units, "xunit^(-1)");	strcpy (Out->header->y_units, "yunit^(-1)");
12312 	strcpy (Out->header->z_units, G->header->z_units);
12313 	strcpy (Out->header->remark, "Applied fftshift: kx,ky = (0,0) now at (n_columns/2 + 1,n_rows/2");
12314 
12315 	offset = Out->header->size / 2;
12316 	i_scale = (gmt_grdfloat)(1.0  / Out->header->nm);
12317 	for (row_in = 0; row_in < K->ny2; row_in++) {
12318 		row_out = (row_in + ny_2) % K->ny2;
12319 		for (col_in = 0; col_in < K->nx2; col_in++) {
12320 			col_out = (col_in + nx_2) % K->nx2;
12321 			o_ij = gmt_M_ij0 (Out->header, row_out, col_out);
12322 			i_ij = 2 * gmt_M_ij0 (G->header,   row_in,  col_in);
12323 			re = G->data[i_ij] * i_scale; im = G->data[i_ij+1] * i_scale;
12324 			if (F->polar) {	/* Want magnitude and phase */
12325 				Out->data[o_ij]   = (gmt_grdfloat)hypot (re, im);
12326 				Out->data[o_ij+offset] = (gmt_grdfloat)d_atan2 (im, re);
12327 			}
12328 			else {		/* Retain real and imag components as is */
12329 				Out->data[o_ij] = re;	Out->data[o_ij+offset] = im;
12330 			}
12331 		}
12332 	}
12333 	for (k = 0; k < 2; k++) {	/* Write the two grids */
12334 		if ((file = gmtapi_fft_file_name_with_suffix (GMT, HH->name, suffix[mode][k])) == NULL) {
12335 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to get file name for file %s\n", HH->name);
12336 			return;
12337 		}
12338 		Out->header->complex_mode = wmode[k];
12339 		for (len = strlen (HH->name); len > 0 && !(HH->name[len-1] == '/' || HH->name[len-1] == '\\'); len--);	/* Find start of file name minus any leading directories */
12340 		snprintf (Out->header->title, GMT_GRID_TITLE_LEN80, "The %s part of FFT transformed grid %s", suffix[mode][k], &HH->name[len]);
12341 		if (k == 1 && mode) strcpy (Out->header->z_units, "radians");
12342 		if (GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA | wmode[k], NULL, file, Out) != GMT_NOERROR) {
12343 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "%s could not be written\n", file);
12344 			return;
12345 		}
12346 	}
12347 	if (GMT_Destroy_Data (GMT->parent, &Out) != GMT_NOERROR) {
12348 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while freeing temporary grid\n");
12349 	}
12350 
12351 	gmt_M_memcpy (GMT->current.io.pad, pad, 4U, unsigned int);	/* Restore GMT pad */
12352 }
12353 
gmtapi_fft_save2d(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int direction,struct GMT_FFT_WAVENUMBER * K)12354 GMT_LOCAL void gmtapi_fft_save2d (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int direction, struct GMT_FFT_WAVENUMBER *K) {
12355 	/* Handle the writing of the grid going into the FFT and comping out of the FFT, per F settings */
12356 
12357 	if (G == NULL || (K == NULL ||  K->info == NULL)) return;
12358 	if (direction == GMT_IN  && K->info->save[GMT_IN])  gmtapi_fft_grd_save_taper (GMT, G, K->info->suffix);
12359 	if (direction == GMT_OUT && K->info->save[GMT_OUT]) gmtapi_fft_grd_save_fft (GMT, G, K->info);
12360 }
12361 
12362 /*! . */
gmtapi_fft_2d(struct GMTAPI_CTRL * API,struct GMT_GRID * G,int direction,unsigned int mode,struct GMT_FFT_WAVENUMBER * K)12363 GMT_LOCAL int gmtapi_fft_2d (struct GMTAPI_CTRL *API, struct GMT_GRID *G, int direction, unsigned int mode, struct GMT_FFT_WAVENUMBER *K) {
12364 	/* The 2-D FFT operating on GMT_GRID arrays */
12365 	int status;
12366 	if (K && direction == GMT_FFT_FWD) gmtapi_fft_save2d (API->GMT, G, GMT_IN, K);	/* Save intermediate grid, if requested, before interleaving */
12367 	gmt_grd_mux_demux (API->GMT, G->header, G->data, GMT_GRID_IS_INTERLEAVED);
12368 #ifdef DEBUG
12369 	gmt_grd_dump (G->header, G->data, true, "After demux");
12370 #endif
12371 	status = GMT_FFT_2D (API, G->data, G->header->mx, G->header->my, direction, mode);
12372 #ifdef DEBUG
12373 	gmt_grd_dump (G->header, G->data, true, "After FFT");
12374 #endif
12375 	if (K && direction == GMT_FFT_FWD) gmtapi_fft_save2d (API->GMT, G, GMT_OUT, K);	/* Save complex grid, if requested */
12376 	return (status);
12377 }
12378 
12379 /*! . */
GMT_FFT(void * V_API,void * X,int direction,unsigned int mode,void * v_K)12380 int GMT_FFT (void *V_API, void *X, int direction, unsigned int mode, void *v_K) {
12381 	/* The 1-D or 2-D FFT operating on GMT_DATASET or GMT_GRID arrays */
12382 	struct GMT_FFT_WAVENUMBER *K = gmtapi_get_fftwave_ptr (v_K);
12383 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
12384 	if (K->dim == 2) return (gmtapi_fft_2d (V_API, X, direction, mode, K));
12385 	else return (gmtapi_fft_1d (V_API, X, direction, mode, K));
12386 }
12387 
12388 #ifdef FORTRAN_API
GMT_FFT_(void * X,int * direction,unsigned int * mode,void * v_K)12389 int GMT_FFT_ (void *X, int *direction, unsigned int *mode, void *v_K) {
12390 	/* Fortran version: We pass the global GMT_FORTRAN structure */
12391 	return (GMT_FFT (GMT_FORTRAN, X, *direction, *mode, v_K));
12392 }
12393 #endif
12394 
12395 /*! . */
GMT_FFT_Destroy(void * V_API,void * v_info)12396 int GMT_FFT_Destroy (void *V_API, void *v_info) {
12397 	/* Perform any final duties, perhaps report.  For now just free */
12398 	struct GMT_FFT_WAVENUMBER **K = NULL;
12399 	struct GMTAPI_CTRL *API = NULL;
12400 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
12401 	API = gmtapi_get_api_ptr (V_API);
12402 	K = gmtapi_get_fftwave_addr (v_info);
12403 	gmt_M_free (API->GMT, (*K)->info);
12404 	gmt_M_free (API->GMT, (*K));
12405 	return_error (V_API, GMT_NOERROR);
12406 }
12407 
12408 #ifdef FORTRAN_API
GMT_FFT_Destroy_(void * v_K)12409 int GMT_FFT_Destroy_ (void *v_K) {
12410 	/* Fortran version: We pass the global GMT_FORTRAN structure */
12411 	return (GMT_FFT_Destroy (GMT_FORTRAN, v_K));
12412 }
12413 #endif
12414 
12415 /*! Pretty print core module names and purposes */
gmt_show_name_and_purpose(void * V_API,const char * component,const char * name,const char * purpose)12416 const char * gmt_show_name_and_purpose (void *V_API, const char *component, const char *name, const char *purpose) {
12417 	char message[GMT_LEN256] = {""};
12418 	static char full_name[GMT_LEN32] = {""};
12419 	const char *lib = NULL, *mode_name = name;
12420 	static char *core = "core";
12421 	struct GMTAPI_CTRL *API = NULL;
12422 	assert (V_API != NULL);
12423 	assert (name != NULL);
12424 	assert (purpose != NULL);
12425 	API = gmtapi_get_api_ptr (V_API);
12426 	mode_name = gmtlib_get_active_name (API, name);
12427 	lib = (component) ? component : core;
12428 	snprintf (full_name, GMT_LEN32, "gmt %s", mode_name);
12429 	snprintf (message, GMT_LEN256, "%s [%s] %s - %s\n", full_name, lib, GMT_version(), purpose);
12430 	GMT_Usage (V_API, 0, message);
12431 	gmtlib_set_KOP_strings (API);
12432 	return full_name;
12433 }
12434 
12435 /* Module Extension: Allow listing and calling modules by name */
12436 
12437 /*! . */
gmtapi_get_shared_module_func(struct GMTAPI_CTRL * API,const char * module,unsigned int lib_no)12438 GMT_LOCAL void * gmtapi_get_shared_module_func (struct GMTAPI_CTRL *API, const char *module, unsigned int lib_no) {
12439 	/* Function that returns a pointer to the function named module in specified shared library lib_no, or NULL if not found  */
12440 	void *p_func = NULL;       /* function pointer */
12441 	if (API->lib[lib_no].skip) return (NULL);	/* Tried to open this shared library before and it was not available */
12442 	if (API->lib[lib_no].handle == NULL && (API->lib[lib_no].handle = dlopen (API->lib[lib_no].path, RTLD_LAZY)) == NULL) {	/* Not opened this shared library yet */
12443 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open GMT shared %s library: %s\n", API->lib[lib_no].name, dlerror());
12444 		API->lib[lib_no].skip = true;	/* Not bother the next time... */
12445 		return (NULL);			/* ...and obviously no function would be found */
12446 	}
12447 	/* Here the library handle is available; try to get pointer to specified module */
12448 	*(void **) (&p_func) = dlsym (API->lib[lib_no].handle, module);
12449 	return (p_func);
12450 }
12451 
12452 /*! . */
gmtapi_get_module_func(struct GMTAPI_CTRL * API,const char * module,unsigned int lib_no)12453 GMT_LOCAL void * gmtapi_get_module_func (struct GMTAPI_CTRL *API, const char *module, unsigned int lib_no) {
12454 	return (gmtapi_get_shared_module_func (API, module, lib_no));
12455 }
12456 
12457 /*! . */
GMT_Call_Module(void * V_API,const char * module,int mode,void * args)12458 int GMT_Call_Module (void *V_API, const char *module, int mode, void *args) {
12459 	/* Call the specified shared module and pass it the mode and args.
12460  	 * mode can be one of the following:
12461 	 * GMT_MODULE_CLASSIC [-7]:	As GMT_MODULE_PURPOSE, but only lists the classic modules.
12462 	 * GMT_MODULE_LIST [-6]:	As GMT_MODULE_PURPOSE, but only lists the modern modules.
12463 	 * GMT_MODULE_CLASSIC_CORE [-5]:	As GMT_MODULE_PURPOSE, but only lists the classic modules (core only).
12464 	 * GMT_MODULE_LIST_CORE [-4]:	As GMT_MODULE_PURPOSE, but only lists the modern modules (core only).
12465 	 * GMT_MODULE_EXIST [-3]:	Return GMT_NOERROR (0) if module exists, GMT_NOT_A_VALID_MODULE otherwise.
12466 	 * GMT_MODULE_PURPOSE [-2]:	As GMT_MODULE_EXIST, but also print the module purpose.
12467 	 * GMT_MODULE_OPT [-1]:		Args is a linked list of option structures.
12468 	 * GMT_MODULE_CMD [0]:		Args is a single textstring with multiple options
12469 	 * mode > 0:				Args is an array of text strings (e.g., argv[]).
12470 	 */
12471 	int status = GMT_NOERROR;
12472 	unsigned int lib;
12473 	struct GMTAPI_CTRL *API = NULL;
12474 	char gmt_module[GMT_LEN64] = "GMT_";
12475 	int (*p_func)(void*, int, void*) = NULL;       /* function pointer */
12476 
12477 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
12478 	if (module == NULL && !(mode == GMT_MODULE_LIST || mode == GMT_MODULE_LIST_CORE || mode == GMT_MODULE_CLASSIC || mode == GMT_MODULE_CLASSIC_CORE || mode == GMT_MODULE_PURPOSE))
12479 		return_error (V_API, GMT_ARG_IS_NULL);
12480 	API = gmtapi_get_api_ptr (V_API);
12481 	API->error = GMT_NOERROR;
12482 
12483 	if (module == NULL) {	/* Did not specify any specific module, so list purpose of all modules in all shared libs */
12484 		char gmt_module[GMT_LEN256] = {""};	/* To form name of gmt_<lib>_module_show|list_all function */
12485 		char *listfunc = (mode == GMT_MODULE_LIST || mode == GMT_MODULE_LIST_CORE) ? "list" : ( (mode == GMT_MODULE_CLASSIC || mode == GMT_MODULE_CLASSIC_CORE) ? "classic" : "show");
12486 		void (*l_func)(void*);       /* function pointer to gmt_<lib>_module_show|list_all which takes one arg (the API) */
12487 		unsigned int n_libs = (mode == GMT_MODULE_LIST_CORE || mode == GMT_MODULE_CLASSIC_CORE) ? 1 : API->n_shared_libs;
12488 		/* Here we list purpose of all the available modules in each shared library */
12489 		for (lib = 0; lib < n_libs; lib++) {
12490 			snprintf (gmt_module, GMT_LEN64, "%s_module_%s_all", API->lib[lib].name, listfunc);
12491 			*(void **) (&l_func) = gmtapi_get_module_func (API, gmt_module, lib);
12492 			if (l_func == NULL) continue;	/* Not found in this shared library */
12493 			(*l_func) (V_API);	/* Run this function */
12494 		}
12495 		return (status);
12496 	}
12497 
12498 	/* Here we call a named module */
12499 
12500 	strncat (gmt_module, module, GMT_LEN64-5);		/* Concatenate GMT_-prefix and module name to get function name */
12501 	for (lib = 0; lib < API->n_shared_libs; lib++) {	/* Look for gmt_module in any of the shared libs */
12502 		*(void **) (&p_func) = gmtapi_get_module_func (API, gmt_module, lib);
12503 		if (p_func) break;	/* Found it in this shared library */
12504 	}
12505 	if (p_func == NULL) {	/* Not in any of the shared libraries */
12506 		status = GMT_NOT_A_VALID_MODULE;	/* Most likely, but we will try again: */
12507 		if (strncasecmp (module, "gmt", 3)) {	/* For any module not already starting with "gmt..." */
12508 			char gmt_module[GMT_LEN64] = "gmt";
12509 			strncat (gmt_module, module, GMT_LEN64-4);	/* Prepend "gmt" to module and try again */
12510 			status = GMT_Call_Module (V_API, gmt_module, mode, args);	/* Recursive call to try with the 'gmt' prefix this time */
12511 		}
12512 	}
12513 	else if (mode == GMT_MODULE_EXIST)	/* Just wanted to know it is a valid module */
12514 		return (GMT_NOERROR);
12515 	else {	/* Call the function and pass back its return value */
12516 		gmt_manage_workflow (API, GMT_USE_WORKFLOW, NULL);		/* First detect and set modern mode if modern mode session dir is found */
12517 		if ((status = gmtapi_check_for_modern_oneliner (API, module, mode, args))) return status;	/* If a modern mode one-liner we must switch run--mode here, or fail if an error */
12518 		if (API->external && gmt_M_is_verbose (API->GMT, GMT_MSG_DEBUG)) {
12519 			/* For externals only, print the equivalent command-line string under -Vd */
12520 			char *text = (mode == GMT_MODULE_OPT) ? GMT_Create_Cmd (API, args) : args;
12521 			GMT_Report (API, GMT_MSG_DEBUG, "GMT_Call_Command string: %s %s\n", module, text);
12522 			if (mode == GMT_MODULE_OPT) GMT_Destroy_Cmd (API, &text);
12523 		}
12524 		status = (*p_func) (V_API, mode, args);				/* Call the module in peace */
12525 	}
12526 	return (status);
12527 }
12528 
12529 #ifdef FORTRAN_API
GMT_Call_Module_(const char * module,int * mode,void * args,int * length)12530 int GMT_Call_Module_ (const char *module, int *mode, void *args, int *length) {
12531 	return (GMT_Call_Module (GMT_FORTRAN, module, *mode, args));
12532 }
12533 #endif
12534 
12535 /*! . */
gmtapi_get_shared_module_keys(struct GMTAPI_CTRL * API,char * module,unsigned int lib_no)12536 GMT_LOCAL const char *gmtapi_get_shared_module_keys (struct GMTAPI_CTRL *API, char *module, unsigned int lib_no) {
12537 	/* Function that returns a pointer to the module keys in specified shared library lib_no, or NULL if not found  */
12538 	/* DO not rename this function */
12539 	char function[GMT_LEN64] = {""};
12540 	const char *keys = NULL;       /* char pointer to module keys */
12541 	const char *(*func)(void*, char*) = NULL;       /* function pointer */
12542 	if (API->lib[lib_no].skip) return (NULL);	/* Tried to open this shared library before and it was not available */
12543 	if (API->lib[lib_no].handle == NULL && (API->lib[lib_no].handle = dlopen (API->lib[lib_no].path, RTLD_LAZY)) == NULL) {	/* Not opened this shared library yet */
12544 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open GMT shared %s library: %s\n", API->lib[lib_no].name, dlerror());
12545 		API->lib[lib_no].skip = true;	/* Not bother the next time... */
12546 		return (NULL);			/* ...and obviously no keys would be found */
12547 	}
12548 	snprintf (function, GMT_LEN64, "%s_module_keys", API->lib[lib_no].name);
12549 	/* Here the library handle is available; try to get pointer to specified module */
12550 	*(void **) (&func) = dlsym (API->lib[lib_no].handle, function);
12551 	if (func) keys = (*func) (API, module);
12552 	return (keys);
12553 }
12554 
12555 /*! . */
gmtapi_get_shared_module_group(struct GMTAPI_CTRL * API,char * module,unsigned int lib_no)12556 GMT_LOCAL const char *gmtapi_get_shared_module_group (struct GMTAPI_CTRL *API, char *module, unsigned int lib_no) {
12557 	/* Function that returns a pointer to the module group string in specified shared library lib_no, or NULL if not found  */
12558 	/* DO not rename this function */
12559 	char function[GMT_LEN64] = {""};
12560 	const char *group = NULL;       /* char pointer to module group */
12561 	const char * (*func)(void*, char*) = NULL;       /* function pointer */
12562 	if (API->lib[lib_no].skip) return (NULL);	/* Tried to open this shared library before and it was not available */
12563 	if (API->lib[lib_no].handle == NULL && (API->lib[lib_no].handle = dlopen (API->lib[lib_no].path, RTLD_LAZY)) == NULL) {	/* Not opened this shared library yet */
12564 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open GMT shared %s library: %s\n", API->lib[lib_no].name, dlerror());
12565 		API->lib[lib_no].skip = true;	/* Not bother the next time... */
12566 		return (NULL);			/* ...and obviously no keys would be found */
12567 	}
12568 	snprintf (function, GMT_LEN64, "%s_module_group", API->lib[lib_no].name);
12569 	/* Here the library handle is available; try to get pointer to specified module */
12570 	*(void **) (&func) = dlsym (API->lib[lib_no].handle, function);
12571 	if (func) group = (*func) (API, module);
12572 	return (group);
12573 }
12574 
12575 /*! . */
gmt_get_module_group(void * V_API,char * module)12576 const char *gmt_get_module_group (void *V_API, char *module) {
12577 	/* Call the specified shared module and retrieve the group of the module.
12578  	 * This function, while in the API, is only for API developers and thus has a
12579 	 * "undocumented" status in the API documentation.
12580 	 */
12581 	unsigned int lib;
12582 	struct GMTAPI_CTRL *API = NULL;
12583 	char gmt_module[GMT_LEN32] = "gmt";
12584 	const char *group = NULL;
12585 
12586 	if (V_API == NULL) return_null (NULL, GMT_NOT_A_SESSION);
12587 	if (module == NULL) return_null (V_API, GMT_ARG_IS_NULL);
12588 	API = gmtapi_get_api_ptr (V_API);
12589 	API->error = GMT_NOERROR;
12590 
12591 	for (lib = 0; lib < API->n_shared_libs; lib++) {	/* Look for module in any of the shared libs */
12592 		group = gmtapi_get_shared_module_group (API, module, lib);
12593 		if (group) return (group);	/* Found it in this shared library, return the group */
12594 	}
12595 	/* If we get here we did not found it.  Try to prefix module with gmt */
12596 	strncat (gmt_module, module, GMT_LEN32-4);		/* Concatenate gmt and module name to get function name */
12597 	for (lib = 0; lib < API->n_shared_libs; lib++) {	/* Look for gmt_module in any of the shared libs */
12598 		group = gmtapi_get_shared_module_group (API, gmt_module, lib);
12599 		if (group) {	/* Found it in this shared library, adjust module name and return the group */
12600 			strncpy (module, gmt_module, strlen(gmt_module));	/* Rewrite module name to contain prefix of gmt */
12601 			return (group);
12602 		}
12603 	}
12604 	/* Not in any of the shared libraries */
12605 	GMT_Report (API, GMT_MSG_ERROR, "Shared GMT module not found: %s \n", module);
12606 	return_null (V_API, GMT_NOT_A_VALID_MODULE);
12607 }
12608 
12609 /*! . */
gmtapi_retrieve_module_keys(void * V_API,char * module)12610 GMT_LOCAL const char *gmtapi_retrieve_module_keys (void *V_API, char *module) {
12611 	/* Call the specified shared module and retrieve the API developer options keys.
12612 	 */
12613 	unsigned int lib;
12614 	struct GMTAPI_CTRL *API = NULL;
12615 	char gmt_module[GMT_LEN32] = "gmt";
12616 	const char *keys = NULL;
12617 
12618 	if (V_API == NULL) return_null (NULL, GMT_NOT_A_SESSION);
12619 	if (module == NULL) return_null (V_API, GMT_ARG_IS_NULL);
12620 	API = gmtapi_get_api_ptr (V_API);
12621 	API->error = GMT_NOERROR;
12622 
12623 	for (lib = 0; lib < API->n_shared_libs; lib++) {	/* Look for module in any of the shared libs */
12624 		keys = gmtapi_get_shared_module_keys (API, module, lib);
12625 		if (keys) return (keys);	/* Found it in this shared library, return the keys */
12626 	}
12627 	/* If we get here we did not found it.  Try to prefix module with gmt */
12628 	strncat (gmt_module, module, GMT_LEN32-4);		/* Concatenate gmt and module name to get function name */
12629 	for (lib = 0; lib < API->n_shared_libs; lib++) {	/* Look for gmt_module in any of the shared libs */
12630 		keys = gmtapi_get_shared_module_keys (API, gmt_module, lib);
12631 		if (keys) {	/* Found it in this shared library, adjust module name and return the keys */
12632 			strncpy (module, gmt_module, strlen(gmt_module));	/* Rewrite module name to contain prefix of gmt */
12633 			return (keys);
12634 		}
12635 	}
12636 	/* Not in any of the shared libraries */
12637 	GMT_Report (API, GMT_MSG_ERROR, "Shared GMT module not found: %s \n", module);
12638 	return_null (V_API, GMT_NOT_A_VALID_MODULE);
12639 }
12640 
gmtapi_extract_argument(char * optarg,char * argument,char ** key,int k,bool colon,int * n_pre,unsigned int * takes_mod)12641 GMT_LOCAL int gmtapi_extract_argument (char *optarg, char *argument, char **key, int k, bool colon, int *n_pre, unsigned int *takes_mod) {
12642 	/* Two separate actions:
12643 	 * 1) If key ends with "=" then we pull out the option argument after stripping off +<stuff>.
12644 	 * 2) If key ends with "=q" then we see if +q is given and return pos to this modifiers argument.
12645 	 * 3) Else we just copy input to output.
12646 	 * We also set n_pre which is the number of characters to skip after the -X option
12647 	 *   before looking for an argument.
12648 	 * If colon is true we also strip argument from the first colon onwards.
12649 	 * If this all sounds messy it is because the GMT command-line syntax of some modules are
12650 	 *   messy and predate the whole API business...
12651 	 */
12652 	char *c = NULL;
12653 	unsigned int pos = 0;
12654 	*n_pre = 0;
12655 	*takes_mod = 0;
12656 	if (k >= 0 && key[k][K_EQUAL] == '=') {	/* Special handling */
12657 		*takes_mod = 1;	/* Flag that KEY was special */
12658 		*n_pre = (key[k][K_MODIFIER] && isdigit (key[k][K_MODIFIER])) ? (int)(key[k][K_MODIFIER]-'0') : 0;
12659 		if ((*n_pre || key[k][K_MODIFIER] == 0) && (c = strchr (optarg, '+'))) {	/* Strip off trailing +<modifiers> */
12660 			c[0] = 0;
12661 			strcpy (argument, optarg);
12662 			c[0] = '+';
12663 			if (!argument[0]) *takes_mod = 2;	/* Flag that option is missing the arg and needs it later */
12664 		}
12665 		else if (key[k][K_MODIFIER]) {	/* Look for a specific +<mod> in the option */
12666 			char code[3] = {"+?"};
12667 			code[1] = key[k][K_MODIFIER];
12668 			if ((c = strstr (optarg, code))) {	/* Found +<modifier> */
12669 				strcpy (argument, optarg);
12670 				if (!c[2] || c[2] == '+') *takes_mod = 2;	/* Flag that option had no argument that KEY was looking for */
12671 				pos = (unsigned int) (c - optarg + 2);	/* Position of this modifier's argument. E.g., -E+f<file> will have pos = 2 as start of <file> */
12672 			}
12673 			else	/* No modifier involved */
12674 				strcpy (argument, optarg);
12675 		}
12676 		else
12677 			strcpy (argument, optarg);
12678 	}
12679 	else
12680 		strcpy (argument, optarg);
12681 	if (colon && (c = strchr (argument, ':')))	/* Also chop of :<more arguments> */
12682 		c[0] = '\0';
12683 
12684 	return pos;
12685 }
12686 
gmtapi_B_custom_annotations(struct GMT_OPTION * opt)12687 GMT_LOCAL int gmtapi_B_custom_annotations (struct GMT_OPTION *opt) {
12688 	/* Replace -B[p|s][x|y|z]c with -B[p|s][x|y|z]c? */
12689 	unsigned int k = 0;
12690 	if (opt->option != 'B') return 0;	/* Not the right option */
12691 	if (strchr ("ps", opt->arg[k])) k++;	/* Skip a leading p|s for primary|secondary axis */
12692 	if (strchr ("xyz", opt->arg[k])) k++;	/* Skip optional x|y|z for specific axis */
12693 	if (opt->arg[k] != 'c') return 0;	/* Not a custom annotation request */
12694 	if (opt->arg[k+1]) return 0;		/* Should be empty if we are to add ? */
12695 	opt->arg = realloc (opt->arg, strlen (opt->arg)+2);	/* Make space for ? */
12696 	strcat (opt->arg, "?");
12697 	return 1;
12698 }
12699 
gmtapi_operator_takes_dataset(struct GMT_OPTION * opt,int * geometry)12700 GMT_LOCAL bool gmtapi_operator_takes_dataset (struct GMT_OPTION *opt, int *geometry) {
12701 	/* Check if the sequence ? OPERATOR is one that requires reading a dataset instead of a grid.
12702 	 * Here, opt is an input argument with value ? and we inquire about the next option (the operator).
12703 	 * geometry is already set to GMT_IS_GRID */
12704 	if (opt == NULL) return false;	/* Just being paranoid */
12705 	if (opt->next == NULL) return false;	/* Just being extra paranoid */
12706 	if (opt->next->option != GMT_OPT_INFILE) return false;	/* Cannot be an operator */
12707 	if (!opt->next->arg[0]) return false;	/* No argument given */
12708 	if (!strncmp (opt->next->arg, "INSIDE", 6U)) {	/* Are nodes inside/outside a polygon */
12709 		*geometry = GMT_IS_POLY;
12710 		return true;
12711 	}
12712 	if (!strncmp (opt->next->arg, "POINT", 5U) || !strncmp (opt->next->arg, "PDIST", 5U)) {
12713 		*geometry = GMT_IS_POINT;
12714 		return true;	/* One of the dataset-requiring operators */
12715 	}
12716 	if (!strncmp (opt->next->arg, "LDIST", 5U)) {	/* Distance to lines of some sort */
12717 		*geometry = GMT_IS_LINE;
12718 		return true;	/* One of the dataset-requiring operators */
12719 	}
12720 	return false;	/* No, something else */
12721 }
12722 
gmtapi_grdinterpolate_type(struct GMTAPI_CTRL * API,struct GMT_OPTION * options)12723 GMT_LOCAL char gmtapi_grdinterpolate_type (struct GMTAPI_CTRL *API, struct GMT_OPTION *options) {
12724 	char type;
12725 	struct GMT_OPTION *opt = NULL, *E = NULL, *S = NULL;
12726 	gmt_M_unused (API);
12727 
12728 	for (opt = options; opt; opt = opt->next) {	/* Look for -E and -S */
12729 		if (opt->option == 'E') E = opt;
12730 		else if (opt->option == 'S') S = opt;
12731 	}
12732 	/* Now determine the various cases that yield different return types */
12733 	if (S)	/* Sample down lines and returning result via a dataset */
12734 		type = 'D';
12735 	else if (E)	/* Return a vertical slice via a grid */
12736 		type = 'G';
12737 	else	/* Interpolating onto a single or multilayer cube */
12738 		type = 'U';
12739 
12740 	return (type);
12741 }
12742 
12743 #define api_is_required_IO(key) (key == API_PRIMARY_INPUT || key == API_PRIMARY_OUTPUT)			/* Returns true if this is a primary input or output item */
12744 #define api_not_required_io(key) ((key == API_PRIMARY_INPUT || key == API_SECONDARY_INPUT) ? API_SECONDARY_INPUT : API_SECONDARY_OUTPUT)	/* Returns the optional input or output flag */
12745 
12746 /*! . */
GMT_Encode_Options(void * V_API,const char * module_name,int n_in,struct GMT_OPTION ** head,unsigned int * n)12747 struct GMT_RESOURCE * GMT_Encode_Options (void *V_API, const char *module_name, int n_in, struct GMT_OPTION **head, unsigned int *n) {
12748 	/* This function determines which input sources and output destinations are required given the module options.
12749 	 * It is only used to assist developers of external APIs, such as the MATLAB, Julia, Python, R, and others.
12750 	 * "Keys" referred to below is the unique combination given near the top of every module via the macro
12751 	 * THIS_MODULE_KEYS.  For instance, here are the keys for grdtrack:
12752 	 *
12753 	 * #define THIS_MODULE_KEYS        "<D{,DD),GG(,>D}"
12754 	 *
12755 	 * Here are the GMT_Encode_Options arguments:
12756 	 *   API	Controls all things within GMT.
12757 	 *   module	Name of the GMT module.
12758 	 *   n_in	Known number of objects given as input resources (-1 if not known).
12759 	 *   head	Linked list of GMT options passed for this module. We may hook on 1-2 additional options.
12760 	 *   *n		Number of structures returned by the function. Struct GMT_RESOURCE is defined in gmt.h
12761 	 *
12762 	 * We also return an array of structures with information about registered resources going to/from GMT.
12763 	 * Basically, given the module we look up the keys for that module, which tells us which options provide
12764 	 * the input and output selections and which ones are required and which ones are optional.  We then
12765 	 * scan the given options and if file arguments to the options listed in the keys are missing we are
12766 	 * to insert ? as the filename. Some options may already have the question mark. After scanning
12767 	 * the options we examine the keys for any required input or output argument that have yet to be specified
12768 	 * explicitly. If so we create the missing options, with filename = ?, and append them to the end of
12769 	 * the option list (head). The API developers can then use this array of encoded options in concert with
12770 	 * the information passed back via the structure list to attach actual resources.
12771 	 *
12772 	 * For each option that may take a file we need to know what kind of file and if it is input or output.
12773 	 * We encode this information in a 3-character word XYZ, explained below.  Note that each module may
12774 	 * need several comma-separated XYZ words and these are returned as one string via GMT_Get_Moduleinfo.
12775 	 * The X, Y, and Z letter position represent different information, as discussed below:
12776 	 *
12777 	 * X stands for the specific program OPTION (e.g., L for -L, F for -F). For tables or grids read from files or
12778 	 * tables processed via standard input we use '<', while '>' is used for standard (table) output.
12779 	 * Y stands for data TYPE (C = CPT, D = Dataset/Point, L = Dataset/Line, P = Dataset/Polygon,
12780 	 *    G = Grid, I = Image, X = PostScript, ? = type specified via a module option [more later]),
12781 	 *    while a hyphen (-) means there is NO data when this option is set (see Z for whether this is for in- or output).
12782 	 * Z stands for PRIMARY inputs '{', primary output '}' OR SECONDARY input '(', or secondary output ')'.
12783 	 *   Primary inputs and outputs MUST be assigned, and if not explicitly given will result in
12784 	 *   a syntax error. However, external APIs (mex, Python) can override this and supply the missing items
12785 	 *   via any given left- and right-hand side arguments to supply inputs or accept outputs.
12786 	 *   Secondary inputs means they are only assigned if an option is actually given.  If the in|out designation
12787 	 *   is irrelevant for an option we use '-'.
12788 	 *
12789 	 * There are a few special cases where X, Y, or Z take on "magic" behavior:
12790 	 *
12791 	 *   A few modules with have X = - (hyphen). This means the primary input or output (determined by Z)
12792 	 *   has a data type that is not known until runtime.  A module option will tells us which type it is, and this
12793 	 *   option is encoded in Y.  So a -Y<type> option is _required_ and that is how we can update the primary
12794 	 *   data type.  Example: gmtread can read any GMT object but requires -T<type>.  It thus has the keys
12795 	 *   "<?{,>?},-T-".  Hence, we examine -T<type> and replace ? with the dataset implied by <type> both for input
12796 	 *   AND output (since Z was indeterminate).  Use i|o if only input or output should have this treatment.
12797 	 *
12798 	 *   A few modules will have Y = - which is another magic key: If the -X option is given then either the input
12799 	 *   or output (depending on what Z is) will NOT be required. As an example of this behavior, consider psxy
12800 	 *   which has a -T option that means "read no input, just write trailer". So the key "T-<" in psxy means that
12801 	 *   when -T is used then NO input is required.  This means the primary input key "<D{" is changed to "<D(" (secondary)
12802 	 *   and no attempt is made to connect external input to the psxy input.  If Z is none of () then we expect Z to
12803 	 *   be one of the options with required input (or output) and we change that option to option input (or output).
12804 	 *   Example: grdtrack has two required inputs (the grid(s) and the track/point file.  However, if -E is set then
12805 	 *   the track/point file is not expected so we need to change it to secondary.  We thus add E-< which then will
12806 	 *   change <D{ to <D(.  A modifier is also possible.  For instance -F<grid>[+d] is used by several modules, such
12807 	 *   as triangulate, to use the non-NaN nodes in a grid as the input data instead of reading the primary input
12808 	 *   source.  So F-( would turn off primary input.  However, if +d is present then we want to combine the grid with
12809 	 *   the primary input and hence we read that as well.
12810 	 *
12811 	 *   A few modules will specify Z as some letter not in {|(|}|)|-, which means that normally these modules
12812 	 *   will expect/produce whatever input/output is specified by the primary setting, but if the "-Z" option is given the primary
12813 	 *   input/output will be changed to the given type Y.  Also, modifiers may be involved. The full syntax for this is
12814 	 *   XYZ[+abc...][-def...]: We do the substitution of output type to Y only if
12815 	 *      1. -Z is given on the command line
12816 	 *      2. -Z contains ALL the modifiers from the first "+"-list: +a, +b, +c, ... [optional]
12817 	 *      3. -Z contains AT LEAST ONE of the modifiers from the second "-"-list: +d, +e, +f. [optional]
12818 	 *   At least on case from 2 or 3 must be specified.
12819 	 *   The Z magic is a bit confusing so here is some examples:
12820 	 *   1. grdcontour normally writes PostScript but grdcontour -D will instead export data to std (or a file set by -D), so its key
12821 	 *      contains the entry "DDD": When -D is active then the PostScript key ">X}" morphs into "DD}" and
12822 	 *      thus allows for a data set export instead.
12823 	 *   2. pscoast normally plots PostSCript but pscoast -E+l only want to return a text listing of countries.  We allow for this
12824 	 *      switch by using the key >DE-lL so that if -E with either +l or +L are used we change primary output to D.
12825 	 *
12826 	 *   After processing, all magic key sequences are set to "---" to render them inactive.
12827 	 *
12828 	 */
12829 
12830 	unsigned int n_keys, direction = 0, kind, pos = 0, n_items = 0, ku, n_out = 0, nn[2][2];
12831 	unsigned int output_pos = 0, input_pos = 0, mod_pos, takes_mod;
12832 	int family = GMT_NOTSET;	/* -1, or one of GMT_IS_DATASET, GMT_IS_GRID, GMT_IS_PALETTE, GMT_IS_IMAGE */
12833 	int geometry = GMT_NOTSET;	/* -1, or one of GMT_IS_NONE, GMT_IS_TEXT, GMT_IS_POINT, GMT_IS_LINE, GMT_IS_POLY, GMT_IS_SURFACE */
12834 	int sdir, k = 0, n_in_added = 0, n_to_add, e, n_pre_arg, n_per_family[GMT_N_FAMILIES];
12835 	bool deactivate_output = false, deactivate_input = false, strip_colon = false, strip = false, is_grdmath = false;
12836 	size_t n_alloc, len;
12837 	const char *keys = NULL;	/* This module's option keys */
12838 	char **key = NULL;		/* Array of items in keys */
12839 	char *text = NULL, *LR[2] = {"rhs", "lhs"}, *S[2] = {" IN", "OUT"}, txt[GMT_LEN256] = {""}, type = 0;
12840 	char module[GMT_LEN32] = {""}, argument[PATH_MAX] = {""}, strip_colon_opt = 0;
12841 	char *special_text[3] = {" [satisfies required input]", " [satisfies required output]", ""}, *satisfy = NULL;
12842 	struct GMT_OPTION *opt = NULL, *new_ptr = NULL;	/* Pointer to a GMT option structure */
12843 	struct GMT_RESOURCE *info = NULL;	/* Our return array of n_items info structures */
12844 	struct GMTAPI_CTRL *API = NULL;
12845 
12846 	*n = 0;	/* Initialize counter to zero in case we return prematurely */
12847 
12848 	if (V_API == NULL) return_null (NULL, GMT_NOT_A_SESSION);
12849 	if (module_name == NULL) return_null (V_API, GMT_ARG_IS_NULL);
12850 
12851 	if ((*head) && ((*head)->option == GMT_OPT_USAGE || (*head)->option == GMT_OPT_SYNOPSIS)) {	/* Nothing to do */
12852 		*n = UINT_MAX;
12853 		return NULL;
12854 	}
12855 	API = gmtapi_get_api_ptr (V_API);
12856 	API->error = GMT_NOERROR;
12857 	(void) gmt_current_name (module_name, module);
12858 	gmt_manage_workflow (API, GMT_USE_WORKFLOW, NULL);		/* Detect and set modern mode if modern mode session dir is found */
12859 	/* 0. Get the keys for the module, possibly prepend "gmt" to module if required, or list modules and return NULL if unknown module */
12860 	if ((keys = gmtapi_retrieve_module_keys (V_API, module)) == NULL) {	/* Gave an unknown module */
12861 		if (GMT_Call_Module (V_API, NULL, GMT_MODULE_PURPOSE, NULL))	/* List the available modules */
12862 			return_null (NULL, GMT_NOT_A_VALID_MODULE);
12863 		return_null (NULL, GMT_NOT_A_VALID_MODULE);	/* Unknown module code */
12864 	}
12865 
12866 	/* First some special checks related to unusual GMT syntax or hidden modules */
12867 
12868 	/* 1a. Check if this is the pscoast module, where output type is either PostScript or Dataset */
12869 	if (!strncmp (module, "pscoast", 7U)) {
12870 		/* Potential problem under modern mode: No -J -R set but will be provided later, and we are doing -E for coloring or lines */
12871 		if (GMT_Find_Option (API, 'M', *head)) type = 'D';	/* -M means dataset dump */
12872 		else if (GMT_Find_Option (API, 'C', *head) || GMT_Find_Option (API, 'G', *head) || GMT_Find_Option (API, 'I', *head) || GMT_Find_Option (API, 'N', *head) || GMT_Find_Option (API, 'W', *head)) type = 'X';	/* Clearly plotting GSHHG */
12873 		else if ((opt = GMT_Find_Option (API, 'E', *head)) && gmt_found_modifier (API->GMT, opt->arg, "cCgp")) type = 'X';	/* Clearly plotting DCW polygons */
12874 		else if ((opt = GMT_Find_Option (API, 'E', *head)) && gmt_found_modifier (API->GMT, opt->arg, "lL")) type = 'D';	/* Clearly requesting listing of DCW countries/states */
12875 		else if (!GMT_Find_Option (API, 'J', *head)) type = 'D';	/* No -M and no -J means -Rstring as dataset */
12876 		else type = 'X';	/* Otherwise we are still most likely plotting PostScript */
12877 	}
12878 	/* 1b. Check if this is psxy or psxyz modules with quoted or decorated lines. For any other -S~|q? flavor we kill the key with ! */
12879 	else if ((!strncmp (module, "psxy", 4U) || !strncmp (module, "psxyz", 5U)) && (opt = GMT_Find_Option (API, 'S', *head))) {
12880 		/* Found the -S option, check if we requested quoted or decorated lines via fixed or crossing lines */
12881 		/* If not f|x then we don't want this at all and set type = ! */
12882 		type = (!strchr ("~q", opt->arg[0]) || !strchr ("fx", opt->arg[1])) ? '!' : 'D';
12883 		strip_colon = (type && strchr (opt->arg, ':'));
12884 		strip_colon_opt = opt->option;
12885 		if (strip_colon)
12886 			GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Got quoted or decorate line and must strip argument %s from colon to end\n", opt->arg);
12887 	}
12888 	/* 1c. Check if this is either gmtmath or grdmath which both use the special = outfile syntax and replace that by -=<outfile> */
12889 	else if (!strncmp (module, "gmtmath", 7U) || (is_grdmath = (strncmp (module, "grdmath", 7U) == 0))) {
12890 		struct GMT_OPTION *delete = NULL;
12891 		for (opt = *head; opt && opt->next; opt = opt->next) {	/* Here opt will end up being the last option */
12892 			if (!strcmp (opt->arg, "=")) {
12893 				if (opt->next) {	/* Combine the previous = and <whatever> options into a single -=<whatever> option, then delete the former */
12894 					opt->next->option = '=';
12895 					delete = opt;
12896 				}
12897 			}
12898 		}
12899 		GMT_Delete_Option (API, delete, head);
12900 	}
12901 	/* 1d. Check if this is the write special module, which has flagged its output file as input... */
12902 	else if (!strncmp (module, "gmtwrite", 8U) && (opt = GMT_Find_Option (API, GMT_OPT_INFILE, *head))) {
12903 		/* Found a -<"file" option; this is actually the output file so we simply change the option to output */
12904 		opt->option = GMT_OPT_OUTFILE;
12905 		deactivate_output = true;	/* Remember to turn off implicit output option since we got one */
12906 	}
12907 	/* 1e. Check if this is the grdconvert module, which uses the syntax "infile outfile" without any option flags */
12908 	else if (!strncmp (module, "grdconvert", 10U) && (opt = GMT_Find_Option (API, GMT_OPT_INFILE, *head))) {
12909 		/* Found a -<"file" option; this is indeed the input file but the 2nd "input" is actually output */
12910 		if (opt->next && (opt = GMT_Find_Option (API, GMT_OPT_INFILE, opt->next)))	/* Found the next input file option */
12911 			opt->option = GMT_OPT_OUTFILE;	/* Switch it to an output option */
12912 	}
12913 	/* 1f. Check if this is the greenspline module, where output type is grid for dimension == 2 else it is dataset */
12914 	else if (!strncmp (module, "greenspline", 11U) && (opt = GMT_Find_Option (API, 'R', *head))) {
12915 		/* Found the -R"domain" option; determine the dimensionality of the output */
12916 		unsigned dim = gmtapi_determine_dimension (API, opt->arg);
12917 		switch (dim) {	/* Determine if output is D, G, or U */
12918 			case 1: type = 'D'; break;	/* 1-D is a data table */
12919 			case 2: type = 'G'; break;	/* 2-D is always a grid */
12920 			default:	/* 3-D, but can be dataset or cube */
12921 				type = ((opt = GMT_Find_Option (API, 'G', *head))) ? 'U' : 'D';
12922 				break;
12923 		}
12924 	}
12925 	/* 1g. Check if this is the triangulate module, where primary dataset output should be turned off if -G given without -M,N,Q,S */
12926 	else if (!strncmp (module, "triangulate", 11U) && (opt = GMT_Find_Option (API, 'G', *head))) {
12927 		/* Found the -G<grid> option; determine if any of -M,N,Q,S are also set */
12928 		if (!((opt = GMT_Find_Option (API, 'M', *head)) || (opt = GMT_Find_Option (API, 'N', *head))
12929 			|| (opt = GMT_Find_Option (API, 'Q', *head)) || (opt = GMT_Find_Option (API, 'S', *head))))
12930 				deactivate_output = true;	/* Turn off implicit output since none is in effect */
12931 	}
12932 	/* 1h. Check if this is a *contour modules with -Gf|x given. For any other -G? flavor we kill the key with ! */
12933 	else if ((!strncmp (module, "grdcontour", 10U) || !strncmp (module, "pscontour", 9U)) && (opt = GMT_Find_Option (API, 'G', *head))) {
12934 		/* Found the -G option, check if any strings are requested */
12935 		/* If not -Gf|x then we don't want this at all and set type = ! */
12936 		type = (opt->arg[0] == 'f' || opt->arg[0] == 'x') ? 'D' : '!';
12937 	}
12938 	/* 1i. Check if this is the talwani3d module, where output type is grid except with -N it is dataset */
12939 	else if (!strncmp (module, "talwani3d", 9U)) {
12940 		/* If we find the -N option, we set type to D, else G */
12941 		type = (GMT_Find_Option (API, 'N', *head)) ? 'D' : 'G';
12942 	}
12943 	/* 1j. Check if this is a blockm* module using -A to set n output grids */
12944 	else if (!strncmp (module, "block", 5U) && (opt = GMT_Find_Option (API, 'A', *head))) {
12945 		/* Below, k is the number of under=the-hood -G? options we must add for returning grids to externals */
12946 		k = 0;	/* Make sure we initialize this first */
12947 		if (opt->arg[0]) {	/* Gave -A: Determine how many output grids are requested */
12948 			for (k = 1, len = 0; len < strlen (opt->arg); len++) if (opt->arg[len] == ',') k++;
12949 		}
12950 		/* Here, k is still 0 or it is the number of fields selected via -A */
12951 		if ((opt = GMT_Find_Option (API, 'G', *head))) {	/* This is a problem unless -G actually sent in a file name */
12952 			if (opt->arg[0] == '\0') {	/* Cannot just give -G here */
12953 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: %s cannot set -G when called externally\n", module);
12954 				return_null (NULL, GMT_NOT_A_VALID_OPTION);
12955 			}
12956 			else	/* Gave a (presumably) file argument, no need to add -G? separately since the user did it */
12957 				k = 0;
12958 		}
12959 		else if (k == 0)	/* No -A or -G; default is to just add the z grid via -G?  */
12960 			k = 1;
12961 		while (k) {	/* Add -G? option k times */
12962 			new_ptr = GMT_Make_Option (API, 'G', "?");	/* Create new output grid option(s) with filename "?" */
12963 			*head = GMT_Append_Option (API, new_ptr, *head);
12964 			k--;
12965 		}
12966 		deactivate_output = true;	/* Turn off implicit table output since only secondary -G output(s) is in effect */
12967 	}
12968 	/* 1k. Check if this is the earthtide module requesting output grids */
12969 	else if (!strncmp (module, "earthtide", 9U) && !GMT_Find_Option (API, 'L', *head) && !GMT_Find_Option (API, 'S', *head)) {
12970 		/* Below, k is the number of under=the-hood -G? options we must add for returning grids to externals */
12971 		k = 0;	/* Make sure we initialize this first */
12972 		if ((opt = GMT_Find_Option (API, 'C', *head))) {	/* Gave -C: Determine how many output grids are requested */
12973 			for (k = 1, len = 0; len < strlen (opt->arg); len++) if (opt->arg[len] == ',') k++;
12974 		}
12975 		if ((opt = GMT_Find_Option(API, 'G', *head))) {	/* This is a problem unless -G actually sent in a file name */
12976 			if (opt->arg[0] == '\0') {	/* Cannot just give -G here */
12977 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: %s cannot set -G (with no argument) when called externally\n", module);
12978 				return_null (NULL, GMT_NOT_A_VALID_OPTION);
12979 			}
12980 			else	/* Gave a (presumably) file argument, no need to add -G? */
12981 				k = 0;
12982 		}
12983 		else if (k == 0) 	/* No -C or -G; default is to just add the -Gz grid via -G? */
12984 			k = 1;
12985 		while (k) {	/* Add -G? option k times */
12986 			new_ptr = GMT_Make_Option (API, 'G', "?");	/* Create new output grid option(s) with filename "?" */
12987 			*head = GMT_Append_Option (API, new_ptr, *head);
12988 			k--;
12989 		}
12990 		deactivate_output = true;	/* Turn off implicit table output since only secondary -G output(s) is in effect */
12991 	}
12992 	/* 1l. Check if this is makecpt using -E or -S with no args */
12993 	else if (!strncmp (module, "makecpt", 7U)) {
12994 		if (((opt = GMT_Find_Option (API, 'E', *head)) || (opt = GMT_Find_Option (API, 'S', *head)))) {
12995 			if (opt->arg[0] == '\0') {	/* Found the -E or -S option without arguments */
12996 				gmt_M_str_free (opt->arg);
12997 				if (opt->option == 'E')	/* Gave -E but we need to pass -E0 */
12998 					opt->arg = strdup ("0");
12999 				else	/* Replace -S with -Sr */
13000 					opt->arg = strdup ("r");
13001 			}
13002 			/* Then add implicit ? if no input file found */
13003 			if ((opt = GMT_Find_Option (API, GMT_OPT_INFILE, *head)) == NULL) {	/* Must assume implicit input file is available */
13004 				new_ptr = GMT_Make_Option (API, GMT_OPT_INFILE, "?");
13005 				*head = GMT_Append_Option (API, new_ptr, *head);
13006 			}
13007 		}
13008 		else if (API->GMT->current.setting.run_mode == GMT_MODERN && (opt = GMT_Find_Option (API, 'H', *head)) == NULL) {	/* Modern mode, no -H */
13009 			deactivate_output = true;	/* Turn off implicit output since none is in effect */
13010 		}
13011 	}
13012 	/* 1m. Check if this is the grdgradient module, where primary dataset output should be turned off if -Qc and no -G is set */
13013 	else if (!strncmp (module, "grdgradient", 11U) && (opt = GMT_Find_Option (API, 'G', *head)) == NULL) {
13014 		/* Found no -G<grid> option; determine if -Qc is set */
13015 		if ((opt = GMT_Find_Option (API, 'Q', *head)) && opt->arg[0] == 'c')
13016 			deactivate_output = true;	/* Turn off implicit output since none is in effect */
13017 	}
13018 	/* 1m. Check if this is the grdgradient module, where primary dataset output should be turned off if -Qc and no -G is set */
13019 	else if (!strncmp (module, "pstext", 6U) && (opt = GMT_Find_Option (API, 'F', *head))) {
13020 		/* With -F+c<just>+t<label> there is no file to read */
13021 		if (gmt_no_pstext_input (API, opt->arg))
13022 			deactivate_input = true;	/* Turn off implicit input since none is in effect */
13023 	}
13024 	/* 1n. Check that in modern mode, grd2cpt requires -H to write out to stdout */
13025 	else if (!strncmp (module, "grd2cpt", 7U)) {
13026 		if (API->GMT->current.setting.run_mode == GMT_MODERN && (opt = GMT_Find_Option (API, 'H', *head)) == NULL) {	/* Modern mode, no -H */
13027 			deactivate_output = true;	/* Turn off implicit output since none is in effect */
13028 		}
13029 	}
13030 	/* 1o. Check if grdinterpolate is producing grids or datasets */
13031 	else if (!strncmp (module, "grdinterpolate", 14U)) {
13032 		type = gmtapi_grdinterpolate_type (API, *head);	/* Determine the right type for the output */
13033 	}
13034 	/* 1p. Check if grdgdal is reading a dataset */
13035 	else if (!strncmp (module, "grdgdal", 7U)) {	/* Set input data type based on options */
13036 		if ((opt = GMT_Find_Option (API, 'A', *head)) && (strstr (opt->arg, "grid") || strstr (opt->arg, "rasterize")))
13037 			type = 'D';
13038 		else
13039 			type = 'G';
13040 	}
13041 	/* 1q. Check if gmtget is downloading dataset */
13042 	else if (!strncmp (module, "gmtget", 6U) && (opt = GMT_Find_Option (API, 'D', *head))) {
13043 		deactivate_output = true;	/* Download, turn off output */
13044 	}
13045 	/* 1r. Check if gmtbinstats is doing hexagonal tiling */
13046 	else if (!strncmp (module, "gmtbinstats", 11U)) {
13047 		type = ((opt = GMT_Find_Option (API, 'T', *head)) && opt->arg[0] != 'r') ? 'D' : 'G';	/* Giving -T[h] means we change default output from grid to dataset */
13048 	}
13049 	/* 1s. Check if psevents is doing data prep */
13050 	else if (!strncmp (module, "psevents", 8U)) {
13051 		type = ((opt = GMT_Find_Option (API, 'A', *head)) && opt->arg[0] == 'r' && opt->arg[1] && isdigit (opt->arg[1])) ? 'D' : 'X';	/* Giving -Ar<dpi> means we resample a line, else plotting */
13052 	}
13053     /* 1t. Check if grdinfo is reading cubes or grids */
13054     else if (!strncmp (module, "grdinfo", 7U)) {
13055         type = ((opt = GMT_Find_Option (API, 'Q', *head))) ? 'U' : 'G'; /* Giving -Q means we are reading 3-D cubes */
13056     }
13057     /* 1u. Check if grdfill is writing grids or datasets */
13058     else if (!strncmp (module, "grdfill", 7U)) {
13059         type = ((opt = GMT_Find_Option (API, 'L', *head))) ? 'D' : 'G'; /* Giving -L means we are writing a table */
13060     }
13061     /* 1v. Check if spectrum1d uses stdout */
13062     else if (!strncmp (module, "spectrum1d", 10U)) {
13063          if ((opt = GMT_Find_Option (API, 'T', *head))) /* Giving -T deactivates stdout writing */
13064             deactivate_output = true;   /* Turn off implicit output since none is in effect */
13065     }
13066     /* 1w. Check if coupe is plotting or returning data */
13067     else if (!strncmp (module, "pscoupe", 7U)) {
13068         type = ((opt = GMT_Find_Option (API, 'A', *head)) && strstr (opt->arg, "+c")) ? 'D' : 'X';
13069     }
13070     /* 1x. Check if grdcut is just getting information and if input is a grid or image */
13071     else if (!strncmp (module, "grdcut", 6U)) {
13072     	if (GMT_Find_Option (API, 'D', *head))
13073 			deactivate_output = true;   /* Turn off implicit output since none is in effect, only secondary -D output */
13074 		type = (GMT_Find_Option (API, 'I', *head)) ? 'I' : 'G'; /* Giving -I means we are reading an image */
13075    }
13076 
13077 	/* 2a. Get the option key array for this module */
13078 	key = gmtapi_process_keys (API, keys, type, *head, n_per_family, &n_keys);	/* This is the array of keys for this module, e.g., "<D{,GG},..." */
13079 	if (n_keys == 0) {		/* gmtset and begin for instances have no keys */
13080 		*n = UINT_MAX;
13081 		return NULL;
13082 	}
13083 
13084 	if (gmt_M_is_verbose (API->GMT, GMT_MSG_DEBUG)) {
13085 		char txt[4] = {""};
13086 		for (k = 0; k < GMT_N_FAMILIES; k++) if (n_per_family[k] != GMT_NOTSET) {
13087 			(n_per_family[k] == GMTAPI_UNLIMITED) ? snprintf (txt, 4, ">1") : snprintf (txt, 4, "%d", n_per_family[k]);
13088 			GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: For %s we expect %s input objects\n", GMT_family[k], txt);
13089 		}
13090 	}
13091 
13092 	/* 2b. Make some specific modifications to the keys given the options passed */
13093 	if (deactivate_output && (k = gmtapi_get_key (API, GMT_OPT_OUTFILE, key, n_keys)) >= 0)
13094 		key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Since an explicit output file already specified or not required */
13095 
13096 	/* 2c. Make some specific modifications to the keys given the options passed */
13097 	if (deactivate_input && (k = gmtapi_get_key (API, GMT_OPT_INFILE, key, n_keys)) >= 0)
13098 		key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Since an explicit input file already specified or not required */
13099 
13100 	/* 3. Count command line output files */
13101 	for (opt = *head; opt; opt = opt->next)
13102 		if (opt->option == GMT_OPT_OUTFILE) n_out++;
13103 	if (n_out > 1) {
13104 		GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: Can only specify one main output object via command line\n");
13105 		return_null (NULL, GMT_ONLY_ONE_ALLOWED);	/* Too many output objects */
13106 	}
13107 	n_alloc = n_keys;	/* Initial number of allocations */
13108 	if ((info = gmt_M_memory (API->GMT, NULL, n_alloc, struct GMT_RESOURCE)) == NULL) {
13109 		GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: Unable to allocate GMT_RESOURCE array\n");
13110 		return_null (NULL, GMT_MEMORY_ERROR);
13111 	}
13112 
13113 	if (!strncmp (module, "psrose", 6U) && (opt = GMT_Find_Option (API, 'E', *head)) && strcmp (opt->arg, "m")) {
13114 		/* Giving any -E option but -Em means we have either input or output so must update the key accordingly */
13115 		if (strstr (opt->arg, "+w")) {	/* Writing output to file */
13116 			k = gmtapi_get_key (API, 'E', key, n_keys);	/* We know this key exist so k is not -1 */
13117 			gmt_M_str_free (key[k]);
13118 			key[k] = strdup ("ED)=w");	/* Require key to select output to file given via -E+w<file> */
13119 		}
13120 	}
13121 
13122 	/* 4. Determine position of file args given as ? or via missing arg (proxy for input matrix) */
13123 	/* Note: All explicit objects must be given after all implicit matrices have been listed */
13124 	for (opt = *head; opt; opt = opt->next) {	/* Process options */
13125 		strip = (strip_colon_opt == opt->option) ? strip_colon : false;	/* Just turn strip possibly true for the relevant option */
13126 		k = gmtapi_get_key (API, opt->option, key, n_keys);	/* If k >= 0 then this option is among those listed in the keys array */
13127 		family = geometry = GMT_NOTSET;	/* Not set yet */
13128 		if (k >= 0) {	/* Got a key, so split out family and geometry flags */
13129 			sdir = gmtapi_key_to_family (API, key[k], &family, &geometry);	/* Get dir, datatype, and geometry */
13130 			if (sdir < 0) {	/* Could not determine direction */
13131 				GMT_Report (API, GMT_MSG_WARNING, "GMT_Encode_Options: Key not understood so direction is undefined? Notify developers\n");
13132 				sdir = GMT_IN;
13133 			}
13134 			direction = (unsigned int) sdir;
13135 		}
13136 		mod_pos = gmtapi_extract_argument (opt->arg, argument, key, k, strip, &n_pre_arg, &takes_mod);	/* Pull out the option argument, possibly modified by the key */
13137 		if (gmtapi_B_custom_annotations (opt)) {	/* Special processing for -B[p|s][x|y|z]c<nofilegiven>] */
13138 			/* Add this item to our list */
13139 			direction = GMT_IN;
13140 			info[n_items].option    = opt;
13141 			info[n_items].family    = GMT_IS_DATASET;
13142 			info[n_items].geometry  = GMT_IS_POINT;
13143 			info[n_items].direction = GMT_IN;
13144 			info[n_items].pos = pos = input_pos++;	/* Explicitly given arguments are the first given on the r.h.s. */
13145 			kind = GMT_FILE_EXPLICIT;
13146 			n_items++;
13147 			n_in_added++;
13148 		}
13149 		else if (gmtapi_found_marker (argument)) {	/* Found an explicit questionmark within the option, e.g., -G?, -R? or -<? */
13150 			if (opt->option == 'R' && !strcmp (opt->arg, "?")) {	/* -R? means append a grid so set those parameters here */
13151 				GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Option -R? found: explicit grid will be substituted\n");
13152 				family = GMT_IS_GRID;
13153 				geometry = GMT_IS_SURFACE;
13154 				direction = GMT_IN;
13155 			}
13156 			else if (k == GMT_NOTSET) {	/* Found questionmark but no corresponding key found? */
13157 				GMT_Report (API, GMT_MSG_WARNING, "GMT_Encode_Options: Got a -<option>? argument but not listed in keys\n");
13158 				direction = GMT_IN;	/* Have to assume it is an input file if not specified */
13159 			}
13160 			if (is_grdmath && gmtapi_operator_takes_dataset (opt, &geometry))
13161 				family = GMT_IS_DATASET;
13162 			info[n_items].mode = (k >= 0 && api_is_required_IO (key[k][K_DIR])) ? K_PRIMARY : K_SECONDARY;
13163 			if (k >= 0 && key[k][K_DIR] != '-')
13164 				key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Make sure required { becomes ( and } becomes ) so we don't add them later */
13165 			/* Add this item to our list */
13166 			info[n_items].option    = opt;
13167 			info[n_items].family    = family;
13168 			info[n_items].geometry  = geometry;
13169 			info[n_items].direction = direction;
13170 			info[n_items].pos = pos = (direction == GMT_IN) ? input_pos++ : output_pos++;	/* Explicitly given arguments are the first given on the r.h.s. */
13171 			kind = GMT_FILE_EXPLICIT;
13172 			n_items++;
13173 			if (direction == GMT_IN) n_in_added++;
13174 		}
13175 		else if (k >= 0 && key[k][K_OPT] != GMT_OPT_INFILE && family != GMT_NOTSET && key[k][K_DIR] != '-') {	/* Got some option like -G or -Lu with further args */
13176 			bool implicit = true;
13177 			if (takes_mod == 1)	/* Got some option like -E[+f] but no +f was given so no implicit file needed */
13178 				implicit = false;
13179 			else if ((len = strlen (argument)) == (size_t)n_pre_arg)	/* Got some option like -G or -Lu with no further args */
13180 				GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Option -%c needs implicit arg [offset = %d]\n", opt->option, n_pre_arg);
13181 			else if (mod_pos && (argument[mod_pos] == '\0' || argument[mod_pos] == '+'))	/* Found an embedded +q<noarg> */
13182 				GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Option -%c needs implicit arg via argument-less +%c modifier\n", opt->option, key[k][K_MODIFIER]);
13183 			else
13184 				implicit = false;
13185 			if (implicit) {
13186 				/* This is an implicit reference and we must explicitly add the missing item by adding the questionmark */
13187 				info[n_items].option    = opt;
13188 				info[n_items].family    = family;
13189 				info[n_items].geometry  = geometry;
13190 				info[n_items].direction = direction;
13191 				info[n_items].mode = (api_is_required_IO (key[k][K_DIR])) ? K_PRIMARY : K_SECONDARY;
13192 				key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Change to ( or ) since option was provided, albeit implicitly */
13193 				info[n_items].pos = pos = (direction == GMT_IN) ? input_pos++ : output_pos++;
13194 				/* Explicitly add the missing marker (e.g., ?) to the option argument */
13195 				if (mod_pos) {	/* Must expand something like 300k+s+d+u into 300k+s?+d+u (assuming +s triggered this test) */
13196 					strncpy (txt, opt->arg, mod_pos);
13197 					strcat (txt, "?");
13198 					if (opt->arg[mod_pos]) strcat (txt, &opt->arg[mod_pos]);
13199 				}
13200 				else if (strip)	/* Special case for quoted and decorated lines with colon separating label info */
13201 					snprintf (txt, GMT_LEN256, "%s?%s", argument, &opt->arg[2]);
13202 				else if (n_pre_arg)	/* Something like -Lu becomes -Lu? */
13203 					snprintf (txt, GMT_LEN256, "%s?", opt->arg);
13204 				else	/* Something like -C or -C+d200k becomes -C? or -C?+d200k */
13205 					snprintf (txt, GMT_LEN256, "?%s", opt->arg);
13206 				gmt_M_str_free (opt->arg);
13207 				opt->arg = strdup (txt);
13208 				kind = GMT_FILE_EXPLICIT;
13209 				n_items++;
13210 				if (direction == GMT_IN) n_in_added++;
13211 			}
13212 			else {	/* No implicit file argument involved, just check if this satisfies a required option */
13213 				kind = GMT_FILE_NONE;
13214 				if (k >= 0) {	/* If this was a required input|output it has now been satisfied */
13215 					/* Add check to make sure argument for input is an existing file! */
13216 					key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Change to ( or ) since option was provided, albeit implicitly */
13217 					satisfy = special_text[direction];
13218 				}
13219 				else	/* Nothing special about this option */
13220 					satisfy = special_text[2];
13221 			}
13222 		}
13223 		else {	/* No implicit file argument involved, just check if this satisfies a required option */
13224 			kind = GMT_FILE_NONE;
13225 			if (k >= 0) {	/* If this was a required input|output it has now been satisfied */
13226 				/* Add check to make sure argument for input is an existing file! */
13227 				key[k][K_DIR] = api_not_required_io (key[k][K_DIR]);	/* Change to ( or ) since option was provided, albeit implicitly */
13228 				satisfy = special_text[direction];
13229 			}
13230 			else	/* Nothing special about this option */
13231 				satisfy = special_text[2];
13232 		}
13233 		if (kind == GMT_FILE_EXPLICIT)
13234 			GMT_Report (API, GMT_MSG_DEBUG, "%s: Option -%c%s includes a memory reference to %s argument # %d\n",
13235 			            S[direction], opt->option, opt->arg, LR[direction], pos);
13236 		else
13237 			GMT_Report (API, GMT_MSG_DEBUG, "---: Option -%c%s includes no memory reference%s\n",
13238 			            opt->option, opt->arg, satisfy);
13239 		if (n_items == n_alloc) {
13240 			n_alloc <<= 1;
13241 			if ((info = gmt_M_memory (API->GMT, info, n_alloc, struct GMT_RESOURCE)) == NULL) {
13242 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: Unable to reallocate GMT_RESOURCE array\n");
13243 				return_null (NULL, GMT_MEMORY_ERROR);
13244 			}
13245 		}
13246 	}
13247 
13248 	/* Done processing references that were explicitly given in the options.  Now determine if module
13249 	 * has required input or output references that we must add (if not specified explicitly above) */
13250 
13251 	for (ku = 0; ku < n_keys; ku++) {	/* Each set of keys specifies if the item is required via the 3rd key letter */
13252 		if (api_is_required_IO (key[ku][K_DIR])) {	/* Required input|output that was not specified explicitly above */
13253 			char str[2] = {'?',0};
13254 			if ((sdir = gmtapi_key_to_family (API, key[ku], &family, &geometry)) == GMT_NOTSET) {	/* Extract family and geometry */
13255 				GMT_Report (API, GMT_MSG_WARNING, "Failure to extract family, geometry, and direction!!!!\n");
13256 				continue;
13257 			}
13258 			if (API->GMT->current.setting.run_mode == GMT_MODERN && family == GMT_IS_POSTSCRIPT) continue;	/* No PS output in modern mode please */
13259 			direction = (unsigned int) sdir;
13260 			/* We need to know how many implicit items for a given family we might have to add.  For instance,
13261 			 * one can usually give any number of data or text tables but only one grid file.  However, this is
13262 			 * not a fixed thing, hence we counted up n_per_family from the keys earlier so we have some limits */
13263 			if (direction == GMT_OUT || n_in == GMT_NOTSET)	/* For output or lack of info we only add one item per key */
13264 				n_to_add = 1;
13265 			else	/* More information to act on for inputs */
13266 				n_to_add = (n_per_family[family] == GMTAPI_UNLIMITED) ? n_in - n_in_added : n_per_family[family];
13267 			for (e = 0; e < n_to_add; e++) {
13268 				new_ptr = GMT_Make_Option (API, key[ku][K_OPT], str);	/* Create new option(s) with filename "?" */
13269 				/* Append the new option to the list */
13270 				*head = GMT_Append_Option (API, new_ptr, *head);
13271 				info[n_items].option    = new_ptr;
13272 				info[n_items].family    = family;
13273 				info[n_items].geometry  = geometry;
13274 				info[n_items].direction = direction;
13275 				info[n_items].pos = (direction == GMT_IN) ? input_pos++ : output_pos++;
13276 				info[n_items].mode = K_PRIMARY;
13277 				GMT_Report (API, GMT_MSG_DEBUG, "%s: Must add -%c? as implicit memory reference to %s argument # %d\n",
13278 					S[direction], key[ku][K_OPT], LR[direction], info[n_items].pos);
13279 				n_items++;
13280 				if (direction == GMT_IN) n_in_added++;
13281 				if (n_items == n_alloc) {
13282 					n_alloc <<= 1;
13283 					if ((info = gmt_M_memory (API->GMT, info, n_alloc, struct GMT_RESOURCE)) == NULL) {
13284 						GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: Unable to reallocate GMT_RESOURCE array\n");
13285 						return_null (NULL, GMT_MEMORY_ERROR);
13286 					}
13287 				}
13288 			}
13289 		}
13290 		gmt_M_str_free (key[ku]);	/* Free up this key */
13291 	}
13292 	/* Free up the temporary key array */
13293 	gmt_M_str_free (key);
13294 
13295 	/* Reallocate the information structure array or remove entirely if nothing given. */
13296 	if (n_items && n_items < n_alloc) {
13297 		if ((info = gmt_M_memory (API->GMT, info, n_items, struct GMT_RESOURCE)) == NULL) {
13298 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Encode_Options: Unable to finalize size of GMT_RESOURCE array\n");
13299 			return_null (NULL, GMT_MEMORY_ERROR);
13300 		}
13301 	}
13302 	else if (n_items == 0)	/* No containers used */
13303 		gmt_M_free (API->GMT, info);
13304 
13305 	gmt_M_memset (nn, 4, unsigned int);
13306 	for (ku = 0; ku < n_items; ku++)	/* Count how many primary and secondary objects each for input and output */
13307 		nn[info[ku].direction][info[ku].mode]++;
13308 
13309 	for (ku = 0; ku < n_items; ku++) {	/* Reorder positions so that primary objects are listed before secondary objects */
13310 		if (info[ku].mode == K_SECONDARY) info[ku].pos += nn[info[ku].direction][K_PRIMARY];	/* Move secondary objects after all primary objects for this direction */
13311 		else info[ku].pos -= nn[info[ku].direction][K_SECONDARY];	/* Move any primary objects to start of list for this direction */
13312 	}
13313 	GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Found %d inputs and %d outputs that need memory hook-up\n", input_pos, output_pos);
13314 #if 0
13315 	{	/* Left here for simple debugging with messages on the GMT side */
13316 		text = GMT_Create_Cmd (API, *head);
13317 		GMT_Report (API, GMT_MSG_NOTICE, "GMT_Encode_Options: Revised command before memory-substitution: %s\n", text);
13318 		GMT_Destroy_Cmd (API, &text);
13319 	}
13320 #endif
13321 	/* Just checking that the options were properly processed */
13322 	if (gmt_M_is_verbose (API->GMT, GMT_MSG_DEBUG)) {
13323 		static char *omode[2] = {"Primary", "Secondary"};
13324 		text = GMT_Create_Cmd (API, *head);
13325 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command before memory-substitution: %s\n", text);
13326 		GMT_Destroy_Cmd (API, &text);
13327 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: List of items returned:\n");
13328 		for (ku = 0; ku < n_items; ku++) {
13329 			GMT_Report (API, GMT_MSG_DEBUG, "External API item %2d: Family: %14s Direction: %6s Pos: %d Mode: %s\n",
13330 				ku, GMT_family[info[ku].family], GMT_direction[info[ku].direction], info[ku].pos, omode[info[ku].mode]);
13331 		}
13332 	}
13333 
13334 	/* Pass back the info array and the number of items */
13335 	*n = (n_items == 0) ? UINT_MAX : n_items;	/* E.g., n_keys = 0 for gmtset, gmtdefaults, gmtlogo */
13336 	return (info);
13337 }
13338 
13339 #ifdef FORTRAN_API
GMT_Encode_Options_(const char * module,int * n_in,struct GMT_OPTION ** head,unsigned int * n,int len)13340 struct GMT_RESOURCE *GMT_Encode_Options_ (const char *module, int *n_in, struct GMT_OPTION **head, unsigned int *n, int len) {
13341 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13342 	return (GMT_Encode_Options (GMT_FORTRAN, module, *n_in, head, n));
13343 }
13344 #endif
13345 
13346 /* Parsing API: to present, examine GMT Common Option current settings and GMT Default settings */
13347 
13348 /*! . */
GMT_Get_Common(void * V_API,unsigned int option,double par[])13349 int GMT_Get_Common (void *V_API, unsigned int option, double par[]) {
13350 	/* Inquires if specified GMT option has been set and obtains current values for some of them, if par is not NULL.
13351 	 * Returns GMT_NOTSET if the option has not been specified.  Otherwise, returns the number of parameters
13352 	 * it passed back via the par[] array.  Only some options passes back parameters; these are
13353 	 * -R, -I, -X, -Y, -b, -f, -i, -o, -r, -t, -:, while the others return 0.
13354 	 */
13355 	int ret = GMT_NOTSET;
13356 	struct GMTAPI_CTRL *API = NULL;
13357 	struct GMT_CTRL *GMT = NULL;
13358 
13359 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13360 	API = gmtapi_get_api_ptr (V_API);
13361 	API->error = GMT_NOERROR;
13362 	GMT = API->GMT;
13363 
13364 	switch (option) {
13365 		case 'B':	if (API->common_snapshot->B.active[0] || API->common_snapshot->B.active[1]) ret = 0; break;
13366 		case 'I':
13367 			if (API->common_snapshot->R.active[ISET]) {
13368 				ret = 2;
13369 				if (par) gmt_M_memcpy (par, API->common_snapshot->R.inc, 2, double);
13370 			}
13371 			break;
13372 		case 'J':	if (API->common_snapshot->J.active) ret = 0; break;
13373 		case 'K':	if (API->common_snapshot->K.active) ret = 0; break;
13374 		case 'O':	if (API->common_snapshot->O.active) ret = 0; break;
13375 		case 'P':	if (API->common_snapshot->P.active) ret = 0; break;
13376 		case 'R':
13377 			if (API->common_snapshot->R.active[RSET]) {
13378 				ret = 4;
13379 				if (par) gmt_M_memcpy (par, API->common_snapshot->R.wesn, 4, double);
13380 			}
13381 			break;
13382 		case 'U':	if (API->common_snapshot->U.active) ret = 0; break;
13383 		case 'V':	if (API->common_snapshot->V.active) ret = GMT->current.setting.verbose; break;
13384 		case 'X':
13385 			if (API->common_snapshot->X.active) {
13386 				ret = 1;
13387 				if (par) par[0] = API->common_snapshot->X.off;
13388 			}
13389 			break;
13390 		case 'Y':
13391 			if (API->common_snapshot->Y.active) {
13392 				ret = 1;
13393 				if (par) par[0] = API->common_snapshot->Y.off;
13394 			}
13395 			break;
13396 		case 'a':	if (API->common_snapshot->a.active) ret = API->common_snapshot->a.geometry; break;
13397 		case 'b':	if (API->common_snapshot->b.active[GMT_IN]) ret = GMT_IN; else if (API->common_snapshot->b.active[GMT_OUT]) ret = GMT_OUT; break;
13398 		case 'f':	if (API->common_snapshot->f.active[GMT_IN]) ret = GMT_IN; else if (API->common_snapshot->f.active[GMT_OUT]) ret = GMT_OUT; break;
13399 		case 'g':	if (API->common_snapshot->g.active) ret = 0; break;
13400 		case 'h':	if (API->common_snapshot->h.active) ret = API->common_snapshot->h.mode; break;
13401 		case 'i':	if (API->common_snapshot->i.select) ret = (int)API->common_snapshot->i.n_cols; break;
13402 		case 'n':	if (API->common_snapshot->n.active) ret = 0; break;
13403 		case 'o':	if (API->common_snapshot->o.select) ret = (int)API->common_snapshot->o.n_cols; break;
13404 		case 'p':	if (API->common_snapshot->p.active) ret = 0; break;
13405 		case 'r':	if (API->common_snapshot->R.active[GSET]) ret = API->common_snapshot->R.registration; break;
13406 		case 's':	if (API->common_snapshot->s.active) ret = 0; break;
13407 		case 't':
13408 			if (API->common_snapshot->t.active) {
13409 				ret = 2;
13410 				if (par) gmt_M_memcpy (par, API->common_snapshot->t.value, 2, double);
13411 			}
13412 			break;
13413 		case ':':	if (API->common_snapshot->colon.toggle[GMT_IN]) ret = GMT_IN; else if (API->common_snapshot->colon.toggle[GMT_OUT]) ret = GMT_OUT; break;
13414 		default:
13415 			gmtlib_report_error (API, GMT_OPTION_NOT_FOUND);
13416 			break;
13417 	}
13418 
13419 	return (ret);
13420 }
13421 
13422 #ifdef FORTRAN_API
GMT_Get_Common_(unsigned int * option,double par[])13423 int GMT_Get_Common_ (unsigned int *option, double par[]) {
13424 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13425 	return (GMT_Get_Common (GMT_FORTRAN, *option, par));
13426 }
13427 #endif
13428 
13429 /*! . */
GMT_Get_Default(void * V_API,const char * keyword,char * value)13430 int GMT_Get_Default (void *V_API, const char *keyword, char *value) {
13431 	/* Given the text representation of a GMT parameter keyword, return its setting as text.
13432 	 * value must have enough space for the return information.
13433 	 */
13434 	int error = GMT_NOERROR;
13435 	struct GMTAPI_CTRL *API = NULL;
13436 
13437 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13438 	if (keyword == NULL) return_error (V_API, GMT_NO_PARAMETERS);
13439 	if (value == NULL) return_error (V_API, GMT_NO_PARAMETERS);
13440 	API = gmtapi_get_api_ptr (V_API);
13441 	/* First intercept any API Keywords */
13442 	if (!strncmp (keyword, "API_VERSION", 11U))	/* The API version */
13443 		sprintf (value, "%s", GMT_PACKAGE_VERSION);
13444 	else if (!strncmp (keyword, "API_PAD", 7U))	/* Change the grid padding setting */
13445 		sprintf (value, "%d", API->pad);
13446 	else if (!strncmp (keyword, "API_BINDIR", 10U))	/* Report binary directory */
13447 		sprintf (value, "%s", API->GMT->init.runtime_bindir);
13448 	else if (!strncmp (keyword, "API_SHAREDIR", 12U))	/* Report share directory */
13449 		sprintf (value, "%s", API->GMT->session.SHAREDIR);
13450 	else if (!strncmp (keyword, "API_DATADIR", 12U))	/* Report data directory */
13451 		sprintf (value, "%s", API->GMT->session.DATADIR);
13452 	else if (!strncmp (keyword, "API_PLUGINDIR", 14U))	/* Report plugin directory */
13453 		sprintf (value, "%s", API->GMT->init.runtime_plugindir);
13454 	else if (!strncmp (keyword, "API_LIBRARY", 11U))	/* Report core library */
13455 		sprintf (value, "%s", API->GMT->init.runtime_library);
13456 	else if (!strncmp (keyword, "API_CORES", 9U))	/* Report number of cores */
13457 		sprintf (value, "%d", API->n_cores);
13458 	else if (!strncmp (keyword, "API_IMAGE_LAYOUT", 16U)) {	/* Report image/band layout */
13459 #ifdef HAVE_GDAL
13460 		gmt_M_memcpy (value, API->GMT->current.gdal_read_in.O.mem_layout, 4, char);
13461 #else
13462 		GMT_Report (API, GMT_MSG_ERROR, "API_IMAGE_LAYOUT only available when GMT is linked with GDAL; request ignored");
13463 		value[0] = '\0';
13464 		error = GMT_NOT_A_VALID_ARG;
13465 #endif
13466 	}
13467 	else if (!strncmp (keyword, "API_GRID_LAYOUT", 15U)) {	/* Report grid layout */
13468 		if (API->shape == GMT_IS_COL_FORMAT)
13469 			strcpy (value, "columns");
13470 		else if (API->shape == GMT_IS_ROW_FORMAT)
13471 			strcpy (value, "rows");
13472 	}
13473 	else {	/* Must process as a GMT setting */
13474 		strcpy (value, gmtlib_getparameter (API->GMT, keyword));
13475 		error = (value[0] == '\0') ? GMT_OPTION_NOT_FOUND : GMT_NOERROR;
13476 	}
13477 	return_error (V_API, error);
13478 }
13479 
13480 #ifdef FORTRAN_API
GMT_Get_Default_(char * keyword,char * value,int len1,int len2)13481 int GMT_Get_Default_ (char *keyword, char *value, int len1, int len2) {
13482 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13483 	return (GMT_Get_Default (GMT_FORTRAN, keyword, value));
13484 }
13485 #endif
13486 
13487 /*! . */
GMT_Set_Default(void * V_API,const char * keyword,const char * txt_val)13488 int GMT_Set_Default (void *V_API, const char *keyword, const char *txt_val) {
13489 	/* Given the text representation of a GMT or API parameter keyword, assign its value.
13490 	 */
13491 	unsigned int error = GMT_NOERROR;
13492 	struct GMTAPI_CTRL *API = NULL;
13493 	char *value = NULL;
13494 
13495 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13496 	if (keyword == NULL) return_error (V_API, GMT_NOT_A_VALID_PARAMETER);
13497 	if (txt_val == NULL) return_error (V_API, GMT_NO_PARAMETERS);
13498 	API = gmtapi_get_api_ptr (V_API);
13499 	value = strdup (txt_val);	/* Make local copy to be safe */
13500 	/* First intercept any API Keywords */
13501 	if (!strncmp (keyword, "API_PAD", 7U)) {	/* Change the grid padding setting */
13502 		int pad = atoi (value);
13503 		if (pad >= 0) {
13504 			gmt_set_pad (API->GMT, pad);	/* Change the default pad; give GMT_NOTSET to leave as is */
13505 			API->pad = pad;
13506 		}
13507 	}
13508 #ifdef HAVE_GDAL
13509 	else if (!strncmp (keyword, "API_IMAGE_LAYOUT", 16U)) {	/* Change image/band layout */
13510 		if (strlen (value) != 4U) {
13511 			error = 1;
13512 			GMT_Report (API, GMT_MSG_ERROR, "API_IMAGE_LAYOUT requires a 4-character specification. %s is ignored",  value);
13513 		}
13514 		else
13515 			gmt_M_memcpy (API->GMT->current.gdal_read_in.O.mem_layout, value, 4, char);
13516 	}
13517 #endif
13518 	else if (!strncmp (keyword, "API_GRID_LAYOUT", 15U)) {	/* Change grid layout */
13519 		if (!strncmp (value, "columns", 7U) || (strlen(value) >= 2 && value[1] == 'C'))		/* Accept also TC, though ignore 1st and 3-end chars. Accept this to be consistent with the "API_IMAGE_LAYOUT" case */
13520 			API->shape = GMT_IS_COL_FORMAT;	/* Switch to column-major format */
13521 		else if (!strncmp (value, "rows", 4U) || (strlen(value) >= 2 && value[1] == 'R'))
13522 			API->shape = GMT_IS_ROW_FORMAT;	/* Switch to row-major format */
13523 		else {
13524 			GMT_Report (API, GMT_MSG_ERROR, "API_GRID_LAYOUT must be either \"columns\" (or TC) or \"rows\" (TR)",  value);
13525 			error = 1;
13526 		}
13527 	}
13528 	else	/* Must process as a GMT setting */
13529 		error = gmtlib_setparameter (API->GMT, keyword, value, false);
13530 	gmt_M_str_free (value);
13531 	return_error (V_API, (error) ? GMT_NOT_A_VALID_PARAMETER : GMT_NOERROR);
13532 }
13533 
13534 #ifdef FORTRAN_API
GMT_Set_Default_(char * keyword,char * value,int len1,int len2)13535 int GMT_Set_Default_ (char *keyword, char *value, int len1, int len2) {
13536 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13537 	return (GMT_Set_Default (GMT_FORTRAN, keyword, value));
13538 }
13539 #endif
13540 
13541 /*! . */
GMT_Option(void * V_API,const char * options)13542 int GMT_Option (void *V_API, const char *options) {
13543 	/* Take comma-separated GMT options and print the corresponding usage message(s).
13544 	 * Taken: jbSGzCDkFyA */
13545 	unsigned int pos = 0, k = 0, n = 0;
13546 	char p[GMT_LEN64] = {""}, arg[GMT_LEN64] = {""};
13547 	struct GMTAPI_CTRL *API = NULL;
13548 
13549 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13550 	if (options == NULL) return_error (V_API, GMT_NO_PARAMETERS);
13551 	API = gmtapi_get_api_ptr (V_API);
13552 
13553 	/* The following does the translation between the rules for the option string and the convoluted items gmtlib_explain_options expects. */
13554 	while (gmt_strtok (options, ",", &pos, p) && k < (GMT_LEN64-1)) {
13555 		switch (p[0]) {
13556 			case 'B':	/* Let B be B and B- be b */
13557 				arg[k++] = (p[1] == '-') ? 'b' : 'B';
13558 				break;
13559 			case 'J':	/* Let J be -J and J- be j, JX is -Jx|X only, and -J[-]3 be adding -Z for 3-D scaling */
13560 				n = 1;
13561 				if (p[1] == '-') { arg[k++] = 'j'; n++; }
13562 				else if (p[1] == 'X') { arg[k++] = 'x'; n++; }
13563 				else arg[k++] = 'J';
13564 				if (p[n] == 'Z' || p[n] == 'z') arg[k++] = 'Z';
13565 				break;
13566 			case 'R':	/* Want -R region usage */
13567 				if (p[1]) {	/* Gave modifiers */
13568 					if (p[1] == 'x') arg[k++] = 'S';	/* CarteSian region */
13569 					else if (p[1] == 'g') arg[k++] = 'G';	/* Geographic region */
13570 					else arg[k++] = 'R';			/* Generic region [Default] */
13571 					if (p[1] == '3' || p[2] == '3') arg[k++] = 'z';	/* 3-D region */
13572 				}
13573 				else arg[k++] = 'R';			/* Generic region [Default] */
13574 				break;
13575 			case 'b':	/* Binary i/o -bi -bo */
13576 				arg[k++] = (p[1] == 'i') ? 'C' : 'D';
13577 				arg[k++] = (p[2]) ? p[2] : '0';
13578 				break;
13579 			case 'd':	/* Nodata flag -d, -di, -do */
13580 				if (p[1] == 'i') arg[k++] = 'k';
13581 				else if (p[1] == 'o') arg[k++] = 'm';
13582 				else arg[k++] = 'd';
13583 				break;
13584 			case 'j':	/* Spherical distance calculation mode */
13585 				arg[k++] = 'A';
13586 				break;
13587 			case 'q':	/* Row selection, either just input, output, or both */
13588 				if (p[1] == 'i')
13589 					arg[k++] = 'u';
13590 				else if (p[1] == 'o')
13591 					arg[k++] = 'v';
13592 				else
13593 					arg[k++] = p[0];
13594 				break;
13595 			case 'r':	/* Pixel registration */
13596 				arg[k++] = 'F';
13597 				break;
13598 			case 'x':	/* Number of threads (for multi-threaded progs) */
13599 				arg[k++] = 'y';
13600 				break;
13601 			default:	/* All others are pass-through */
13602 				arg[k++] = p[0];
13603 				break;
13604 		}
13605 	}
13606 	gmtlib_explain_options (API->GMT, arg);	/* Call the underlying explain_options machinery */
13607 	return_error (V_API, GMT_NOERROR);
13608 }
13609 
13610 #ifdef FORTRAN_API
GMT_Option_(char * options,int len)13611 int GMT_Option_ (char *options, int len) {
13612 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13613 	return (GMT_Option (GMT_FORTRAN, options));
13614 }
13615 #endif
13616 
13617 /*! . */
GMT_Message(void * V_API,unsigned int mode,const char * format,...)13618 int GMT_Message (void *V_API, unsigned int mode, const char *format, ...) {
13619 	/* Message independent of verbosity, optionally with timestamp.
13620 	 * mode = 0:	No time stamp
13621 	 * mode = 1:	Abs time stamp formatted via GMT_TIME_STAMP
13622 	 * mode = 2:	Report elapsed time since last reset.
13623 	 * mode = 4:	Reset elapsed time to 0, no time stamp.
13624 	 * mode = 6:	Reset elapsed time and report it as well.
13625 	 */
13626 	size_t source_info_len;
13627 	char *stamp = NULL;
13628 	struct GMTAPI_CTRL *API = NULL;
13629 	FILE *err = stderr;
13630 	va_list args;
13631 
13632 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13633 	if (format == NULL) return GMT_PTR_IS_NULL;	/* Format cannot be NULL */
13634 	API = gmtapi_get_api_ptr (V_API);	/* Get the typecast structure pointer to API */
13635 	API->message[0] = '\0';	/* Start fresh */
13636 	if (mode) stamp = gmtapi_tictoc_string (API, mode);	/* Pointer to a timestamp string */
13637 	if (mode % 4) sprintf (API->message, "%s | ", stamp);	/* Lead with the time stamp */
13638 	source_info_len = strlen (API->message);
13639 
13640 	va_start (args, format);
13641 	vsnprintf (API->message + source_info_len, GMT_MSGSIZ - source_info_len, format, args);
13642 	va_end (args);
13643 	assert (strlen (API->message) < GMT_MSGSIZ);
13644 	if (API->GMT) err = API->GMT->session.std[GMT_ERR];
13645 	API->print_func (err, API->message);	/* Do the printing */
13646 	return_error (V_API, GMT_NOERROR);
13647 }
13648 
13649 #ifdef FORTRAN_API
GMT_Message_(unsigned int * mode,const char * message,int len)13650 int GMT_Message_ (unsigned int *mode, const char *message, int len) {
13651 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13652 	return (GMT_Message (GMT_FORTRAN, *mode, message));
13653 }
13654 #endif
13655 
13656 /*! . */
GMT_Report(void * V_API,unsigned int level,const char * format,...)13657 int GMT_Report (void *V_API, unsigned int level, const char *format, ...) {
13658 	/* Message whose output depends on verbosity setting */
13659 	size_t source_info_len = 0;
13660 	unsigned int g_level;
13661 	const char *module_name;
13662 	char not_used[GMT_LEN32];
13663 	FILE *err = stderr;
13664 	struct GMTAPI_CTRL *API = NULL;
13665 	struct GMT_CTRL *GMT = NULL;
13666 	va_list args;
13667 	/* GMT_Report may be called before GMT is set so take precautions */
13668 	if (V_API == NULL) return GMT_NOERROR;		/* Not a fatal issue here but we cannot report anything */
13669 	if (level == GMT_MSG_QUIET)		/* We don't want to hear it, period. */
13670 		return GMT_NOERROR;
13671 	API = gmtapi_get_api_ptr (V_API);	/* Get the typecast structure pointer to API */
13672 	GMT = API->GMT;	/* Short-hand for the GMT sub-structure */
13673 	g_level = (GMT) ? GMT->current.setting.verbose : GMT_MSG_QUIET;
13674 	if (GMT) err = GMT->session.std[GMT_ERR];
13675 	if (level > MAX(API->verbose, g_level))
13676 		return GMT_NOERROR;
13677 	if (format == NULL) return GMT_PTR_IS_NULL;	/* Format cannot be NULL */
13678 	API->message[0] = '\0';	/* Start fresh */
13679 	if (GMT && GMT->current.setting.timer_mode > GMT_NO_TIMER) {
13680 		char *stamp = gmtapi_tictoc_string (API, GMT->current.setting.timer_mode);	/* NULL or pointer to a timestamp string */
13681 		if (stamp) {
13682 			sprintf (API->message, "%s | ", stamp);	/* Lead with the time stamp */
13683 			source_info_len = strlen (API->message);	/* Update length of message from 0 */
13684 		}
13685 	}
13686 	if (GMT && GMT->init.module_name)
13687 		module_name = ((GMT->current.setting.run_mode == GMT_MODERN)) ? gmt_current_name (GMT->init.module_name, not_used) : GMT->init.module_name;
13688 	else
13689 		module_name = API->session_tag;
13690 
13691 	snprintf (API->message + source_info_len, GMT_MSGSIZ-source_info_len, "%s [%s]: ", module_name, GMT_class[level]);
13692 	source_info_len = strlen (API->message);
13693 	va_start (args, format);
13694 	/* append format to the message: */
13695 	vsnprintf (API->message + source_info_len, GMT_MSGSIZ - source_info_len, format, args);
13696 	va_end (args);
13697 	assert (strlen (API->message) < GMT_MSGSIZ);
13698 	gmt_M_memcpy (API->error_msg, API->message, GMT_BUFSIZ-1, char);
13699 	API->print_func (err, API->message);
13700 	return_error (V_API, GMT_NOERROR);
13701 }
13702 
13703 #ifdef FORTRAN_API
GMT_Report_(unsigned int * level,const char * format,int len)13704 int GMT_Report_ (unsigned int *level, const char *format, int len) {
13705 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13706 	return (GMT_Report (GMT_FORTRAN, *level, format));
13707 }
13708 #endif
13709 
gmtapi_hyphen(const char * line,unsigned int stop)13710 GMT_LOCAL unsigned int gmtapi_hyphen (const char *line, unsigned int stop) {
13711     /* Examine a few cases:
13712      * 0) line[stop] is not a hyphen
13713      * 1) -option : Start of an option is not hyphenation.
13714      *    --PAR=value: Special case of option.
13715      * 2) hyphen-word: Detected if text/numbers on each side.  Just break, no continuation character needed.
13716      */
13717     if (line[stop] != '-') return 0;    /* Not a hyphenated case */
13718     if (stop == 0) return 2;    /* Nothing before the hyphen means option or negative number, cannot split */
13719     if (line[stop-1] == '-') return 0;   /* --PAR case caught */
13720     if (line[stop-1] == '[') return 0;   /*  [-option] case caught */
13721     if (line[stop-1] == ' ') return 0;   /*  -option case caught */
13722     //return (isalpha (line[stop+1]) && isalpha (line[stop-1]) ? 2 : 1);
13723     return (2); /* Regular hyphenated word or number range or similar */
13724 }
13725 
gmtapi_split_words(const char * line)13726 GMT_LOCAL struct GMT_WORD * gmtapi_split_words (const char *line) {
13727 	/* Split line into an array of words where words are separated by either
13728 	 * a space (like between options), the occurrence of "][" sequences, or
13729 	 * items separated by slashes "/", commas ",", bars "|" or hyphens "-".
13730 	 * These are the places where we are allowed to break the line. */
13731 	struct GMT_WORD *array = NULL;
13732 	unsigned int n = 0, c, start = 0, next, end, j, stop, space = 0, n_alloc = GMT_LEN256, hyphen = 0;
13733 	array = calloc (n_alloc, sizeof (struct GMT_WORD));
13734 	while (line[start]) {	/* More line to chop up */
13735         hyphen = 0; /* Initialize */
13736 		/* Find the next break location */
13737 		stop = start;
13738 		while (line[stop] && !(strchr (" ,/|", line[stop]) || (line[stop] == ']' && line[stop+1] == '[') || (hyphen = gmtapi_hyphen (line, stop)))) stop++;
13739 		end = next = stop;	/* Mark likely end */
13740 		array[n].space = space;	/* Do we need a leading space (set via previous word)? */
13741 		if (line[stop] == ' ') {	/* Skip the space to start over at next word */
13742 			while (line[stop] == ' ') stop++;	/* In case there are more than one space */
13743 			next = stop; space = 1;
13744 		}
13745         else if (line[stop] && strchr (",/|]", line[stop])) {   /* Include this char then break */
13746             next = ++end, space = 0;
13747         }
13748         else if (line[stop] == '-') {   /* Include this char then break with no break symbol */
13749             next = ++end, space = hyphen;
13750         }
13751 		array[n].word = calloc (end - start + 1, sizeof (char));	/* Allocate space for word */
13752 		for (j = start, c = 0; j < end; j++, c++) array[n].word[c] = line[j];
13753 		n++;	/* Got another word */
13754 		if (n == n_alloc) {	/* Need more memory, must be a long line */
13755 			n_alloc += GMT_LEN256;
13756 			array = realloc (array, n_alloc*sizeof (struct GMT_WORD));
13757 		}
13758 		start = next;	/* Advance to start of next word */
13759 	}
13760 	/* Finalize array length - keep one extra to indicate end of array */
13761 	array = realloc (array, (n+1)*sizeof (struct GMT_WORD));
13762 #if 0	/* Left for possible debug in case there are unresolved issues of breaking things up into words */
13763 	fprintf (stderr, "Found %d words\n", n);
13764 	for (j = 0; j < n; j++)
13765 		fprintf (stderr, "%2.2d: [%d] %s\n", j, array[j].space, array[j].word);
13766 	fprintf (stderr, "\n");
13767 #endif
13768 
13769 	return (array);
13770 }
13771 
gmtapi_space(unsigned int space)13772 GMT_LOCAL unsigned int gmtapi_space (unsigned int space) {
13773     return (space == 2) ? 0 : space;
13774 }
13775 
gmtapi_wrap_the_line(struct GMTAPI_CTRL * API,int level,FILE * fp,const char * in_line)13776 GMT_LOCAL void gmtapi_wrap_the_line (struct GMTAPI_CTRL *API, int level, FILE *fp, const char *in_line) {
13777 	/* Break the in_ine across multiple lines determined by the terminal line width API->terminal_width */
13778 	bool keep_same_indent = (level < 0), go = true, force = false;
13779 	int width, k, j, next_level, current_width = 0, try = 0;
13780 	static int gmtapi_indent[8] = {0, 2, 5, 7, 10, 13, 15, 0}; /* Last one is for custom negative values exceeding 6 */
13781 	struct GMT_WORD *W = gmtapi_split_words (in_line);	/* Create array of words */
13782 	char message[GMT_MSGSIZ] = {""};
13783 
13784 	/* Start with any fixed indent */
13785 	level = abs (level);	/* In case it was negative */
13786     if (level > 6) {    /* Place custom level in last entry.  This is to help g**math issue wrapped operator messages */
13787         gmtapi_indent[7] = level;
13788         level = 7;
13789         go = false;   /* Already indented and ready to go */
13790     }
13791 	next_level = (keep_same_indent) ? level : level + 1;	/* Do we indent when we wrap or not */
13792 	for (j = 0; go && j < gmtapi_indent[level]; j++) strcat (message, " ");	/* Starting spaces */
13793 	current_width = gmtapi_indent[level];
13794 	for (k = 0; W[k].word; k++) {	/* As long as there are more words... */
13795 		width = (gmtapi_space (W[k+1].space)) ? API->terminal_width : API->terminal_width - 1;	/* May need one space for ellipsis at end */
13796 		if (force || (current_width + strlen (W[k].word) + W[k].space) < width) {	/* Word will fit on current line */
13797 			if (gmtapi_space (W[k].space))	/* This word requires a leading space */
13798 				strcat (message, " ");
13799 			strcat (message, W[k].word);
13800 			current_width += strlen (W[k].word) + gmtapi_space (W[k].space);	/* Update line width so far */
13801 			free (W[k].word);	/* Free the word we are done with */
13802 			if (W[k+1].word == NULL)	/* Finalize the last line */
13803 				strcat (message, "\n");
13804             force = false;  /* In case it was set */
13805 		}
13806 		else {	/* Must split at the current break point and continue on next line */
13807 			if (W[k].space) { /* No break character needed since space separation is expected */
13808 				strcat (message, "\n");	/* Move to new line */
13809 				for (j = 0; j < gmtapi_indent[next_level]; j++) strcat (message, " ");	/* Initial indent plus possibly next indent*/
13810 				current_width = gmtapi_indent[next_level];	/* Indent plus next indent */
13811 			}
13812 			else {	/* Split in the middle of an option so append breakline and start new line with ellipsis after indent */
13813 				strcat (message, GMT_LINE_BREAK);
13814 				strcat (message, "\n");
13815 				for (j = 0; j < gmtapi_indent[next_level]; j++) strcat (message, " ");	/* Initial indent plus possibly next indent */
13816 				strcat (message, GMT_LINE_CONT);		/* And the ellipsis */
13817 				current_width = gmtapi_indent[next_level];	/* Indent plus the next indent */
13818 			}
13819 			W[k].space = 0;	/* Can be no leading space if starting a the line */
13820 			k--;	/* Since k will be incremented by loop and we did not write this word yet */
13821             try++;
13822             if (try == 2) /* Word is longer than effective terminal width - must force output (even if too long) to get past this stalemate */
13823                 force = true, try = 0;
13824 		}
13825 	}
13826 	free (W);	/* Free the structure array */
13827 	API->print_func (fp, message);	/* Do the printing */
13828 }
13829 
13830 /*! . */
GMT_Usage(void * V_API,int level,const char * format,...)13831 int GMT_Usage (void *V_API, int level, const char *format, ...) {
13832 	/* Wrapped usage message independent of verbosity.
13833 	 * level is the starting indent level of the line
13834 	 * format and optional args must be printed to a string first, then wrapped.
13835 	 *
13836 	 * Explanation for the use of level:
13837 	 * 0  : Only use for the synopsis message where we start all the way to the left.
13838 	 *      Once the first line wraps we indent all subsequent lines by the same amount.
13839 	 *      Below, | here means the start of the terminal's left margin and the number
13840 	 *      in braces {2} indicates which level was used for that line.
13841 	 *      |usage: gmt gmtsimplify [<table>] -T<tolerance>[<unit>] [-Q...]<return> {0}
13842 	 *      |  [-d[i|o]<nodata>] [-e[~]<pattern>] ...
13843 	 * 1  : First level is used for listing of options:
13844 	 *      |  -Rwest/east/south/north[+r]   {1}
13845 	 *      |     Set the region of the plot, blah blah...
13846 	 *      The text set with level = 1 that wraps will indent one more step
13847 	 * -1 : A negative level means no indent once the line wraps. Thus, multi-line
13848 	 *      wrapped text will all share the same left margin.
13849 	 *      |  Furthermore, this options is deadly because ... {-1}
13850 	 *      |  and that is why we skip this line.
13851 	 * 2  : Used for a paragraph under an option that should be wrapped and indented
13852 	 *      if it exceeds a line, e.g.
13853 	 *      |     You may optionally do this or that or this or that, for instance, {2}
13854 	 *      |       you could append a constant....
13855 	 * -2 : Same as -1 but for level 2.  No further indent, just stays at that level.
13856 	 * 3  : Used to indent separate modifiers under an option.  E.g. (for +a,b below):
13857 	 *      |  -Q<value][+a][+b]...   {1}
13858 	 *      |     Mess up the plot by random lines ... blah blah.  Available modifiers: {-1}
13859 	 *      |       +a Draw random lines from a Poisson distribution so that we get {3}
13860 	 *      |          a quirky plot.
13861 	 *      |       +b Add 7 to all answers just for fun.  {3}
13862 	 * -3:  Same as -1 but for level 3.  No further indent, just stay at that level.
13863 	 * etc, etc.
13864 	 */
13865 	struct GMTAPI_CTRL *API = NULL;
13866 	unsigned int k = 0;
13867 	FILE *err = stderr;
13868 	va_list args;
13869 
13870 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13871 	if (format == NULL) return GMT_PTR_IS_NULL;	/* Format cannot be NULL */
13872 	API = gmtapi_get_api_ptr (V_API);	/* Get the typecast structure pointer to API */
13873 	API->message[0] = '\0';	/* Start fresh */
13874 
13875 	va_start (args, format);
13876 	vsnprintf (API->message, GMT_MSGSIZ, format, args);
13877 	va_end (args);
13878 	assert (strlen (API->message) < GMT_MSGSIZ);
13879 	if (API->GMT) err = API->GMT->session.std[GMT_ERR];
13880 	if (API->message[0] == '\n') {
13881 		API->print_func (err, "\n");	/* Blank line to separate paragraphs */
13882 		k = 1;
13883 	}
13884 	gmtapi_wrap_the_line (API, level, err, &(API->message)[k]);
13885 	return_error (V_API, GMT_NOERROR);
13886 }
13887 
13888 #ifdef FORTRAN_API
GMT_Usage_(unsigned int * level,const char * format,int len)13889 int GMT_Usage_ (unsigned int *level, const char *format, int len) {
13890 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13891 	return (GMT_Usage (GMT_FORTRAN, *level, format));
13892 }
13893 #endif
13894 
13895 
GMT_Error_Message(void * V_API)13896 char * GMT_Error_Message (void *V_API) {
13897 	struct GMTAPI_CTRL *API = NULL;
13898 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
13899 	API = gmtapi_get_api_ptr (V_API);	/* Get the typecast structure pointer to API */
13900 	return (API->error_msg);
13901 }
13902 
13903 #ifdef FORTRAN_API
GMT_Error_Message_()13904 char * GMT_Error_Message_ () {
13905 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13906 	return (GMT_Error_Message (GMT_FORTRAN));
13907 }
13908 #endif
13909 
13910 /*! . */
GMT_Handle_Messages(void * V_API,unsigned int mode,unsigned int method,void * dest)13911 int GMT_Handle_Messages (void *V_API, unsigned int mode, unsigned int method, void *dest) {
13912 	/* Change where verbosity messages go */
13913 	struct GMTAPI_CTRL *API = NULL;
13914 	FILE *fp = NULL;
13915 	int *fd = NULL;
13916 
13917 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13918 	API = gmtapi_get_api_ptr (V_API);
13919 	switch (mode) {
13920 		case GMT_LOG_OFF:	/* Close log file and reset to stderr */
13921 			if (API->log_level == GMT_LOG_SET)
13922 				fclose (API->GMT->session.std[GMT_ERR]);
13923 			API->GMT->session.std[GMT_ERR] = stderr;
13924 			break;
13925 		case GMT_LOG_ONCE:	/* Redirect message just until end of next module */
13926 		case GMT_LOG_SET:	/* Redirect message until end of session (or changed) */
13927 			if (API->log_level)	/* Cannot turn on when already on */
13928 				return_error (V_API, GMT_LOGGING_ALREADY_ACTIVE);
13929 			switch (method) {
13930 				case GMT_IS_FILE:
13931 					if ((fp = fopen (dest, "w")) == NULL) {
13932 						GMT_Report (API, GMT_MSG_ERROR, "Unable to open error log file %s\n", dest);
13933 						return_error (API, GMT_ERROR_ON_FOPEN);
13934 					}
13935 					break;
13936 				case GMT_IS_STREAM:
13937 					fp = dest;
13938 					break;
13939 				case GMT_IS_FDESC:
13940 					fd = (int *)dest;	/* Extract the file handle integer */
13941 					if ((fp = fdopen (*fd, "w")) == NULL) {	/* Reopen handle as stream */
13942 						GMT_Report (API, GMT_MSG_ERROR, "Unable to open file descriptor %d for error log\n", *fd);
13943 						return_error (API, GMT_ERROR_ON_FDOPEN);
13944 					}
13945 					break;
13946 				default:
13947 					return_error (API, GMT_NOT_A_VALID_METHOD);
13948 					break;
13949 			}
13950 			API->GMT->session.std[GMT_ERR] = fp;	/* Set the error fp pointer */
13951 			API->log_level = mode;
13952 			break;
13953 		default:
13954 			return_error (API, GMT_NOT_A_VALID_LOGMODE);
13955 			break;
13956 	}
13957 	return (GMT_NOERROR);
13958 }
13959 
13960 #ifdef FORTRAN_API
GMT_Handle_Messages_(unsigned int * mode,unsigned int * method,void * dest)13961 int GMT_Handle_Messages_ (unsigned int *mode, unsigned int *method, void *dest) {
13962 	/* Fortran version: We pass the global GMT_FORTRAN structure */
13963 	return (GMT_Handle_Messages (GMT_FORTRAN, *mode, *method, dest));
13964 }
13965 #endif
13966 
13967 /*! . */
GMT_Get_Values(void * V_API,const char * arg,double par[],int maxpar)13968 int GMT_Get_Values (void *V_API, const char *arg, double par[], int maxpar) {
13969 	/* Parse any number of comma, space, tab, semi-colon or slash-separated values.
13970 	 * The array par must have enough space to hold a maximum of maxpar items.
13971 	 * Function returns the number of items, or GMT_NOTSET if there was an error.
13972 	 * When there are more than maxpar items, only the first maxpar are stored, and
13973 	 * the value of maxpar is returned.
13974 	 * We can handle dimension units (c|i|p), distance units (d|m|s|e|f|k|M|n|u),
13975 	 * geographic coordinates, absolute dateTtime strings, and regular floats.
13976 	 *
13977 	 * Dimensions are returned in the current length unit [inch or cm].
13978 	 * Distances are returned in meters.
13979 	 * Arc distances are returned in degrees.
13980 	 * Geographic dd:mm:ss[W|E|S|N] coordinates are returned in decimal degrees.
13981 	 * DateTtime moments are returned in time in chosen units [sec] since chosen epoch [1970] */
13982 
13983 	unsigned int pos = 0, mode, col_type_save[2][2];
13984 	int npar = 0;
13985 	size_t len;
13986 	char p[GMT_BUFSIZ] = {""}, unit, col_set_save[2][2];
13987 	double value;
13988 	struct GMTAPI_CTRL *API = NULL;
13989 	struct GMT_CTRL *GMT = NULL;
13990 	static const char separators[] = " \t,;/";
13991 
13992 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
13993 	if (arg == NULL || arg[0] == '\0') return_value (V_API, GMT_NO_PARAMETERS, GMT_NOTSET);
13994 	API = gmtapi_get_api_ptr (V_API);
13995 	GMT = API->GMT;
13996 	API->error = GMT_NOERROR;
13997 
13998 	/* Because gmt_init_distaz and possibly gmt_scanf_arg may decide to change the GMT col_type
13999 	 * for x and y we make a copy here and reset when done */
14000 	gmt_M_memcpy (col_type_save[GMT_IN],  GMT->current.io.col_type[GMT_IN],   2, unsigned int);
14001 	gmt_M_memcpy (col_type_save[GMT_OUT], GMT->current.io.col_type[GMT_OUT],  2, unsigned int);
14002 	gmt_M_memcpy (col_set_save[GMT_IN],   GMT->current.io.col_set[GMT_IN],    2, char);
14003 	gmt_M_memcpy (col_set_save[GMT_OUT],  GMT->current.io.col_set[GMT_OUT],   2, char);
14004 
14005 	while (gmt_strtok (arg, separators, &pos, p)) {	/* Loop over input arguments */
14006 		if ((len = strlen (p)) == 0) continue;
14007 		if (npar >= maxpar) {	/* Bail out when already maxpar values are stored */
14008 			gmtlib_report_error (API, GMT_DIM_TOO_LARGE);
14009 			break;
14010 		}
14011 		len--;	/* Position of last char, possibly a unit */
14012 		if (strchr (GMT_DIM_UNITS, p[len]))	/* Dimension unit (c|i|p), return distance in GMT default length unit [inch or cm] */
14013 			value = gmt_convert_units (GMT, p, GMT->current.setting.proj_length_unit, GMT->current.setting.proj_length_unit);
14014 		else if (strchr (GMT_LEN_UNITS, p[len])) {	/* Distance units, return as meters [or degrees if arc] */
14015 			mode = gmt_get_distance (GMT, p, &value, &unit);
14016 			if (gmt_init_distaz (GMT, unit, mode, GMT_MAP_DIST) == GMT_NOT_A_VALID_TYPE) return_value (V_API, GMT_NOT_A_VALID_TYPE, GMT_NOTSET);
14017 			value /= GMT->current.map.dist[GMT_MAP_DIST].scale;	/* Convert to default unit */
14018 		}
14019 		else	/* Perhaps coordinates or floats */
14020 			(void) gmt_scanf_arg (GMT, p, GMT_IS_UNKNOWN, false, &value);
14021 		par[npar++] = value;
14022 	}
14023 	/* Reset col_types to what they were before the parsing */
14024 	gmt_M_memcpy (GMT->current.io.col_type[GMT_IN],  col_type_save[GMT_IN],  2, unsigned int);
14025 	gmt_M_memcpy (GMT->current.io.col_type[GMT_OUT], col_type_save[GMT_OUT], 2, unsigned int);
14026 	gmt_M_memcpy (GMT->current.io.col_set[GMT_IN],   col_set_save[GMT_IN],   2, char);
14027 	gmt_M_memcpy (GMT->current.io.col_set[GMT_OUT],  col_set_save[GMT_OUT],  2, char);
14028 
14029 	return (npar);
14030 }
14031 
14032 #ifdef FORTRAN_API
GMT_Get_Values_(char * arg,double par[],int len)14033 int GMT_Get_Values_ (char *arg, double par[], int len) {
14034 	/* Fortran version: We pass the global GMT_FORTRAN structure */
14035 	return (GMT_Get_Values (GMT_FORTRAN, arg, par, len));
14036 }
14037 #endif
14038 
14039 /* Here lies the very basic F77 support for grid read and write only. It is assumed that no grid padding is required */
14040 
14041 #define F_STRNCPY(dst,src,ldst,lsrc) { int l = MIN(ldst-1, lsrc); strncpy (dst, src, l); dst[l] = '\0'; }
14042 
gmt_f77_readgrdinfo_(unsigned int dim[],double limit[],double inc[],char * title,char * remark,const char * name,int ltitle,int lremark,int lname)14043 int gmt_f77_readgrdinfo_ (unsigned int dim[], double limit[], double inc[], char *title, char *remark, const char *name, int ltitle, int lremark, int lname) {
14044 	/* Note: When returning, dim[2] holds the registration (0 = gridline, 1 = pixel).
14045 	 * limit[4-5] holds zmin/zmax. limit must thus at least have a length of 6.
14046 	 */
14047 	const char *argv = "GMT_F77_readgrdinfo";
14048 	char *file = NULL;
14049 	struct GMT_GRID_HEADER header;
14050 	struct GMTAPI_CTRL *API = NULL;	/* The API pointer assigned below */
14051 
14052 	if (name == NULL) {
14053 		GMT_Report (API, GMT_MSG_ERROR, "No filename given to GMT_F77_readgrdinfo\n");
14054 		return GMT_ARG_IS_NULL;
14055 	}
14056 	if ((API = GMT_Create_Session (argv, 0U, 0U, NULL)) == NULL) return GMT_MEMORY_ERROR;
14057 	file = strndup (name, lname);
14058 
14059 	/* Read the grid header */
14060 
14061 	gmt_M_memset (&header, 1, struct GMT_GRID_HEADER);	/* To convince Coverity that header->index_function has been initialized */
14062 	if (gmtlib_read_grd_info (API->GMT, file, &header)) {
14063 		GMT_Report (API, GMT_MSG_ERROR, "Failure while opening file %s\n", file);
14064 		gmt_M_str_free (file);
14065 		GMT_Destroy_Session (API);
14066 		return GMT_GRID_READ_ERROR;
14067 	}
14068 	gmt_M_str_free (file);
14069 
14070 	/* Assign variables from header structure items */
14071 	dim[GMT_X] = header.n_columns;	dim[GMT_Y] = header.n_rows;
14072 	gmt_M_memcpy (limit, header.wesn, 4U, double);
14073 	gmt_M_memcpy (inc, header.inc, 2U, double);
14074 	limit[ZLO] = header.z_min;
14075 	limit[ZHI] = header.z_max;
14076 	dim[GMT_Z] = header.registration;
14077 	if (title) F_STRNCPY (title, header.title, ltitle, GMT_GRID_TITLE_LEN80);
14078 	if (remark) F_STRNCPY (remark, header.remark, lremark, GMT_GRID_REMARK_LEN160);
14079 
14080 	if (GMT_Destroy_Session (API) != GMT_NOERROR) return GMT_RUNTIME_ERROR;
14081 	return GMT_NOERROR;
14082 }
14083 
gmt_f77_readgrd_(gmt_grdfloat * array,unsigned int dim[],double limit[],double inc[],char * title,char * remark,const char * name,int ltitle,int lremark,int lname)14084 int gmt_f77_readgrd_ (gmt_grdfloat *array, unsigned int dim[], double limit[], double inc[], char *title, char *remark, const char *name, int ltitle, int lremark, int lname) {
14085 	/* Note: When called, dim[2] is 1 we allocate the array, otherwise we assume it has enough space
14086 	 * Also, if dim[3] == 1 then we transpose the array before writing.
14087 	 * When returning, dim[2] holds the registration (0 = gridline, 1 = pixel).
14088 	 * limit[4-5] holds zmin/zmax. limit must thus at least have a length of 6.
14089 	 */
14090 	double no_wesn[4] = {0.0, 0.0, 0.0, 0.0};
14091 	const char *argv = "GMT_F77_readgrd";
14092 	char *file = NULL;
14093 	struct GMT_GRID_HEADER *header = NULL;
14094 	struct GMTAPI_CTRL *API = NULL;	/* The API pointer assigned below */
14095 
14096 	if (name == NULL) {
14097 		GMT_Report (API, GMT_MSG_ERROR, "No filename given to GMT_F77_readgrd\n");
14098 		return GMT_ARG_IS_NULL;
14099 	}
14100 	if ((API = GMT_Create_Session (argv, 0U, 0U, NULL)) == NULL) return GMT_MEMORY_ERROR;
14101 	file = strndup (name, lname);
14102 
14103 	header = gmt_get_header (API->GMT);
14104 	/* Read the grid header */
14105 	gmt_grd_init (API->GMT, header, NULL, false);
14106 	if (gmtlib_read_grd_info (API->GMT, file, header)) {
14107 		GMT_Report (API, GMT_MSG_ERROR, "Failure while opening file %s\n", file);
14108 		gmt_M_str_free (file);
14109 		gmt_free_header (API->GMT, &header);
14110 		GMT_Destroy_Session (API);
14111 		return GMT_GRID_READ_ERROR;
14112 	}
14113 
14114 	/* Read the grid, possibly after first allocating array space */
14115 	if (dim[GMT_Z] == 1) array = gmt_M_memory (API->GMT, NULL, header->size, gmt_grdfloat);
14116 	if (gmtlib_read_grd (API->GMT, file, header, array, no_wesn, GMT_no_pad, 0)) {
14117 		GMT_Report (API, GMT_MSG_ERROR, "Failure while reading file %s\n", file);
14118 		gmt_M_str_free (file);
14119 		gmt_free_header (API->GMT, &header);
14120 		GMT_Destroy_Session (API);
14121 		return GMT_GRID_READ_ERROR;
14122 	}
14123 	gmt_M_str_free (file);
14124 
14125 	if (dim[3] == 1) gmtlib_inplace_transpose (array, header->n_rows, header->n_columns);
14126 
14127 	/* Assign variables from header structure items */
14128 	dim[GMT_X] = header->n_columns;	dim[GMT_Y] = header->n_rows;
14129 	gmt_M_memcpy (limit, header->wesn, 4U, double);
14130 	gmt_M_memcpy (inc, header->inc, 2U, double);
14131 	limit[ZLO] = header->z_min;
14132 	limit[ZHI] = header->z_max;
14133 	dim[GMT_Z] = header->registration;
14134 	if (title) F_STRNCPY (title, header->title, ltitle, GMT_GRID_TITLE_LEN80);
14135 	if (remark) F_STRNCPY (remark, header->remark, lremark, GMT_GRID_REMARK_LEN160);
14136 
14137 	gmt_M_free (API->GMT, header->hidden);
14138 	gmt_M_free (API->GMT, header);
14139 
14140 	if (GMT_Destroy_Session (API) != GMT_NOERROR) return GMT_RUNTIME_ERROR;
14141 	return GMT_NOERROR;
14142 }
14143 
gmt_f77_writegrd_(gmt_grdfloat * array,unsigned int dim[],double limit[],double inc[],const char * title,const char * remark,const char * name,int ltitle,int lremark,int lname)14144 int gmt_f77_writegrd_ (gmt_grdfloat *array, unsigned int dim[], double limit[], double inc[], const char *title, const char *remark, const char *name, int ltitle, int lremark, int lname) {
14145 	/* Note: When called, dim[2] holds the registration (0 = gridline, 1 = pixel).
14146 	 * Also, if dim[3] == 1 then we transpose the array before writing.  */
14147 	const char *argv = "GMT_F77_writegrd";
14148 	char *file = NULL;
14149 	double no_wesn[4] = {0.0, 0.0, 0.0, 0.0};
14150 	struct GMT_GRID_HEADER header;
14151 	struct GMTAPI_CTRL *API = NULL;	/* The API pointer assigned below */
14152 
14153 	/* Initialize with default values */
14154 
14155 	if (name == NULL) {
14156 		GMT_Report (API, GMT_MSG_ERROR, "No filename given to GMT_F77_writegrd\n");
14157 		return GMT_ARG_IS_NULL;
14158 	}
14159 	if ((API = GMT_Create_Session (argv, 0U, 0U, NULL)) == NULL) return GMT_MEMORY_ERROR;
14160 	file = strndup (name, lname);
14161 
14162 	gmt_M_memset (&header, 1, struct GMT_GRID_HEADER);	/* To convince Coverity that header->index_function has been initialized */
14163 	gmt_grd_init (API->GMT, &header, NULL, false);
14164 	if (full_region (limit)) {	/* Here that means limit was not properly given */
14165 		GMT_Report (API, GMT_MSG_ERROR, "Grid domain not specified for %s\n", file);
14166 		gmt_M_str_free (file);
14167 		GMT_Destroy_Session (API);
14168 		return GMT_ARG_IS_NULL;
14169 	}
14170 	if (inc[GMT_X] == 0.0 || inc[GMT_Y] == 0.0) {	/* Here that means grid spacing was not properly given */
14171 		GMT_Report (API, GMT_MSG_ERROR, "Grid spacing not specified for %s\n", file);
14172 		gmt_M_str_free (file);
14173 		GMT_Destroy_Session (API);
14174 		return GMT_ARG_IS_NULL;
14175 	}
14176 
14177 	/* Set header parameters */
14178 
14179 	gmt_M_memcpy (header.wesn, limit, 4U, double);
14180 	gmt_M_memcpy (header.inc, inc, 2U, double);
14181 	header.n_columns = dim[GMT_X];	header.n_rows = dim[GMT_Y];
14182 	header.registration = dim[GMT_Z];
14183 	gmt_set_grddim (API->GMT, &header);
14184 	if (title) F_STRNCPY (header.title, title, GMT_GRID_TITLE_LEN80, ltitle);
14185 	if (remark) F_STRNCPY (header.remark, remark, GMT_GRID_REMARK_LEN160, lremark);
14186 
14187 	if (dim[3] == 1) gmtlib_inplace_transpose (array, header.n_rows, header.n_columns);
14188 
14189 	/* Write the file */
14190 
14191 	if (gmtlib_write_grd (API->GMT, file, &header, array, no_wesn, GMT_no_pad, 0)) {
14192 		GMT_Report (API, GMT_MSG_ERROR, "Failure while writing file %s\n", file);
14193 		gmt_M_str_free (file);
14194 		GMT_Destroy_Session (API);
14195 		return GMT_GRID_WRITE_ERROR;
14196 	}
14197 	gmt_M_str_free (file);
14198 
14199 	if (GMT_Destroy_Session (API) != GMT_NOERROR) return GMT_MEMORY_ERROR;
14200 	return GMT_NOERROR;
14201 }
14202 
14203 /* wrappers for several Fortran compilers */
14204 #define F77_ARG1 unsigned int dim[], double limit[], double inc[], char *title, char *remark, const char *name, int ltitle, int lremark, int lname
14205 #define F77_ARG2 dim, limit, inc, title, remark, name, ltitle, lremark, lname
gmt_f77_readgrdinfo__(F77_ARG1)14206 int gmt_f77_readgrdinfo__(F77_ARG1) { return gmt_f77_readgrdinfo_ (F77_ARG2); }
gmt_f77_readgrdinfo(F77_ARG1)14207 int gmt_f77_readgrdinfo  (F77_ARG1) { return gmt_f77_readgrdinfo_ (F77_ARG2); }
GMT_F77_READGRDINFO_(F77_ARG1)14208 int GMT_F77_READGRDINFO_ (F77_ARG1) { return gmt_f77_readgrdinfo_ (F77_ARG2); }
GMT_F77_READGRDINFO(F77_ARG1)14209 int GMT_F77_READGRDINFO  (F77_ARG1) { return gmt_f77_readgrdinfo_ (F77_ARG2); }
14210 #undef  F77_ARG1
14211 #undef  F77_ARG2
14212 
14213 #define F77_ARG1 gmt_grdfloat *array, unsigned int dim[], double limit[], double inc[], char *title, char *remark, const char *name, int ltitle, int lremark, int lname
14214 #define F77_ARG2 array, dim, limit, inc, title, remark, name, ltitle, lremark, lname
gmt_f77_readgrd__(F77_ARG1)14215 int gmt_f77_readgrd__ (F77_ARG1) { return gmt_f77_readgrd_ (F77_ARG2); }
gmt_f77_readgrd(F77_ARG1)14216 int gmt_f77_readgrd   (F77_ARG1) { return gmt_f77_readgrd_ (F77_ARG2); }
GMT_F77_READGRD_(F77_ARG1)14217 int GMT_F77_READGRD_  (F77_ARG1) { return gmt_f77_readgrd_ (F77_ARG2); }
GMT_F77_READGRD(F77_ARG1)14218 int GMT_F77_READGRD   (F77_ARG1) { return gmt_f77_readgrd_ (F77_ARG2); }
14219 #undef  F77_ARG1
14220 
14221 #define F77_ARG1 gmt_grdfloat *array, unsigned int dim[], double limit[], double inc[], const char *title, const char *remark, const char *name, int ltitle, int lremark, int lname
gmt_f77_writegrd__(F77_ARG1)14222 int gmt_f77_writegrd__ (F77_ARG1) { return gmt_f77_writegrd_ (F77_ARG2); }
gmt_f77_writegrd(F77_ARG1)14223 int gmt_f77_writegrd   (F77_ARG1) { return gmt_f77_writegrd_ (F77_ARG2); }
GMT_F77_WRITEGRD_(F77_ARG1)14224 int GMT_F77_WRITEGRD_  (F77_ARG1) { return gmt_f77_writegrd_ (F77_ARG2); }
GMT_F77_WRITEGRD(F77_ARG1)14225 int GMT_F77_WRITEGRD   (F77_ARG1) { return gmt_f77_writegrd_ (F77_ARG2); }
14226 #undef  F77_ARG1
14227 #undef  F77_ARG2
14228 
GMT_Duplicate_String(void * API,const char * string)14229 char *GMT_Duplicate_String (void *API, const char* string) {
14230 	/* Duplicate a string. The interest of this function is to make the memory allocation
14231 	   inside the GMT lib so that GMT_Destroy_Data we can free it without any concerns of
14232 	   Windows DLL hell */
14233 	gmt_M_unused(API);
14234 	return strdup (string);
14235 }
14236 
14237 
14238 /* Sub-functions to perform specific conversions */
14239 
14240 #define do_tbl_header(flag) (flag == 0 || flag == 2)
14241 #define do_seg_header(flag) (flag == 0 || flag == 1)
14242 
14243 /* GMT_DATASET to GMT_* : */
14244 
gmtapi_dataset2dataset(struct GMTAPI_CTRL * API,struct GMT_DATASET * In,struct GMT_DATASET * Out,unsigned int header,unsigned int mode)14245 GMT_LOCAL void * gmtapi_dataset2dataset (struct GMTAPI_CTRL *API, struct GMT_DATASET *In, struct GMT_DATASET *Out, unsigned int header, unsigned int mode) {
14246 	/* Convert a dataset to another dataset using current formatting and column type information.
14247 	 * If Out is not NULL then we assume it has exact same dimension as the dataset, but no headers/records allocated.
14248 	 * header controls what we do with headers.
14249 	 * If mode == GMT_WRITE_TABLE_SEGMENT then we combine all segments into a SINGLE segment in ONE table
14250 	 * If mode == GMT_WRITE_TABLE then we collect all segments into ONE table.
14251 	 * If mode == GMT_WRITE_SEGMENT then we combine segments into ONE segment per table.
14252 	 */
14253 	unsigned int hdr;
14254 	uint64_t tbl, seg, row, col, n_rows, tbl_out = 0, row_out = 0, seg_out = 0;
14255 	bool s_alloc, t_alloc, alloc, was;
14256 	struct GMT_CTRL *GMT = API->GMT;
14257 	struct GMT_DATATABLE *Tin = NULL;
14258 	struct GMT_DATATABLE *Tout = NULL;
14259 	struct GMT_DATASEGMENT *Sin = NULL;
14260 	struct GMT_DATASEGMENT *Sout = NULL;
14261 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
14262 	s_alloc = t_alloc = alloc = (Out == NULL);
14263 	if (alloc) {	/* Must allocate output dataset */
14264 		Out = gmt_get_dataset (GMT);
14265 		Out->n_tables = (mode == GMT_WRITE_TABLE || mode == GMT_WRITE_TABLE_SEGMENT) ? 1 : In->n_tables;
14266 		Out->table = gmt_M_memory (GMT, NULL, Out->n_tables, struct GMT_DATATABLE *);
14267 	}
14268 	was = GMT->current.setting.io_header[GMT_OUT];
14269 	GMT->current.setting.io_header[GMT_OUT] = do_tbl_header (header);
14270 	Out->n_segments = (mode == GMT_WRITE_TABLE_SEGMENT) ? 1 : ((mode == GMT_WRITE_SEGMENT) ? In->n_tables : In->n_segments);
14271 	Out->n_records  = In->n_records;
14272 	Out->n_columns  = In->n_columns;
14273 	for (tbl = 0; tbl < In->n_tables; tbl++) {
14274 		if (mode == GMT_WRITE_SEGMENT) row_out = 0;	/* Reset row output counter on a per table basis */
14275 		else if (mode == 0) seg_out = 0;	/* Reset segment output counter on a per table basis */
14276 		if (alloc && (mode == 0 || mode == GMT_WRITE_SEGMENT)) s_alloc = true;	/* Need to allocate at least one segment per table */
14277 		Tin = In->table[tbl];	/* Shorthand to current input data table */
14278 		if (t_alloc) {
14279 			Out->table[tbl_out] = Tout = gmt_get_table (GMT);
14280 			TH = gmt_get_DT_hidden (Tout);
14281 			Tout->n_segments = TH->n_alloc = (mode == GMT_WRITE_TABLE_SEGMENT || mode == GMT_WRITE_SEGMENT) ? 1 : ((mode == GMT_WRITE_TABLE) ? In->n_segments : Tin->n_segments);	/* Number of segments in this table */
14282 			Tout->n_records  = (mode == GMT_WRITE_TABLE || mode == GMT_WRITE_TABLE_SEGMENT) ? In->n_records : Tin->n_records;	/* Number of data records int this table */
14283 			Tout->n_columns = In->n_columns;
14284 		}
14285 		else
14286 			Tout = Out->table[tbl_out];
14287 		if (t_alloc) {
14288 			if (Tin->n_headers && do_tbl_header(header)) {	/* Allocate and duplicate headers */
14289 				Tout->n_headers = Tin->n_headers;	/* Same number of header records as input table */
14290 				if (alloc) Tout->header = gmt_M_memory (GMT, NULL, Tout->n_headers, char *);
14291 				for (hdr = 0; hdr < Tout->n_headers; hdr++) Tout->header[hdr] = strdup (Tin->header[hdr]);
14292 			}
14293 			Tout->segment = gmt_M_memory (GMT, NULL, Tout->n_segments, struct GMT_DATASEGMENT *);
14294 		}
14295 		for (seg = 0; seg < Tin->n_segments; seg++) {	/* For each input table segment */
14296 			if (mode == 0 || mode == GMT_WRITE_TABLE) row_out = 0;	/* Reset row output counter on a per segment basis */
14297 			Sin = Tin->segment[seg];	/* Shorthand to current data segment */
14298 			if (s_alloc) {	/* Allocate another segment */
14299 				unsigned int smode = (Sin->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
14300 				n_rows = (mode == GMT_WRITE_TABLE_SEGMENT) ? In->n_records : ((mode == GMT_WRITE_SEGMENT) ? Tin->n_records : Sin->n_rows);
14301 				Tout->segment[seg_out] = GMT_Alloc_Segment (API, smode, n_rows, In->n_columns, NULL, NULL);
14302 				Sout = Tout->segment[seg_out];	/* Shorthand to current text segment */
14303 				if (Sin->header && do_seg_header(header)) Sout->header = strdup (Sin->header);
14304 			}
14305 			else
14306 				Sout = Tout->segment[seg_out];
14307 			for (row = 0; row < Sin->n_rows; row++, row_out++) {	/* Copy each row to (new) segment */
14308 				for (col = 0; col < Sin->n_columns; col++)
14309 					Sout->data[col][row_out] = Sin->data[col][row];
14310 			}
14311 			if (mode == GMT_WRITE_SEGMENT || mode == GMT_WRITE_TABLE_SEGMENT) s_alloc = false;	/* Only allocate this single segment, at least for this table */
14312 			if (mode == 0 || mode == GMT_WRITE_TABLE) seg_out++;	/* More than one segment on output */
14313 		}
14314 		if (mode == GMT_WRITE_TABLE || mode == GMT_WRITE_TABLE_SEGMENT) t_alloc = false;	/* Only allocate this single table */
14315 		if (mode == 0 || mode == GMT_WRITE_SEGMENT) tbl_out++;	/* More than one segment on output */
14316 	}
14317 	GMT->current.setting.io_header[GMT_OUT] = was;
14318 	return Out;
14319 }
14320 
gmtapi_dataset2matrix(struct GMTAPI_CTRL * API,struct GMT_DATASET * In,struct GMT_MATRIX * Out,unsigned int header,unsigned int mode)14321 GMT_LOCAL void *gmtapi_dataset2matrix (struct GMTAPI_CTRL *API, struct GMT_DATASET *In, struct GMT_MATRIX *Out, unsigned int header, unsigned int mode) {
14322 	/* Convert a dataset to a matrix.
14323 	 * If Out is not NULL then we assume it has enough rows and columns to hold the dataset records.
14324 	 * Header controls if segment headers are written as NaN recs
14325 	 * If mode > 0 then it is assumed to hold GMT_TYPE-1, else we assume the GMT default setting.
14326 	 * If there are more than one segment we will insert NaN-records between segments.
14327 	 */
14328 	uint64_t tbl, seg, row, row_out, col, ij;
14329 	bool alloc = (Out == NULL), add_NaN_record = (In->n_segments > 1 && do_seg_header(header));
14330 	struct GMT_CTRL *GMT = API->GMT;
14331 	struct GMT_DATATABLE *D = NULL;
14332 	struct GMT_DATASEGMENT *SD = NULL;
14333 	GMT_putfunction api_put_val = NULL;
14334 	p_func_uint64_t GMT_2D_to_index = NULL;
14335 
14336 	if (alloc) {	/* Must allocate the output matrix */
14337 		struct GMT_MATRIX_HIDDEN *MH = NULL;
14338 		if ((Out = gmtlib_create_matrix (GMT, 1U, GMT_OUT, 0)) == NULL) return (NULL);
14339 		Out->n_rows = In->n_records + (add_NaN_record ? In->n_segments : 0);
14340 		Out->n_columns = Out->dim = In->n_columns;
14341 		Out->type = (mode) ? mode - 1 : API->GMT->current.setting.export_type;
14342 		if (gmtlib_alloc_univector (GMT, &(Out->data), Out->type, Out->n_rows * Out->n_columns)) {
14343 			gmt_M_free (GMT, Out);
14344 			return (NULL);
14345 		}
14346 		MH = gmt_get_M_hidden (Out);
14347 		MH->alloc_mode = GMT_ALLOC_INTERNALLY;
14348 	}
14349 	if ((api_put_val = gmtapi_select_put_function (API, Out->type)) == NULL) {
14350 		gmt_M_free (GMT, Out);
14351 		return (NULL);
14352 	}
14353 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, Out->shape, GMT_GRID_IS_REAL)) == NULL) {
14354 		gmt_M_free (GMT, Out);
14355 		return (NULL);
14356 	}
14357 	for (tbl = row_out = 0; tbl < In->n_tables; tbl++) {
14358 		D = In->table[tbl];	/* Shorthand to current input data table */
14359 		for (seg = 0; seg < D->n_segments; seg++) {
14360 			SD = D->segment[seg];	/* Shorthand */
14361 			if (add_NaN_record) {
14362 				for (col = 0; col < SD->n_columns; col++) {
14363 					ij = GMT_2D_to_index (row_out, col, Out->dim);
14364 					api_put_val (&(Out->data), ij, GMT->session.d_NaN);
14365 				}
14366 				row_out++;	/* Due to the extra NaN-data header record we just wrote */
14367 			}
14368 			for (row = 0; row < SD->n_rows; row++, row_out++) {
14369 				for (col = 0; col < SD->n_columns; col++) {
14370 					ij = GMT_2D_to_index (row_out, col, Out->dim);
14371 					api_put_val (&(Out->data), ij, SD->data[col][row]);
14372 				}
14373 			}
14374 		}
14375 	}
14376 	return Out;
14377 }
14378 
gmtapi_dataset2vector(struct GMTAPI_CTRL * API,struct GMT_DATASET * In,struct GMT_VECTOR * Out,unsigned int header,unsigned int mode)14379 GMT_LOCAL void * gmtapi_dataset2vector (struct GMTAPI_CTRL *API, struct GMT_DATASET *In, struct GMT_VECTOR *Out, unsigned int header, unsigned int mode) {
14380 	/* Convert a dataset to vectors.
14381 	 * If Out is not NULL then we assume it has enough rows and columns to hold the dataset records.
14382 	 * If mode > 0 then it is assumed to hold GMT_TYPE-1, else we assume the GMT default setting.
14383 	 * If there are more than one segment we will insert NaN-records between segments.
14384 	 */
14385 	uint64_t tbl, seg, row, row_out, col;
14386 	bool alloc = (Out == NULL), add_NaN_record = (In->n_segments > 1 && do_seg_header(header));
14387 	struct GMT_CTRL *GMT = API->GMT;
14388 	struct GMT_DATATABLE *D = NULL;
14389 	struct GMT_DATASEGMENT *SD = NULL;
14390 	GMT_putfunction api_put_val = NULL;
14391 	if (alloc) {
14392 		if ((Out = gmt_create_vector (GMT, In->n_columns, GMT_OUT)) == NULL) return NULL;
14393 		Out->n_rows = In->n_records + (add_NaN_record ? In->n_segments : 0);
14394 		for (col = 0; col < Out->n_columns; col++)	/* Set same export data type for all vectors */
14395 			Out->type[col] = (mode) ? mode - 1 : API->GMT->current.setting.export_type;
14396 		if ((API->error = gmtlib_alloc_vectors (GMT, Out, Out->n_rows)) != GMT_NOERROR) {
14397 			gmt_M_free (GMT, Out);
14398 			return (NULL);
14399 		}
14400 	}
14401 	if ((api_put_val = gmtapi_select_put_function (API, Out->type[0])) == NULL) { /* Since all columns are of same type we get the pointer here */
14402 		gmt_M_free (GMT, Out);
14403 		return NULL;
14404 	}
14405 	for (tbl = row_out = 0; tbl < In->n_tables; tbl++) {
14406 		D = In->table[tbl];	/* Shorthand to current input data table */
14407 		for (seg = 0; seg < D->n_segments; seg++) {
14408 			SD = D->segment[seg];	/* Shorthand */
14409 			if (add_NaN_record) {
14410 				for (col = 0; col < SD->n_columns; col++)
14411 					api_put_val (&(Out->data[col]), row_out, GMT->session.d_NaN);
14412 				row_out++;	/* Due to the extra NaN-data header record we just wrote */
14413 			}
14414 			for (row = 0; row < SD->n_rows; row++, row_out++) {
14415 				for (col = 0; col < SD->n_columns; col++)
14416 					api_put_val (&(Out->data[col]), row_out, SD->data[col][row]);
14417 			}
14418 		}
14419 	}
14420 	return Out;
14421 }
14422 
14423 /* GMT_MATRIX to GMT_* : */
14424 
gmtapi_matrix2dataset(struct GMTAPI_CTRL * API,struct GMT_MATRIX * In,struct GMT_DATASET * Out,unsigned int header)14425 GMT_LOCAL void * gmtapi_matrix2dataset (struct GMTAPI_CTRL *API, struct GMT_MATRIX *In, struct GMT_DATASET *Out, unsigned int header) {
14426 	/* Convert a matrix to a dataset (one table with one segment).
14427 	 * If Out is not NULL then we assume it has enough rows and columns to hold the dataset records.
14428 	 * header controls what we do with headers.
14429 	 */
14430 	uint64_t row, col, ij;
14431 	unsigned int mode = (In->text) ? GMT_WITH_STRINGS : 0;
14432 	bool alloc = (Out == NULL);
14433 	struct GMT_CTRL *GMT = API->GMT;
14434 	struct GMT_DATASEGMENT *SD = NULL;
14435 	GMT_getfunction api_get_val = NULL;
14436 	p_func_uint64_t GMT_2D_to_index = NULL;
14437 	if (header) GMT_Report (API, GMT_MSG_WARNING, "gmtapi_matrix2dataset: Header stripping not implemented yet - ignored!\n");
14438 	if (alloc && (Out = gmtlib_create_dataset (GMT, 1U, 1U, In->n_rows, In->n_columns, GMT_IS_POINT, mode, true)) == NULL)
14439 		return_null (API, GMT_MEMORY_ERROR);
14440 	SD = Out->table[0]->segment[0];	/* Shorthand to only segment in the dataset */
14441 	if ((api_get_val = gmtapi_select_get_function (API, In->type)) == NULL)
14442 		return_null (API, GMT_NOT_A_VALID_TYPE);
14443 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, In->shape, GMT_GRID_IS_REAL)) == NULL)
14444 		return_null (API, GMT_WRONG_MATRIX_SHAPE);
14445 	for (row = 0; row < In->n_rows; row++) {
14446 		for (col = 0; col < In->n_columns; col++) {
14447 			ij = GMT_2D_to_index (row, col, In->dim);	/* Index into the user data matrix depends on layout (M->shape) */
14448 			api_get_val (&(In->data), ij, &(SD->data[col][row]));
14449 		}
14450 		if (mode) SD->text[row] = strdup (In->text[row]);
14451 	}
14452 	return Out;
14453 }
14454 
gmtapi_matrix2vector(struct GMTAPI_CTRL * API,struct GMT_MATRIX * In,struct GMT_VECTOR * Out,unsigned int header,unsigned int mode)14455 GMT_LOCAL void *gmtapi_matrix2vector (struct GMTAPI_CTRL *API, struct GMT_MATRIX *In, struct GMT_VECTOR *Out, unsigned int header, unsigned int mode) {
14456 	/* Convert a matrix to vectors.
14457 	 * If Out is not NULL then we assume it has enough rows to hold the vector rows.
14458 	 * header controls what we do with headers.
14459 	 * If mode > 0 then it is assumed to hold GMT_TYPE-1, else we assume the GMT default setting.
14460 	 */
14461 	uint64_t row, col, ij;
14462 	bool alloc = (Out == NULL);
14463 	double value;
14464 	struct GMT_CTRL *GMT = API->GMT;
14465 	GMT_getfunction api_get_val_m = NULL;
14466 	GMT_putfunction api_put_val_v = NULL;
14467 	p_func_uint64_t GMT_2D_to_index = NULL;
14468 	if (header) GMT_Report (API, GMT_MSG_WARNING, "gmtapi_matrix2vector: Header stripping not implemented yet - ignored!\n");
14469 	if (alloc) {
14470 		if ((Out = gmt_create_vector (GMT, In->n_columns, GMT_OUT)) == NULL)
14471 			return_null (API, GMT_MEMORY_ERROR);
14472 		Out->n_rows = In->n_rows;
14473 		for (col = 0; col < Out->n_columns; col++)	/* Set same export data type for all vectors */
14474 			Out->type[col] = (mode) ? mode - 1 : API->GMT->current.setting.export_type;
14475 		if ((API->error = gmtlib_alloc_vectors (GMT, Out, Out->n_rows)) != GMT_NOERROR) {
14476 			gmt_M_free (GMT, Out);
14477 			return_null (API, GMT_MEMORY_ERROR);
14478 		}
14479 	}
14480 
14481 	if ((api_get_val_m = gmtapi_select_get_function (API, In->type)) == NULL) {
14482 		if (alloc) gmt_M_free (GMT, Out);
14483 		return_null (API, GMT_NOT_A_VALID_TYPE);
14484 	}
14485 	if ((api_put_val_v = gmtapi_select_put_function (API, GMT->current.setting.export_type)) == NULL) {	/* Since all columns are of same type we get the pointer here */
14486 		if (alloc) gmt_M_free (GMT, Out);
14487 		return_null (API, GMT_NOT_A_VALID_TYPE);
14488 	}
14489 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, In->shape, GMT_GRID_IS_REAL)) == NULL) {
14490 		if (alloc) gmt_M_free (GMT, Out);
14491 		return_null (API, GMT_WRONG_MATRIX_SHAPE);
14492 	}
14493 	for (row = 0; row < In->n_rows; row++) {
14494 		for (col = 0; col < In->n_columns; col++) {
14495 			ij = GMT_2D_to_index (row, col, In->dim);	/* Index into the user data matrix depends on layout (M->shape) */
14496 			api_get_val_m (&(In->data), ij, &value);
14497 			api_put_val_v (&(Out->data[col]), row, value);
14498 		}
14499 	}
14500 	return Out;
14501 }
14502 
14503 /* GMT_VECTOR to GMT_* : */
14504 
gmtapi_vector2dataset(struct GMTAPI_CTRL * API,struct GMT_VECTOR * In,struct GMT_DATASET * Out,unsigned int header)14505 GMT_LOCAL void * gmtapi_vector2dataset (struct GMTAPI_CTRL *API, struct GMT_VECTOR *In, struct GMT_DATASET *Out, unsigned int header) {
14506 	/* Convert a vector to a dataset (one table with one segment).
14507 	 * header controls what we do with headers.
14508 	 * If Out is not NULL then we assume it has enough rows and columns to hold the dataset records.
14509 	 */
14510 	uint64_t row, col;
14511 	unsigned int mode = (In->text) ? GMT_WITH_STRINGS : 0;
14512 	bool alloc = (Out == NULL);
14513 	struct GMT_CTRL *GMT = API->GMT;
14514 	struct GMT_DATASEGMENT *SD = NULL;
14515 	GMT_getfunction api_get_val;
14516 	if (header) GMT_Report (API, GMT_MSG_WARNING, "gmtapi_vector2dataset: Header stripping not implemented yet - ignored!\n");
14517 	if (alloc && (Out = gmtlib_create_dataset (GMT, 1U, 1U, In->n_rows, In->n_columns, GMT_IS_POINT, mode, true)) == NULL)
14518 		return_null (API, GMT_MEMORY_ERROR);
14519 	SD = Out->table[0]->segment[0];	/* Shorthand to only segment in the dataset */
14520 	for (col = 0; col < In->n_columns; col++) {
14521 		if ((api_get_val = gmtapi_select_get_function (API, In->type[col])) == NULL)
14522 			return_null (API, GMT_NOT_A_VALID_TYPE);
14523 		for (row = 0; row < In->n_rows; row++)
14524 			api_get_val (&(In->data[col]), row, &(SD->data[col][row]));
14525 	}
14526 	if (mode) {	/* Duplicate the strings */
14527 		for (row = 0; row < In->n_rows; row++)
14528 			SD->text[row] = strdup (In->text[row]);	/* Duplicate the strings */
14529 	}
14530 	return Out;
14531 }
14532 
gmtapi_vector2matrix(struct GMTAPI_CTRL * API,struct GMT_VECTOR * In,struct GMT_MATRIX * Out,unsigned int header,unsigned int mode)14533 GMT_LOCAL void * gmtapi_vector2matrix (struct GMTAPI_CTRL *API, struct GMT_VECTOR *In, struct GMT_MATRIX *Out, unsigned int header, unsigned int mode) {
14534 	/* Convert a vector to a matrix.
14535 	 * If Out is not NULL then we assume it has enough rows to hold the rows.
14536 	 * header controls what we do with headers.
14537 	 * If mode > 0 then it is assumed to hold GMT_TYPE-1, else we assume the GMT default setting.
14538 	 */
14539 	uint64_t row, col, ij;
14540 	bool alloc = (Out == NULL);
14541 	double value;
14542 	struct GMT_CTRL *GMT = API->GMT;
14543 	GMT_getfunction api_get_val = NULL;
14544 	GMT_putfunction api_put_val = NULL;
14545 	p_func_uint64_t GMT_2D_to_index = NULL;
14546 	if (header) GMT_Report (API, GMT_MSG_WARNING, "gmtapi_vector2matrix: Header stripping not implemented yet - ignored!\n");
14547 	if (alloc) {
14548 		struct GMT_MATRIX_HIDDEN *MH = NULL;
14549 		Out = gmtlib_create_matrix (GMT, 1U, GMT_OUT, 0);
14550 		Out->n_columns = In->n_columns;
14551 		Out->n_rows = In->n_rows;
14552 		Out->type = (mode) ? mode - 1 : API->GMT->current.setting.export_type;
14553 		if (gmtlib_alloc_univector (GMT, &(Out->data), Out->type, Out->n_rows * Out->n_columns)) {
14554 			gmt_M_free (GMT, Out);
14555 			return (NULL);
14556 		}
14557 		MH = gmt_get_M_hidden (Out);
14558 		MH->alloc_mode = GMT_ALLOC_INTERNALLY;
14559 	}
14560 
14561 	if ((GMT_2D_to_index = gmtapi_get_2d_to_index (API, Out->shape, GMT_GRID_IS_REAL)) == NULL) {
14562 		gmt_M_free (GMT, Out);
14563 		return (NULL);
14564 	}
14565 	if ((api_put_val = gmtapi_select_put_function (API, Out->type)) == NULL) {	/* Since all columns are of same type we get the pointer here */
14566 		gmt_M_free (GMT, Out);
14567 		return (NULL);
14568 	}
14569 	for (col = 0; col < In->n_columns; col++) {
14570 		if ((api_get_val = gmtapi_select_get_function (API, In->type[col])) == NULL) {
14571 			gmt_M_free (GMT, Out);
14572 			return (NULL);
14573 		}
14574 		for (row = 0; row < In->n_rows; row++) {
14575 			api_get_val (&(In->data[col]), row, &value);
14576 			ij = GMT_2D_to_index (row, col, Out->dim);
14577 			api_put_val (&(Out->data), ij, value);
14578 		}
14579 	}
14580 	return Out;
14581 }
14582 
14583 /* New function to convert between objects */
14584 
14585 #define GMT_HEADER_MODE	0
14586 #define GMT_TYPE_MODE	1
14587 #define GMT_FORMAT_MODE	1	/* Same as GMT_TYPE_MODE [not a typo] */
14588 
GMT_Convert_Data(void * V_API,void * In,unsigned int family_in,void * Out,unsigned int family_out,unsigned int flag[])14589 void *GMT_Convert_Data (void *V_API, void *In, unsigned int family_in, void *Out, unsigned int family_out, unsigned int flag[]) {
14590 	/* Convert between valid pairs of objects,  If Out == NULL then we allocate an output object,
14591 	 * otherwise we assume we are given adequate space already.  This is most likely restricted to a GMT_MATRIX.
14592 	 * flag is an array with two unsigned integers controlling various aspects of the conversion:
14593 	 * flag[0]: Controls how headers are handled on output:
14594 	 * 	 0 : All headers are passed on as is.  For Matrix/Vector all table headers are always ignored but
14595 	 * 	     segment headers will be encoded as NaN records
14596 	 * 	 1 : Headers are not copied, but segment headers are preserved
14597 	 * 	 2 : Headers are preserved, but segment headers are initialized to blank
14598 	 * 	 3 : All headers headers are eliminated
14599 	 *	     The GMT Default settings in effect will control any output to files later.
14600 	 * [Note if that happens it is not considered an error, so API->error is GMT_NOERROR].
14601 	 * flag[1]: Controls the data type to use for MATRIX and VECTOR.
14602 	 * 	0: Use the GMT default data type [GMT_EXPORT_TYPE]
14603 	 * 	>0: Assumed to contain datatype + 1 (e.g., GMT_FLOAT+1, GMT_DOUBLE+1)
14604 	 * If DATASET, this integer controls the restructuring of the set:
14605 	 * 	GMT_WRITE_TABLE_SEGMENT: Combine all segments into a SINGLE segment in ONE table
14606 	 * 	GMT_WRITE_TABLE: Collect all segments into ONE table.
14607 	 * 	GMT_WRITE_SEGMENT: Combine segments into ONE segment per table.
14608 	 * 	0: Retain initial layout.
14609 	 * If GRID then flags are not yet used
14610 	 *
14611 	 * The following conversions are valid; the brackets indicate any side-effects or limitations]
14612 	 *
14613 	 * DATASET -> MATRIX,  VECTOR
14614 	 * MATRIX  -> DATASET, VECTOR
14615 	 * VECTOR  -> DATASET, MATRIX
14616 	 * GRID    -> MATRIX
14617 	 */
14618 	int object_ID, item;
14619 	void *X = NULL;
14620 	struct GMTAPI_CTRL *API = NULL;
14621 
14622 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
14623 	API = gmtapi_get_api_ptr (V_API);
14624 	API->error = GMT_NOERROR;
14625 
14626 	switch (family_in) {
14627 		case GMT_IS_DATASET:
14628 			switch (family_out) {
14629 				case GMT_IS_DATASET:
14630 					X = gmtapi_dataset2dataset (API, In, Out, flag[GMT_HEADER_MODE], flag[GMT_TYPE_MODE]);
14631 					break;
14632 				case GMT_IS_MATRIX:
14633 					X = gmtapi_dataset2matrix(API, In, Out, flag[GMT_HEADER_MODE], flag[GMT_TYPE_MODE]);
14634 					break;
14635 				case GMT_IS_VECTOR:
14636 					X = gmtapi_dataset2vector (API, In, Out, flag[GMT_HEADER_MODE], flag[GMT_TYPE_MODE]);
14637 					break;
14638 				default:
14639 					API->error = GMT_NOT_A_VALID_FAMILY;
14640 					break;
14641 			}
14642 			break;
14643 		case GMT_IS_MATRIX:
14644 			switch (family_out) {
14645 				case GMT_IS_DATASET:
14646 					X = gmtapi_matrix2dataset (API, In, Out, flag[GMT_HEADER_MODE]);
14647 					break;
14648 				case GMT_IS_VECTOR:
14649 					X = gmtapi_matrix2vector (API, In, Out, flag[GMT_HEADER_MODE], flag[GMT_TYPE_MODE]);
14650 					break;
14651 				default:
14652 					API->error = GMT_NOT_A_VALID_FAMILY;
14653 					break;
14654 			}
14655 			break;
14656 		case GMT_IS_VECTOR:
14657 			switch (family_out) {
14658 				case GMT_IS_DATASET:
14659 					X = gmtapi_vector2dataset (API, In, Out, flag[GMT_HEADER_MODE]);
14660 					break;
14661 				case GMT_IS_MATRIX:
14662 					X = gmtapi_vector2matrix (API, In, Out, flag[GMT_HEADER_MODE], flag[GMT_TYPE_MODE]);
14663 					break;
14664 				default:
14665 					API->error = GMT_NOT_A_VALID_FAMILY;
14666 					break;
14667 			}
14668 			break;
14669 		case GMT_IS_GRID:
14670 			switch (family_out) {
14671 				case GMT_IS_MATRIX:
14672 					X = gmtapi_grid2matrix (API, In, Out);
14673 					break;
14674 				default:
14675 					API->error = GMT_NOT_A_VALID_FAMILY;
14676 					break;
14677 			}
14678 			break;
14679 		default:
14680 			API->error = GMT_NOT_A_VALID_FAMILY;
14681 			break;
14682 	}
14683 	if (X == NULL)
14684 		return_null (API, GMT_PTR_IS_NULL);
14685 	if (API->error)
14686 		return_null (API, API->error);
14687 	if ((object_ID = GMT_Register_IO (API, family_out, GMT_IS_REFERENCE, GMT_IS_POINT, GMT_IN, NULL, X)) == GMT_NOTSET)
14688 		return_null (API, API->error);	/* Failure to register */
14689 	if ((item = gmtlib_validate_id (API, family_out, object_ID, GMT_IN, GMT_NOTSET)) == GMT_NOTSET)
14690 		return_null (API, API->error);
14691 	API->object[item]->resource = X;	/* Retain pointer to the allocated data so we use garbage collection later */
14692 #ifdef DEBUG
14693 	gmtapi_list_objects (API, "GMT_Convert_Data");
14694 #endif
14695 	return (X);
14696 }
14697 
14698 #ifdef FORTRAN_API
GMT_Convert_Data_(void * In,unsigned int * family_in,void * Out,unsigned int * family_out,unsigned int flag[])14699 void * GMT_Convert_Data_ (void *In, unsigned int *family_in, void *Out, unsigned int *family_out, unsigned int flag[]) {
14700 	/* Fortran version: We pass the global GMT_FORTRAN structure */
14701 	return (GMT_Convert_Data (GMT_FORTRAN, In, *family_in, Out, *family_out, flag));
14702 }
14703 #endif
14704 
GMT_Alloc_Segment(void * V_API,unsigned int mode,uint64_t n_rows,uint64_t n_columns,char * header,void * S)14705 void * GMT_Alloc_Segment (void *V_API, unsigned int mode, uint64_t n_rows, uint64_t n_columns, char *header, void *S) {
14706 	/* Allocate or reallocate a GMT_DATASEGMENT.
14707 	 * The n_columns may be 0 if no numerical data in the segment.
14708 	 * header, if not NULL or blank, sets the segment header.
14709 	 * if mode == GMT_WITH_STRINGS then we also allocate the empty array of string pointers */
14710 	struct GMT_DATASEGMENT *Snew = NULL;
14711 	struct GMTAPI_CTRL *API = NULL;
14712 	bool first = true, alloc;
14713 	char *H = header;
14714 
14715 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
14716 	API = gmtapi_get_api_ptr (V_API);
14717 	API->error = GMT_NOERROR;
14718 	if ((Snew = S) != NULL)	/* Existing segment given */
14719 		first = false;
14720 	else if ((Snew = gmt_get_segment (API->GMT, n_columns)) == NULL) /* Something went wrong */
14721 		return_null (V_API, GMT_MEMORY_ERROR);
14722 		/* Only reallocate if desired n_rows differ from current n_rows */
14723 	alloc = (first || (n_rows && n_rows != Snew->n_rows));	/* Alloc first time or reallocate later if necessary */
14724 	if (alloc && gmt_alloc_segment (API->GMT, Snew, n_rows, n_columns, mode, first))  {	/* Something went wrong */
14725 		if (first) gmt_M_free (API->GMT, Snew);
14726 		return_null (V_API, GMT_MEMORY_ERROR);
14727 	}
14728 	if (H && H[0] == API->GMT->current.setting.io_seg_marker[GMT_IN]) {	/* User gave a record with segment marker in it */
14729 		H++;	/* Skip the segment marker */
14730 		while (*H && (*H == ' ' || *H == '\t')) H++;	/* Then skip any leading whitespace */
14731 	}
14732 	if (H && strlen (H)) {	/* Gave a header string to (re)place in the segment */
14733 		if (Snew->header) gmt_M_str_free (Snew->header);
14734 		Snew->header = strdup (H);
14735 	}
14736 	return Snew;
14737 }
14738 
14739 #ifdef FORTRAN_API
GMT_Alloc_Segment_(unsigned int * family,uint64_t * n_rows,uint64_t * n_columns,char * header,void * S,int len)14740 void * GMT_Alloc_Segment_ (unsigned int *family, uint64_t *n_rows, uint64_t *n_columns, char *header, void *S, int len) {
14741 	/* Fortran version: We pass the global GMT_FORTRAN structure */
14742 	return (GMT_Alloc_Segment (GMT_FORTRAN, *family, *n_rows, *n_columns, header, S));
14743 }
14744 #endif
14745 
GMT_Set_Columns(void * V_API,unsigned int direction,unsigned int n_cols,unsigned int mode)14746 int GMT_Set_Columns (void *V_API, unsigned int direction, unsigned int n_cols, unsigned int mode) {
14747 	/* Specify how many input or output columns to use for record-by-record output, if fixed */
14748 	int error = 0;
14749 	uint64_t n_in = 0;
14750 	struct GMTAPI_CTRL *API = NULL;
14751 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (V_API, GMT_NOT_A_VALID_DIRECTION);
14752 	if (direction == GMT_IN && !(mode == GMT_COL_FIX || mode == GMT_COL_VAR || mode == GMT_COL_FIX_NO_TEXT)) return_error (V_API, GMT_NOT_A_VALID_MODE);
14753 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
14754 	API = gmtapi_get_api_ptr (V_API);
14755 	API->error = GMT_NOERROR;
14756 
14757 	if (direction == GMT_OUT) {	/* Output */
14758 		if ((mode == GMT_COL_ADD || mode == GMT_COL_SUB) && (n_in = gmt_get_cols (API->GMT, GMT_IN)) == 0) {	/* Get number of input columns */
14759 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Set_Columns: Premature call - number of input columns not known yet\n");
14760 			return_error (API, GMT_N_COLS_NOT_SET);
14761 		}
14762 		/* If no columns specified we set output to the same as input columns */
14763 		if (n_cols == 0 && mode != GMT_COL_FIX && (error = gmt_set_cols (API->GMT, GMT_OUT, n_in)) != 0)
14764 			return_error (API, GMT_N_COLS_NOT_SET);
14765 		/* Set output record type */
14766 		if (n_cols == 0)
14767 			API->GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_TEXT;
14768 		else if (mode == GMT_COL_FIX_NO_TEXT)
14769 			API->GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_DATA;
14770 		else
14771 			API->GMT->current.io.record_type[GMT_OUT] = GMT_WRITE_MIXED;
14772 	}
14773 
14774 	/* Get here when n_cols is not zero (of we have a special case), so must consult mode */
14775 
14776 	switch (mode) {
14777 		case GMT_COL_FIX_NO_TEXT:	/* Specific a fixed number of columns, and ignore trailing text */
14778 			API->GMT->current.io.trailing_text[direction] = false;
14779 			/* Intentionally fall through - to set columns */
14780 		case GMT_COL_FIX:	/* Specific a fixed number of columns */
14781 			error = gmt_set_cols (API->GMT, direction, n_cols);
14782 			break;
14783 		case GMT_COL_VAR:	/* Flag we have a variable number of input columns */
14784 			API->GMT->current.io.variable_in_columns = true;
14785 			break;
14786 		case GMT_COL_ADD:	/* Add to the number of input columns */
14787 			error = gmt_set_cols (API->GMT, GMT_OUT, n_in + n_cols);
14788 			break;
14789 		case GMT_COL_SUB:	/* Subtract from the number of input columns */
14790 			if (n_cols >= n_in) {
14791 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Set_Columns: Cannot specify less than one output column!\n");
14792 				return_error (API, GMT_DIM_TOO_SMALL);
14793 			}
14794 			error = gmt_set_cols (API->GMT, GMT_OUT, n_in - n_cols);
14795 			break;
14796 	}
14797 	if (error) return_error (API, GMT_N_COLS_NOT_SET);
14798 	return (GMT_NOERROR);
14799 }
14800 
14801 #ifdef FORTRAN_API
GMT_Set_Columns_(unsigned int * direction,unsigned int * n_cols,unsigned int * mode)14802 int GMT_Set_Columns_ (unsigned int *direction, unsigned int *n_cols, unsigned int *mode) {
14803 	/* Fortran version: We pass the global GMT_FORTRAN structure */
14804 	return (GMT_Set_Columns (GMT_FORTRAN, *direction, *n_cols, *mode));
14805 }
14806 #endif
14807 
gmtapi_change_gridlayout(struct GMTAPI_CTRL * API,char * code,unsigned int mode,struct GMT_GRID * G,gmt_grdfloat * out)14808 GMT_LOCAL int gmtapi_change_gridlayout (struct GMTAPI_CTRL *API, char *code, unsigned int mode, struct GMT_GRID *G, gmt_grdfloat *out) {
14809 	enum GMT_enum_family family;
14810 	unsigned int row, col, pad[4], old_layout, new_layout;
14811 	uint64_t from_node, to_node;
14812 	gmt_grdfloat *tmp = NULL;
14813 	gmt_M_unused(mode);
14814 
14815 	old_layout = gmtapi_decode_layout (API, G->header->mem_layout, &family);
14816 	if (family != GMT_IS_GRID) return GMT_NOT_A_VALID_FAMILY;
14817 	new_layout = gmtapi_decode_layout (API, code, &family);
14818 	if (old_layout == new_layout) return GMT_NOERROR;	/* Nothing to do */
14819 
14820 	/* Remove the high bits for complex data */
14821 	old_layout &= 3;	new_layout &= 3;
14822 	/* Grids may be column vs row oriented and from top or from bottom */
14823 	gmt_M_memcpy (pad, G->header->pad, 4, unsigned int);	/* Remember the pad */
14824 	if (((tmp = out) == NULL) && (tmp = gmt_M_memory_aligned (API->GMT, NULL, G->header->size, gmt_grdfloat)) == NULL)
14825 		return (GMT_MEMORY_ERROR);		/* Something went wrong */
14826 
14827 	gmt_grd_pad_off (API->GMT, G);	/* Simplify working with no pad */
14828 	if (old_layout == 0 && new_layout == 2) { /* Change from TR to TC */
14829 		for (row = 0, from_node = 0; row < G->header->n_rows; row++)
14830 			for (col = 0; col < G->header->n_columns; col++, from_node++)
14831 				tmp[(uint64_t)col * (uint64_t)G->header->n_rows + row] = G->data[from_node];
14832 	}
14833 	else if (old_layout == 0 && new_layout == 3) {	/* Change from TR to BC */
14834 		for (row = 0, from_node = 0; row < G->header->n_rows; row++)
14835 			for (col = 0; col < G->header->n_columns; col++, from_node++)
14836 				tmp[(uint64_t)col * (uint64_t)G->header->n_rows + (G->header->n_rows - row - 1)] = G->data[from_node];
14837 	}
14838 	else if (old_layout == 2 && new_layout == 0) {	/* Change from TC to TR */
14839 		for (row = 0, to_node = 0; row < G->header->n_rows; row++)
14840 			for (col = 0; col < G->header->n_columns; col++, to_node++)
14841 				tmp[to_node] = G->data[(uint64_t)col * (uint64_t)G->header->n_rows + row];
14842 	}
14843 	else if (old_layout == 3 && new_layout == 0) {	/* Change from BC to TR */
14844 		for (row = 0, to_node = 0; row < G->header->n_rows; row++)
14845 			for (col = 0; col < G->header->n_columns; col++, to_node++)
14846 				tmp[to_node] = G->data[(uint64_t)col * (uint64_t)G->header->n_rows + (G->header->n_rows - row - 1)];
14847 	}
14848 	else {		/* Other cases to be added later ...*/
14849 		GMT_Report (API, GMT_MSG_WARNING, "gmtapi_change_gridlayout: reordering function for case %s -> %s not yet written. Doing nothing\n",
14850 		            G->header->mem_layout, code);
14851 		for (to_node = 0; to_node < G->header->size; to_node++)
14852 			tmp[to_node] = G->data[to_node];
14853 	}
14854 
14855 	if (out == 0) {	/* Means we must update the grid data */
14856 		gmt_M_free_aligned (API->GMT, G->data);			/* Free previous aligned grid memory */
14857 		G->data = tmp;
14858 	}
14859 	gmt_grd_pad_on (API->GMT, G, pad);	/* Restore pad on grid */
14860 	return (GMT_NOERROR);
14861 }
14862 
gmtapi_change_imagelayout(struct GMTAPI_CTRL * API,char * code,unsigned int mode,struct GMT_IMAGE * I,unsigned char * out1,unsigned char * out2)14863 GMT_LOCAL int gmtapi_change_imagelayout (struct GMTAPI_CTRL *API, char *code, unsigned int mode, struct GMT_IMAGE *I, unsigned char *out1, unsigned char *out2) {
14864 	/* code: The new memory layout code, e.g "TRB"
14865 	   mode: Currently unused (for future expansion)
14866 	   out1: Array with the image data converted to the requested layout.
14867 	   out2: Array with the transparencies converted to the requested layout.
14868 	         If NULL on input the necessary memory is allocated inside this function, otherwise
14869 	         it is ASSUMED that it points a memory chunk big enough to hold the reshuffled data.
14870 	*/
14871 	bool changed = true;
14872 	unsigned char *tmp = NULL, *alpha = NULL;
14873 	enum GMT_enum_family family;
14874 	unsigned int old_layout, new_layout;
14875 	uint64_t band, row, col, to_node, from_node;
14876 	struct GMT_IMAGE_HIDDEN *IH = NULL;
14877 	gmt_M_unused(mode);
14878 
14879 	old_layout = gmtapi_decode_layout (API, I->header->mem_layout, &family);
14880 	new_layout = gmtapi_decode_layout(API, code, &family);
14881 	if (old_layout == new_layout) return GMT_NOERROR;	/* Nothing to do */
14882 
14883 	if ((tmp = out1) == NULL && (tmp = gmt_M_memory_aligned (API->GMT, NULL, I->header->n_bands * I->header->size, unsigned char)) == NULL)
14884 		return (GMT_MEMORY_ERROR);		/* Something went wrong */
14885 	if (I->alpha && (alpha = out2) == NULL && (alpha = gmt_M_memory_aligned (API->GMT, NULL, I->header->size, unsigned char)) == NULL) {
14886 		if (out2 == NULL) gmt_M_free (API->GMT, alpha);
14887 		gmt_M_free_aligned (API->GMT, tmp);
14888 		return (GMT_MEMORY_ERROR);		/* Something went wrong */
14889 	}
14890 
14891 	/* Images may be column vs row oriented, from top or from bottom and may be Band|Line|Pixel interleaved
14892 	   That sums up to a lot of combinations. We will add them on a by-need basis. */
14893 
14894 	if (old_layout == 8 && new_layout == 2) {		/* Change from TRP to TCB */
14895 		for (row = from_node = 0; row < I->header->my; row++)
14896 			for (col = 0; col < I->header->mx; col++)
14897 				for (band = 0; band < I->header->n_bands; band++, from_node++) {
14898 					to_node = row + col*I->header->my + band * I->header->size;
14899 					tmp[to_node] = (uint8_t)I->data[from_node];
14900 				}
14901 		if (I->alpha) {
14902 			for (row = from_node = 0; row < I->header->my; row++)
14903 				for (col = 0; col < I->header->mx; col++, from_node++) {
14904 					to_node = row + col*I->header->my;
14905 					alpha[to_node] = (uint8_t)I->alpha[from_node];
14906 				}
14907 		}
14908 	}
14909 	else if (old_layout == 0 && new_layout == 4) {		/* Change from TRB to TRL [UNTESTED] */
14910 		for (row = 0; row < I->header->my; row++)
14911 			for (col = 0; col < I->header->mx; col++)
14912 				for (band = 0; band < I->header->n_bands; band++) {
14913 					from_node = col + row*I->header->mx + band * I->header->size;
14914 					to_node = col + (band + row * I->header->n_bands) * I->header->mx;
14915 					tmp[to_node] = (uint8_t)I->data[from_node];
14916 				}
14917 		if (I->alpha)	/* Same since only one band of alpha */
14918 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14919 	}
14920 	else if (old_layout == 4 && new_layout == 0) {		/* Change from TRL to TRB [UNTESTED] */
14921 		for (row = 0; row < I->header->my; row++)
14922 			for (col = 0; col < I->header->mx; col++)
14923 				for (band = 0; band < I->header->n_bands; band++) {
14924 					to_node = col + row*I->header->mx + band * I->header->size;
14925 					from_node = col + (band + row * I->header->n_bands) * I->header->mx;
14926 					tmp[to_node] = (uint8_t)I->data[from_node];
14927 				}
14928 		if (I->alpha)	/* Same since only one band of alpha */
14929 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14930 	}
14931 	else if (old_layout == 0 && new_layout == 8) {		/* Change from TRB to TRP */
14932 		for (row = to_node = 0; row < I->header->my; row++)
14933 			for (col = 0; col < I->header->mx; col++)
14934 				for (band = 0; band < I->header->n_bands; band++, to_node++) {
14935 					from_node = col + row*I->header->mx + band * I->header->size;
14936 					tmp[to_node] = (uint8_t)I->data[from_node];
14937 				}
14938 		if (I->alpha)	/* Same since only one band of alpha */
14939 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14940 	}
14941 	else if (old_layout == 8 && new_layout == 0) {		/* Change from TRP to TRB [UNTESTED] */
14942 		for (row = from_node = 0; row < I->header->my; row++)
14943 			for (col = 0; col < I->header->mx; col++)
14944 				for (band = 0; band < I->header->n_bands; band++, from_node++) {
14945 					to_node = col + row*I->header->mx + band * I->header->size;
14946 					tmp[to_node] = (uint8_t)I->data[from_node];
14947 				}
14948 		if (I->alpha)	/* Same since only one band of alpha */
14949 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14950 	}
14951 	else if (old_layout == 0 && new_layout == 9) {		/* Change from TRB to BRP */
14952 		for (row = to_node = 0; row < I->header->my; row++)		/* Not UD flipping so what we call B is probably T */
14953 			for (col = 0; col < I->header->mx; col++)
14954 				for (band = 0; band < I->header->n_bands; band++, to_node++) {
14955 					from_node = col + row*I->header->mx + band * I->header->size;
14956 					tmp[to_node] = (uint8_t)I->data[from_node];
14957 				}
14958 		if (I->alpha)	/* Same since only one band of alpha and no transposition */
14959 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14960 	}
14961 	else if (old_layout == 2 && new_layout == 9) {		/* Change from TCB to BRP */
14962 		for (row = to_node = 0; row < I->header->my; row++)		/* Not UD flipping so what we call B is probably T */
14963 			for (col = 0; col < I->header->mx; col++)
14964 				for (band = 0; band < I->header->n_bands; band++, to_node++) {
14965 					from_node = row + col*I->header->my + band * I->header->size;
14966 					tmp[to_node] = (uint8_t)I->data[from_node];
14967 				}
14968 		if (I->alpha) {
14969 			for (row = to_node = 0; row < I->header->my; row++)
14970 				for (col = 0; col < I->header->mx; col++, to_node++) {
14971 					from_node = row + col*I->header->my;
14972 					alpha[to_node] = (uint8_t)I->alpha[from_node];
14973 				}
14974 		}
14975 	}
14976 	else if (old_layout == 11 && new_layout == 9) {		/* Change from BCP to BRP */
14977 		for (row = to_node = 0; row < I->header->my; row++)
14978 			for (col = 0; col < I->header->mx; col++)
14979 				for (band = 0; band < I->header->n_bands; band++, to_node++) {
14980 					from_node = row + col*I->header->my + band * I->header->size;
14981 					tmp[to_node] = (uint8_t)I->data[from_node];
14982 				}
14983 		if (I->alpha)	/* Same since only one band of alpha and no transposition */
14984 			gmt_M_memcpy (alpha, I->alpha, I->header->size, uint8_t);
14985 	}
14986 	else if (old_layout == 3 && new_layout == 9) {		/* Change from BCB to BRP. Here we believe in first B but not 2nd */
14987 		for (row = to_node = 0; row < I->header->my; row++)
14988 			for (col = 0; col < I->header->mx; col++)
14989 				for (band = 0; band < I->header->n_bands; band++, to_node++) {
14990 					from_node = (I->header->my - 1 - row) + col*I->header->my + band * I->header->size;
14991 					tmp[to_node] = (uint8_t)I->data[from_node];
14992 				}
14993 		if (I->alpha) {
14994 			for (row = to_node = 0; row < I->header->my; row++)
14995 				for (col = 0; col < I->header->mx; col++, to_node++) {
14996 					from_node = (I->header->my - 1 - row) + col*I->header->my;
14997 					alpha[to_node] = (uint8_t)I->alpha[from_node];
14998 				}
14999 		}
15000 	}
15001 	else if (old_layout == 9 && new_layout == 0) {		/* Change from BRP to TRB */
15002 		for (row = from_node = 0; row < I->header->my; row++)
15003 			for (col = 0; col < I->header->mx; col++)
15004 				for (band = 0; band < I->header->n_bands; band++, from_node++) {
15005 					//to_node = col + (I->header->my - 1 - row)*I->header->my + band*I->header->size;  /* PW: This was always like this - commented out, so where is the B->T happening? */
15006 					to_node = col + row*I->header->my + band * I->header->size;
15007 					tmp[to_node] = (uint8_t)I->data[from_node];
15008 				}
15009 		if (I->alpha) {
15010 			for (row = from_node = 0; row < I->header->my; row++)
15011 				for (col = 0; col < I->header->mx; col++, from_node++) {
15012 					to_node = col + row*I->header->my;
15013 					alpha[to_node] = (uint8_t)I->alpha[from_node];
15014 				}
15015 		}
15016 	}
15017 	//else if (old_layout == 2 && new_layout == 0) {}	/* Change from TCB to TRB */
15018 	else {		/* Other cases to be added later ...*/
15019 		GMT_Report (API, GMT_MSG_WARNING, "gmtapi_change_imagelayout: reordering function for case %s -> %s not yet written. Doing nothing.\n",
15020 			I->header->mem_layout, code);
15021 		for (from_node = 0; from_node < I->header->size; from_node++)
15022 			tmp[from_node] = I->data[from_node];
15023 		changed = false;
15024 	}
15025 
15026 	if (out1 == NULL) {	/* Means we must update the Image data */
15027 		IH = gmt_get_I_hidden (I);
15028 		if (IH->alloc_mode != GMT_ALLOC_EXTERNALLY)
15029 			gmt_M_free_aligned (API->GMT, I->data);			/* Free previous aligned image memory */
15030 		I->data = tmp;
15031 	}
15032 	if (out2 == NULL && alpha) {	/* Means we must update the alpha data */
15033 		if (!IH) IH = gmt_get_I_hidden (I);
15034 		if (IH->alloc_mode != GMT_ALLOC_EXTERNALLY)
15035 			gmt_M_free_aligned (API->GMT, I->alpha);		/* Free previous aligned image transparency */
15036 		I->alpha = alpha;
15037 	}
15038 
15039 	if (changed) {	/* Update the mem_layout for this image */
15040 		strncpy (I->header->mem_layout, code, MIN(strlen(code),4));
15041 		if (I->alpha) I->header->mem_layout[3] = 'a';	/* Flag that we have transparency */
15042 	}
15043 
15044 	return (GMT_NOERROR);
15045 }
15046 
GMT_Change_Layout(void * V_API,unsigned int family,char * code,unsigned int mode,void * obj,void * out,void * alpha)15047 int GMT_Change_Layout (void *V_API, unsigned int family, char *code, unsigned int mode, void *obj, void *out, void *alpha) {
15048 	/* Reorder the memory layout of a grid or image given the new desired layout in code.
15049 	 * If out == NULL then we allocate space to hold the new grid|image and replace obj->data with this new array.
15050 	 *   For grids we preserve any padding in effect for the object but for out we have no padding.
15051 	 * Otherwise we assume out points to allocated memory and we simply fill it out, assuming no pad.
15052 	 * mode is presently unused.
15053 	 * alpha is only considered for images and may be used to return a modified transparency array.
15054 	 */
15055 	struct GMTAPI_CTRL *API = NULL;
15056 	int error;
15057 	if (V_API == NULL) return_error (V_API, GMT_NOERROR);	/* Not fuss if nothing is given */
15058 	API = gmtapi_get_api_ptr (V_API);
15059 	API->error = GMT_NOERROR;
15060 	if (code == NULL || code[0] == '\0') return_error (V_API, GMT_NOT_A_SESSION);
15061 	switch (family) {
15062 		case GMT_IS_GRID:
15063 			error = gmtapi_change_gridlayout (V_API, code, mode, obj, out);
15064 			break;
15065 		case GMT_IS_IMAGE:
15066 			error = gmtapi_change_imagelayout (V_API, code, mode, obj, out, alpha);
15067 			break;
15068 		default:
15069 			error = GMT_NOT_A_VALID_FAMILY;
15070 			break;
15071 	}
15072 	return_error (API, error);
15073 }
15074 
15075 #ifdef FORTRAN_API
GMT_Change_Layout_(unsigned int * family,char * code,unsigned int * mode,void * obj,void * out,void * alpha,int len)15076 int GMT_Change_Layout_ (unsigned int *family, char *code, unsigned int *mode, void *obj, void *out, void *alpha, int len) {
15077 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15078 	return (GMT_Change_Layout (GMT_FORTRAN, *family, code, *mode, obj, out, alpha));
15079 }
15080 #endif
15081 
15082 /* Deal with assignments of custom vectors and matrices to GMT containers */
15083 
gmtapi_insert_vector(struct GMTAPI_CTRL * API,union GMT_UNIVECTOR * V,unsigned int type,void * vector)15084 GMT_LOCAL int gmtapi_insert_vector (struct GMTAPI_CTRL *API, union GMT_UNIVECTOR *V, unsigned int type, void *vector) {
15085 	/* Hook a vector to the correct union member given data type */
15086 	gmt_M_unused (API);
15087 	switch (type) {
15088 		case GMT_DOUBLE:	V->f8  = vector;	break;
15089 		case GMT_FLOAT:		V->f4  = vector;	break;
15090 		case GMT_ULONG:		V->ui8 = vector;	break;
15091 		case GMT_LONG:		V->si8 = vector;	break;
15092 		case GMT_UINT:		V->ui4 = vector;	break;
15093 		case GMT_INT:		V->si4 = vector;	break;
15094 		case GMT_USHORT:	V->ui2 = vector;	break;
15095 		case GMT_SHORT:		V->si2 = vector;	break;
15096 		case GMT_UCHAR:		V->uc1 = vector;	break;
15097 		case GMT_CHAR:		V->sc1 = vector;	break;
15098 		default:
15099 			return (GMT_NOT_A_VALID_TYPE);
15100 			break;
15101 	}
15102 	return (GMT_NOERROR);
15103 }
15104 
gmtapi_retrieve_vector(void * API,union GMT_UNIVECTOR * V,unsigned int type)15105 GMT_LOCAL void * gmtapi_retrieve_vector (void *API, union GMT_UNIVECTOR *V, unsigned int type) {
15106 	void *vector = NULL;
15107 	gmt_M_unused (API);
15108 	switch (type) {
15109 		case GMT_DOUBLE:	vector = V->f8;	break;
15110 		case GMT_FLOAT:		vector = V->f4;	break;
15111 		case GMT_ULONG:		vector = V->ui8;	break;
15112 		case GMT_LONG:		vector = V->si8;	break;
15113 		case GMT_UINT:		vector = V->ui4;	break;
15114 		case GMT_INT:		vector = V->si4;	break;
15115 		case GMT_USHORT:	vector = V->ui2;	break;
15116 		case GMT_SHORT:		vector = V->si2;	break;
15117 		case GMT_UCHAR:		vector = V->uc1;	break;
15118 		case GMT_CHAR:		vector = V->sc1;	break;
15119 		default:
15120 			return NULL;
15121 			break;
15122 	}
15123 	return vector;
15124 }
15125 
GMT_Put_Vector(void * V_API,struct GMT_VECTOR * V,unsigned int col,unsigned int type,void * vector)15126 int GMT_Put_Vector (void *V_API, struct GMT_VECTOR *V, unsigned int col, unsigned int type, void *vector) {
15127 	/* Hooks a users custom vector onto V's column array and sets the type.
15128 	 * It is the user's responsibility to pass correct type for the given vector.
15129 	 * We also check that the number of rows have been set earlier.
15130 	 * We also allow special text-based arrays for longitude, latitude, datetime or Cartesian data to be passed
15131 	 * which may be logically OR'ed with desired array type (e.g., GMT_LONG|GMT_TEXT).
15132 	 * Note: We do not check for data loss in the conversion (e.g., GMT_UCHAR|GMT_TEXT) */
15133 	unsigned int special_type;
15134 	enum GMT_enum_alloc alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Default is to pass vectors in read-only */
15135 	struct GMTAPI_CTRL *API = NULL;
15136 	struct GMT_VECTOR_HIDDEN *VH = NULL;
15137 
15138 	API = gmtapi_get_api_ptr (V_API);
15139 	if (API == NULL) return_error (API, GMT_NOT_A_SESSION);
15140 	if (V == NULL) return_error (API, GMT_PTR_IS_NULL);
15141 	if (V->n_rows == 0) return_error (API, GMT_DIM_TOO_SMALL);
15142 	if (col >= V->n_columns) return_error (API, GMT_DIM_TOO_LARGE);
15143 
15144 	special_type = type & (GMT_TEXT | GMT_DATETIME);	/* Backwards compatible with just GMT_DATETIME */
15145 	if (special_type == 0) {	/* Just passing in a numerical array directly via read-only pointer; hook it up */
15146 		if (gmtapi_insert_vector (API, &(V->data[col]), type, vector))
15147 			return_error (API, GMT_NOT_A_VALID_TYPE);
15148 		V->type[col] = type;	/* Set column type */
15149 	}
15150 	else {	/* Convert text to something else */
15151 		bool no_T = false;
15152 		unsigned L_type = GMT_IS_UNKNOWN, got;
15153 		double value;
15154 		uint64_t row, n_bad = 0, L;
15155 		char **dt = NULL;
15156 		char text[GMT_LEN64] = {""};
15157 		GMT_putfunction api_put_val = NULL;
15158 
15159 		if (gmtapi_retrieve_vector (API, &(V->data[col]), type)) {	/* Refuse to overwrite existing pointer unless NULL */
15160 			GMT_Report (API, GMT_MSG_ERROR, "Array already exist for column %d\n", col);
15161 			return_error (API, GMT_PTR_NOT_NULL);
15162 		}
15163 		type -= special_type;	/* Remove the higher bit flag(s) */
15164 		if (type == 0) type = GMT_DOUBLE;	/* Default is double precision if a type was not specified */
15165 		if ((dt = gmtapi_get_char_char_ptr (vector)) == NULL) {
15166 			GMT_Report (API, GMT_MSG_ERROR, "Given string array is NULL\n");
15167 			return_error (API, GMT_MEMORY_ERROR);
15168 		}
15169 		strncpy (text, dt[0], GMT_LEN64);	/* Since gmt_scanf may try to temporarily change the string... */
15170 		if ((L = strlen (text)) == 0) {
15171 			GMT_Report (API, GMT_MSG_ERROR, "Given blank string in array\n");
15172 			return_error (API, GMT_MEMORY_ERROR);
15173 		}
15174 		if (special_type == GMT_DATETIME || gmtlib_maybe_abstime (API->GMT, text, &no_T))	/* Honor backwards compatibility for GMT_DATETIME */
15175 			L_type = GMT_IS_ABSTIME;
15176 		else if (strchr ("WE", text[L]))
15177 			L_type = GMT_IS_LON;
15178 		else if (strchr ("SN", text[L]))
15179 			L_type = GMT_IS_LAT;
15180 		else if (strchr (text, ':'))
15181 			L_type = GMT_IS_GEO;
15182 		if ((api_put_val = gmtapi_select_put_function (API, type)) == NULL)
15183 			return_error (API, GMT_NOT_A_VALID_TYPE);
15184 		/* Here we know the type is valid */
15185 		if (gmtlib_alloc_univector (API->GMT, &V->data[col], type, V->n_rows) != GMT_NOERROR) {
15186 			GMT_Report (API, GMT_MSG_ERROR, "Unable to allocate array of %" PRIu64 " %s-values for converted strings\n", V->n_rows, GMT_type[type]);
15187 			return_error (API, GMT_MEMORY_ERROR);
15188 		}
15189 		/* Do the conversion to double precision */
15190 		for (row = 0; row < V->n_rows; row++) {
15191 			strncpy (text, dt[row], GMT_LEN64);
15192 			if ((got = gmt_scanf (API->GMT, text, L_type, &value)) == GMT_IS_NAN) {
15193 				n_bad++;	/* Check for bad conversions */
15194 				value = API->GMT->session.d_NaN;
15195 			}
15196 			else if (got != GMT_IS_FLOAT && L_type == GMT_IS_UNKNOWN)	/* Got something other than plain float, use that from now on */
15197 				L_type = got;
15198 			api_put_val (&(V->data[col]), row, value);	/* Place value in vector of selected type */
15199 		}
15200 		V->type[col] = type;	/* Flag as the new type after conversion */
15201 		if (L_type == GMT_IS_UNKNOWN) L_type = GMT_IS_FLOAT;	/* We held out this long but now must default to it */
15202 		gmt_set_column_type (API->GMT, GMT_IO, col, L_type);
15203 		if (n_bad) {	/* Report on values that could not be converted */
15204 			if (L_type == GMT_IS_LON)
15205 				GMT_Report (API, GMT_MSG_WARNING, "Unable to parse %" PRIu64 " longitude strings\n", n_bad);
15206 			else if (L_type == GMT_IS_LAT)
15207 				GMT_Report (API, GMT_MSG_WARNING, "Unable to parse %" PRIu64 " latitude strings\n", n_bad);
15208 			else if (L_type == GMT_IS_ABSTIME)
15209 				GMT_Report (API, GMT_MSG_WARNING, "Unable to parse %" PRIu64 " datetime strings (ISO datetime format required)\n", n_bad);
15210 		}
15211 		alloc_mode = GMT_ALLOC_INTERNALLY;
15212 	}
15213 
15214 	VH = gmt_get_V_hidden (V);
15215 	VH->alloc_mode[col] = alloc_mode;
15216 
15217 	return GMT_NOERROR;
15218 }
15219 
15220 #ifdef FORTRAN_API
GMT_Put_Vector_(struct GMT_VECTOR * V,unsigned int * col,unsigned int * type,void * vector)15221 int GMT_Put_Vector_ (struct GMT_VECTOR *V, unsigned int *col, unsigned int *type, void *vector) {
15222 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15223 	return (GMT_Put_Vector (GMT_FORTRAN, V, *col, *type, vector));
15224 }
15225 #endif
15226 
GMT_Get_Vector(void * API,struct GMT_VECTOR * V,unsigned int col)15227 void * GMT_Get_Vector (void *API, struct GMT_VECTOR *V, unsigned int col) {
15228 	/* Returns a pointer to the specified column array. Users can consult
15229 	 * V->type[col] to know what data type is pointed to.  */
15230 	void *vector = NULL;
15231 	if (API == NULL) return_null (API, GMT_NOT_A_SESSION);
15232 	if (V == NULL) return_null (API, GMT_PTR_IS_NULL);
15233 	if (col >= V->n_columns) return_null (API, GMT_DIM_TOO_LARGE);
15234 	if ((vector = gmtapi_retrieve_vector (API, &(V->data[col]), V->type[col])) == NULL)
15235 		return_null (API, GMT_NOT_A_VALID_TYPE);
15236 	return vector;
15237 }
15238 
15239 #ifdef FORTRAN_API
GMT_Get_Vector_(struct GMT_VECTOR * V,unsigned int * col)15240 void * GMT_Get_Vector_ (struct GMT_VECTOR *V, unsigned int *col) {
15241 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15242 	return (GMT_Get_Vector (GMT_FORTRAN, V, *col));
15243 }
15244 #endif
15245 
GMT_Put_Matrix(void * V_API,struct GMT_MATRIX * M,unsigned int type,int pad,void * matrix)15246 int GMT_Put_Matrix (void *V_API, struct GMT_MATRIX *M, unsigned int type, int pad, void *matrix) {
15247 	/* Hooks a user's custom matrix onto M's data array and sets the type.
15248 	 * It is the user's responsibility to pass correct type for the given matrix.
15249 	 * We check that dimensions have been set earlier */
15250 	int item;
15251 	struct GMT_MATRIX_HIDDEN *MH = NULL;
15252 	struct GMTAPI_CTRL *API = NULL;
15253 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15254 	if (M == NULL) return_error (V_API, GMT_PTR_IS_NULL);
15255 	if (M->n_columns == 0 || M->n_rows == 0) return_error (V_API, GMT_DIM_TOO_SMALL);
15256 	API = gmtapi_get_api_ptr (V_API);
15257 	if (gmtapi_insert_vector (API, &(M->data), type, matrix))
15258 		return_error (API, GMT_NOT_A_VALID_TYPE);
15259 	M->type = type;
15260 	MH = gmt_get_M_hidden (M);
15261 	MH->alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Since it clearly is a user array */
15262 	MH->pad = pad;	/* Placing the pad argument here */
15263 	if ((item = gmtapi_get_item (API, GMT_IS_GRID, M)) != GMT_NOTSET)	/* Found in list, update type here as well */
15264 		API->object[item]->type = type;
15265 
15266 	return GMT_NOERROR;
15267 }
15268 
15269 #ifdef FORTRAN_API
GMT_Put_Matrix_(struct GMT_MATRIX * M,unsigned int * type,int * pad,void * matrix)15270 int GMT_Put_Matrix_ (struct GMT_MATRIX *M, unsigned int *type, int *pad, void *matrix) {
15271 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15272 	return (GMT_Put_Matrix (GMT_FORTRAN, M, *type, *pad, matrix));
15273 }
15274 #endif
15275 
GMT_Get_Matrix(void * API,struct GMT_MATRIX * M)15276 void * GMT_Get_Matrix (void *API, struct GMT_MATRIX *M) {
15277 	/* Returns a pointer to the matrix.  Users can consult
15278 	 * M->type to know what data type is pointed to.  */
15279 	void *matrix = NULL;
15280 	if (API == NULL) return_null (API, GMT_NOT_A_SESSION);
15281 	if (M == NULL) return_null (API, GMT_PTR_IS_NULL);
15282 	if ((matrix = gmtapi_retrieve_vector (API, &(M->data), M->type)) == NULL)
15283 		return_null (API, GMT_NOT_A_VALID_TYPE);
15284 	return matrix;
15285 }
15286 
15287 #ifdef FORTRAN_API
GMT_Get_Matrix_(struct GMT_MATRIX * M)15288 void * GMT_Get_Matrix_ (struct GMT_MATRIX *M) {
15289 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15290 	return (GMT_Get_Matrix (GMT_FORTRAN, M));
15291 }
15292 #endif
15293 
GMT_Put_Strings(void * V_API,unsigned int family,void * object,char ** array)15294 int GMT_Put_Strings (void *V_API, unsigned int family, void *object, char **array) {
15295 	/* Hook pointer to the text array in a matrix or vector */
15296 	bool dup = false;
15297 	enum GMT_enum_CPT code;
15298 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15299 	if (object == NULL) return_error (V_API, GMT_PTR_IS_NULL);
15300 	if (array == NULL) return_error (V_API, GMT_PTR_IS_NULL);
15301 	if (family & GMT_IS_DUPLICATE) {	/* Need to duplicate the strings */
15302 		dup = true;
15303 		family -= GMT_IS_DUPLICATE;
15304 	}
15305 	else if (family & GMT_IS_REFERENCE)	/* This is the default action, just remove the mode */
15306 		family -= GMT_IS_REFERENCE;
15307 	if (family & GMT_IS_PALETTE_KEY) {
15308 		family -= GMT_IS_PALETTE_KEY;
15309 		code = GMT_IS_PALETTE_KEY;
15310 	}
15311 	else if (family & GMT_IS_PALETTE_LABEL) {
15312 		family -= GMT_IS_PALETTE_LABEL;
15313 		code = GMT_IS_PALETTE_LABEL;
15314 	}
15315 	if (family == GMT_IS_PALETTE && code == 0) {	/* This is specific to CPTs */
15316 		return_error (V_API, GMT_VALUE_NOT_SET);
15317 	}
15318 	if (!(family == GMT_IS_VECTOR || family == GMT_IS_MATRIX || family == GMT_IS_PALETTE)) return_error (V_API, GMT_NOT_A_VALID_FAMILY);
15319 
15320 	if (family == GMT_IS_VECTOR) {
15321 		struct GMT_VECTOR *V = gmtapi_get_vector_data (object);
15322 		struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V);
15323 		if (dup) {	/* Must duplicate the input array of strings */
15324 			uint64_t k;
15325 			struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
15326 			if ((V->text = gmt_M_memory (API->GMT, NULL, V->n_rows, char **)) == NULL) {
15327 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Put_Strings: Unable to allocate text string array for vector\n");
15328 				return (GMT_MEMORY_ERROR);
15329 			}
15330 			for (k = 0; k < V->n_rows; k++)
15331 				if (array[k]) V->text[k] = strdup (array[k]);
15332 			VH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
15333 		}
15334 		else {	/* By reference */
15335 			V->text = array;
15336 			VH->alloc_mode_text = GMT_ALLOC_EXTERNALLY;
15337 		}
15338 	}
15339 	else if (family == GMT_IS_MATRIX) {
15340 		struct GMT_MATRIX *M = gmtapi_get_matrix_data (object);
15341 		struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M);
15342 		if (dup) {	/* Must duplicate the input array of strings */
15343 			uint64_t k;
15344 			struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
15345 			if ((M->text = gmt_M_memory (API->GMT, NULL, M->n_rows, char **)) == NULL) {
15346 				GMT_Report (API, GMT_MSG_ERROR, "GMT_Put_Strings: Unable to allocate text string array for matrix\n");
15347 				return (GMT_MEMORY_ERROR);
15348 			}
15349 			for (k = 0; k < M->n_rows; k++)
15350 				if (array[k]) M->text[k] = strdup (array[k]);
15351 			MH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
15352 		}
15353 		else {	/* By reference */
15354 			M->text = array;
15355 			MH->alloc_mode_text = GMT_ALLOC_EXTERNALLY;
15356 		}
15357 	}
15358 	else if (family == GMT_IS_PALETTE) {
15359 		unsigned int k, item = (code == GMT_IS_PALETTE_LABEL) ? GMT_CPT_INDEX_LBL : GMT_CPT_INDEX_KEY;
15360 		struct GMT_PALETTE *P = gmtapi_get_palette_data (object);
15361 		struct GMT_PALETTE_HIDDEN *CH = gmt_get_C_hidden (P);
15362 		for (k = 0; k < P->n_colors; k++) {
15363 			if (array[k] == NULL) continue;	/* No string given for this entry */
15364 			if (code == GMT_IS_PALETTE_LABEL) {
15365 				if (dup && P->data[k].label) gmt_M_str_free (P->data[k].label);	/* Free any old entry */
15366 				P->data[k].label = (dup) ? strdup (array[k]) : array[k];
15367 			}
15368 			else if (code == GMT_IS_PALETTE_KEY) {
15369 				if (dup && P->data[k].key) gmt_M_str_free (P->data[k].key);	/* Free any old entry */
15370 				P->data[k].key = (dup) ? strdup (array[k]) : array[k];
15371 			}
15372 		}
15373 		CH->alloc_mode_text[item] = (dup) ? GMT_ALLOC_INTERNALLY : GMT_ALLOC_EXTERNALLY;
15374 	}
15375 	return (GMT_NOERROR);
15376 }
15377 
15378 #ifdef FORTRAN_API
GMT_Put_Strings_(unsigned int * family,void * object,char ** array,int len)15379 int GMT_Put_Strings_ (unsigned int *family, void *object, char **array, int len) {
15380 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15381 	return (GMT_Put_Strings (GMT_FORTRAN, *family, object, array));
15382 }
15383 #endif
15384 
GMT_Get_Strings(void * V_API,unsigned int family,void * object)15385 char ** GMT_Get_Strings (void *V_API, unsigned int family, void *object) {
15386 	/* Return pointer to the text array in a matrix or vector */
15387 	char **array = NULL;
15388 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
15389 	if (object == NULL) return_null (V_API, GMT_PTR_IS_NULL);
15390 	if (!(family == GMT_IS_VECTOR || family == GMT_IS_MATRIX)) return_null (V_API, GMT_NOT_A_VALID_FAMILY);
15391 	if (family == GMT_IS_VECTOR) {
15392 		struct GMT_VECTOR *V = gmtapi_get_vector_data (object);
15393 		array = V->text;
15394 	}
15395 	else if (family == GMT_IS_MATRIX) {
15396 		struct GMT_MATRIX *M = gmtapi_get_matrix_data (object);
15397 		array = M->text;
15398 	}
15399 	if (array == NULL)
15400 		return_null (V_API, GMT_PTR_IS_NULL);
15401 	return (array);
15402 }
15403 
15404 #ifdef FORTRAN_API
GMT_Get_Strings_(unsigned int * family,void * object)15405 char ** GMT_Get_Strings_ (unsigned int *family, void *object) {
15406 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15407 	return (GMT_Get_Strings (GMT_FORTRAN, *family, object));
15408 }
15409 #endif
15410 
15411 #define GMT_NO_SUCH_ENUM -99999
15412 #include "gmt_enum_dict.h"
15413 
GMT_Get_Enum(void * V_API,char * key)15414 int GMT_Get_Enum (void *V_API, char *key) {
15415 	/* Access to GMT enums from environments unable to parse in gmt_resources.h.
15416 	 * Return value of enum or GMT_NO_SUCH_ENUM if not found */
15417 	int lo = 0, hi = GMT_N_API_ENUMS, mid, value;
15418 	gmt_M_unused (V_API);
15419 	if (key == NULL || key[0] == '\0') return GMT_NO_SUCH_ENUM;
15420 	while (lo != hi) {	/* Do a binary search since gmt_api_enums is lexically sorted */
15421 		mid = (lo + hi) / 2;
15422 		value = strcmp (key, gmt_api_enums[mid].name);
15423 		if (value == 0) return gmt_api_enums[mid].value;
15424 		if ((hi-lo) == 1)
15425 			lo = hi = mid;
15426 		else if (value > 0)
15427 			lo = mid;
15428 		else if (value < 0)
15429 			hi = mid;
15430 	}
15431 	return GMT_NO_SUCH_ENUM;
15432 }
15433 
15434 #ifdef FORTRAN_API
GMT_Get_Enum_(char * arg,int len)15435 int GMT_Get_Enum_ (char *arg, int len) {
15436 	return (GMT_Get_Enum (GMT_FORTRAN, arg));
15437 }
15438 #endif
15439 
15440 /* A few more FORTRAN bindings moved from gmt_fft.c: */
15441 
15442 #ifdef FORTRAN_API
GMT_FFT_Wavenumber_(uint64_t * k,unsigned int * mode,void * v_K)15443 double GMT_FFT_Wavenumber_ (uint64_t *k, unsigned int *mode, void *v_K) {
15444 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15445 	return (GMT_FFT_Wavenumber (GMT_FORTRAN, *k, *mode, v_K));
15446 }
15447 #endif
15448 
15449 #ifdef FORTRAN_API
GMT_FFT_1D_(gmt_grdfloat * data,uint64_t * n,int * direction,unsigned int * mode)15450 int GMT_FFT_1D_ (gmt_grdfloat *data, uint64_t *n, int *direction, unsigned int *mode) {
15451 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15452 	return (GMT_FFT_1D (GMT_FORTRAN, data, *n, *direction, *mode));
15453 }
15454 #endif
15455 
15456 #ifdef FORTRAN_API
GMT_FFT_2D_(gmt_grdfloat * data,unsigned int * n_columns,unsigned int * n_rows,int * direction,unsigned int * mode)15457 int GMT_FFT_2D_ (gmt_grdfloat *data, unsigned int *n_columns, unsigned int *n_rows, int *direction, unsigned int *mode) {
15458 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15459 	return (GMT_FFT_2D (GMT_FORTRAN, data, *n_columns, *n_rows, *direction, *mode));
15460 }
15461 #endif
15462 
GMT_Get_Family(void * V_API,unsigned int direction,struct GMT_OPTION * head)15463 int GMT_Get_Family (void *V_API, unsigned int direction, struct GMT_OPTION *head) {
15464 	/* Scan the registered module input|output resources to learn what their family is.
15465 	 * direction:	Either GMT_IN or GMT_OUT
15466 	 * head:	Head of the list of module options
15467 	 *
15468 	 * Returns:	The family value (GMT_IS_DATASET|CPT|GRID|IMAGE|PS) or GMT_NOTSET if not known
15469 	 */
15470 	struct GMTAPI_CTRL *API = NULL;
15471 	struct GMT_OPTION *current = NULL;
15472 	int item, object_ID, family = GMT_NOTSET;
15473 	//int flag = (direction == GMT_IN) ? GMTAPI_MODULE_INPUT : GMT_NOTSET;
15474 	unsigned int n_kinds = 0, k, counter[GMT_N_FAMILIES];
15475 	char desired_option = (direction == GMT_IN) ? GMT_OPT_INFILE : GMT_OPT_OUTFILE;
15476 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15477 	API = gmtapi_get_api_ptr (V_API);
15478 	gmt_M_memset (counter, GMT_N_FAMILIES, unsigned int);	/* Initialize counter */
15479 	API->error = GMT_NOERROR;	/* Reset in case it has some previous error */
15480 
15481 	for (current = head; current; current = current->next) {		/* Loop over the list and look for input files */
15482 		if (current->option != desired_option) continue;				/* Not a module resource argument */
15483 		if ((object_ID = gmtapi_decode_id (current->arg)) == GMT_NOTSET) continue;	/* Not a registered resource */
15484 		//if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, direction, flag)) == GMT_NOTSET) continue;	/* Not the right attributes */
15485 		if ((item = gmtlib_validate_id (API, GMT_NOTSET, object_ID, direction, GMT_NOTSET)) == GMT_NOTSET) continue;	/* Not the right attributes */
15486 		counter[(API->object[item]->family)]++;	/* Update counts of this family */
15487 	}
15488 	for (k = 0; k < GMT_N_FAMILIES; k++) {	/* Determine which family we found, if any */
15489 		if (counter[k]) n_kinds++, family = k;
15490 	}
15491 	if (n_kinds != 1) {	/* Could not determine family */
15492 		family = GMT_NOTSET;
15493 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Get_Family: Could not determine family\n");
15494 	}
15495 	else	/* Found a unique family */
15496 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Get_Family: Determined family to be %s\n", GMT_family[family]);
15497 	return (family);
15498 }
15499 
15500 #ifdef FORTRAN_API
GMT_Get_Family_(unsigned int * direction,struct GMT_OPTION * head)15501 int GMT_Get_Family_ (unsigned int *direction, struct GMT_OPTION *head) {
15502 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15503 	return (GMT_Get_Family (GMT_FORTRAN, *direction, head));
15504 }
15505 #endif
15506 
GMT_Set_AllocMode(void * V_API,unsigned int family,void * object)15507 int GMT_Set_AllocMode (void *V_API, unsigned int family, void *object) {
15508 	int error = GMT_NOERROR;
15509 	uint64_t col;
15510 	struct GMT_VECTOR      *V = NULL;
15511 	struct GMT_DATASET_HIDDEN     *DH = NULL;
15512 	struct GMT_PALETTE_HIDDEN     *CH = NULL;
15513 	struct GMT_POSTSCRIPT_HIDDEN  *PH = NULL;
15514 	struct GMT_CUBE_HIDDEN    *UH = NULL;
15515 	struct GMT_VECTOR_HIDDEN      *VH = NULL;
15516 	struct GMT_MATRIX_HIDDEN      *MH = NULL;
15517 	struct GMT_GRID_HIDDEN        *GH = NULL;
15518 	struct GMT_IMAGE_HIDDEN       *IH = NULL;
15519 
15520 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15521 	if (object == NULL) return_error (V_API, GMT_PTR_IS_NULL);
15522 
15523 	switch (family) {	/* grid, image, or matrix */
15524 		case GMT_IS_GRID:	/* GMT grid */
15525 			GH = gmt_get_G_hidden (gmtapi_get_grid_data (object));
15526 			GH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15527 			break;
15528 		case GMT_IS_IMAGE:	/* GMT image */
15529 			IH = gmt_get_I_hidden (gmtapi_get_image_data (object));
15530 			IH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15531 			break;
15532 		case GMT_IS_DATASET:	/* GMT dataset */
15533 			DH = gmt_get_DD_hidden (gmtapi_get_dataset_data (object));
15534 			DH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15535 			break;
15536 		case GMT_IS_PALETTE:	/* GMT CPT */
15537 			CH = gmt_get_C_hidden (gmtapi_get_palette_data (object));
15538 			CH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15539 			break;
15540 		case GMT_IS_CUBE:	/* GMT cube */
15541 			UH = gmt_get_U_hidden (gmtapi_get_cube_data (object));
15542 			UH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15543 			break;
15544 		case GMT_IS_POSTSCRIPT:		/* GMT PS */
15545 			PH = gmt_get_P_hidden (gmtapi_get_postscript_data (object));
15546 			PH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15547 			break;
15548 		case GMT_IS_VECTOR:	/* GMT Vector */
15549 			V = gmtapi_get_vector_data (object);
15550 			VH = gmt_get_V_hidden (V);
15551 			for (col = 0; col < V->n_columns; col++)
15552 				VH->alloc_mode[col] = GMT_ALLOC_EXTERNALLY;
15553 			break;
15554 		case GMT_IS_MATRIX:	/* GMT Matrix */
15555 			MH = gmt_get_M_hidden (gmtapi_get_matrix_data (object));
15556 			MH->alloc_mode = GMT_ALLOC_EXTERNALLY;
15557 			break;
15558 		default:
15559 			error = GMT_NOT_A_VALID_FAMILY;
15560 			break;
15561 	}
15562 	return_error (V_API, error);
15563 }
15564 
15565 #ifdef FORTRAN_API
GMT_Set_AllocMode_(unsigned int * family,void * object)15566 int GMT_Set_AllocMode_ (unsigned int *family, void *object) {
15567 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15568 	return (GMT_Set_AllocMode (GMT_FORTRAN, *family, object));
15569 }
15570 #endif
15571 
15572 /*! . */
GMT_Get_FilePath(void * V_API,unsigned int family,unsigned int direction,unsigned int mode,char ** file_ptr)15573 int GMT_Get_FilePath (void *V_API, unsigned int family, unsigned int direction, unsigned int mode, char **file_ptr) {
15574 	/* Replace file with its full path if that file exists, else return an error.
15575 	 * If (mode & GMT_FILE_REMOTE) then we try to download any remote files
15576 	 * given but not yet cached locally), and if the downloaded file is readable then
15577 	 * we update file_ptr with the local path, otherwise return an error.
15578 	 * If (mode & GMT_FILE_CHECK) then we only return error code and don't update file_ptr.
15579 	 * The explicit mode for only examining local files is GMT_FILE_LOCAL [0].
15580 	 *
15581 	 * Filename complications:  Both grid, image and CPT filenames may have modifiers or
15582 	 * format identifiers appended to their names.  Thus, as given, file may name be a valid
15583 	 * filename until we have chopped off these strings.  Here is a summary of what GMT allows:
15584 	 *
15585 	 * imagefile[=gd[+b<band>]]
15586 	 * grdfile[=<id>][+o<offset>][+n<invalid>][+s<scale>][+u|U<unit>]
15587 	 * cptfile[+h<hinge>][+u|U<unit>]
15588 	 *   Note: Some modules also allows cptfile[+h<hinge>][+u|U<unit>][i<dz>] but the +d
15589 	 *   modifier is processed and removed in the module (grd-image/view/vector/2kml).
15590 	 *
15591 	 * gridfiles may also have strings to select specific layers of nigher-dimension netCDFfiles, using
15592 	 * grdfile?<variables>[layer]|(value).
15593 	 *
15594 	 * URL queries also use ? as in http://<address>?<par1>=<val1>...
15595 	 * Remote grids may also have format specification and modifiers like local grids:
15596 	 * https://<address>/grdfile[=<id>][+o<offset>][+n<invalid>][+s<scale>][+u|U<unit>]
15597 	 */
15598 
15599 	char remote_path[PATH_MAX] = {""}, local_path[PATH_MAX] = {""}, was = '\0', *file = NULL, *c = NULL, *f = NULL;
15600 	struct GMTAPI_CTRL *API = NULL;
15601 
15602 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15603 	if (!(direction == GMT_IN || direction == GMT_OUT)) return_error (API, GMT_NOT_A_VALID_DIRECTION);
15604 	if (!gmtapi_valid_input_family (family)) return_error (API, GMT_NOT_A_VALID_FAMILY);
15605 	if (mode > (GMT_FILE_CHECK+GMT_FILE_REMOTE)) return_error (API, GMT_NOT_A_VALID_MODE);
15606 	API = gmtapi_get_api_ptr (V_API);
15607 	API->error = GMT_NOERROR;
15608 
15609 	if (file_ptr == NULL || (file = *file_ptr) == NULL || file[0] == '\0') {
15610 		GMT_Report (API, GMT_MSG_ERROR, "No filename provided\n");
15611 		return_error (V_API, GMT_ARG_IS_NULL);
15612 	}
15613 
15614 	if (direction == GMT_OUT) return GMT_NOERROR;
15615 
15616 	if (gmt_M_file_is_memory (file)) return GMT_NOERROR;	/* Memory files are always fine */
15617 
15618 	if (gmtlib_found_url_for_gdal (file)) {	/* Special URLs for grids to be read via GDAL */
15619 		return GMT_NOERROR;
15620 	}
15621 
15622 	if ((mode & GMT_FILE_CHECK) == 0) gmt_set_unspecified_remote_registration (API, file_ptr);	/* Complete remote filenames without registration information */
15623 
15624 	gmt_filename_get (file);	/* Replace any ASCII 29 with spaces (if filename had spaces they may now be ASCII 29) */
15625 
15626 	switch (family) {
15627 		case GMT_IS_GRID:
15628 			if (!gmt_file_is_tiled_list (API, file, NULL, NULL, NULL) && (c = strchr (file, '='))) {	/* Got filename=id[+modifiers] */
15629 				/* Nothing*/
15630 			}
15631 			else if (gmt_M_file_is_netcdf (file))	/* Meaning it specifies a layer etc via ?<args> */
15632 				c = strchr (file, '?');
15633 			else {	/* Check for possible file modifiers */
15634 				if ((f = gmt_strrstr (file, ".grd")) || (f = gmt_strrstr (file, ".nc")))
15635 					c = gmtlib_last_valid_file_modifier (API, f, GMT_GRIDFILE_MODIFIERS);
15636 				else
15637 					c = gmtlib_last_valid_file_modifier (API, file, GMT_GRIDFILE_MODIFIERS);
15638 			}
15639 			break;
15640 		case GMT_IS_IMAGE:
15641 			c = strstr (file, "=gd");	/* Got image=gd[+modifiers] */
15642 			break;
15643 		case GMT_IS_PALETTE:
15644 			if ((f = gmt_strrstr (file, GMT_CPT_EXTENSION)))
15645 				c = gmtlib_last_valid_file_modifier (API, f, GMT_CPTFILE_MODIFIERS);
15646 			else
15647 				c = gmtlib_last_valid_file_modifier (API, file, GMT_CPTFILE_MODIFIERS);
15648 			break;
15649 		default:	/* No checks for the other families */
15650 			break;
15651 	}
15652 
15653 	if (c && !gmt_M_file_is_url (file)) {	/* Other that queries, we don't want to pass modifiers when copying files */
15654 		was = c[0];
15655 		c[0] = '\0';
15656 	}
15657 
15658 	if (gmt_set_remote_and_local_filenames (API->GMT, file, local_path, remote_path, GMT_AUTO_DIR)) {
15659 		GMT_Report (API, GMT_MSG_ERROR, "Cannot find file %s\n", file);
15660 		return_error (V_API, GMT_FILE_NOT_FOUND);
15661 	}
15662 
15663 	/* Here we have found a local file or we must download from server first */
15664 
15665 	if (remote_path[0]) {	/* Remote file given but not yet stored locally */
15666 		if (mode & GMT_FILE_REMOTE) {
15667 			GMT_Report (API, GMT_MSG_DEBUG, "Download %s to %s\n", remote_path, local_path);
15668 			if (gmt_download_file (API->GMT, file, remote_path, local_path, true)) {
15669 				GMT_Report (API, GMT_MSG_ERROR, "Unable to obtain remote file %s\n", file);
15670 				return_error (V_API, GMT_FILE_NOT_FOUND);
15671 			}
15672 		}
15673 		else {
15674 			GMT_Report (API, GMT_MSG_DEBUG, "Given a remote file %s but mode is not GMT_ADD_REMOTE\n", file);
15675 			return_error (V_API, GMT_FILE_NOT_FOUND);
15676 		}
15677 	}
15678 
15679 	if (c) c[0] = was; /* Restore what we did*/
15680 	if ((mode & GMT_FILE_CHECK) == 0) {	/* Pass the local path back */
15681 		GMT_Report (API, GMT_MSG_DEBUG, "Replace file %s with %s\n", file, local_path);
15682 		if (c) /* Also append any file directives via modifiers */
15683 			strncat (local_path, c, PATH_MAX-1);
15684 		gmt_M_str_free (*file_ptr);
15685 		*file_ptr = strdup (local_path);
15686 	}
15687 
15688 	return GMT_NOERROR;
15689 }
15690 
15691 #ifdef FORTRAN_API
GMT_Get_FilePath_(unsigned int * family,unsigned int * direction,unsigned int * mode,char ** file,int len)15692 int GMT_Get_FilePath_ (unsigned int *family, unsigned int *direction, unsigned int *mode, char **file, int len) {
15693 	/* Fortran version: We pass the global GMT_FORTRAN structure */
15694 	return (GMT_Get_FilePath (GMT_FORTRAN, *family, *direction, *mode, file));
15695 }
15696 #endif
15697 
GMT_Extract_Region(void * V_API,char * file,double wesn[])15698 int GMT_Extract_Region (void *V_API, char *file, double wesn[]) {
15699 	FILE *fp = NULL;
15700 	bool found = false;
15701 	struct GMTAPI_CTRL *API = gmtapi_get_api_ptr (V_API);
15702 	char xx1[GMT_LEN64] = {""}, xx2[GMT_LEN64] = {""}, yy1[GMT_LEN64] = {""}, yy2[GMT_LEN64] = {""}, line[GMT_LEN256] = {""};
15703 
15704 	if (V_API == NULL) return_error (V_API, GMT_NOT_A_SESSION);
15705 	if (wesn == NULL) return_error (V_API, GMT_PTR_IS_NULL);
15706 
15707 	if (API->GMT->current.setting.run_mode == GMT_MODERN) {	/* Modern mode, file must be NULL */
15708 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Extract_Region: Modern mode\n");
15709 		if (file) {	/* Cannot specify file in modern mode */
15710 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Extract_Region: Cannot give a PostScript filename in modern mode\n");
15711 			return_error (V_API, GMT_FILE_NOT_FOUND);
15712 		}
15713 		if (gmt_set_psfilename (API->GMT) == 0) {	/* Get hidden file name for current PS */
15714 			GMT_Report (API, GMT_MSG_ERROR, "No hidden PS file found\n");
15715 			return_error (V_API, GMT_FILE_NOT_FOUND);
15716 		}
15717 		GMT_Report (API, GMT_MSG_DEBUG, "Hidden PS file %s found\n", API->GMT->current.ps.filename);
15718 		if ((fp = fopen (API->GMT->current.ps.filename, "r")) == NULL) {
15719 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Extract_Region: Failed to find/open current PS file %s\n", API->GMT->current.ps.filename);
15720 			return_error (V_API, GMT_FILE_NOT_FOUND);
15721 		}
15722 	}
15723 	else {	/* Classic mode, file must be given */
15724 		GMT_Report (API, GMT_MSG_DEBUG, "GMT_Extract_Region: Classic mode\n");
15725 		if (file == NULL) {	/* Must specify file in classic mode */
15726 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Extract_Region: Filename required in classic mode\n");
15727 			return_error (V_API, GMT_FILE_NOT_FOUND);
15728 		}
15729 		if ((fp = fopen (file, "r")) == NULL) {
15730 			GMT_Report (API, GMT_MSG_ERROR, "GMT_Extract_Region: Failed to find/open %s\n", file);
15731 			return_error (V_API, GMT_FILE_NOT_FOUND);
15732 		}
15733 	}
15734 
15735 	/* We expect GMT_Extract_Region to be applied to GMT-produced PS files so we know they are clean records readable with fgets */
15736 
15737 	while (!found && gmt_fgets (API->GMT, line, GMT_LEN256, fp)) {
15738 		if (!strncmp (&line[2], "PROJ", 4)) {	/* Search for the PROJ tag in the ps file */
15739 			sscanf (&line[8], "%*s %s %s %s %s", xx1, xx2, yy1, yy2);
15740 			wesn[XLO] = atof (xx1);		wesn[XHI] = atof (xx2);
15741 			wesn[YLO] = atof (yy1);		wesn[YHI] = atof (yy2);
15742 			if (wesn[XLO] > 180.0 && wesn[XHI] > 180.0) {
15743 				wesn[XLO] -= 360.0;
15744 				wesn[XHI] -= 360.0;
15745 			}
15746 			found = true;
15747 		}
15748 	}
15749 	fclose (fp);
15750 	if (!found) {
15751 		GMT_Report (API, GMT_MSG_ERROR, "GMT_Extract_Region: Failed to find the PROJ tag with the region\n");
15752 		return_error (V_API, GMT_VALUE_NOT_SET);
15753 	}
15754 	return_error (V_API, GMT_NOERROR);
15755 }
15756 
GMT_Get_Version(void * API,unsigned int * major,unsigned int * minor,unsigned int * patch)15757 float GMT_Get_Version (void *API, unsigned int *major, unsigned int *minor, unsigned int *patch) {
15758 	/* Return the current lib version as a float, e.g. 6.0, and optionally its constituents.
15759 	 * Either one or all of in *major, *minor, *patch args can be NULL. If they are not, one
15760 	 * gets the corresponding version component. */
15761 	int major_loc, minor_loc, patch_loc;
15762 	gmt_M_unused(API);
15763 
15764 	major_loc = GMT_PACKAGE_VERSION_MAJOR;
15765 	minor_loc = GMT_PACKAGE_VERSION_MINOR;
15766 	patch_loc = GMT_PACKAGE_VERSION_PATCH;
15767 	if (major) *major = (unsigned int)major_loc;
15768 	if (minor) *minor = (unsigned int)minor_loc;
15769 	if (patch) *patch = (unsigned int)patch_loc;
15770 	return major_loc + (float)minor_loc / 10;
15771 }
15772 
15773 /* Help functions specific to the Julia/GMT API.  They are not documented */
15774 
gmtlib_blind_change_struct(void * V_API,void * ptr,void * what,char * type,size_t off)15775 EXTERN_MSC int gmtlib_blind_change_struct(void *V_API, void *ptr, void *what, char *type, size_t off) {
15776 	/* This is a magic backdoor to change static members of API structures that had to be declared as
15777 	   immutables types in Julia and therefore impossible to change from within Julia.
15778 	   *ptr  -> structure pointer whose member identified by the offset 'off' is to be changed.
15779 	   *what -> pointer to the new value of the struct member that will be changed.
15780 	   *type -> string with the type description, using the Julia types names. e.g. 'UInt32' or 'Float64'
15781 	   The offset value 'off' is that obtained with the Julia's fieldoffsets() function, which is
15782 	   equivalent to the 'offsetof()' C macro.
15783 	*/
15784 	if (!strcmp(type, "Int32"))
15785 		*(int *)((char *)ptr + off) = *(int *)what;
15786 	else if (!strcmp(type, "UInt32"))
15787 		*(unsigned int *)((char *)ptr + off) = *(unsigned int *)what;
15788 	else if (!strcmp(type, "Int64"))
15789 		*(int64_t *)((char *)ptr + off) = *(int64_t *)what;
15790 	else if (!strcmp(type, "UInt64"))
15791 		*(uint64_t *)((char *)ptr + off) = *(uint64_t *)what;
15792 	else if (!strcmp(type, "Float32"))
15793 		*(float *)((char *)ptr + off) = *(float *)what;
15794 	else if (!strcmp(type, "Float64"))
15795 		*(double *)((char *)ptr + off) = *(double *)what;
15796 	else if (!strcmp(type, "Int16"))
15797 		*(signed short *)((char *)ptr + off) = *(signed short *)what;
15798 	else if (!strcmp(type, "UInt16"))
15799 		*(unsigned short *)((char *)ptr + off) = *(unsigned short *)what;
15800 	else if (!strcmp(type, "UInt8"))
15801 		*(unsigned char *)((char *)ptr + off) = *(unsigned char *)what;
15802 	else if (!strcmp(type, "Int8"))
15803 		*(char *)((char *)ptr + off) = *(char *)what;
15804 	else {
15805 		GMT_Report(V_API, GMT_MSG_ERROR, "GMT/Julia Backdoor: Type (%s) not accepted. Possibly a pointer to something.\n", type);
15806 		return_error (V_API, GMT_NOT_A_VALID_PARAMETER);
15807 	}
15808 	return GMT_NOERROR;
15809 }
15810 
gmtlib_get_ctrl(void * V_API)15811 EXTERN_MSC void * gmtlib_get_ctrl (void *V_API) {
15812 	/* For external environments that need to get the GMT pointer for calling
15813 	 * lower-level GMT library functions that expects the GMT pointer */
15814 	struct GMTAPI_CTRL *API = NULL;
15815 
15816 	if (V_API == NULL) return_null (V_API, GMT_NOT_A_SESSION);
15817 	API = gmtapi_get_api_ptr (V_API);
15818 	return API->GMT;	/* Pass back the GMT ctrl pointer as void pointer */
15819 }
15820 
gmt_eliminate_duplicates(struct GMTAPI_CTRL * API,struct GMT_DATASET * D,uint64_t cols[],uint64_t ncols,bool text)15821 int64_t gmt_eliminate_duplicates (struct GMTAPI_CTRL *API, struct GMT_DATASET *D, uint64_t cols[], uint64_t ncols, bool text) {
15822 	/* Scan dataset per segment and eliminate any duplicate records as identified by having no change in all the specified cols.
15823 	 * If no change then we skip the duplicate records.  No segment will be eliminated since first record always survives.
15824 	 * Including the trailing text in the comparison is optional and requires setting of the text flag to true. */
15825 	bool may_be_duplicate;	/* Initially true, gets set to false if we fail any of the one or more tests */
15826 	uint64_t tbl, seg, row, last_row, k, n_dup_seg, n_dup = 0;
15827 	int64_t n_skip;	/* Number of consecutive duplicate rows */
15828 	unsigned int mode;
15829 	struct GMT_DATASEGMENT *S = NULL;
15830 
15831 	if (ncols == 0 || cols == NULL) {
15832 		gmtlib_report_error (API, GMT_N_COLS_NOT_SET);
15833 		return -GMT_N_COLS_NOT_SET;
15834 	}
15835 	for (k = 0; k < ncols; k++) if (cols[k] >= D->n_columns) {
15836 		gmtlib_report_error (API, GMT_DIM_TOO_LARGE);
15837 		return -GMT_DIM_TOO_LARGE;
15838 	}
15839 
15840 	for (tbl = 0; tbl < D->n_tables; tbl++) {	/* Examine each table */
15841 		for (seg = 0; seg < D->table[tbl]->n_segments; seg++) {	/* Examine each segment */
15842 			S = D->table[tbl]->segment[seg];	/* Current segment shorthand */
15843 			mode = (S->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
15844 			last_row = 0;	/* Always keep the first row of any segment */
15845 			n_dup_seg = 0;	/* None yet found in this segment */
15846 			row = 0;
15847 			while (row < (S->n_rows-1)) {	/* Since we increase row inside we must stop this loop at one less */
15848 				n_skip = -1;	/* Since incremented before the test */
15849 				do {	/* Check if this row is same as last, for given cols */
15850 					row++;	/* Advance to next record */
15851 					n_skip++;	/* So now it is 0 the very first time */
15852 					may_be_duplicate = true;	/* See if we can fail a test */
15853 					for (k = 0; may_be_duplicate && k < ncols; k++) {	/* Check the columns indicated as long as the records may be duplicates */
15854 						if (!doubleAlmostEqualZero (S->data[cols[k]][row], S->data[cols[k]][last_row]))
15855 							may_be_duplicate = false;	/* Failed to match across these two rows for this column */
15856 					}
15857 					if (may_be_duplicate && text && mode && S->text[row] && S->text[last_row] && strcmp (S->text[row], S->text[last_row]))
15858 						may_be_duplicate = false;	/* Failed to match across these two rows for trailing text */
15859 				} while (may_be_duplicate && row < S->n_rows);
15860 
15861 				if (n_skip) {	/* Must move up all memory and bury this repeat record */
15862 					for (k = 0; k < S->n_columns; k++)
15863 						memmove (&S->data[k][row-n_skip], &S->data[k][row], (S->n_rows-row)*sizeof(double));
15864 					if (mode & GMT_WITH_STRINGS)
15865 						memmove (&S->text[row-n_skip], &S->text[row], (S->n_rows-row)*sizeof(char *));
15866 					S->n_rows -= n_skip;	/* Since we lost records */
15867 					n_dup_seg += n_skip;
15868 					row -= n_skip;
15869 				}
15870 				last_row++;
15871 			}
15872 			if (n_dup_seg) {	/* Found duplicates, need  to reallocate arrays */
15873 				GMT_Report (API, GMT_MSG_DEBUG, "Removed %" PRIu64 " duplicate records from table %" PRIu64", segment %" PRIu64"\n", n_dup_seg, tbl, seg);
15874 				if (gmt_alloc_segment (API->GMT, S, S->n_rows, S->n_columns, mode, false))
15875 					return -GMT_RUNTIME_ERROR;	/* Failure of some sort */
15876 				n_dup += n_dup_seg;
15877 			}
15878 		}
15879 	}
15880 	if (n_dup) {
15881 		gmt_set_dataset_minmax (API->GMT, D);	/* Update min/max for each column */
15882 		GMT_Report (API, GMT_MSG_DEBUG, "Removed %" PRIu64 " duplicate records from the entire dataset\n", n_dup);
15883 	}
15884 
15885 	return (n_dup);
15886 }
15887