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  * Table input/output in GMT can be either ASCII, binary, or netCDF COARDS files
19  * and may consist of single or multiple segments.  These files are accessed
20  * via the GMT->current.io.input function pointer which either points to the
21  * ASCII read (gmtio_ascii_input), the binary read (gmtio_bin_input), or the
22  * netCDF read (gmtio_nc_input) functions, depending on the -bi setting.
23  * Similarly, writing of such tables are done via the GMT->current.io.output
24  * function pointer which is set to either gmtio_ascii_output, gmtio_bin_output,
25  * or GMT_nc_output [not implemented yet].
26  * For special processing of z-data by xyz2grd & grd2xyz we also use the
27  * GMT->current.io.input/output functions but these are reset in those two
28  * programs to the corresponding read/write functions pointed to by the
29  * GMT_Z_IO member ->read_item or ->write_item as these can handle additional
30  * binary data types such as char, int, short etc.
31  * The structure GMT_IO holds parameters that are used during the reading
32  * and processing of ASCII tables.  For compliance with a wide variety of
33  * binary data formats for grids and their internal nesting the GMT_Z_IO
34  * structure and associated functions are used (in xyz2grd and grd2xyz)
35  *
36  * The following functions are here:
37  *
38  *  gmtlib_getuserpath  Get pathname of file in "user directories" (GMT->session.TMPDIR, CWD, HOME, GMT->session.USERDIR, GMT->session.CACHEDIR)
39  *  gmt_getdatapath     Get pathname of file in "data directories" (CWD, GMT_{USER,CACHE,DATA,GRID,IMG}DIR)
40  *  gmt_getsharepath    Get pathname of file in "share directories" (CWD, GMT->session.USERDIR, GMT->session.SHAREDIR tree)
41  *  gmt_fopen:          Open a file using gmt_getdatapath
42  *  gmt_fclose:         Close a file
43  *  gmt_strncpy:        Copy n characters of a char string without triggering a Coverity issue
44  *  gmtlib_io_init:     Init GMT_IO structure
45  *  gmt_write_segmentheader:   Write header record for multisegment files
46  *  gmt_scanf:          Robust scanf function with optional dd:mm:ss conversion
47  *  gmt_set_z_io:       Set GMT_Z_IO structure based on -Z
48  *  gmt_check_z_io:     Fill in implied missing row/column
49  * gmt_set_geographic
50  * gmt_set_cartesian
51  * gmt_input_col_is_nan_proxy
52  * gmtlib_write_tableheader
53  * gmt_fgets
54  * gmt_skip_xy_duplicates
55  * gmtlib_gap_detected
56  * gmt_ascii_output_col
57  * gmtlib_set_gap
58  * gmt_set_segmentheader
59  * gmt_set_tableheader
60  * gmt_z_output
61  * gmt_z_input
62  * gmtlib_process_binary_input
63  * gmtlib_nc_get_att_text
64  * gmtlib_io_banner
65  * gmt_get_cols
66  * gmt_set_cols
67  * gmt_access
68  * gmt_get_ogr_id
69  * gmtlib_ascii_textinput
70  * gmt_is_a_blank_line
71  * gmtio_bin_colselect
72  * gmt_skip_output
73  * gmtlib_set_bin_io
74  * gmt_format_abstime_output
75  * gmt_ascii_format_col
76  * gmt_disable_bghio_opts
77  * gmt_reenable_bghio_opts
78  * gmt_lon_range_adjust
79  * gmt_quad_reset
80  * gmt_quad_init
81  * gmt_quad_add
82  * gmt_quad_finalize
83  * gmtlib_get_lon_minmax
84  * gmt_eliminate_lon_jumps
85  * gmtlib_determine_pole
86  * gmt_set_seg_polar
87  * gmtlib_geo_to_dms
88  * gmt_add_to_record
89  * gmtlib_io_binary_header
90  * gmt_parse_z_io
91  * gmt_get_io_type
92  * gmtlib_get_io_ptr
93  * gmt_init_z_io
94  * gmtlib_clock_C_format
95  * gmtlib_date_C_format
96  * gmtlib_geo_C_format
97  * gmtlib_plot_C_format
98  * gmt_scanf_arg
99  * gmtlib_read_texttable
100  * gmt_set_seg_minmax
101  * gmt_set_tbl_minmax
102  * gmt_set_dataset_minmax
103  * gmt_parse_segment_header
104  * gmt_extract_label
105  * gmt_parse_segment_item
106  * gmt_duplicate_ogr_seg
107  * gmtlib_write_newheaders
108  * gmt_set_xycolnames
109  * gmt_adjust_dataset
110  * gmt_alloc_segment
111  * gmtlib_assign_segment
112  * gmtlib_assign_vector
113  * gmt_create_table
114  * gmtlib_create_dataset
115  * gmtlib_read_table
116  * gmt_duplicate_segment
117  * gmt_alloc_dataset
118  * gmt_free_segment
119  * gmt_free_table
120  * gmtlib_free_dataset_ptr
121  * gmt_free_dataset
122  * gmtlib_create_image
123  * gmtlib_duplicate_image
124  * gmtlib_free_image_ptr
125  * gmtlib_free_image
126  * gmt_create_vector
127  * gmtlib_alloc_univector
128  * gmtlib_free_vector_ptr
129  * gmt_free_vector
130  * gmtlib_create_matrix
131  * gmtlib_duplicate_matrix
132  * gmtlib_free_matrix_ptr
133  * gmtlib_free_matrix
134  * gmt_not_numeric
135  * gmt_conv_intext2dbl
136  * gmtlib_ogr_get_type
137  * gmtlib_free_ogr
138  * gmtlib_duplicate_ogr
139  * gmt_get_aspatial_value
140  * gmt_load_aspatial_string
141  * gmtlib_get_dir_list
142  * gmtlib_free_dir_list
143  *
144  * Author:  Paul Wessel
145  * Date:    1-JAN-2010
146  * Version: 5
147  * Now 64-bit enabled.
148  */
149 
150 /*!
151  * \file gmt_io.c
152  * \brief gmt_io.c Table input/output in GMT
153  */
154 
155 #include "gmt_dev.h"
156 #include "gmt_internals.h"
157 #include "gmt_common_byteswap.h"
158 
159 #ifdef HAVE_DIRENT_H_
160 #	include <dirent.h>
161 #endif
162 
163 #ifdef HAVE_SYS_DIR_H_
164 #	include <sys/dir.h>
165 #endif
166 
167 #ifndef DT_DIR
168 #	define DT_DIR 4
169 #endif
170 
171 #ifdef _WIN32
172 #include <windows.h>
173 #endif
174 
175 #define return_null(GMT,err) { GMT->parent->error = err; return (NULL);}
176 
177 static const char *GMT_type[GMT_N_TYPES] = {"byte", "byte", "integer", "integer", "integer", "integer",
178                                             "integer", "integer", "double", "double", "string", "datetime"};
179 static const char *GMT_coltype_name[] = {"String", "Number", "Latitude", "Longitude", "Geographic", "RelTime", "AbsTime", "Duration", "Dimension", "Length", "Azimuth", "Angle", "String"};
180 
181 /* Byteswap widths used with gmt_byteswap_file */
182 typedef enum {
183 	Int16len = 2,
184 	Int32len = 4,
185 	Int64len = 8
186 } SwapWidth;
187 
188 /* These functions are defined and used below but not in any *.h file so we repeat them here */
189 int gmt_get_ogr_id (struct GMT_OGR *G, char *name);
190 void gmt_format_abstime_output (struct GMT_CTRL *GMT, double dt, char *text);
191 
192 /* Local functions */
193 
194 /*! Returns true if record is NaN NaN [NaN NaN] etc */
gmtio_is_a_NaN_line(struct GMT_CTRL * GMT,char * line)195 GMT_LOCAL bool gmtio_is_a_NaN_line (struct GMT_CTRL *GMT, char *line) {
196 	unsigned int pos = 0;
197 	char p[GMT_LEN256] = {""};
198 
199 	while ((gmt_strtok (line, GMT->current.io.scan_separators, &pos, p))) {
200 		gmt_str_tolower (p);
201 		if (strncmp (p, "nan", 3U)) return (false);
202 	}
203 	return (true);
204 }
205 
206 /*! . */
gmtio_is_segment_header(struct GMT_CTRL * GMT,char * line)207 GMT_LOCAL unsigned int gmtio_is_segment_header (struct GMT_CTRL *GMT, char *line) {
208 	/* Returns 1 if this record is a GMT segment header;
209 	 * Returns 2 if this record is a segment breaker;
210 	 * Otherwise returns 0 */
211 	if (GMT->current.setting.io_blankline[GMT_IN] && gmt_is_a_blank_line (line)) return (2);	/* Treat blank line as segment break */
212 	if (GMT->current.setting.io_nanline[GMT_IN] && gmtio_is_a_NaN_line (GMT, line)) return (2);		/* Treat NaN-records as segment break */
213 	if (line[0] == GMT->current.setting.io_seg_marker[GMT_IN]) return (1);	/* Got a regular GMT segment header */
214 	return (0);	/* Not a segment header */
215 }
216 
217 
218 /*! . */
gmtio_outside_in_row_range(struct GMT_CTRL * GMT,int64_t row)219 static inline bool gmtio_outside_in_row_range (struct GMT_CTRL *GMT, int64_t row) {
220 	/* Returns true of this row should be skipped according to -qi[~]<rows>,...[+a|f|s] */
221 	bool pass;
222 	if (GMT->common.q.mode != GMT_RANGE_ROW_IN) return false;	/* -qi<rows> not active */
223 	pass = GMT->common.q.inverse[GMT_IN];
224 	for (unsigned int k = 0; k < GMT->current.io.n_row_ranges[GMT_IN]; k++) {
225 		if (row >= GMT->current.io.row_range[GMT_IN][k].first && row <= GMT->current.io.row_range[GMT_IN][k].last) {	/* row is inside this range */
226 			if (GMT->current.io.row_range[GMT_IN][k].inc == 1) return pass;	/* Return if we want to use this row or not */
227 			if ((row - GMT->current.io.row_range[GMT_IN][k].first) % GMT->current.io.row_range[GMT_IN][k].inc == 0) return pass;	/* Hit one of the steps */
228 		}
229 	}
230 	return !pass;
231 }
232 
233 /*! . */
gmtio_outside_out_row_range(struct GMT_CTRL * GMT,int64_t row)234 static inline bool gmtio_outside_out_row_range (struct GMT_CTRL *GMT, int64_t row) {
235 	/* Returns true if this row should be skipped according to -qo[~]<rows>,...[+a|s] */
236 	bool pass;
237 	if (GMT->common.q.mode != GMT_RANGE_ROW_OUT) return false;	/* -qo<rows> not active */
238 	pass = GMT->common.q.inverse[GMT_OUT];
239 	for (unsigned int k = 0; k < GMT->current.io.n_row_ranges[GMT_OUT]; k++) {
240 		if (row >= GMT->current.io.row_range[GMT_OUT][k].first && row <= GMT->current.io.row_range[GMT_OUT][k].last) {	/* row is inside this range */
241 			if (GMT->current.io.row_range[GMT_OUT][k].inc == 1) return pass;	/* Return if we want to use this row or not */
242 			if ((row - GMT->current.io.row_range[GMT_OUT][k].first) % GMT->current.io.row_range[GMT_OUT][k].inc == 0) return pass;	/* Hit one of the steps */
243 		}
244 	}
245 	return !pass;
246 }
247 
248 /*! . */
gmtio_outside_in_data_range(struct GMT_CTRL * GMT,unsigned int col)249 static inline bool gmtio_outside_in_data_range (struct GMT_CTRL *GMT, unsigned int col) {
250 	/* Returns true if this row should be skipped according to -qi[~]<rangevalues>,...+c<col> */
251 	bool pass;
252 	if (GMT->common.q.mode != GMT_RANGE_DATA_IN) return false;	/* -qi<data> not active */
253 	pass = GMT->common.q.inverse[GMT_IN];
254 	if (gmt_M_is_dnan (GMT->current.io.curr_rec[col])) return !pass;	/* A NaN value is never in a range */
255 	for (unsigned int k = 0; k < GMT->current.io.n_row_ranges[GMT_IN]; k++) {
256 		if (GMT->current.io.curr_rec[col] >= GMT->current.io.data_range[GMT_IN][k].first && GMT->current.io.curr_rec[col] <= GMT->current.io.data_range[GMT_IN][k].last) return pass;	/* Inside this range at least */
257 	}
258 	return !pass;
259 }
260 
261 /*! . */
gmtio_outside_out_data_range(struct GMT_CTRL * GMT,unsigned int col,double * data)262 static inline bool gmtio_outside_out_data_range (struct GMT_CTRL *GMT, unsigned int col, double *data) {
263 	/* Returns true of this row should be skipped according to -qo[~]<rangevalues>,...+c<col> */
264 	bool pass = GMT->common.q.inverse[GMT_OUT];
265 	for (unsigned int k = 0; k < GMT->current.io.n_row_ranges[GMT_OUT]; k++) {
266 		if (data[col] >= GMT->current.io.data_range[GMT_OUT][k].first && data[col] <= GMT->current.io.data_range[GMT_OUT][k].last) return pass;	/* Inside this range at least */
267 	}
268 	return !pass;
269 }
270 
271 /*! . */
gmtio_n_cols_needed_for_gaps(struct GMT_CTRL * GMT,uint64_t n)272 static inline uint64_t gmtio_n_cols_needed_for_gaps (struct GMT_CTRL *GMT, uint64_t n) {
273 	/* Return the actual items needed (which may be more than n if gap testing demands it) */
274 	if (GMT->common.g.active) return (MAX (n, GMT->common.g.n_col));	/* n or n_col (if larger) */
275 	return (n);	/* No gap checking, n it is */
276 }
277 
278 /*! . */
gmtio_update_prev_rec(struct GMT_CTRL * GMT,uint64_t n_use)279 static inline void gmtio_update_prev_rec (struct GMT_CTRL *GMT, uint64_t n_use) {
280 	/* Update previous record before reading the new record*/
281 	if (GMT->current.io.need_previous) gmt_M_memcpy (GMT->current.io.prev_rec, GMT->current.io.curr_rec, n_use, double);
282 }
283 
284 /* Begin private functions used by gmt_byteswap_file() */
285 
286 #define DEBUG_BYTESWAP
287 
288 /*! . */
gmtio_fwrite_check(struct GMT_CTRL * GMT,const void * ptr,size_t size,size_t nitems,FILE * stream)289 static inline bool gmtio_fwrite_check (struct GMT_CTRL *GMT, const void *ptr,
290 		size_t size, size_t nitems, FILE *stream) {
291 	if (fwrite (ptr, size, nitems, stream) != nitems) {
292 		char message[GMT_LEN256] = {""};
293 		snprintf (message, GMT_LEN256, "%s: error writing %" PRIuS " bytes to stream.\n", __func__, size * nitems);
294 			GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
295 		return true;
296 	}
297 	return false;
298 }
299 
300 /*! . */
gmtio_swap_uint16(char * buffer,const size_t len)301 static inline void gmtio_swap_uint16 (char *buffer, const size_t len) {
302 	/* byteswap uint16_t in buffer of length 'len' bytes */
303 	uint16_t u;
304 	size_t n;
305 
306 	for (n = 0; n < len; n+=Int16len) {
307 		memcpy (&u, &buffer[n], Int16len);
308 		u = bswap16 (u);
309 		memcpy (&buffer[n], &u, Int16len);
310 	}
311 }
312 
313 /*! . */
gmtio_swap_uint32(char * buffer,const size_t len)314 static inline void gmtio_swap_uint32 (char *buffer, const size_t len) {
315 	/* byteswap uint32_t in buffer of length 'len' bytes */
316 	uint32_t u;
317 	size_t n;
318 
319 	for (n = 0; n < len; n+=Int32len) {
320 		memcpy (&u, &buffer[n], Int32len);
321 		u = bswap32 (u);
322 		memcpy (&buffer[n], &u, Int32len);
323 	}
324 }
325 
326 /*! . */
gmtio_swap_uint64(char * buffer,const size_t len)327 static inline void gmtio_swap_uint64 (char *buffer, const size_t len) {
328 	/* byteswap uint64_t in buffer of length 'len' bytes */
329 	uint64_t u;
330 	size_t n;
331 
332 	for (n = 0; n < len; n+=Int64len) {
333 		memcpy (&u, &buffer[n], Int64len);
334 		u = bswap64 (u);
335 		memcpy (&buffer[n], &u, Int64len);
336 	}
337 }
338 
339 /* End private functions used by gmt_byteswap_file() */
340 
341 #ifdef HAVE_DIRENT_H_
342 /*! . */
gmtio_traverse_dir(const char * file,char * path)343 GMT_LOCAL bool gmtio_traverse_dir (const char *file, char *path) {
344 	/* Look for file in the directory pointed to by path, recursively */
345 	DIR *D = NULL;
346 	struct dirent *F = NULL;
347 	int len, d_namlen;
348 	bool ok = false;
349 	char savedpath[PATH_MAX];
350 
351  	if ((D = opendir (path)) == NULL) return (false);	/* Unable to open directory listing */
352 	len = (int)strlen (file);
353 	strncpy (savedpath, path, PATH_MAX-1);	/* Make copy of current directory path */
354 
355 	while (!ok && (F = readdir (D)) != NULL) {	/* For each directory entry until end or ok becomes true */
356 		d_namlen = (int)strlen (F->d_name);
357 		if (d_namlen == 1 && F->d_name[0] == '.') continue;				/* Skip current dir */
358 		if (d_namlen == 2 && F->d_name[0] == '.' && F->d_name[1] == '.') continue;	/* Skip parent dir */
359 #ifdef HAVE_SYS_DIR_H_
360 		if (F->d_type == DT_DIR) {	/* Entry is a directory; must search this directory recursively */
361 			sprintf (path, "%s/%s", savedpath, F->d_name);
362 			ok = gmtio_traverse_dir (file, path);
363 		}
364 		else if (d_namlen == len && !strcmp (F->d_name, file)) {	/* Found the file in this dir (i.e., F_OK) */
365 			sprintf (path, "%s/%s", savedpath, file);
366 			ok = true;
367 		}
368 #endif /* HAVE_SYS_DIR_H_ */
369 	}
370 	(void)closedir (D);
371 	return (ok);	/* did or did not find file */
372 }
373 #endif /* HAVE_DIRENT_H_ */
374 
375 /*! . */
gmtio_trim_segheader(struct GMT_CTRL * GMT,char * line)376 GMT_LOCAL char *gmtio_trim_segheader (struct GMT_CTRL *GMT, char *line) {
377 	/* Trim trailing junk and return pointer to first non-space/tab/> part of segment header
378 	 * Do not try to free the returned pointer!
379 	 */
380 	gmt_strstrip (line, false); /* Strip trailing whitespace */
381 	/* Skip over leading whitespace and segment marker */
382 	while (*line && (isspace(*line) || *line == GMT->current.setting.io_seg_marker[GMT_IN]))
383 		++line;
384 	/* Return header string */
385 	return (line);
386 }
387 
388 /*! . */
gmtio_adjust_periodic_lon(struct GMT_CTRL * GMT,double * val)389 GMT_LOCAL void gmtio_adjust_periodic_lon (struct GMT_CTRL *GMT, double *val) {
390 	while (*val > GMT->common.R.wesn[XHI] && (*val - 360.0) >= GMT->common.R.wesn[XLO])
391 		*val -= 360.0;
392 	while (*val < GMT->common.R.wesn[XLO] && (*val + 360.0) <= GMT->common.R.wesn[XLO]) *val += 360.0;
393 	/* If data is not inside the given range it will satisfy (lon > east) */
394 	/* Now it will be outside the region on the same side it started out at */
395 }
396 
397 /*! . */
gmtio_adjust_projected(struct GMT_CTRL * GMT)398 GMT_LOCAL void gmtio_adjust_projected (struct GMT_CTRL *GMT) {
399 	/* Case of incoming projected map coordinates that we wish to revert to lon/lat */
400 	if (GMT->current.proj.inv_coord_unit != GMT_IS_METER) {	/* Must first scale to meters */
401 		GMT->current.io.curr_rec[GMT_X] *= GMT->current.proj.m_per_unit[GMT->current.proj.inv_coord_unit];
402 		GMT->current.io.curr_rec[GMT_Y] *= GMT->current.proj.m_per_unit[GMT->current.proj.inv_coord_unit];
403 	}
404 	(*GMT->current.proj.inv) (GMT, &GMT->current.io.curr_rec[GMT_X], &GMT->current.io.curr_rec[GMT_Y],
405 	                          GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]);
406 }
407 
408 /*! . */
gmtio_bin_colselect(struct GMT_CTRL * GMT)409 GMT_LOCAL uint64_t gmtio_bin_colselect (struct GMT_CTRL *GMT) {
410 	/* When -i<cols> is used we must pull out and reset the current record */
411 	uint64_t col, order;
412 	static double tmp[GMT_BUFSIZ];
413 	struct GMT_COL_INFO *S = NULL;
414 	for (col = 0; col < GMT->common.i.n_cols; col++) {
415 		S = &(GMT->current.io.col[GMT_IN][col]);
416 		order = S->order;
417 		if (GMT->current.io.cycle_col == order)
418 			gmtlib_modulo_time_calculator (GMT, &(tmp[order]));
419 		tmp[order] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col], GMT->current.io.curr_rec[S->col]);
420 		switch (gmt_M_type (GMT, GMT_IN, order)) {
421 			case GMT_IS_LON:	/* Must account for periodicity in 360 as per current rule */
422 				gmtio_adjust_periodic_lon (GMT, &tmp[order]);
423 				break;
424 			case GMT_IS_LAT:
425 				if (tmp[order] < -90.0 || tmp[order] > +90.0) {
426 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Latitude (%g) at line # %" PRIu64 " exceeds -|+ 90! - set to NaN\n", tmp[order], GMT->current.io.rec_no);
427 					tmp[S->order] = GMT->session.d_NaN;
428 				}
429 				break;
430 			case GMT_IS_DIMENSION:	/* Convert to internal inches */
431 				tmp[order] *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
432 				break;
433 			default:	/* Nothing to do */
434 				break;
435 		}
436 	}
437 	gmt_M_memcpy (GMT->current.io.curr_rec, tmp, GMT->common.i.n_cols, double);
438 	return (GMT->common.i.n_cols);
439 }
440 
441 /*! Return the floating point value associated with the aspatial value V given its type as a double */
gmtio_convert_aspatial_value(struct GMT_CTRL * GMT,unsigned int type,char * V)442 GMT_LOCAL double gmtio_convert_aspatial_value (struct GMT_CTRL *GMT, unsigned int type, char *V) {
443 
444 	double value;
445 
446 	switch (type) {
447 		case GMT_DOUBLE:
448 		case GMT_FLOAT:
449 		case GMT_ULONG:
450 		case GMT_LONG:
451 		case GMT_UINT:
452 		case GMT_INT:
453 		case GMT_USHORT:
454 		case GMT_SHORT:
455 		case GMT_CHAR:
456 		case GMT_UCHAR:
457 			value = atof (V);
458 			break;
459 		case GMT_DATETIME:
460 			gmt_scanf_arg (GMT, V, GMT_IS_ABSTIME, false, &value);
461 			break;
462 		default:	/* Give NaN */
463 			value = GMT->session.d_NaN;
464 			break;
465 	}
466 	return (value);
467 }
468 
469 /*! . */
gmtio_handle_bars(struct GMT_CTRL * GMT,char * in,unsigned way)470 GMT_LOCAL void gmtio_handle_bars (struct GMT_CTRL *GMT, char *in, unsigned way) {
471 	/* Way = 0: replace | inside quotes with ASCII 1, Way = 1: Replace ASCII 1 with | */
472 	/* Need to handle cases like ...|"St. George's Channel"|... with a mix of quotes. */
473 	char the_quote = '\0';	/* Don't know what type of quote is used */
474 	gmt_M_unused(GMT);
475 	if (in == NULL || in[0] == '\0') return;	/* No string to check */
476 	if (way == 0) {	/* Replace | found inside quotes with a single ASCII 1 */
477 		char *c = in;
478 		bool replace = false;
479 		while (*c && the_quote == '\0') {	/* Find the first quote character which we assume is what is used for a sentence which may contain another quote */
480 			if (*c == '\"' || *c == '\'') the_quote = *c;
481 			++c;
482 		}
483 		if (the_quote == '\0') the_quote = '\"';	/* No quotes found so just set the default quote */
484 		c = in;	/* Start over */
485 		while (*c) {
486 			if (*c == the_quote)
487 				replace = !replace;
488 			else if (*c == '|' && replace)
489 				*c = 1;
490 			++c;
491 		}
492 	}
493 	else /* way != 0: Replace single ASCII 1 with | */
494 		gmt_strrepc (in, 1, '|');
495 }
496 
497 /*! . */
gmtio_skip_record(struct GMT_CTRL * GMT,struct GMT_TEXT_SELECTION * S,char * record)498 GMT_LOCAL bool gmtio_skip_record (struct GMT_CTRL *GMT, struct GMT_TEXT_SELECTION *S, char *record) {
499 	/* Return true if the pattern was found; see gmt_set_text_selection for details */
500 	bool match = false;
501 	if (S == NULL || S->n == 0) return (true);	/* No selection criteria given, so can only return true */
502 	/* Could be one or n patterns to check */
503 	for (uint64_t k = 0; !match && k < S->n; k++) {
504 #if !defined(WIN32) || (defined(WIN32) && defined(HAVE_PCRE)) || (defined(WIN32) && defined(HAVE_PCRE2))
505 		if (S->regexp[k])
506 		 	match = gmtlib_regexp_match (GMT, record, S->pattern[k], S->caseless[k]);	/* true if we matched */
507 		else
508 #endif
509 			match = (strstr (record, S->pattern[k]) != NULL);
510 	}
511 	/* Here, match == true if we found the pattern we specified.  There are now two cases to consider:
512 	   1) invert is false, which means we want to skip records when match == false.
513 	      So if match == false and invert == false then we will skip.
514 	   2) invert == true, which means we want to skip recprds when match == true.
515 	      So if match == true and invert == true then we will skip
516 	   This means in either scenario, the test below is true if we want to skip. */
517 	return (S->invert == match);	/* Returns true if we should skip this record */
518 }
519 
520 /*! . */
gmtio_ogr_decode_aspatial_values(struct GMT_CTRL * GMT,char * record,struct GMT_OGR * S)521 GMT_LOCAL unsigned int gmtio_ogr_decode_aspatial_values (struct GMT_CTRL *GMT, char *record, struct GMT_OGR *S) {
522 	/* Parse @D<vals> aspatial values; this is done once per feature (segment).  We store
523  	 * both the text representation (value) and attempt to convert to double in dvalue.
524  	 * We use S->n_aspatial to know how many values there are .*/
525 	unsigned int col = 0;
526 	char buffer[GMT_BUFSIZ] = {""}, *token, *stringp;
527 
528 	if (S->n_aspatial == 0) return (0);	/* Nothing to do */
529 	if (S->tvalue == NULL) {			/* First time, allocate space */
530 		S->tvalue = gmt_M_memory (GMT, S->tvalue, S->n_aspatial, char *);
531 		S->dvalue = gmt_M_memory (GMT, S->dvalue, S->n_aspatial, double);
532 	}
533 	strncpy (buffer, record, GMT_BUFSIZ-1); /* working copy */
534 	gmtio_handle_bars (GMT, buffer, 0);	/* Replace vertical bars inside quotes with ASCII 1 */
535 	stringp = buffer;
536 	while ( (token = strsep (&stringp, "|")) != NULL ) {
537 		if (col >= S->n_aspatial) {
538 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad OGR/GMT: @D record has more items than declared by @N\n");
539 			continue;
540 		}
541 		gmtio_handle_bars (GMT, token, 1);		/* Put back any vertical bars replaced above */
542 		gmt_M_str_free (S->tvalue[col]);	/* Free previous item */
543 		S->tvalue[col] = strdup (token);
544 		S->dvalue[col] = gmtio_convert_aspatial_value (GMT, S->type[col], token);
545 		col++;
546 	}
547 	if (col == (S->n_aspatial-1)) {	/* Last item was blank and hence not returned by strsep */
548 		S->tvalue[col] = strdup ("");	/* Allocate space for blank string */
549 	}
550 	return (col);
551 }
552 
553 /*! Duplicate in to out, then find the first space not inside quotes and truncate string there */
gmtio_copy_and_truncate_quotes(char * out,char * in)554 GMT_LOCAL void gmtio_copy_and_truncate_quotes (char *out, char *in) {
555 	bool quote = false;
556 	while (*in && (quote || *in != ' ')) {
557 		*out++ = *in;	/* Copy char */
558 		if (*in++ == '\"') quote = !quote;	/* Wind to next space except skip if inside double quotes */
559 	}
560 	*out = '\0';	/* Terminate string */
561 }
562 
gmtio_copy_and_truncate(char * out,char * in)563 GMT_LOCAL void gmtio_copy_and_truncate (char *out, char *in) {
564 	if (strchr (in, ' ') && !strchr (in, '\"'))	/* Has spaces yet no quotes... assume we can get the line as is */
565 		strcpy (out, in);
566 	else
567 		gmtio_copy_and_truncate_quotes (out, in);
568 }
569 
570 /*! Parse @T aspatial types; this is done once per dataset and follows @N */
gmtio_ogr_decode_aspatial_types(struct GMT_CTRL * GMT,char * record,struct GMT_OGR * S)571 GMT_LOCAL unsigned int gmtio_ogr_decode_aspatial_types (struct GMT_CTRL *GMT, char *record, struct GMT_OGR *S) {
572 	unsigned int pos = 0, col = 0;
573 	int t;
574 	size_t n_alloc = 0;
575 	char buffer[GMT_BUFSIZ] = {""}, p[GMT_BUFSIZ];
576 
577 	gmtio_copy_and_truncate (buffer, record);
578 	while ((gmt_strtok (buffer, "|", &pos, p))) {
579 		if (col >= S->n_aspatial) {
580 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad OGR/GMT: @T record has more items than declared by @N - skipping\n");
581 			continue;
582 		}
583 		if (col == n_alloc) S->type = gmt_M_memory (GMT, S->type, n_alloc += GMT_TINY_CHUNK, enum GMT_enum_type);
584 		if ((t = gmtlib_ogr_get_type (p)) < 0) {
585 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad OGR/GMT: @T: No such type: %s - skipping\n", p);
586 			continue;
587 		}
588 		S->type[col] = t;
589 		col++;
590 	}
591 	if (col < n_alloc) S->type = gmt_M_memory (GMT, S->type, col, enum GMT_enum_type);
592 	return (col);
593 }
594 
gmtio_select_all_ogr_if_requested(struct GMT_CTRL * GMT)595 GMT_LOCAL unsigned int gmtio_select_all_ogr_if_requested (struct GMT_CTRL *GMT) {
596 	/* If -a with no args was provided we select all available aspatial information to be added to input record */
597 	unsigned int k, kn;
598 	if (GMT->current.io.OGR == NULL) return (GMT_NOERROR);		/* No can do */
599 	if (GMT->current.io.OGR->n_aspatial == 0) return (GMT_NOERROR);	/* No can do */
600 	if (GMT->common.a.active == false) return (GMT_NOERROR);		/* -a not given */
601 	if (GMT->common.a.n_aspatial) {		/* -a parsed and stuff was found; check if -a names are correct */
602 		bool found;
603 		for (k = 0; k < GMT->common.a.n_aspatial; k++) {
604 			for (kn = 0, found = false; !found && kn < GMT->current.io.OGR->n_aspatial; kn++)
605 				found = (!strcmp (GMT->common.a.name[k], GMT->current.io.OGR->name[kn]));
606 			if (!found) {
607 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -a: No such named aspatial item: %s.\n", GMT->common.a.name[k]);
608 				return (GMT_NOT_OUTPUT_OBJECT);
609 			}
610 		}
611 		return (GMT_NOERROR);
612 	}
613 	GMT->common.a.n_aspatial = GMT->current.io.OGR->n_aspatial;
614 	for (k = kn = 0; k < GMT->common.a.n_aspatial; k++) {
615 		GMT->common.a.col[k] = 2 + kn;
616 		GMT->common.a.ogr[k] = k;
617 		GMT->common.a.type[k] = GMT->current.io.OGR->type[k];
618 		gmt_M_str_free (GMT->common.a.name[k]);	/* Just in case */
619 		GMT->common.a.name[k] = strdup (GMT->current.io.OGR->name[k]);
620 		if (GMT->common.a.type[k] != GMT_TEXT) kn++;	/* Since that is the order in the numerical part of the record */
621 	}
622 	return (GMT_NOERROR);
623 }
624 
625 /*! Decode @N aspatial names; this is done once per dataset */
gmtio_ogr_decode_aspatial_names(struct GMT_CTRL * GMT,char * record,struct GMT_OGR * S)626 GMT_LOCAL unsigned int gmtio_ogr_decode_aspatial_names (struct GMT_CTRL *GMT, char *record, struct GMT_OGR *S) {
627 	unsigned int pos = 0, col = 0;
628 	size_t n_alloc = 0;
629 	char buffer[GMT_BUFSIZ] = {""}, p[GMT_BUFSIZ] = {""};
630 
631 	gmtio_copy_and_truncate (buffer, record);
632 	while ((gmt_strtok (buffer, "|", &pos, p))) {
633 		if (col == n_alloc) S->name = gmt_M_memory (GMT, S->name, n_alloc += GMT_TINY_CHUNK, char *);
634 		S->name[col++] = strdup (p);
635 	}
636 	if (col < n_alloc) S->name = gmt_M_memory (GMT, S->name, col, char *);
637 	return (col);
638 }
639 
640 /*! Parsing of the GMT/OGR vector specification (v 1.0). See Appendix R */
gmtio_ogr_parser(struct GMT_CTRL * GMT,char * record)641 GMT_LOCAL bool gmtio_ogr_parser (struct GMT_CTRL *GMT, char *record) {
642 	return (GMT->current.io.ogr_parser (GMT, record));	/* We call either the header or data parser depending on pointer */
643 }
644 
gmtio_ogr_data_parser(struct GMT_CTRL * GMT,char * record)645 GMT_LOCAL bool gmtio_ogr_data_parser (struct GMT_CTRL *GMT, char *record) {
646 	/* Parsing of the GMT/OGR vector specification (v 1.0) for data feature records.
647  	 * We KNOW GMT->current.io.ogr == GMT_OGR_TRUE, i.e., current file is a GMT/OGR file.
648 	 * We also KNOW that GMT->current.io.OGR has been allocated by gmtio_ogr_header_parser.
649 	 * For GMT/OGR files we must parse and store the metadata in GMT->current.io.OGR,
650 	 * from where higher-level functions can access it.  GMT_End_IO will free the structure.
651 	 * This function returns true if we parsed a GMT/OGR record and false otherwise.
652 	 * If we encounter a parsing error we stop parsing any further by setting GMT->current.io.ogr = GMT_OGR_FALSE.
653 	 * We loop until all @<info> tags have been processed on this record.
654 	 */
655 
656 	unsigned int n_aspatial;
657 	bool quote;
658 	char *p = NULL;
659 	struct GMT_OGR *S = NULL;
660 
661 	if (record[0] != '#') return (false);			/* Not a comment record so no point looking further */
662 	if (!(p = strchr (record, '@'))) return (false);	/* Not an OGR/GMT record since @ was not found */
663 
664 	/* Here we are reasonably sure that @? strings are OGR/GMT feature specifications */
665 
666 	gmt_chop (record);	/* Get rid of linefeed etc */
667 
668 	S = GMT->current.io.OGR;	/* Set S shorthand */
669 	quote = false;
670 
671 	while (*p == '@') {
672 		++p;	/* Move to first char after @ */
673 		switch (p[0]) {	/* These are the feature tags only: @D, @P, @H */
674 			case 'D':	/* Aspatial data values, store in segment header  */
675 				if (!S->geometry) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @D given but no geometry set\n"); return (false);}
676 				n_aspatial = gmtio_ogr_decode_aspatial_values (GMT, &p[1], S);
677 				if (S->n_aspatial != n_aspatial) {
678 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "OGR/GMT: Some @D items not specified (set to NULL) near line %" PRIu64 "\n", GMT->current.io.rec_no);
679 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "OGR/GMT: Offending record: %s\n", record);
680 				}
681 				break;
682 
683 			case 'P':	/* Polygon perimeter, store in segment header  */
684 				if (!(S->geometry == GMT_IS_POLYGON || S->geometry == GMT_IS_MULTIPOLYGON)) {
685 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @P only valid for polygons\n");
686 					GMT->current.io.ogr = GMT_OGR_FALSE;
687 					return (false);
688 				}
689 				S->pol_mode = GMT_IS_PERIMETER;
690 				break;
691 
692 			case 'H':	/* Polygon hole, store in segment header  */
693 				if (!(S->geometry == GMT_IS_POLYGON || S->geometry == GMT_IS_MULTIPOLYGON)) {
694 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @H only valid for polygons\n");
695 					GMT->current.io.ogr = GMT_OGR_FALSE;
696 					return (false);
697 				}
698 				S->pol_mode = GMT_IS_HOLE;
699 				break;
700 
701 			default:	/* Bad OGR record? */
702 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: Cannot have @%c after FEATURE_DATA\n", p[0]);
703 				GMT->current.io.ogr = GMT_OGR_FALSE;
704 				break;
705 		}
706 		while (*p && (quote || *p != '@')) if (*p++ == '\"') quote = !quote;	/* Wind to next @ except skip if inside double quotes */
707 	}
708 	return (true);
709 }
710 
711 /*! Simplify aspatial data grabbing when -a is used */
gmtio_align_ogr_values(struct GMT_CTRL * GMT)712 GMT_LOCAL void gmtio_align_ogr_values (struct GMT_CTRL *GMT) {
713 	unsigned int k;
714 	int id;
715 	if (!GMT->common.a.active) return;	/* Nothing selected with -a */
716 	for (k = 0; k < GMT->common.a.n_aspatial; k++) {	/* Process the requested columns */
717 		id = gmt_get_ogr_id (GMT->current.io.OGR, GMT->common.a.name[k]);	/* See what order in the OGR struct this -a column appear */
718 		GMT->common.a.ogr[k] = id;
719 	}
720 }
721 
722 /*! . */
gmtio_ogr_header_parser(struct GMT_CTRL * GMT,char * record)723 GMT_LOCAL bool gmtio_ogr_header_parser (struct GMT_CTRL *GMT, char *record) {
724 	/* Parsing of the GMT/OGR vector specification (v 1.0).
725  	 * GMT->current.io.ogr can have three states:
726 	 *	GMT_OGR_UNKNOWN (-1) if not yet set [this is how it is initialized in GMTAPI_Begin_IO].
727 	 *	GMT_OGR_FALSE    (0) if file has been determined NOT to be a GMT/OGR file.
728 	 *	GMT_OGR_TRUE    (+1) if it has met the criteria and is a GMT/OGR file.
729 	 * For GMT/OGR files we must parse and store the metadata in GMT->current.io.OGR,
730 	 * from where higher-level functions can access it.  GMT_End_IO will free the structure.
731 	 * This function returns true if we parsed a GMT/OGR record and false otherwise.
732 	 * If we encounter a parsing error we stop parsing any further by setting GMT->current.io.ogr = GMT_OGR_FALSE.
733 	 * We loop until all @<info> tags have been processed on this record.
734 	 * gmtio_ogr_parser will point to this function until the header has been parsed, then it is
735 	 * set to point to gmtio_ogr_data_parser instead, to speed up data record processing.
736 	 */
737 
738 	unsigned int n_aspatial, k, geometry = 0;
739 	bool quote;
740 	char *p = NULL;
741 	struct GMT_OGR *S = NULL;
742 
743 	if (GMT->current.io.ogr == GMT_OGR_FALSE) return (false);	/* No point parsing further if we KNOW it is not OGR */
744 	if (record[0] != '#') return (false);			/* Not a comment record so no point looking any further */
745 	if (GMT->current.io.ogr == GMT_OGR_TRUE && !strncmp (record, "# FEATURE_DATA", 14)) {	/* It IS an OGR file and we found end of OGR header section and start of feature data */
746 		GMT->current.io.ogr_parser = &gmtio_ogr_data_parser;	/* From now on only parse for feature tags */
747 		gmtio_align_ogr_values (GMT);	/* Simplify copy from aspatial values to input columns as per -a option */
748 		return (true);
749 	}
750 	if (!(p = strchr (record, '@'))) return (false);	/* Not an OGR/GMT record since @ was not found */
751 
752 	if (GMT->current.io.ogr == GMT_OGR_UNKNOWN && !strncmp (p, "@VGMT", 5)) {	/* Found the OGR version identifier, look for @G if on the same record */
753 		if (GMT->common.a.output) {	/* Cannot read OGR files when -a is used to define output */
754 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot read OGR/GMT files when -a is used to define output format\n");
755 			GMT->parent->error = GMT_NOT_OUTPUT_OBJECT;
756 			return false;
757 		}
758 		GMT->current.io.ogr = GMT_OGR_TRUE;		/* File is now known to be a GMT/OGR geospatial file */
759 		if (!(p = strchr (&p[5], '@'))) return (true);	/* No more @ codes; goto next record */
760 	}
761 	if (GMT->current.io.ogr != GMT_OGR_TRUE) return (false);	/* No point parsing further since file is not GMT/OGR (at least not yet) */
762 
763 	/* Here we are reasonably sure that @? strings are OGR/GMT header specifications */
764 
765 	gmt_chop (record);	/* Get rid of linefeed etc */
766 
767 	/* Allocate S the first time we get here */
768 
769 	if (!GMT->current.io.OGR) GMT->current.io.OGR = gmt_M_memory (GMT, NULL, 1, struct GMT_OGR);
770 	S = GMT->current.io.OGR;
771 	quote = false;
772 
773 	while (*p == '@') {
774 		++p;	/* Move to first char after @ */
775 
776 		switch (p[0]) {	/* These are the header tags */
777 
778 			case 'G':	/* Geometry */
779 				if (!strncmp (&p[1], "LINESTRING", 10))
780 					geometry = GMT_IS_LINESTRING;
781 				else if (p[1] == 'P') {
782 					if (!strncmp (&p[2], "OLYGON", 6))
783 						geometry = GMT_IS_POLYGON;
784 					else if (!strncmp (&p[2], "OINT", 4))
785 						geometry = GMT_IS_POINT;
786 				}
787 				else if (!strncmp (&p[1], "MULTI", 5)) {
788 					if (!strncmp (&p[6], "POINT", 5))
789 						geometry = GMT_IS_MULTIPOINT;
790 					else if (!strncmp (&p[6], "LINESTRING", 10))
791 						geometry = GMT_IS_MULTILINESTRING;
792 					else if (!strncmp (&p[6], "POLYGON", 7))
793 						geometry = GMT_IS_MULTIPOLYGON;
794 					else {
795 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @G unrecognized geometry\n");
796 						GMT->current.io.ogr = GMT_OGR_FALSE;
797 						return (false);
798 					}
799 				}
800 				if (!S->geometry)
801 					S->geometry = geometry;
802 				else if (S->geometry != geometry) {
803 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @G cannot have different geometries\n");
804 					GMT->current.io.ogr = GMT_OGR_FALSE;
805 				}
806 				break;
807 
808 			case 'N':	/* Aspatial name fields, store in table header */
809 				if (!S->geometry) {GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @N given but no geometry set\n"); return (false);}
810 				if (S->name) {	/* Already set */
811 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @N Cannot have more than one per segment\n");
812 					GMT->current.io.ogr = GMT_OGR_FALSE;
813 					return (false);
814 				}
815 				else
816 					n_aspatial = gmtio_ogr_decode_aspatial_names (GMT, &p[1], S);
817 				if (S->n_aspatial == 0)
818 					S->n_aspatial = n_aspatial;
819 				else if (S->n_aspatial != n_aspatial) {
820 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @N number of items vary\n");
821 					GMT->current.io.ogr = GMT_OGR_FALSE;
822 				}
823 				break;
824 
825 			case 'J':	/* Dataset projection strings (one of 4 kinds) */
826 				switch (p[1]) {
827 					case 'e': k = 0;	break;	/* EPSG code */
828 					case 'g': k = 1;	break;	/* GMT proj code */
829 					case 'p': k = 2;	break;	/* Proj.4 code */
830 					case 'w': k = 3;	break;	/* OGR WKT representation */
831 					default:
832 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @J given unknown format (%c)\n", (int)p[1]);
833 						GMT->current.io.ogr = GMT_OGR_FALSE;
834 						return (false);
835 				}
836 				S->proj[k] = strdup (&p[2]);
837 				break;
838 
839 			case 'R':	/* Dataset region */
840 				if (S->region) { /* Already set */
841 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @R can only appear once\n");
842 					GMT->current.io.ogr = GMT_OGR_FALSE;
843 					return (false);
844 				}
845 				S->region = strdup (&p[1]);
846 				break;
847 
848 			case 'T':	/* Aspatial field types, store in table header  */
849 				if (!S->geometry) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @T given but no geometry set\n"); return (false);}
850 				if (S->type) {	/* Already set */
851 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @T Cannot have more than one per segment\n");
852 					GMT->current.io.ogr = GMT_OGR_FALSE;
853 					return (false);
854 				}
855 				n_aspatial = gmtio_ogr_decode_aspatial_types (GMT, &p[1], S);
856 				if (S->n_aspatial == 0)
857 					S->n_aspatial = n_aspatial;
858 				else if (S->n_aspatial != n_aspatial) {
859 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @T number of items vary\n");
860 					GMT->current.io.ogr = GMT_OGR_FALSE;
861 				}
862 				break;
863 
864 			default:	/* Just record, probably means this is NOT a GMT/OGR file after all */
865 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad OGR/GMT: @%c not allowed before FEATURE_DATA\n", (int)p[0]);
866 				GMT->current.io.ogr = GMT_OGR_FALSE;
867 				break;
868 		}
869 
870 		while (*p && (quote || *p != '@')) if (*p++ == '\"') quote = !quote;	/* Wind to next @ except skip if inside double quotes */
871 	}
872 	return (true);
873 }
874 
875 /*! . */
gmtio_reconsider_rectype(struct GMT_CTRL * GMT)876 GMT_LOCAL unsigned int gmtio_reconsider_rectype (struct GMT_CTRL *GMT) {
877 	/* If any text aspatial field is selected the record type must change to mixed. */
878 
879 	unsigned int k, val = 0;
880 	if (GMT->current.io.ogr != GMT_OGR_TRUE) return (0);	/* No point checking further since file is not GMT/OGR */
881 
882 	for (k = 0; k < GMT->common.a.n_aspatial; k++) {	/* For each item specified in -a */
883 		if (GMT->common.a.col[k] < 0) continue;		/* Not meant for data columns but for segment headers */
884 		if (GMT->current.io.OGR->type[GMT->common.a.ogr[k]] == GMT_TEXT)
885 			val |= GMT_READ_TEXT;	/* Since it may not have been set */
886 	}
887 	return (val);
888 }
889 
890 /*! . */
gmtio_assign_aspatial_cols(struct GMT_CTRL * GMT)891 GMT_LOCAL unsigned int gmtio_assign_aspatial_cols (struct GMT_CTRL *GMT) {
892 	/* This function will load input columns with aspatial data as requested by -a.
893  	 * It will then handle any possible -i scalings/offsets as well for those columns.
894  	 * This is how the @D values end up in the input data record we read
895      * Not: This applies to numerical aspatial columns.  Any numerical aspatial
896      * columns will be appended to the current trailing text string. */
897 
898 	unsigned int k, n, nt;
899 	if (GMT->current.io.ogr != GMT_OGR_TRUE) return (0);	/* No point checking further since file is not GMT/OGR */
900 
901 	for (k = n = nt = 0; k < GMT->common.a.n_aspatial; k++) {	/* For each item specified in -a */
902 		if (GMT->common.a.col[k] < 0) continue;	/* Not meant for data columns but for segment headers */
903 		if (GMT->current.io.OGR->type[GMT->common.a.ogr[k]] == GMT_TEXT) {	/* Text goes into trailing text */
904 			char *tvalue = GMT->current.io.OGR->tvalue[GMT->common.a.ogr[k]];
905 			size_t len = strlen (tvalue);
906 			unsigned int pos = 0;
907 			if (len && tvalue[0] == '\"' && tvalue[len-1] == '\"') {	/* Eliminate quotes for quoted text */
908 				tvalue[len-1] = '\0';
909 				pos = 1;
910 			}
911 			if (nt) strcat (GMT->current.io.curr_trailing_text, GMT->current.setting.io_col_separator);
912 			strncat (GMT->current.io.curr_trailing_text, &tvalue[pos], GMT_BUFSIZ-1);
913 			GMT->current.io.record.text = GMT->current.io.curr_trailing_text;	/* Since it may not have been set */
914 			nt++;
915 		}
916 		else {	/* Numerical adds to data columns */
917 			double value = GMT->current.io.OGR->dvalue[GMT->common.a.ogr[k]];
918 			GMT->current.io.curr_rec[GMT->common.a.col[k]] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][GMT->common.a.col[k]], value);
919 			n++;
920 		}
921 	}
922 	return (n);	/* Only numerical columns add to the count */
923 }
924 
gmtio_type_index(unsigned int type)925 GMT_LOCAL unsigned int gmtio_type_index (unsigned int type) {
926 	if (type == GMT_TEXT) return GMT_N_TYPES-2;
927 	if (type == GMT_DATETIME) return GMT_N_TYPES-1;
928 	return type;
929 }
930 
931 /*! Fill a string with the aspatial columns */
gmt_list_aspatials(struct GMT_CTRL * GMT,char buffer[])932 void gmt_list_aspatials (struct GMT_CTRL *GMT, char buffer[]) {
933 	char item[GMT_LEN64] = {""};
934 	unsigned int type;
935 	sprintf (buffer, "Aspatial columns:");
936 	for (unsigned int k = 0; k < GMT->common.a.n_aspatial; k++) {
937 		type = gmtio_type_index (GMT->common.a.type[k]);
938 		sprintf (item, " %s[%s]", GMT->common.a.name[k], GMT_type[type]);
939 		strcat (buffer, item);
940 	}
941 }
942 
943 /* Sub functions for gmtio_bin_input */
944 
945 /*! . */
gmtio_x_read(struct GMT_CTRL * GMT,FILE * fp,off_t rel_move)946 GMT_LOCAL int gmtio_x_read (struct GMT_CTRL *GMT, FILE *fp, off_t rel_move) {
947 	/* Used to skip rel_move bytes; no reading takes place */
948 	if (fseek (fp, rel_move, SEEK_CUR)) {
949 		GMT->current.io.status = GMT_IO_EOF;
950 		return (GMT_DATA_READ_ERROR);
951 	}
952 	return (GMT_OK);
953 }
954 
955 /*! Reads the n binary doubles from input and saves to GMT->current.io.curr_rec[] */
gmtio_get_binary_input(struct GMT_CTRL * GMT,FILE * fp,uint64_t n)956 GMT_LOCAL bool gmtio_get_binary_input (struct GMT_CTRL *GMT, FILE *fp, uint64_t n) {
957 	uint64_t i;
958 
959 	if (n > GMT_MAX_COLUMNS) {
960 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Number of data columns (%d) exceeds limit (GMT_MAX_COLUMNS = %d)\n", n, GMT_MAX_COLUMNS);
961 		return (true);	/* Done with this file */
962 	}
963 	for (i = 0; i < n; i++) {
964 		if (GMT->current.io.fmt[GMT_IN][i].skip < 0) gmtio_x_read (GMT, fp, -GMT->current.io.fmt[GMT_IN][i].skip);	/* Pre-skip */
965 		if (GMT->current.io.fmt[GMT_IN][i].io (GMT, fp, 1, &GMT->current.io.curr_rec[i]) == GMT_DATA_READ_ERROR) {
966 			/* EOF or came up short */
967 			GMT->current.io.status = (feof (fp)) ? GMT_IO_EOF : GMT_IO_MISMATCH;
968 			if (GMT->current.io.give_report && GMT->current.io.n_bad_records) {
969 				/* Report summary and reset */
970 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "This file had %" PRIu64 " data records with invalid x and/or y values\n", GMT->current.io.n_bad_records);
971 				GMT->current.io.n_bad_records = GMT->current.io.rec_no = GMT->current.io.data_record_number_in_set[GMT_IN] = GMT->current.io.n_clean_rec = 0;
972 			}
973 			return (true);	/* Done with this file */
974 		}
975 		if (GMT->current.io.fmt[GMT_IN][i].skip > 0) gmtio_x_read (GMT, fp, GMT->current.io.fmt[GMT_IN][i].skip);	/* Post-skip */
976 	}
977 	return (false);	/* OK so far */
978 }
979 
980 /*! . */
gmtio_x_write(struct GMT_CTRL * GMT,FILE * fp,off_t n)981 GMT_LOCAL int gmtio_x_write (struct GMT_CTRL *GMT, FILE *fp, off_t n) {
982 	/* Used to write n bytes of space for filler on binary output */
983 	char c = ' ';
984 	off_t i;
985 	gmt_M_unused(GMT);
986 	for (i = 0; i < n; ++i) {
987 		if (gmt_M_fwrite (&c, sizeof (char), 1U, fp) != 1U)
988 		return (GMT_DATA_WRITE_ERROR);
989 	}
990 	return (GMT_NOERROR);
991 }
992 
993 /*! . */
gmtio_bin_output(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * ptr,char * txt)994 GMT_LOCAL int gmtio_bin_output (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *ptr, char *txt) {
995 	/* Return 0 if record was suppressed, otherwise number of items written */
996 	int k;
997 	uint64_t i, n_out, col_pos;
998 	double val;
999 	gmt_M_unused (txt);
1000 
1001 	if (gmt_skip_output (GMT, ptr, n)) return (GMT_NOTSET);	/* Record was skipped via -s[a|r] */
1002 	if (GMT->current.setting.io_lonlat_toggle[GMT_OUT])		/* Write lat/lon instead of lon/lat */
1003 		gmt_M_double_swap (ptr[GMT_X], ptr[GMT_Y]);
1004 	n_out = (GMT->common.o.select) ? GMT->common.o.n_cols : n;
1005 	for (i = 0, k = 0; i < n_out; i++) {
1006 		col_pos = (GMT->common.o.select) ? GMT->current.io.col[GMT_OUT][i].col : i;	/* Which data column to pick */
1007 		val = (col_pos >= n) ? GMT->session.d_NaN : ptr[col_pos];	/* If we request beyond length of array, return NaN */
1008 		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 */
1009 		if (gmt_M_type (GMT, GMT_OUT, col_pos) == GMT_IS_LON) gmt_lon_range_adjust (GMT->current.io.geo.range, &val);
1010 		if (GMT->current.io.fmt[GMT_OUT][i].skip < 0) gmtio_x_write (GMT, fp, -GMT->current.io.fmt[GMT_OUT][i].skip);	/* Pre-fill */
1011 		k += GMT->current.io.fmt[GMT_OUT][i].io (GMT, fp, 1, &val);
1012 		if (GMT->current.io.fmt[GMT_OUT][i].skip > 0) gmtio_x_write (GMT, fp, GMT->current.io.fmt[GMT_OUT][i].skip);	/* Post-fill */
1013 	}
1014 	return (0);
1015 }
1016 
1017 /*! . */
gmt_ascii_output_no_text(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * ptr,char * txt)1018 int gmt_ascii_output_no_text (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *ptr, char *txt) {
1019 	uint64_t i, col, last, n_out;
1020 	int e = 0, wn = 0;
1021 	double val;
1022 	gmt_M_unused (txt);
1023 
1024 	if (gmt_skip_output (GMT, ptr, n)) return (GMT_NOTSET);	/* Record was skipped via -s[a|r] */
1025 	n_out = (GMT->common.o.select) ? GMT->common.o.n_cols : n;
1026 
1027 	last = n_out - 1;				/* Last filed, need to output linefeed instead of delimiter */
1028 
1029 	for (i = 0; i < n_out && e >= 0; i++) {		/* Keep writing all fields unless there is a read error (e == -1) */
1030 		if (GMT->common.o.select)	/* Which data column to pick */
1031 			col = GMT->current.io.col[GMT_OUT][i].col;
1032 		else if (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && i < 2)
1033 			col = 1 - i;	/* Write lat/lon instead of lon/lat */
1034 		else
1035 			col = i;	/* Just goto next column */
1036 		val = (col >= n) ? GMT->session.d_NaN : ptr[col];	/* If we request beyond length of array, return NaN */
1037 		if (GMT->common.d.active[GMT_OUT] && gmt_M_is_dnan (val))	/* Write this value instead of NaNs */
1038 			val = GMT->common.d.nan_proxy[GMT_OUT];
1039 
1040 		e = gmt_ascii_output_col (GMT, fp, val, col);	/* Write one item without any separator at the end */
1041 
1042 		if (i == last)					/* This is the last field, must add newline */
1043 			putc ('\n', fp);
1044 		else if (GMT->current.setting.io_col_separator[0])		/* Not last field, and a separator is required */
1045 			fprintf (fp, "%s", GMT->current.setting.io_col_separator);
1046 
1047 		wn += e;
1048 	}
1049 	return ((e < 0) ? GMT_NOTSET : 0);
1050 }
1051 
gmtio_output_trailing_text(struct GMT_CTRL * GMT,FILE * fp,char * txt)1052 GMT_LOCAL void gmtio_output_trailing_text (struct GMT_CTRL *GMT, FILE *fp, char *txt) {
1053 	if (GMT->common.o.word) {	/* Must output a specific word from the trailing text only */
1054 		char *word = NULL, *orig = strdup (txt), *trail = orig;
1055 		uint64_t col = 0;
1056 		while (col != GMT->common.o.w_col && (word = strsep (&trail, GMT_TOKEN_SEPARATORS)) != NULL) {
1057 			if (*word != '\0')	/* Skip empty strings */
1058 				col++;
1059 		}
1060 		if (word)	/* Only write word if not NULL */
1061 			fprintf (fp, "%s", word);
1062 		else
1063 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Trailing text did not have %" PRIu64 " words - no trailing word written\n", GMT->common.o.w_col);
1064 		fprintf (fp, "\n");
1065 		gmt_M_str_free (orig);
1066 	}
1067 	else	/* Output the whole enchilada */
1068 		fprintf (fp, "%s\n", txt);
1069 }
1070 
1071 /*! . */
gmtlib_ascii_output_trailing_text(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * ptr,char * txt)1072 int gmtlib_ascii_output_trailing_text (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *ptr, char *txt) {
1073 
1074 	if (gmt_skip_output (GMT, ptr, n)) return (GMT_NOTSET);	/* Record was skipped via -s[a|r] */
1075 
1076 	gmtio_output_trailing_text (GMT, fp, txt);
1077 
1078 	return (0);
1079 }
1080 
1081 /*! . */
gmtio_ascii_output_with_text(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * ptr,char * txt)1082 GMT_LOCAL int gmtio_ascii_output_with_text (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *ptr, char *txt) {
1083 	uint64_t i, col, n_out;
1084 	int e = 0, wn = 0;
1085 	double val;
1086 
1087 	if (gmt_skip_output (GMT, ptr, n)) return (GMT_NOTSET);	/* Record was skipped via -s[a|r] */
1088 	n_out = (GMT->common.o.select) ? GMT->common.o.n_cols : n;
1089 
1090 	for (i = 0; i < n_out && e >= 0; i++) {		/* Keep writing all fields unless there is a read error (e == -1) */
1091 		if (GMT->common.o.select)	/* Which data column to pick */
1092 			col = GMT->current.io.col[GMT_OUT][i].col;
1093 		else if (GMT->current.setting.io_lonlat_toggle[GMT_OUT] && i < 2)
1094 			col = 1 - i;	/* Write lat/lon instead of lon/lat */
1095 		else
1096 			col = i;	/* Just goto next column */
1097 		val = (col >= n) ? GMT->session.d_NaN : ptr[col];	/* If we request beyond length of array, return NaN */
1098 		if (GMT->common.d.active[GMT_OUT] && gmt_M_is_dnan (val))	/* Write this value instead of NaNs */
1099 			val = GMT->common.d.nan_proxy[GMT_OUT];
1100 
1101 		e = gmt_ascii_output_col (GMT, fp, val, col);	/* Write one item without any separator at the end */
1102 
1103 		if (GMT->current.setting.io_col_separator[0])		/* Not last field, and a separator is required */
1104 			fprintf (fp, "%s", GMT->current.setting.io_col_separator);
1105 
1106 		wn += e;
1107 	}
1108 	gmtio_output_trailing_text (GMT, fp, txt);
1109 
1110 	return ((e < 0) ? GMT_NOTSET : 0);
1111 }
1112 
gmtio_ascii_output(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * ptr,char * txt)1113 GMT_LOCAL int gmtio_ascii_output (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *ptr, char *txt) {
1114 	/* First time we decide if we have floats only or a mix and finalize pointer settings */
1115 	if (txt && GMT->current.io.trailing_text[GMT_OUT]) {
1116 		if (n == 0 || (GMT->common.o.select && GMT->common.o.n_cols == 0))
1117 			GMT->current.io.output = gmtlib_ascii_output_trailing_text;	/* Just print trailing text */
1118 		else
1119 			GMT->current.io.output = gmtio_ascii_output_with_text;	/* Have trailing text after numerical output */
1120 		return GMT->current.io.output (GMT, fp, n, ptr, txt);
1121 	}
1122 	else {
1123 		GMT->current.io.output = gmt_ascii_output_no_text;	/* Just numbers */
1124 		return gmt_ascii_output_no_text (GMT, fp, n, ptr, txt);
1125 	}
1126 }
1127 
1128 /*! . */
gmtlib_reset_input(struct GMT_CTRL * GMT)1129 void gmtlib_reset_input (struct GMT_CTRL *GMT) {
1130 	if (GMT->current.io.output == gmtio_ascii_output_with_text) GMT->current.io.output = gmtio_ascii_output;	/* Go back to being agnostic */
1131 }
1132 
1133 /*! . */
gmtio_format_geo_output(struct GMT_CTRL * GMT,bool is_lat,double geo,char * text)1134 GMT_LOCAL void gmtio_format_geo_output (struct GMT_CTRL *GMT, bool is_lat, double geo, char *text) {
1135 	int k, n_items, d, m, s, m_sec, h_pos = 0;
1136 	bool minus;
1137 	char hemi[3] = {""}, *f = NULL;
1138 	static char *suffix[2][2] = {{"W", "E"}, {"S", "N"}};	/* Just for decimal degrees when no_sign is true */
1139 
1140 	if (is_lat) {	/* Column is supposedly latitudes */
1141 		if (fabs (geo) > 90.0) {
1142 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Column selected for latitude-formatting has values that exceed +/- 90; set to NaN\n");
1143 			sprintf (text, "NaN");
1144 			return;
1145 		}
1146 	}
1147 	else gmt_lon_range_adjust (GMT->current.io.geo.range, &geo);	/* Adjust longitudes */
1148 	if (GMT->current.io.geo.decimal) {	/* Easy */
1149 		f = (GMT->current.io.o_format[is_lat]) ? GMT->current.io.o_format[is_lat] : GMT->current.setting.format_float_out;
1150 		if (GMT->current.io.geo.no_sign) {
1151 			k = (geo < 0.0) ? 0 : 1;
1152 			sprintf (text, f, fabs(geo));
1153 			strcat (text, suffix[is_lat][k]);
1154 		}
1155 		else
1156 			sprintf (text, f, geo);
1157 		return;
1158 	}
1159 
1160 	if (GMT->current.io.geo.wesn) {	/* Trailing WESN */
1161 		if (GMT->current.io.geo.wesn == 2) hemi[h_pos++] = ' ';	/* Want space between numbers and hemisphere letter */
1162 		if (is_lat)
1163 			hemi[h_pos] = (gmt_M_is_zero (geo)) ? 0 : ((geo < 0.0) ? 'S' : 'N');
1164 		else
1165 			hemi[h_pos] = (gmt_M_is_zero (geo) || doubleAlmostEqual (geo, 180.0)) ? 0 : ((geo < 0.0) ? 'W' : 'E');
1166 		geo = fabs (geo);
1167 		if (hemi[h_pos] == 0) hemi[0] = 0;
1168 	}
1169 
1170 	for (k = n_items = 0; k < 3; k++)
1171 		if (GMT->current.io.geo.order[k] >= 0) n_items++;	/* How many of d, m, and s are requested as integers */
1172 	minus = gmtlib_geo_to_dms (geo, n_items, GMT->current.io.geo.f_sec_to_int, &d, &m, &s, &m_sec);	/* Break up into d, m, s, and remainder */
1173 	if (minus) text[0] = '-';	/* Must manually insert leading minus sign when degree == 0 */
1174 	if (GMT->current.io.geo.n_sec_decimals) {		/* Wanted fraction printed */
1175 		if (n_items == 3)
1176 			sprintf (&text[minus], GMT->current.io.geo.y_format, d, m, s, m_sec, hemi);
1177 		else if (n_items == 2)
1178 			sprintf (&text[minus], GMT->current.io.geo.y_format, d, m, m_sec, hemi);
1179 		else
1180 			sprintf (&text[minus], GMT->current.io.geo.y_format, d, m_sec, hemi);
1181 	}
1182 	else if (n_items == 3)
1183 		sprintf (&text[minus], GMT->current.io.geo.y_format, d, m, s, hemi);
1184 	else if (n_items == 2)
1185 		sprintf (&text[minus], GMT->current.io.geo.y_format, d, m, hemi);
1186 	else
1187 		sprintf (&text[minus], GMT->current.io.geo.y_format, d, hemi);
1188 }
1189 
1190 /* Various functions to support {grd2xyz,xyz2grd}_func.c */
1191 
1192 /* NOTE: In the following we check GMT->current.io.col_type[GMT_IN][2] and GMT->current.io.col_type[GMT_OUT][2] for formatting help for the first column.
1193  * We use column 3 ([2] or GMT_Z) instead of the first ([0]) since we really are dealing with the z in z (x,y) here
1194  * and the x,y are implicit from the -R -I arguments.
1195  */
1196 
1197 /*! . */
gmtio_A_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1198 GMT_LOCAL int gmtio_A_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1199 	/* Can read one or more items from input records. Limitation is
1200 	 * that they must be floating point values (no dates or ddd:mm:ss) */
1201 	uint64_t i;
1202  	gmt_M_unused(GMT);
1203 	for (i = 0; i < n; ++i) {
1204 		if (fscanf (fp, "%lg", &d[i]) <= 0)		/* Read was unsuccessful */
1205 			return (GMT_DATA_READ_ERROR);
1206 	}
1207 	return (GMT_OK);
1208 }
1209 
1210 /*! . */
gmtio_a_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1211 GMT_LOCAL int gmtio_a_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1212 	/* Only reads one item regardless of *n */
1213 	char line[GMT_LEN64] = {""}, *p;
1214 	gmt_M_unused(GMT); gmt_M_unused(n);
1215 	if (!fgets (line, GMT_LEN64, fp)) {
1216 		/* Read was unsuccessful */
1217 		GMT->current.io.status = GMT_IO_EOF;
1218 		return (GMT_DATA_READ_ERROR);
1219 	}
1220 	/* Find end of string */
1221 	p = line;
1222 	while (*p)
1223 		++p;
1224 	/* Remove trailing whitespace */
1225 	while ((--p != line) && strchr (" \t,\r\n", (int)*p));
1226 	*(p + 1) = '\0';
1227 	/* Convert whatever it is to double */
1228 	if (gmt_scanf (GMT, line, gmt_M_type (GMT, GMT_IN, GMT_Z), d) == GMT_IS_NAN)
1229 		*d = GMT->session.d_NaN;
1230 	return (GMT_OK);
1231 }
1232 
1233 /*! . */
gmtio_c_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1234 GMT_LOCAL int gmtio_c_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1235 	/* read int8_t aka char */
1236 	uint64_t i;
1237 	int8_t s;
1238 	size_t k;
1239 	for (i = 0; i < n; ++i) {
1240 		if ((k = gmt_M_fread (&s, sizeof (int8_t), 1U, fp)) != 1) {
1241 			/* Read was unsuccessful */
1242 			GMT->current.io.status = GMT_IO_EOF;
1243 			return (GMT_DATA_READ_ERROR);
1244 		}
1245 		d[i] = (double) s;
1246 	}
1247 	return (GMT_OK);
1248 }
1249 
1250 /*! . */
gmtio_u_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1251 GMT_LOCAL int gmtio_u_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1252 	/* read uint8_t aka unsigned char */
1253 	uint64_t i;
1254 	uint8_t u;
1255 	size_t k;
1256 	for (i = 0; i < n; ++i) {
1257 		if ((k = gmt_M_fread (&u, sizeof (uint8_t), 1U, fp)) != 1) {
1258 			GMT->current.io.status = GMT_IO_EOF;
1259 			return (GMT_DATA_READ_ERROR);
1260 		}
1261 		d[i] = (double) u;
1262 	}
1263 	return (GMT_OK);
1264 }
1265 
1266 /*! . */
gmtio_h_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1267 GMT_LOCAL int gmtio_h_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1268 	/* read int16_t */
1269 	uint64_t i;
1270 	int16_t s;
1271 	size_t k;
1272 	for (i = 0; i < n; ++i) {
1273 		if ((k = gmt_M_fread (&s, sizeof (int16_t), 1U, fp)) != 1) {
1274 			GMT->current.io.status = GMT_IO_EOF;
1275 			return (GMT_DATA_READ_ERROR);
1276 		}
1277 		d[i] = (double) s;
1278 	}
1279 	return (GMT_OK);
1280 }
1281 
1282 /*! . */
gmtio_h_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1283 GMT_LOCAL int gmtio_h_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1284 	/* read byteswapped int16_t */
1285 	uint64_t i;
1286 	uint16_t u;
1287 	size_t k;
1288 	int16_t *s = (int16_t *)&u;
1289 	for (i = 0; i < n; ++i) {
1290 		if ((k = gmt_M_fread (&u, sizeof (uint16_t), 1U, fp)) != 1) {
1291 			GMT->current.io.status = GMT_IO_EOF;
1292 			return (GMT_DATA_READ_ERROR);
1293 		}
1294 		u = bswap16 (u);
1295 		d[i] = (double) *s;
1296 	}
1297 	return (GMT_OK);
1298 }
1299 
1300 /*! . */
gmtio_H_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1301 GMT_LOCAL int gmtio_H_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1302 	/* read uint16_t */
1303 	uint64_t i;
1304 	uint16_t u;
1305 	size_t k;
1306 	for (i = 0; i < n; ++i) {
1307 		if ((k = gmt_M_fread (&u, sizeof (uint16_t), 1U, fp)) != 1) {
1308 			GMT->current.io.status = GMT_IO_EOF;
1309 			return (GMT_DATA_READ_ERROR);
1310 		}
1311 		d[i] = (double) u;
1312 	}
1313 	return (GMT_OK);
1314 }
1315 
1316 /*! . */
gmtio_H_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1317 GMT_LOCAL int gmtio_H_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1318 	/* read byteswapped uint16_t */
1319 	uint64_t i;
1320 	uint16_t u;
1321 	size_t k;
1322 	for (i = 0; i < n; ++i) {
1323 		if ((k = gmt_M_fread (&u, sizeof (uint16_t), 1U, fp)) != 1) {
1324 			GMT->current.io.status = GMT_IO_EOF;
1325 			return (GMT_DATA_READ_ERROR);
1326 		}
1327 		d[i] = (double) bswap16 (u);
1328 	}
1329 	return (GMT_OK);
1330 }
1331 
1332 /*! . */
gmtio_i_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1333 GMT_LOCAL int gmtio_i_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1334 	/* read int32_t */
1335 	uint64_t i;
1336 	int32_t s;
1337 	size_t k;
1338 	for (i = 0; i < n; ++i) {
1339 		if ((k = gmt_M_fread (&s, sizeof (int32_t), 1U, fp)) != 1) {
1340 			GMT->current.io.status = GMT_IO_EOF;
1341 			return (GMT_DATA_READ_ERROR);
1342 		}
1343 		d[i] = (double) s;
1344 	}
1345 	return (GMT_OK);
1346 }
1347 
1348 /*! . */
gmtio_i_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1349 GMT_LOCAL int gmtio_i_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1350 	/* read byteswapped int32_t */
1351 	uint64_t i;
1352 	uint32_t u;
1353 	int32_t *s = (int32_t *)&u;
1354 	size_t k;
1355 	for (i = 0; i < n; ++i) {
1356 		if ((k = gmt_M_fread (&u, sizeof (uint32_t), 1U, fp)) != 1) {
1357 			GMT->current.io.status = GMT_IO_EOF;
1358 			return (GMT_DATA_READ_ERROR);
1359 		}
1360 		u = bswap32 (u);
1361 		d[i] = (double) *s;
1362 	}
1363 	return (GMT_OK);
1364 }
1365 
1366 /*! . */
gmtio_I_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1367 GMT_LOCAL int gmtio_I_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1368 	/* read uint32_t */
1369 	uint64_t i;
1370 	uint32_t u;
1371 	size_t k;
1372 	for (i = 0; i < n; ++i) {
1373 		if ((k = gmt_M_fread (&u, sizeof (uint32_t), 1U, fp)) != 1) {
1374 			GMT->current.io.status = GMT_IO_EOF;
1375 			return (GMT_DATA_READ_ERROR);
1376 		}
1377 		d[i] = (double) u;
1378 	}
1379 	return (GMT_OK);
1380 }
1381 
1382 /*! . */
gmtio_I_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1383 GMT_LOCAL int gmtio_I_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1384 	/* read byteswapped uint32_t */
1385 	uint64_t i;
1386 	uint32_t u;
1387 	size_t k;
1388 	for (i = 0; i < n; ++i) {
1389 		if ((k = gmt_M_fread (&u, sizeof (uint32_t), 1U, fp)) != 1) {
1390 			GMT->current.io.status = GMT_IO_EOF;
1391 			return (GMT_DATA_READ_ERROR);
1392 		}
1393 		d[i] = (double) bswap32 (u);
1394 	}
1395 	return (GMT_OK);
1396 }
1397 
1398 /*! . */
gmtio_l_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1399 GMT_LOCAL int gmtio_l_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1400 	/* read int64_t */
1401 	uint64_t i;
1402 	int64_t s;
1403 	size_t k;
1404 
1405 	for (i = 0; i < n; ++i) {
1406 		if ((k = gmt_M_fread (&s, sizeof (int64_t), 1U, fp)) != 1) {
1407 			GMT->current.io.status = GMT_IO_EOF;
1408 			return (GMT_DATA_READ_ERROR);
1409 		}
1410 		d[i] = (double) s;
1411 	}
1412 	return (GMT_OK);
1413 }
1414 
1415 /*! . */
gmtio_l_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1416 GMT_LOCAL int gmtio_l_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1417 	/* read byteswapped int64_t */
1418 	uint64_t i;
1419 	uint64_t u;
1420 	int64_t *s = (int64_t *)&u;
1421 	size_t k;
1422 	for (i = 0; i < n; ++i) {
1423 		if ((k = gmt_M_fread (&u, sizeof (uint64_t), 1U, fp)) != 1) {
1424 			GMT->current.io.status = GMT_IO_EOF;
1425 			return (GMT_DATA_READ_ERROR);
1426 		}
1427 		u = bswap64(u);
1428 		d[i] = (double) *s;
1429 	}
1430 	return (GMT_OK);
1431 }
1432 
1433 /*! . */
gmtio_L_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1434 GMT_LOCAL int gmtio_L_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1435 	/* read uint64_t */
1436 	uint64_t i;
1437 	uint64_t u;
1438 	size_t k;
1439 
1440 	for (i = 0; i < n; ++i) {
1441 		if ((k = gmt_M_fread (&u, sizeof (uint64_t), 1U, fp)) != 1) {
1442 			GMT->current.io.status = GMT_IO_EOF;
1443 			return (GMT_DATA_READ_ERROR);
1444 		}
1445 		d[i] = (double) u;
1446 	}
1447 	return (GMT_OK);
1448 }
1449 
1450 /*! . */
gmtio_L_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1451 GMT_LOCAL int gmtio_L_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1452 	/* read byteswapped uint64_t */
1453 	uint64_t i;
1454 	uint64_t u;
1455 	size_t k;
1456 
1457 	for (i = 0; i < n; ++i) {
1458 		if ((k = gmt_M_fread (&u, sizeof (uint64_t), 1U, fp)) != 1) {
1459 			GMT->current.io.status = GMT_IO_EOF;
1460 			return (GMT_DATA_READ_ERROR);
1461 		}
1462 		d[i] = (double) bswap64(u);
1463 	}
1464 	return (GMT_OK);
1465 }
1466 
1467 /*! . */
gmtio_f_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1468 GMT_LOCAL int gmtio_f_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1469 	/* read float */
1470 	uint64_t i;
1471 	size_t k;
1472 	float f;
1473 	for (i = 0; i < n; ++i) {
1474 		if ((k = gmt_M_fread (&f, sizeof (float), 1U, fp)) != 1) {
1475 			GMT->current.io.status = GMT_IO_EOF;
1476 			return (GMT_DATA_READ_ERROR);
1477 		}
1478 		d[i] = (double) f;
1479 	}
1480 	return (GMT_OK);
1481 }
1482 
1483 /*! . */
gmtio_f_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1484 GMT_LOCAL int gmtio_f_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1485 	/* read byteswapped float */
1486 	size_t k;
1487 	uint64_t i;
1488 	union {
1489 		float f;
1490 		uint32_t bits;
1491 	} u;
1492 	for (i = 0; i < n; ++i) {
1493 		if ((k = gmt_M_fread (&u.bits, sizeof (uint32_t), 1U, fp)) != 1) {
1494 			GMT->current.io.status = GMT_IO_EOF;
1495 			return (GMT_DATA_READ_ERROR);
1496 		}
1497 		u.bits = bswap32 (u.bits);
1498 		d[i] = (double) u.f;
1499 	}
1500 	return (GMT_OK);
1501 }
1502 
1503 /*! . */
gmtio_d_read(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1504 GMT_LOCAL int gmtio_d_read (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1505 	/* read double */
1506 	size_t k;
1507 	uint64_t i;
1508 	for (i = 0; i < n; ++i) {
1509 		if ((k = gmt_M_fread (&d[i], sizeof (double), 1U, fp)) != 1) {
1510 			GMT->current.io.status = GMT_IO_EOF;
1511 			return (GMT_DATA_READ_ERROR);
1512 		}
1513 	}
1514 	return (GMT_OK);
1515 }
1516 
1517 /*! . */
gmtio_d_read_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1518 GMT_LOCAL int gmtio_d_read_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1519 	/* read byteswapped double */
1520 	size_t k;
1521 	uint64_t i;
1522 	union {
1523 		double d;
1524 		uint64_t bits;
1525 	} u;
1526 	for (i = 0; i < n; ++i) {
1527 		if ((k = gmt_M_fread (&u.bits, sizeof (uint64_t), 1U, fp)) !=1) {
1528 			GMT->current.io.status = GMT_IO_EOF;
1529 			return (GMT_DATA_READ_ERROR);
1530 		}
1531 		u.bits = bswap64 (u.bits);
1532 		d[i] = u.d;
1533 	}
1534 	return (GMT_OK);
1535 }
1536 
1537 /*! . */
gmtio_a_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1538 GMT_LOCAL int gmtio_a_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1539 	/* write ASCII */
1540 	uint64_t i;
1541 	for (i = 0; i < (n - 1); ++i) {
1542 		gmt_ascii_output_col (GMT, fp, d[i], GMT_Z);
1543 		fprintf (fp, "\t");
1544 	}
1545 	/* last col */
1546 	gmt_ascii_output_col (GMT, fp, d[i], GMT_Z);
1547 	fprintf (fp, "\n");
1548 	return (GMT_OK);
1549 }
1550 
1551 /*! . */
gmtio_c_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1552 GMT_LOCAL int gmtio_c_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1553 	/* write int8_t aka char */
1554 	uint64_t i;
1555 	int8_t s;
1556 	gmt_M_unused(GMT);
1557 	for (i = 0; i < n; ++i) {
1558 		s = (int8_t) d[i];
1559 		if (gmt_M_fwrite (&s, sizeof (int8_t), 1U, fp) != 1U)
1560 			return (GMT_DATA_WRITE_ERROR);
1561 	}
1562 	return (GMT_OK);
1563 }
1564 
1565 /*! . */
gmtio_u_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1566 GMT_LOCAL int gmtio_u_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1567 	/* write uint8_t aka unsigned char */
1568 	uint64_t i;
1569 	uint8_t u;
1570 	gmt_M_unused(GMT);
1571 	for (i = 0; i < n; ++i) {
1572 		u = (uint8_t) d[i];
1573 		if (gmt_M_fwrite (&u, sizeof (uint8_t), 1U, fp) != 1U)
1574 			return (GMT_DATA_WRITE_ERROR);
1575 	}
1576 	return (GMT_OK);
1577 }
1578 
1579 /*! . */
gmtio_h_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1580 GMT_LOCAL int gmtio_h_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1581 	/* write int16_t */
1582 	uint64_t i;
1583 	int16_t s;
1584 	gmt_M_unused(GMT);
1585 	for (i = 0; i < n; ++i) {
1586 		s = (int16_t) d[i];
1587 		if (gmt_M_fwrite (&s, sizeof (int16_t), 1U, fp) != 1U)
1588 			return (GMT_DATA_WRITE_ERROR);
1589 	}
1590 	return (GMT_OK);
1591 }
1592 
1593 /*! . */
gmtio_h_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1594 GMT_LOCAL int gmtio_h_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1595 	/* write byteswapped int16_t */
1596 	uint64_t i;
1597 	uint16_t u;
1598 	int16_t *s = (int16_t *)&u;
1599 	gmt_M_unused(GMT);
1600 	for (i = 0; i < n; ++i) {
1601 		*s = (int16_t) d[i];
1602 		u = bswap16 (u);
1603 		if (gmt_M_fwrite (&u, sizeof (uint16_t), 1U, fp) != 1U)
1604 			return (GMT_DATA_WRITE_ERROR);
1605 	}
1606 	return (GMT_OK);
1607 }
1608 
1609 /*! . */
gmtio_H_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1610 GMT_LOCAL int gmtio_H_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1611 	/* write uint16_t */
1612 	uint64_t i;
1613 	uint16_t u;
1614 	gmt_M_unused(GMT);
1615 	for (i = 0; i < n; ++i) {
1616 		u = (uint16_t) d[i];
1617 		if (gmt_M_fwrite (&u, sizeof (uint16_t), 1U, fp) != 1U)
1618 			return (GMT_DATA_WRITE_ERROR);
1619 	}
1620 	return (GMT_OK);
1621 }
1622 
1623 /*! . */
gmtio_H_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1624 GMT_LOCAL int gmtio_H_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1625 	/* write byteswapped uint16_t */
1626 	uint64_t i;
1627 	uint16_t u;
1628 	gmt_M_unused(GMT);
1629 	for (i = 0; i < n; ++i) {
1630 		u = bswap16 ((uint16_t) d[i]);
1631 		if (gmt_M_fwrite (&u, sizeof (uint16_t), 1U, fp) != 1U)
1632 			return (GMT_DATA_WRITE_ERROR);
1633 	}
1634 	return (GMT_OK);
1635 }
1636 
1637 /*! . */
gmtio_i_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1638 GMT_LOCAL int gmtio_i_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1639 	/* write int32_t */
1640 	uint64_t i;
1641 	int32_t s;
1642 	gmt_M_unused(GMT);
1643 	for (i = 0; i < n; ++i) {
1644 		s = (int32_t) d[i];
1645 		if (gmt_M_fwrite (&s, sizeof (int32_t), 1U, fp) != 1U)
1646 			return (GMT_DATA_WRITE_ERROR);
1647 	}
1648 	return (GMT_OK);
1649 }
1650 
1651 /*! . */
gmtio_i_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1652 GMT_LOCAL int gmtio_i_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1653 	/* write byteswapped int32_t */
1654 	uint64_t i;
1655 	uint32_t u;
1656 	int32_t *s = (int32_t *)&u;
1657 	gmt_M_unused(GMT);
1658 	for (i = 0; i < n; ++i) {
1659 		*s = (int32_t) d[i];
1660 		u = bswap32 (u);
1661 		if (gmt_M_fwrite (&u, sizeof (uint32_t), 1U, fp) != 1U)
1662 			return (GMT_DATA_WRITE_ERROR);
1663 	}
1664 	return (GMT_OK);
1665 }
1666 
1667 /*! . */
gmtio_I_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1668 GMT_LOCAL int gmtio_I_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1669 	/* write uint32_t */
1670 	uint64_t i;
1671 	uint32_t u;
1672 	gmt_M_unused(GMT);
1673 	for (i = 0; i < n; ++i) {
1674 		u = (uint32_t) d[i];
1675 		if (gmt_M_fwrite (&u, sizeof (uint32_t), 1U, fp) != 1U)
1676 			return (GMT_DATA_WRITE_ERROR);
1677 	}
1678 	return (GMT_OK);
1679 }
1680 
1681 /*! . */
gmtio_I_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1682 GMT_LOCAL int gmtio_I_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1683 	/* write byteswapped uint32_t */
1684 	uint64_t i;
1685 	uint32_t u;
1686 	gmt_M_unused(GMT);
1687 	for (i = 0; i < n; ++i) {
1688 		u = bswap32 ((uint32_t) d[i]);
1689 		if (gmt_M_fwrite (&u, sizeof (uint32_t), 1U, fp) != 1U)
1690 			return (GMT_DATA_WRITE_ERROR);
1691 	}
1692 	return (GMT_OK);
1693 }
1694 
1695 /*! . */
gmtio_l_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1696 GMT_LOCAL int gmtio_l_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1697 	/* write int64_t */
1698 	uint64_t i;
1699 	int64_t s;
1700 	gmt_M_unused(GMT);
1701 	for (i = 0; i < n; ++i) {
1702 		s = (int64_t) d[i];
1703 		if (gmt_M_fwrite (&s, sizeof (int64_t), 1U, fp) != 1U)
1704 			return (GMT_DATA_WRITE_ERROR);
1705 	}
1706 	return (GMT_OK);
1707 }
1708 
1709 /*! . */
gmtio_l_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1710 GMT_LOCAL int gmtio_l_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1711 	/* write byteswapped int64_t */
1712 	uint64_t i;
1713 	uint64_t u;
1714 	int64_t *s = (int64_t *)&u;
1715 	gmt_M_unused(GMT);
1716 	for (i = 0; i < n; ++i) {
1717 		*s = (int64_t) d[i];
1718 		u = bswap64(u);
1719 		if (gmt_M_fwrite (&u, sizeof (uint64_t), 1U, fp) != 1U)
1720 			return (GMT_DATA_WRITE_ERROR);
1721 	}
1722 	return (GMT_OK);
1723 }
1724 
1725 /*! . */
gmtio_L_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1726 GMT_LOCAL int gmtio_L_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1727 	/* write uint64_t */
1728 	uint64_t i;
1729 	uint64_t u;
1730 	gmt_M_unused(GMT);
1731 	for (i = 0; i < n; ++i) {
1732 		u = (uint64_t) d[i];
1733 		if (gmt_M_fwrite (&u, sizeof (int64_t), 1U, fp) != 1U)
1734 			return (GMT_DATA_WRITE_ERROR);
1735 	}
1736 	return (GMT_OK);
1737 }
1738 
1739 /*! . */
gmtio_L_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1740 GMT_LOCAL int gmtio_L_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1741 	/* write byteswapped uint64_t */
1742 	uint64_t i;
1743 	uint64_t u;
1744 	gmt_M_unused(GMT);
1745 	for (i = 0; i < n; ++i) {
1746 		u = bswap64((uint64_t) d[i]);
1747 		if (gmt_M_fwrite (&u, sizeof (uint64_t), 1U, fp) != 1U)
1748 			return (GMT_DATA_WRITE_ERROR);
1749 	}
1750 	return (GMT_OK);
1751 }
1752 
1753 /*! . */
gmtio_f_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1754 GMT_LOCAL int gmtio_f_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1755 	/* write float */
1756 	uint64_t i;
1757 	gmt_M_unused(GMT);
1758 	for (i = 0; i < n; ++i) {
1759 		float f = (float) d[i];
1760 		if (gmt_M_fwrite (&f, sizeof (float), 1U, fp) != 1U)
1761 			return (GMT_DATA_WRITE_ERROR);
1762 	}
1763 	return (GMT_OK);
1764 }
1765 
1766 /*! . */
gmtio_f_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1767 GMT_LOCAL int gmtio_f_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1768 	/* write byteswapped float */
1769 	uint64_t i;
1770 	union {
1771 		float f;
1772 		uint32_t bits;
1773 	} u;
1774 	gmt_M_unused(GMT);
1775 	for (i = 0; i < n; ++i) {
1776 		u.f = (float) d[i];
1777 		u.bits = bswap32(u.bits);
1778 		if (gmt_M_fwrite (&u.bits, sizeof (uint32_t), 1U, fp) != 1U)
1779 			return (GMT_DATA_WRITE_ERROR);
1780 	}
1781 	return (GMT_OK);
1782 }
1783 
1784 /*! . */
gmtio_d_write(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1785 GMT_LOCAL int gmtio_d_write (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1786 	/* write double */
1787 	gmt_M_unused(GMT);
1788 	if (gmt_M_fwrite (d, sizeof (double), n, fp) != n)
1789 		return (GMT_DATA_WRITE_ERROR);
1790 	return (GMT_OK);
1791 }
1792 
1793 /*! . */
gmtio_d_write_swab(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * d)1794 GMT_LOCAL int gmtio_d_write_swab (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *d) {
1795 	/* write byteswapped double */
1796 	uint64_t i;
1797 	union {
1798 		double d;
1799 		uint64_t bits;
1800 	} u;
1801 	gmt_M_unused(GMT);
1802 	for (i = 0; i < n; ++i) {
1803 		u.d = d[i];
1804 		u.bits = bswap64 (u.bits);
1805 		if (gmt_M_fwrite (&u.bits, sizeof (uint64_t), 1U, fp) != 1U)
1806 			return (GMT_DATA_WRITE_ERROR);
1807 	}
1808 	return (GMT_OK);
1809 }
1810 
1811 /*! . */
gmtio_get_ymdj_order(struct GMT_CTRL * GMT,char * text,struct GMT_DATE_IO * S)1812 GMT_LOCAL bool gmtio_get_ymdj_order (struct GMT_CTRL *GMT, char *text, struct GMT_DATE_IO *S) {
1813 	/* Reads a YYYY-MM-DD or YYYYMMDD-like string and determines order.
1814 	 * order[0] is the order of the year, [1] is month, etc.
1815 	 * Items not encountered are left as -1.
1816 	 */
1817 
1818 	unsigned int i, j, order, n_y, n_m, n_d, n_j, n_w, error = 0;
1819 	int k, last, n_delim;
1820 	bool watch = false;
1821 	const size_t s_length = strlen(text);
1822 
1823 	gmt_M_memset (S, 1, struct GMT_DATE_IO);
1824 	for (i = 0; i < 4; i++) S->item_order[i] = S->item_pos[i] = GMT_NOTSET;	/* Meaning not encountered yet */
1825 
1826 	n_y = n_m = n_d = n_j = n_w = n_delim = 0;
1827 
1828 	i = 0;
1829 	if (text[i] == '-') {	/* Leading hyphen means use %d and not %x.xd for integer formats */
1830 		S->compact = true;
1831 		i++;
1832 	}
1833 	for (order = 0; i < s_length; i++) {
1834 		switch (text[i]) {
1835 			case 'y':	/* Year */
1836 				if (S->item_pos[0] < 0)		/* First time we encounter a y */
1837 					S->item_pos[0] = order++;
1838 				else if (text[i-1] != 'y')	/* Done it before, previous char must be y */
1839 					error++;
1840 				n_y++;
1841 				break;
1842 			case 'm':	/* Month */
1843 				if (S->item_pos[1] < 0)		/* First time we encounter a m */
1844 					S->item_pos[1] = order++;
1845 				else if (text[i-1] != 'm')	/* Done it before, previous char must be m */
1846 					error++;
1847 				n_m++;
1848 				break;
1849 			case 'o':	/* Month name (plot output only) */
1850 				if (S->item_pos[1] < 0)		/* First time we encounter an o */
1851 					S->item_pos[1] = order++;
1852 				else				/* Done it before is error here */
1853 					error++;
1854 				S->mw_text = true;
1855 				n_m = 2;	/* This just flags it as properly reading 'mm' to past check below */
1856 				break;
1857 
1858 			case 'W':	/* ISO Week flag */
1859 				S->iso_calendar = true;
1860 				break;
1861 			case 'w':	/* ISO Week */
1862 				if (S->item_pos[1] < 0) {		/* First time we encounter a w */
1863 					S->item_pos[1] = order++;
1864 					if (text[i-1] != 'W') error++;	/* Must have the format W just before */
1865 				}
1866 				else if (text[i-1] != 'w')	/* Done it before, previous char must be w */
1867 					error++;
1868 				n_w++;
1869 				break;
1870 			case 'u':	/* ISO Week name ("Week 04") (plot output only) */
1871 				S->iso_calendar = true;
1872 				if (S->item_pos[1] < 0) {		/* First time we encounter a u */
1873 					S->item_pos[1] = order++;
1874 				}
1875 				else 				/* Done it before is an error */
1876 					error++;
1877 				S->mw_text = true;
1878 				n_w = 2;
1879 				break;
1880 			case 'd':	/* Day of month */
1881 				if (S->item_pos[2] < 0)		/* First time we encounter a d */
1882 					S->item_pos[2] = order++;
1883 				else if (text[i-1] != 'd')	/* Done it before, previous char must be d */
1884 					error++;
1885 				n_d++;
1886 				break;
1887 			case 'j':	/* Day of year  */
1888 				S->day_of_year = true;
1889 				if (S->item_pos[3] < 0)		/* First time we encounter a j */
1890 					S->item_pos[3] = order++;
1891 				else if (text[i-1] != 'j')	/* Done it before, previous char must be j */
1892 					error++;
1893 				n_j++;
1894 				break;
1895 			default:	/* Delimiter of some kind */
1896 				if (n_delim == 2)
1897 					error++;
1898 				else
1899 					S->delimiter[n_delim++][0] = text[i];
1900 				break;
1901 		}
1902 	}
1903 
1904 	/* Then get the actual order by inverting table */
1905 
1906 	for (k = 0; k < 4; k++) for (j = 0; j < 4; j++) if (S->item_pos[j] == k) S->item_order[k] = j;
1907 	S->Y2K_year = (n_y == 2);		/* Must supply the century when reading and take it out when writing */
1908 	S->truncated_cal_is_ok = true;		/* May change in the next loop */
1909 	for (i = 1, last = S->item_order[0]; S->truncated_cal_is_ok && i < 4; i++) {
1910 		if (S->item_order[i] == GMT_NOTSET) continue;
1911 		if (S->item_order[i] < last) S->truncated_cal_is_ok = false;
1912 		last = S->item_order[i];
1913 	}
1914 	last = (n_y > 0) + (n_m > 0) + (n_w > 0) + (n_d > 0) + (n_j > 0);	/* This is the number of items to read */
1915 	error += (n_delim && (last - 1) != n_delim);				/* If there are delimiters, must be one less than the items */
1916 	if (S->iso_calendar) {		/* Check if ISO Week format is ok */
1917 		error += (!S->truncated_cal_is_ok);
1918 		error += (n_w != 2);			/* Gotta have 2 ww */
1919 		error += !(n_d == 1 || n_d == 0);	/* Gotta have 1 d if present */
1920 	}
1921 	else {				/* Check if Gregorian format is ok */
1922 		error += (n_w != 0);	/* Should have no w */
1923 		error += (n_j == 3 && !(n_m == 0 && n_d == 0));	/* day of year should have m = d = 0 */
1924 		error += (n_j == 0 && !((n_m == 2 || n_m == 0) && (n_d == 2 || n_d == 0) && n_d <= n_m));	/* mm/dd must have jjj = 0 and m >= d and m,d 0 or 2 */
1925 		if (S->mw_text && S->item_order[last-1] == 1) watch = true;	/* Input ends with monthname */
1926 	}
1927 	if (error) {
1928 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unacceptable date template %s\n", text);
1929 		GMT->parent->error = GMT_PARSE_ERROR;
1930 		return false;
1931 	}
1932 	return (watch);
1933 }
1934 
1935 /*! . */
gmtio_get_hms_order(struct GMT_CTRL * GMT,char * text,struct GMT_CLOCK_IO * S)1936 GMT_LOCAL int gmtio_get_hms_order (struct GMT_CTRL *GMT, char *text, struct GMT_CLOCK_IO *S) {
1937 	/* Reads a HH:MM:SS or HHMMSS-like string and determines order.
1938 	 * hms_order[0] is the order of the hour, [1] is min, etc.
1939 	 * Items not encountered are left as -1.
1940 	 */
1941 
1942 	int i, j, order, n_delim, sequence[3], last, n_h, n_m, n_s, n_x, n_dec, error = 0;
1943 	bool big_to_small;
1944 	char *p = NULL;
1945 	ptrdiff_t off;
1946 
1947 	for (i = 0; i < 3; i++) S->order[i] = GMT_NOTSET;	/* Meaning not encountered yet */
1948 	sequence[0] = sequence[1] = sequence[2] = GMT_NOTSET;
1949 
1950 	S->delimiter[0][0] = S->delimiter[0][1] = S->delimiter[1][0] = S->delimiter[1][1] = 0;
1951 	n_h = n_m = n_s = n_x = n_dec = n_delim = 0;
1952 
1953 	/* Determine if we do 12-hour clock (and what form of am/pm suffix) or 24-hour clock */
1954 
1955 	if ((p = strstr (text, "am"))) {	/* Want 12 hour clock with am/pm */
1956 		S->twelve_hr_clock = true;
1957 		strcpy (S->ampm_suffix[0], "am");
1958 		strcpy (S->ampm_suffix[1], "pm");
1959 		off = p - text;
1960 	}
1961 	else if ((p = strstr (text, "AM"))) {	/* Want 12 hour clock with AM/PM */
1962 		S->twelve_hr_clock = true;
1963 		strcpy (S->ampm_suffix[0], "AM");
1964 		strcpy (S->ampm_suffix[1], "PM");
1965 		off = p - text;
1966 	}
1967 	else if ((p = strstr (text, "a.m."))) {	/* Want 12 hour clock with a.m./p.m. */
1968 		S->twelve_hr_clock = true;
1969 		strcpy (S->ampm_suffix[0], "a.m.");
1970 		strcpy (S->ampm_suffix[1], "p.m.");
1971 		off = p - text;
1972 	}
1973 	else if ((p = strstr (text, "A.M."))) {	/* Want 12 hour clock with A.M./P.M. */
1974 		S->twelve_hr_clock = true;
1975 		strcpy (S->ampm_suffix[0], "A.M.");
1976 		strcpy (S->ampm_suffix[1], "P.M.");
1977 		off = p - text;
1978 	}
1979 	else
1980 		off = strlen (text);
1981 
1982 	i = 0;
1983 	if (text[i] == '-') {	/* Leading hyphen means use %d and not %x.xd for integer formats */
1984 		S->compact = true;
1985 		i++;
1986 	}
1987 	for (order = 0; i < off; i++) {
1988 		switch (text[i]) {
1989 			case 'h':	/* Hour */
1990 				if (S->order[0] < 0)		/* First time we encountered an h */
1991 					S->order[0] = order++;
1992 				else if (text[i-1] != 'h')	/* Must follow a previous h */
1993 					error++;
1994 				n_h++;
1995 				break;
1996 			case 'm':	/* Minute */
1997 				if (S->order[1] < 0)		/* First time we encountered an m */
1998 					S->order[1] = order++;
1999 				else if (text[i-1] != 'm')	/* Must follow a previous m */
2000 					error++;
2001 				n_m++;
2002 				break;
2003 			case 's':	/* Seconds */
2004 				if (S->order[2] < 0)		/* First time we encountered an s */
2005 					S->order[2] = order++;
2006 				else if (text[i-1] != 's')	/* Must follow a previous s */
2007 					error++;
2008 				n_s++;
2009 				break;
2010 			case '.':	/* Decimal point for seconds? */
2011 				if (text[i+1] == 'x')
2012 					n_dec++;
2013 				else {	/* Must be a delimiter */
2014 					if (n_delim == 2)
2015 						error++;
2016 					else
2017 						S->delimiter[n_delim++][0] = text[i];
2018 				}
2019 				break;
2020 			case 'x':	/* Fraction of seconds */
2021 				if (n_x > 0 && text[i-1] != 'x')	/* Must follow a previous x */
2022 					error++;
2023 				n_x++;
2024 				break;
2025 			default:	/* Delimiter of some kind */
2026 				if (n_delim == 2)
2027 					error++;
2028 				else
2029 					S->delimiter[n_delim++][0] = text[i];
2030 				break;
2031 		}
2032 	}
2033 
2034 	/* Then get the actual order by inverting table */
2035 
2036 	for (i = 0; i < 3; i++)
2037 		for (j = 0; j < 3; j++)
2038 			if (S->order[j] == i) sequence[i] = j;
2039 	for (i = 0; i < 3; i++) S->order[i] = sequence[i];
2040 	big_to_small = true;		/* May change in the next loop */
2041 	for (i = 1, last = S->order[0]; big_to_small && i < 3; i++) {
2042 		if (S->order[i] == GMT_NOTSET) continue;
2043 		if (S->order[i] < last) big_to_small = false;
2044 		last = S->order[i];
2045 	}
2046 	if (!big_to_small) error++;
2047 	last = (n_h > 0) + (n_m > 0) + (n_s > 0);	/* This is the number of items to read */
2048 	error += (n_delim && (last - 1) != n_delim);	/* If there are delimiters, must be one less than the items */
2049 	error += (!(n_h == 0 || n_h == 2) || !(n_m == 0 || n_m == 2) || !(n_s == 0 || n_s == 2));	/* h, m, s are all either 2 or 0 */
2050 	error += (n_s > n_m || n_m > n_h);		/* Cannot have secs without m etc */
2051 	error += (n_x && n_dec != 1);			/* .xxx is the proper form */
2052 	error += (n_x == 0 && n_dec);			/* Period by itself and not delimiter? */
2053 	error += (n_dec > 1);				/* Only one period with xxx */
2054 	S->n_sec_decimals = n_x;
2055 	S->f_sec_to_int = rint (pow (10.0, (double)S->n_sec_decimals));			/* To scale fractional seconds to an integer form */
2056 	if (error) {
2057 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unacceptable clock template %s\n", text);
2058 		GMT->parent->error = GMT_PARSE_ERROR;
2059 		return GMT_PARSE_ERROR;
2060 	}
2061 	return (GMT_NOERROR);
2062 }
2063 
2064 /*! . */
gmtio_get_dms_order(struct GMT_CTRL * GMT,char * text,struct GMT_GEO_IO * S)2065 GMT_LOCAL int gmtio_get_dms_order (struct GMT_CTRL *GMT, char *text, struct GMT_GEO_IO *S) {
2066 	/* Reads a ddd:mm:ss-like string and determines order.
2067 	 * order[0] is the order of the degree, [1] is minutes, etc.
2068 	 * Order is checked since we only allow d, m, s in that order.
2069 	 * Items not encountered are left as -1.
2070 	 */
2071 
2072 	unsigned int j, n_d, n_m, n_s, n_x, n_dec, n_period, order, error = 0;
2073 	int sequence[3], last, i_signed, n_delim;
2074 	size_t i1, i;
2075 	bool big_to_small;
2076 
2077 	for (i = 0; i < 3; i++) S->order[i] = GMT_NOTSET;	/* Meaning not encountered yet */
2078 
2079 	n_d = n_m = n_s = n_x = n_dec = n_delim = n_period = 0;
2080 	S->delimiter[0][0] = S->delimiter[0][1] = S->delimiter[1][0] = S->delimiter[1][1] = 0;
2081 	sequence[0] = sequence[1] = sequence[2] = GMT_NOTSET;
2082 
2083 	S->range = GMT_IS_M180_TO_P180_RANGE;			/* -180/+180 range, may be overwritten below by + or - */
2084 	S->decimal = S->no_sign = false;
2085 	S->wesn = 0;
2086 
2087 	i1 = strlen (text) - 1;
2088 	for (i = order = 0; i <= i1; i++) {
2089 		switch (text[i]) {
2090 			case '+':	/* Want [0-360 range [Default] */
2091 				S->range = GMT_IS_0_TO_P360_RANGE;
2092 				if (i != 0) error++;		/* Only valid as first flag */
2093 				break;
2094 			case '-':	/* Want [-360-0] range [i.e., western longitudes] */
2095 				S->range = GMT_IS_M360_TO_0_RANGE;
2096 				if (i != 0) error++;		/* Only valid as first flag */
2097 				break;
2098 			case 'D':	/* Want to use decimal degrees using FORMAT_FLOAT_OUT [Default] */
2099 				S->decimal = true;
2100 				if (i > 1) error++;		/* Only valid as first or second flag */
2101 				break;
2102 			case 'F':	/* Want to use WESN to encode sign */
2103 				S->wesn = 1;
2104 				if (i != i1 || S->no_sign) error++;		/* Only valid as last flag */
2105 				break;
2106 			case 'G':	/* Want to use WESN to encode sign but have leading space */
2107 				S->wesn = 2;
2108 				if (i != i1 || S->no_sign) error++;		/* Only valid as last flag */
2109 				break;
2110 			case 'A':	/* Want no sign in plot string */
2111 				S->no_sign = true;
2112 				if (i != i1 || S->wesn) error++;		/* Only valid as last flag */
2113 				break;
2114 			case 'd':	/* Degree */
2115 				if (S->order[0] < 0)		/* First time we encounter a d */
2116 					S->order[0] = order++;
2117 				else if (text[i-1] != 'd')	/* Done it before, previous char must be y */
2118 					error++;
2119 				n_d++;
2120 				break;
2121 			case 'm':	/* Minute */
2122 				if (S->order[1] < 0)		/* First time we encounter a m */
2123 					S->order[1] = order++;
2124 				else if (text[i-1] != 'm')	/* Done it before, previous char must be m */
2125 					error++;
2126 				n_m++;
2127 				break;
2128 			case 's':	/* Seconds */
2129 				if (S->order[2] < 0) {		/* First time we encounter a s */
2130 					S->order[2] = order++;
2131 				}
2132 				else if (text[i-1] != 's')	/* Done it before, previous char must be s */
2133 					error++;
2134 				n_s++;
2135 				break;
2136 			case '.':	/* Decimal point for seconds? */
2137 				n_period++;
2138 				if (text[i+1] == 'x')
2139 					n_dec++;
2140 				else {	/* Must be a delimiter */
2141 					if (n_delim == 2)
2142 						error++;
2143 					else
2144 						S->delimiter[n_delim++][0] = text[i];
2145 				}
2146 				break;
2147 			case 'x':	/* Fraction of seconds */
2148 				if (n_x > 0 && text[i-1] != 'x')	/* Must follow a previous x */
2149 					error++;
2150 				n_x++;
2151 				break;
2152 			default:	/* Delimiter of some kind */
2153 				if (n_delim == 2)
2154 					error++;
2155 				else
2156 					S->delimiter[n_delim++][0] = text[i];
2157 				break;
2158 		}
2159 	}
2160 
2161 	if (S->decimal) return (GMT_NOERROR);	/* Easy formatting choice */
2162 
2163 	/* Then get the actual order by inverting table */
2164 
2165 	for (i_signed = 0; i_signed < 3; i_signed++)
2166 		for (j = 0; j < 3; j++)
2167 			if (S->order[j] == i_signed) sequence[i_signed] = j;
2168 	for (i = 0; i < 3; i++) S->order[i] = sequence[i];
2169 	big_to_small = true;		/* May change in the next loop */
2170 	for (i = 1, last = S->order[0]; big_to_small && i < 3; i++) {
2171 		if (S->order[i] == GMT_NOTSET) continue;
2172 		if (S->order[i] < last) big_to_small = false;
2173 		last = S->order[i];
2174 	}
2175 	if (!big_to_small) error++;
2176 	last = (n_d > 0) + (n_m > 0) + (n_s > 0);	/* This is the number of items to read */
2177 	error += (n_delim && (last - 1) != n_delim);	/* If there are delimiters, must be one less than the items */
2178 	error += (!(n_d == 0 || n_d == 3) || !(n_m == 0 || n_m == 2) || !(n_s == 0 || n_s == 2));	/* d, m, s are all either 2(3) or 0 */
2179 	error += (n_s > n_m || n_m > n_d);		/* Cannot have secs without m etc */
2180 	error += (n_x && n_dec != 1);			/* .xxx is the proper form */
2181 	error += (n_x == 0 && n_dec);			/* Period by itself and not delimiter? */
2182 	error += (n_dec > 1);				/* Only one period with xxx */
2183 	S->n_sec_decimals = n_x;
2184 	S->f_sec_to_int = rint (pow (10.0, (double)S->n_sec_decimals));			/* To scale fractional seconds to an integer form */
2185 	if (error) {
2186 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unacceptable dmmss template %s\n", text);
2187 		GMT->parent->error = GMT_PARSE_ERROR;
2188 		return GMT_PARSE_ERROR;
2189 	}
2190 	else if (n_period > 1)
2191 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Multiple periods in dmmss template %s is likely to lead to confusion\n", text);
2192 	return (GMT_NOERROR);
2193 }
2194 
2195 /*! . */
gmtio_scanf_clock(struct GMT_CTRL * GMT,char * s,double * val)2196 GMT_LOCAL int gmtio_scanf_clock (struct GMT_CTRL *GMT, char *s, double *val) {
2197 	/* On failure, return -1.  On success, set val and return 0.
2198 
2199 	Looks for apAP, but doesn't discover a failure if called with "11:13:15 Hello, Walter",
2200 	because it will find an a.
2201 
2202 	Doesn't check whether use of a or p matches stated intent to use twelve_hour_clock.
2203 
2204 	ISO standard allows 24:00:00, so 86400 is not too big.
2205 	If the day of this clock might be a day with a leap second, (this routine doesn't know that)
2206 	then we should also allow 86401.  A value exceeding 86401 is an error.
2207 	*/
2208 
2209 	int k, hh, mm, add_noon = 0, hh_limit = 24;	/* ISO std allows 24:00:00  */
2210 	double ss, x;
2211 	char *p = NULL;
2212 
2213 	if ( (p = strpbrk (s, "apAP") ) ) {
2214 		switch (p[0]) {
2215 			case 'a':
2216 			case 'A':
2217 				add_noon = 0;
2218 				hh_limit = 12;
2219 				break;
2220 			case 'p':
2221 			case 'P':
2222 				add_noon = 43200;
2223 				hh_limit = 12;
2224 				break;
2225 			default:
2226 				return (GMT_NOTSET);
2227 				break;
2228 		}
2229 	}
2230 
2231 	k = sscanf (s, GMT->current.io.clock_input.format, &hh, &mm, &ss);
2232 	if (k == 0) return (GMT_NOTSET);
2233 	if (hh < 0 || hh > hh_limit) return (GMT_NOTSET);
2234 
2235 	x = (double)(add_noon + 3600*hh);
2236 	if (k > 1) {
2237 		if (mm < 0 || mm > 59) return (GMT_NOTSET);
2238 		x += 60*mm;
2239 	}
2240 	if (k > 2) {
2241 		x += ss;
2242 		if (x > 86401.0) return (GMT_NOTSET);
2243 	}
2244 	*val = x;
2245 	return (0);
2246 }
2247 
2248 /*! . */
gmtio_scanf_ISO_calendar(struct GMT_CTRL * GMT,char * s,int64_t * rd)2249 GMT_LOCAL int gmtio_scanf_ISO_calendar (struct GMT_CTRL *GMT, char *s, int64_t *rd) {
2250 
2251 	/* On failure, return -1.  On success, set rd and return 0.
2252 	Assumes that year, week of year, day of week appear in that
2253 	order only, and that the format string can handle the W.
2254 	Assumes also that it is always OK to fill in missing bits.  */
2255 
2256 	int k, n, ival[3];
2257 
2258 	if ((n = sscanf (s, GMT->current.io.date_input.format, &ival[0], &ival[1], &ival[2])) <= 0) return (GMT_NOTSET);
2259 
2260 	/* Handle possible missing bits */
2261 	for (k = n; k < 3; k++) ival[k] = 1;
2262 
2263 	if (ival[1] < 1 || ival[1] > 53) return (GMT_NOTSET);
2264 	if (ival[2] < 1 || ival[2] > 7) return (GMT_NOTSET);
2265 	if (GMT->current.io.date_input.Y2K_year) {
2266 		if (ival[0] < 0 || ival[0] > 99) return (GMT_NOTSET);
2267 		ival[0] = gmtlib_y2_to_y4_yearfix (GMT, ival[0]);
2268 	}
2269 	*rd = gmtlib_rd_from_iywd (GMT, ival[0], ival[1], ival[2]);
2270 	return (0);
2271 }
2272 
2273 /*! . */
gmtio_scanf_g_calendar(struct GMT_CTRL * GMT,char * s,int64_t * rd)2274 GMT_LOCAL int gmtio_scanf_g_calendar (struct GMT_CTRL *GMT, char *s, int64_t *rd) {
2275 	/* Return -1 on failure.  Set rd and return 0 on success.
2276 
2277 	For gregorian calendars.  */
2278 
2279 	int i, k, ival[4];
2280 	char month[16];
2281 
2282 	if (GMT->current.io.date_input.day_of_year) {
2283 		/* Calendar uses year and day of year format.  */
2284 		if ( (k = sscanf (s, GMT->current.io.date_input.format,
2285 			&ival[GMT->current.io.date_input.item_order[0]],
2286 			&ival[GMT->current.io.date_input.item_order[1]]) ) == 0) return (GMT_NOTSET);
2287 		if (k < 2) {
2288 			if (!GMT->current.io.date_input.truncated_cal_is_ok) return (GMT_NOTSET);
2289 			ival[1] = 1;	/* Set first day of year  */
2290 		}
2291 		if (GMT->current.io.date_input.Y2K_year) {
2292 			if (ival[0] < 0 || ival[0] > 99) return (GMT_NOTSET);
2293 			ival[0] = gmtlib_y2_to_y4_yearfix (GMT, ival[0]);
2294 		}
2295 		k = (gmtlib_is_gleap (ival[0])) ? 366 : 365;
2296 		if (ival[3] < 1 || ival[3] > k) return (GMT_NOTSET);
2297 		*rd = gmt_rd_from_gymd (GMT, ival[0], 1, 1) + ival[3] - 1;
2298 		return (0);
2299 	}
2300 
2301 	/* Get here when calendar type has months and days of months.  */
2302 
2303 	if (GMT->current.io.date_input.mw_text) {	/* Have month name abbreviation in data format */
2304 		switch (GMT->current.io.date_input.item_pos[1]) {	/* Order of month in data string */
2305 			case 0:	/* e.g., JAN-24-1987 or JAN-1987-24 */
2306 				k = sscanf (s, GMT->current.io.date_input.format, month, &ival[GMT->current.io.date_input.item_order[1]],
2307 				            &ival[GMT->current.io.date_input.item_order[2]]);
2308 				break;
2309 			case 1:	/* e.g., 24-JAN-1987 or 1987-JAN-24 */
2310 				k = sscanf (s, GMT->current.io.date_input.format, &ival[GMT->current.io.date_input.item_order[0]], month,
2311 				            &ival[GMT->current.io.date_input.item_order[2]]);
2312 				break;
2313 			case 2:	/* e.g., JAN-24-1987 ? */
2314 				k = sscanf (s, GMT->current.io.date_input.format, &ival[GMT->current.io.date_input.item_order[0]],
2315 				            &ival[GMT->current.io.date_input.item_order[1]], month);
2316 				break;
2317 			default:
2318 				return (GMT_NOTSET);
2319 				break;
2320 		}
2321 		gmt_str_toupper (month);
2322 		for (i = ival[1] = 0; i < 12 && ival[1] == 0; i++) {
2323 			if (!strcmp (month, GMT->current.language.month_name[3][i])) ival[1] = i + 1;
2324 		}
2325 		if (ival[1] == 0) return (GMT_NOTSET);	/* No match for month name */
2326 	}
2327 	else if ((k = sscanf (s, GMT->current.io.date_input.format, &ival[GMT->current.io.date_input.item_order[0]],
2328 	                      &ival[GMT->current.io.date_input.item_order[1]], &ival[GMT->current.io.date_input.item_order[2]])) == 0)
2329 		return (GMT_NOTSET);
2330 	if (k < 3) {
2331 		if (GMT->current.io.date_input.truncated_cal_is_ok) {
2332 			ival[2] = 1;	/* Set first day of month  */
2333 			if (k == 1) ival[1] = 1;	/* Set first month of year */
2334 		}
2335 		else
2336 			return (GMT_NOTSET);
2337 	}
2338 	if (GMT->current.io.date_input.Y2K_year) {
2339 		if (ival[0] < 0 || ival[0] > 99) return (GMT_NOTSET);
2340 		ival[0] = gmtlib_y2_to_y4_yearfix (GMT, ival[0]);
2341 	}
2342 
2343 	if (gmtlib_g_ymd_is_bad (ival[0], ival[1], ival[2]) ) return (GMT_NOTSET);
2344 
2345 	*rd = gmt_rd_from_gymd (GMT, ival[0], ival[1], ival[2]);
2346 	return (0);
2347 }
2348 
2349 /*! . */
gmtio_scanf_calendar(struct GMT_CTRL * GMT,char * s,int64_t * rd)2350 GMT_LOCAL int gmtio_scanf_calendar (struct GMT_CTRL *GMT, char *s, int64_t *rd) {
2351 	/* On failure, return -1.  On success, set rd and return 0 */
2352 	if (GMT->current.io.date_input.iso_calendar) return (gmtio_scanf_ISO_calendar (GMT, s, rd));
2353 	return (gmtio_scanf_g_calendar (GMT, s, rd));
2354 }
2355 
2356 /*! . */
gmtio_scanf_geo(char * s,double * val)2357 GMT_LOCAL int gmtio_scanf_geo (char *s, double *val) {
2358 	/* Try to read a character string token stored in s, knowing that it should be a geographical variable.
2359 	If successful, stores value in val and returns one of GMT_IS_FLOAT, GMT_IS_GEO, GMT_IS_LAT, GMT_IS_LON,
2360 	whichever can be determined from the format of s.
2361 	If unsuccessful, does not store anything in val and returns GMT_IS_NAN.
2362 	This should have essentially the same functionality as the GMT3.4 gmt_scanf, except that the expectation
2363 	is now used and returned, and this also permits a double precision format in the minutes or seconds,
2364 	and does more error checking.  However, this is not optimized for speed (yet).  WHFS, 16 Aug 2001
2365 
2366 	Note: Mismatch handling (e.g. this routine finds a lon but calling routine expected a lat) is not
2367 	done here.
2368 	*/
2369 
2370 	int retval = GMT_IS_FLOAT, id, im;
2371 	bool negate = false;
2372 	unsigned int ncolons;
2373 	size_t k;
2374 	char scopy[GMT_LEN64] = {""}, suffix, *p = NULL, *p2 = NULL;
2375 	double dd, dm, ds;
2376 
2377 	k = strlen (s);
2378 	if (k == 0 || (int)s[k - 1] < 0) return (GMT_IS_NAN);		/* On Win and debug mode isdigit assert crashes with negative argument */
2379 	if (!(isdigit ((int)s[k-1]))) {
2380 		suffix = s[k-1];
2381 		switch (suffix) {
2382 			case 'W': case 'w':
2383 				negate = true;
2384 				retval = GMT_IS_LON;
2385 				break;
2386 			case 'E': case 'e':
2387 				retval = GMT_IS_LON;
2388 				break;
2389 			case 'S': case 's':
2390 				negate = true;
2391 				retval = GMT_IS_LAT;
2392 				break;
2393 			case 'N': case 'n':
2394 				retval = GMT_IS_LAT;
2395 				break;
2396 			case 'G': case 'g': case 'D': case 'd':
2397 				retval = GMT_IS_GEO;
2398 				break;
2399 			case '.':	/* Decimal point without decimals, e.g., 123. */
2400 				break;
2401 			default:
2402 				return (GMT_IS_NAN);
2403 				break;
2404 		}
2405 		k--;
2406 	}
2407 	if (k >= GMT_LEN64) return (GMT_IS_NAN);
2408 	strncpy (scopy, s, k);				/* Copy all but the suffix  */
2409 	scopy[k] = 0;
2410 	ncolons = 0;
2411 	if ((p = strpbrk (scopy, "dD"))) {
2412 		/* We found a D or d.  */
2413 		if (strlen (p) == 1 || (strpbrk (&p[1], "dD:") ) ){
2414 			/* It is at the end, or followed by a colon or another d or D.  */
2415 			return (GMT_IS_NAN);
2416 		}
2417 		/* Map it to an e, permitting FORTRAN Double Precision formats.  */
2418 		p[0] = 'e';
2419 	}
2420 	p = scopy;
2421 	while ((p2 = strpbrk (p, ":"))) {
2422 		if (strlen (p2) == 1) return (GMT_IS_NAN);	/* Shouldn't end with a colon  */
2423 		ncolons++;
2424 		if (ncolons > 2) return (GMT_IS_NAN);
2425 		p = &p2[1];
2426 	}
2427 
2428 	if (ncolons && retval == GMT_IS_FLOAT) retval = GMT_IS_GEO;
2429 
2430 	dd = 0.0;
2431 	switch (ncolons) {
2432 		case 0:
2433 			if ((sscanf (scopy, "%lf", &dd)) != 1) return (GMT_IS_NAN);
2434 			break;
2435 		case 1:
2436 			if ((sscanf (scopy, "%d:%lf", &id, &dm)) != 2) return (GMT_IS_NAN);
2437 			dd = dm * GMT_MIN2DEG;
2438 			if (id < 0)	/* Negative degrees present, subtract the fractional part */
2439 				dd = id - dd;
2440 			else if (id > 0)	/* Positive degrees present, add the fractional part */
2441 				dd = id + dd;
2442 			else {			/* degree part is 0; check if a leading sign is present */
2443 				if (scopy[0] == '-') dd = -dd;	/* Make fraction negative */
2444 			}
2445 			break;
2446 		case 2:
2447 			if ((sscanf (scopy, "%d:%d:%lf", &id, &im, &ds)) != 3) return (GMT_IS_NAN);
2448 			dd = im * GMT_MIN2DEG + ds * GMT_SEC2DEG;
2449 			if (id < 0)	/* Negative degrees present, subtract the fractional part */
2450 				dd = id - dd;
2451 			else if (id > 0)	/* Positive degrees present, add the fractional part */
2452 				dd = id + dd;
2453 			else {			/* degree part is 0; check if a leading sign is present */
2454 				if (scopy[0] == '-') dd = -dd;	/* Make fraction negative */
2455 			}
2456 			break;
2457 	}
2458 	*val = (negate) ? -dd : dd;
2459 	if (fabs (dd) > 90.0 && retval == GMT_IS_GEO) retval = GMT_IS_LON;
2460 	return (retval);
2461 }
2462 
gmtio_is_pi(char * txt)2463 GMT_LOCAL bool gmtio_is_pi (char *txt) {
2464 	/* Return true if txt is of the form [+|-][s]pi[f] */
2465 	unsigned int k = 0;
2466 	if (txt == NULL) return false;
2467 	if (txt[k] == '+' || txt[k] == '-') k++;	/* Skip a leading sign */
2468 	while (txt[k] && isdigit (txt[k])) k++;	/* Skip the s number, which must be integer */
2469 	if (txt[k] == '\0' || strncmp (&txt[k], "pi", 2U)) return false;	/* No pi found */
2470 	if (txt[k+1] == '\0') return false;	/* Did not have two characters */
2471 	k += 2;	/* Skip the pi part */
2472 	if (txt[k] == '\0') return true;	/* OK so far; no fraction f involved */
2473 	while (txt[k] && isdigit (txt[k])) k++;	/* Skip the f number, which must be integer */
2474 	if (txt[k]) return false;	/* Got something at the end of the token that is not part of a pi specification */
2475 	return true;	/* Made it past all tests */
2476 }
2477 
2478 /*! . */
gmt_scanf_float(struct GMT_CTRL * GMT,char * s,double * val)2479 int gmt_scanf_float (struct GMT_CTRL *GMT, char *s, double *val) {
2480 	/* Try to decode a value from s and store
2481 	in val.  s should not have any special format
2482 	(neither geographical, with suffixes or
2483 	separating colons, nor calendar nor clock).
2484 	However, D and d are permitted to map to e
2485 	if this would result in a success.  This
2486 	allows Fortran Double Precision to be readable.
2487 
2488 	On success, return GMT_IS_FLOAT and store val.
2489 	On failure, return GMT_IS_NAN and do not touch val.
2490 	*/
2491 
2492 	char scopy[GMT_LEN64] = {""}, *p = NULL;
2493 	double x;
2494 	size_t j, k;
2495 
2496 	if (gmtio_is_pi (s)) {	/* Got a number given via multiple/fraction of pi */
2497 		/* Only allow parsing of [-|+][s]pi[f], with s and f are any number */
2498 		GMT->current.plot.substitute_pi = true;	/* Used in formatting labels */
2499 		k = 0;
2500 		if (s[0] == '-') x = -1.0, k = 1;
2501 		else if (s[0] == '+') x = 1.0, k = 1;
2502 		else x = 1.0;
2503 		if (isdigit (s[k])) {	/* Need an integer multiple of pi */
2504 			double fval = atof (&s[k]);
2505 			x *= fval;	/* Get multiples of pi */
2506 			while (isdigit (s[k])) k++;
2507 		}
2508 		k += 2;	/* Now skip the pi part */
2509 		if (s[k]) x /= atof (&s[k]);	/* Get pi fraction */
2510 		*val = x * M_PI;	/* Scale up by pi */
2511 		return (GMT_IS_FLOAT);
2512 	}
2513 
2514 	x = strtod (s, &p);
2515 	if (p[0] == 0) {	/* Success (non-Fortran).  */
2516 		*val = x;
2517 		return (GMT_IS_FLOAT);
2518 	}
2519 	if (p[0] != 'D' && p[0] != 'd') return (GMT_IS_NAN);
2520 	k = strlen (p);
2521 	if (k == 1) return (GMT_IS_NAN);	/* A string ending in e would be invalid  */
2522 	/* Make a copy of s in scopy, mapping the d or D to an e */
2523 	j = strlen (s);
2524 	if (j > GMT_LEN64) return (GMT_IS_NAN);
2525 	j -= k;
2526 	strncpy (scopy, s, j);
2527 	scopy[j] = 'e';
2528 	strncpy (&scopy[j+1], &p[1], GMT_LEN64-1);
2529 	x = strtod (scopy, &p);
2530 	if (p[0] != 0) return (GMT_IS_NAN);
2531 	*val = x;
2532 	return (GMT_IS_FLOAT);
2533 }
2534 
2535 /*! . */
gmtio_scanf_dim(struct GMT_CTRL * GMT,char * s,double * val)2536 GMT_LOCAL int gmtio_scanf_dim (struct GMT_CTRL *GMT, char *s, double *val) {
2537 	/* Try to decode a value from s and store
2538 	in val.  s is a regular float with optional
2539 	unit info, e.g., 8.5i or 7.5c.  If a valid unit
2540 	is found we convert the number to inch.
2541 	We also skip any trailing modifiers like +<mods>, e.g.
2542 	vector specifications like 0.5i+jc+b+s
2543 
2544 	We return GMT_IS_FLOAT and pass val.
2545 	*/
2546 
2547 	if (isalpha ((int)s[0]) || (s[1] == 0 && (s[0] == '-' || s[0] == '+')))	/* Probably a symbol character; quietly return 0 */
2548 		*val = 0.0;
2549 	else {	/* Probably a dimension with optional unit.  First check if there are modifiers to ignore here */
2550 		char *p = NULL;
2551 		if ((p = strchr (s, '+')) && !isdigit (p[1])) { /* Found trailing +mod args [and not values like 123.33E+01c] */
2552 			*p = 0;	/* Chop off modifier */
2553 			*val = gmt_M_to_inch (GMT, s);	/* Get dimension */
2554 			*p = '+';	/* Restore modifier */
2555 		}
2556 		else
2557 			*val = gmt_M_to_inch (GMT, s);
2558 	}
2559 	return (GMT_IS_FLOAT);
2560 }
2561 
2562 /*! . */
gmt_scanf_argtime(struct GMT_CTRL * GMT,char * s,double * t)2563 int gmt_scanf_argtime (struct GMT_CTRL *GMT, char *s, double *t) {
2564 	/* s is a string from a command-line argument.
2565 	   The argument is known to refer to a time variable.  For example, the argument is
2566 	   a token from -R<t_min>/<t_max>/a/b[/c/d].  However, we will permit it to be in EITHER
2567 	   -- generic floating point format, in which case we interpret it as relative time
2568 	      in user units since epoch;
2569 	   OR
2570 	   -- absolute time in a restricted format, which is to be converted to relative time.
2571 
2572 	   The absolute format must be restricted because we cannot use '/' as a delimiter in an arg
2573 	   string, but we might allow the user to use that in a data file (in GMT->current.setting.[in/out]put_date_format.
2574 	   Therefore we cannot use the user's date format string here, and we hard-wire something here.
2575 
2576 	   The relative format must be decodable by gmt_scanf_float().  It may optionally end in 't'
2577 	   (which will be stripped off by this routine).
2578 
2579 	   The absolute format must have a T.  If it has a clock string then it must be of the form
2580 	   <complete_calstring>T<clockstring> or just T<clockstring>.  If it has no clockstring then
2581 	   it must be of the form <partial or complete calstring>T.
2582 
2583 	   A <clockstring> may be partial (e.g. hh or hh:mm) or complete (hh:mm:ss[.xxx]) but it must use
2584 	   ':' for a delimiter and it must be readable with "%2d:%2d:%lf".
2585 	   Also, it must be a 24 hour clock (00:00:00 to 23:59:59.xxx,
2586 	   or 60.xxx on a leap second); no am/pm suffixes allowed.
2587 
2588 	   A <calstring> must be of the form
2589 	   [-]yyyy[-mm[-dd]]T readable after first '-' with "%4d-%2d-%2dT" (Gregorian year,month,day)
2590 	   [-]yyyy[-jjj]T readable after first '-' with "%4d-%3dT" (Gregorian year, day-of-year)
2591 	   yyyy[-Www[-d]]T (ISO week calendar)
2592 
2593 	Upon failure, returns GMT_IS_NAN.  Upon success, sets t and returns GMT_IS_ABSTIME.
2594 	We have it return either ABSTIME or RELTIME to indicate which one it thinks it decoded.
2595 	This is inconsistent with the use of gmt_scanf which always returns ABSTIME, even when RELTIME is
2596 	expected and ABSTIME conversion is done internal to the routine, as it is here.
2597 	The reason for returning RELTIME instead is that the -R option needs
2598 	to know which was decoded and hence which is expected as column input.
2599 	*/
2600 
2601 	int ival[3];
2602 	bool negate_year = false, got_yd = false;
2603 	int hh, mm;
2604 	unsigned int j, k, dash;
2605 	size_t i;
2606 	double ss, x;
2607 	char *pw = NULL, *pt = NULL;
2608 
2609 	i = strlen (s) - 1;
2610 	if (s[i] == 't') s[i] = '\0';
2611 	if ( (pt = strchr (s, (int)'T') ) == NULL) {
2612 		/* There is no T.  This must decode with gmt_scanf_float() or we die.  */
2613 		if ((gmt_scanf_float (GMT, s, t)) == GMT_IS_NAN) return (GMT_IS_NAN);
2614 		return (GMT_IS_RELTIME);
2615 	}
2616 	x = 0.0;	/* x will be the seconds since start of today.  */
2617 	if (pt[1]) {	/* There is a string following the T:  Decode a clock */
2618 		k = sscanf (&pt[1], "%2d:%2d:%lf", &hh, &mm, &ss);
2619 		if (k == 0) return (GMT_IS_NAN);
2620 		if (hh < 0 || hh >= 24) return (GMT_IS_NAN);
2621 		x = GMT_HR2SEC_F * hh;
2622 		if (k > 1) {
2623 			if (mm < 0 || mm > 59) return (GMT_IS_NAN);
2624 			x += GMT_MIN2SEC_F * mm;
2625 		}
2626 		if (k > 2) {
2627 			if (ss < 0.0 || ss >= 61.0) return (GMT_IS_NAN);
2628 			x += ss;
2629 		}
2630 	}
2631 
2632 	k = 0;
2633 	while (s[k] && s[k] == ' ') k++;
2634 	if (s[k] == '-') negate_year = true;
2635 	if (s[k] == 'T') {	/* There is no calendar.  Set day to today and use that */
2636 		*t = gmt_rdc2dt (GMT, GMT->current.time.today_rata_die, x);
2637 		return (GMT_IS_ABSTIME);
2638 	}
2639 
2640 	if (!(isdigit ((int)s[k]))) return (GMT_IS_NAN);	/* Bad format */
2641 
2642 	if ((pw = strchr (s, (int)'W'))) {
2643 		/* There is a W.  ISO calendar or junk.  */
2644 		if (strlen (pw) <= strlen (pt)) return (GMT_IS_NAN);	/* The W is after the T.  Wrong format.  */
2645 		if (negate_year) return (GMT_IS_NAN);			/* negative years not allowed in ISO calendar  */
2646 		if ( (j = sscanf(&s[k], "%4d-W%2d-%1d", &ival[0], &ival[1], &ival[2]) ) == 0) return (GMT_IS_NAN);
2647 		for (k = j; k < 3; k++) ival[k] = 1;
2648 		if (gmtlib_iso_ywd_is_bad (ival[0], ival[1], ival[2]) ) return (GMT_IS_NAN);
2649 		*t = gmt_rdc2dt (GMT, gmtlib_rd_from_iywd (GMT, ival[0], ival[1], ival[2]), x);
2650 		return (GMT_IS_ABSTIME);
2651 	}
2652 
2653 	for (i = (negate_year) ? 1 : 0; s[k+i] && s[k+i] != '-'; i++); /* Goes to first - between yyyy and jjj or yyyy and mm */
2654 	dash = (unsigned int)i++;	/* Position of first character after the first dash (could be end of string if no dash) */
2655     if (s[dash] && (dash-negate_year) > 4) return (GMT_IS_NAN);  /* Clearly got some junk or perhaps not using - as delimiter */
2656 	while (s[k+i] && !(s[k+i] == '-' || s[k+i] == 'T')) i++;	/* Goto the ending T character or get stuck on a second - */
2657 	got_yd = ((i - dash - 1) == 3 && s[k+i] == 'T');		/* Must have a field of 3-characters between - and T to constitute a valid day-of-year format */
2658 
2659 	if (got_yd) {	/* Gregorian yyyy-jjj calendar */
2660 		if ((j = sscanf(&s[k], "%4d-%3d", &ival[0], &ival[1]) ) != 2)
2661 			return (GMT_IS_NAN);
2662 		ival[2] = 1;
2663 	}
2664 	else {	/* Gregorian yyyy-mm-dd calendar */
2665 		if ((j = sscanf(&s[k], "%4d-%2d-%2d", &ival[0], &ival[1], &ival[2]) ) == 0)
2666 			return (GMT_IS_NAN);
2667 		for (k = j; k < 3; k++) ival[k] = 1;
2668 	}
2669 	if (negate_year) ival[0] = -ival[0];
2670 	if (got_yd) {
2671 		if (ival[1] < 1 || ival[1] > 366)
2672 			return (GMT_IS_NAN);	/* Simple range check on day-of-year (1-366) */
2673 		*t = gmt_rdc2dt (GMT, gmt_rd_from_gymd (GMT, ival[0], 1, 1) + ival[1] - 1, x);
2674 	}
2675 	else {
2676 		if (gmtlib_g_ymd_is_bad (ival[0], ival[1], ival[2]) )
2677 			return (GMT_IS_NAN);
2678 		*t = gmt_rdc2dt (GMT, gmt_rd_from_gymd (GMT, ival[0], ival[1], ival[2]), x);
2679 	}
2680 
2681 	return (GMT_IS_ABSTIME);
2682 }
2683 
2684 /*! . */
gmtio_write_formatted_ogr_value(struct GMT_CTRL * GMT,FILE * fp,int col,int type,struct GMT_OGR_SEG * G)2685 GMT_LOCAL void gmtio_write_formatted_ogr_value (struct GMT_CTRL *GMT, FILE *fp, int col, int type, struct GMT_OGR_SEG *G) {
2686 	char text[GMT_LEN64] = {""};
2687 
2688 	switch (type) {
2689 		case GMT_TEXT:
2690 			fprintf (fp, "%s", G->tvalue[col]);
2691 			break;
2692 		case GMT_DOUBLE:
2693 		case GMT_FLOAT:
2694 			gmt_ascii_format_col (GMT, text, G->dvalue[col], GMT_OUT, GMT_Z);
2695 			fprintf (fp, "%s", text);
2696 			break;
2697 		case GMT_CHAR:
2698 		case GMT_UCHAR:
2699 		case GMT_INT:
2700 		case GMT_UINT:
2701 		case GMT_LONG:
2702 		case GMT_ULONG:
2703 			fprintf (fp, "%ld", lrint (G->dvalue[col]));
2704 			break;
2705 		case GMT_DATETIME:
2706 			gmt_format_abstime_output (GMT, G->dvalue[col], text);
2707 			fprintf (fp, "%s", text);
2708 			break;
2709 		default:
2710 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad type passed to gmtio_write_formatted_ogr_value - assumed to be double\n");
2711 			gmt_ascii_format_col (GMT, text, G->dvalue[col], GMT_OUT, GMT_Z);
2712 			fprintf (fp, "%s", text);
2713 			break;
2714 	}
2715 }
2716 
2717 /*! . */
gmtio_write_ogr_segheader(struct GMT_CTRL * GMT,FILE * fp,struct GMT_DATASEGMENT * S)2718 GMT_LOCAL void gmtio_write_ogr_segheader (struct GMT_CTRL *GMT, FILE *fp, struct GMT_DATASEGMENT *S) {
2719 	/* Write out segment-level OGR/GMT header metadata */
2720 	unsigned int col, virt_col;
2721 	char *kind = "PH";
2722 	char *sflag[7] = {"-D", "-G", "-I", "-L", "-T", "-W", "-Z"}, *quote[7] = {"", "", "\"", "\"", "\"", "", ""};
2723 	char buffer[GMT_BUFSIZ];
2724 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
2725 
2726 	if (GMT->common.a.geometry == GMT_IS_POLYGON || GMT->common.a.geometry == GMT_IS_MULTIPOLYGON)
2727 		fprintf (fp, "# @%c\n", (int)kind[SH->ogr->pol_mode]);
2728 	if (GMT->common.a.n_aspatial) {
2729 		fprintf (fp, "# @D");
2730 		for (col = 0; col < GMT->common.a.n_aspatial; col++) {
2731 			if (col) fprintf (fp, "|");
2732 			switch (GMT->common.a.col[col]) {
2733 				case GMT_IS_D:	/* Pick up from -D<distance> */
2734 				case GMT_IS_G:	/* Pick up from -G<fill> */
2735 				case GMT_IS_I:	/* Pick up from -I<ID> */
2736 				case GMT_IS_T:	/* Pick up from -T<text> */
2737 				case GMT_IS_W:	/* Pick up from -W<pen> */
2738 				case GMT_IS_Z:	/* Pick up from -Z<value> */
2739 					virt_col = abs (GMT->common.a.col[col]) - 1;	/* So -3 becomes 2 etc */
2740 					if (gmt_parse_segment_item (GMT, GMT->current.io.segment_header, sflag[virt_col], buffer))
2741 						fprintf (fp, "%s%s%s", quote[virt_col], buffer, quote[virt_col]);
2742 					break;
2743 				case GMT_IS_L:	/* Pick up from -L<value> */
2744 					virt_col = abs (GMT->common.a.col[col]) - 1;	/* So -3 becomes 2 etc */
2745 					if (S->label) fprintf (fp, "%s%s%s", quote[virt_col], S->label, quote[virt_col]);
2746 					else if (gmt_parse_segment_item (GMT, GMT->current.io.segment_header, sflag[virt_col], buffer))
2747 						fprintf (fp, "%s%s%s", quote[virt_col], buffer, quote[virt_col]);
2748 					break;
2749 				default:	/* Regular column cases */
2750 					if (SH->ogr) gmtio_write_formatted_ogr_value (GMT, fp, col, GMT->common.a.type[col], SH->ogr);
2751 					break;
2752 			}
2753 		}
2754 		fprintf (fp, "\n");
2755 	}
2756 }
2757 
2758 /*! . */
gmtio_build_text_from_ogr(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * seg,char * buffer)2759 GMT_LOCAL void gmtio_build_text_from_ogr (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *seg, char *buffer) {
2760 	/* Build segment-level OGR/GMT header metadata */
2761 	unsigned int col, virt_col, n;
2762 	bool space = false;
2763 	char *sflag[7] = {"-D", "-G", "-I", "-L", "-T", "-W", "-Z"};
2764 	char **T = NULL;
2765 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
2766 
2767 	if (seg) SH = gmt_get_DS_hidden (seg);
2768 	T = (seg) ? SH->ogr->tvalue     : GMT->current.io.OGR->tvalue;	/* Input means system OGR, output from segment ogr struct */
2769 	n = (seg) ? SH->ogr->n_aspatial : GMT->common.a.n_aspatial;
2770 	if (n == 0) return;	/* Either input was not OGR or there are no aspatial fields */
2771 	buffer[0] = 0;
2772 	for (col = 0; col < n; col++) {
2773 		switch (GMT->common.a.col[col]) {
2774 			case GMT_IS_D:	/* Format -D<distance> */
2775 			case GMT_IS_G:	/* Format -G<fill> */
2776 			case GMT_IS_L:  /* Format -L<label> */
2777 			case GMT_IS_I:	/* Format -I<ID> */
2778 			case GMT_IS_T:	/* Format -T<text> */
2779 			case GMT_IS_W:	/* Format -W<pen> */
2780 			case GMT_IS_Z:	/* Format -Z<value> */
2781 				virt_col = abs (GMT->common.a.col[col]) - 1;	/* So -3 becomes 2 etc */
2782 				if (space) strcat (buffer, " ");
2783 				strncat (buffer, sflag[virt_col], GMT_BUFSIZ-1);
2784 				strncat (buffer, T[GMT->common.a.ogr[col]], GMT_BUFSIZ-1);
2785 				space = true;
2786 				break;
2787 			default:	/* Regular column cases are skipped */
2788 				break;
2789 		}
2790 	}
2791 }
2792 
2793 /*! . */
gmtio_build_segheader_from_ogr(struct GMT_CTRL * GMT,unsigned int direction,struct GMT_DATASEGMENT * S)2794 GMT_LOCAL void gmtio_build_segheader_from_ogr (struct GMT_CTRL *GMT, unsigned int direction, struct GMT_DATASEGMENT *S) {
2795 	/* Build segment-level OGR/GMT header metadata */
2796 	char buffer[GMT_BUFSIZ] = {""};
2797 
2798 	if (direction == GMT_IN && GMT->common.a.output) return;	/* Input was not OGR (but output will be) */
2799 	gmtio_build_text_from_ogr (GMT, S, buffer);	/* Fill in the buffer for -D, -G, Z etc */
2800 
2801 	if (gmt_polygon_is_hole (GMT, S))		/* Indicate this is a polygon hole [Default is perimeter] */
2802 		strcat (buffer, " -Ph");
2803 	if (S->header && buffer[0] && strcmp (S->header, buffer)) {strcat (buffer, " "); strncat (buffer, S->header, GMT_BUFSIZ-1);}	/* Append rest of previous header */
2804 	if (S->header) gmt_M_str_free (S->header);
2805 	S->header = strdup (buffer);
2806 }
2807 
2808 /*! . */
gmtio_alloc_ogr_seg(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,int n_aspatial)2809 GMT_LOCAL void gmtio_alloc_ogr_seg (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, int n_aspatial) {
2810 	/* Allocates the OGR structure for a given segment and copies current values from table OGR segment */
2811 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
2812 	if (SH->ogr) return;	/* Already allocated */
2813 	SH->ogr = gmt_M_memory (GMT, NULL, 1, struct GMT_OGR_SEG);
2814 	SH->ogr->n_aspatial = n_aspatial;
2815 	if (n_aspatial) {
2816 		SH->ogr->tvalue = gmt_M_memory (GMT, NULL, n_aspatial, char *);
2817 		SH->ogr->dvalue = gmt_M_memory (GMT, NULL, n_aspatial, double);
2818 	}
2819 }
2820 
2821 /*! . */
gmtio_copy_ogr_seg(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,struct GMT_OGR * G)2822 GMT_LOCAL void gmtio_copy_ogr_seg (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, struct GMT_OGR *G) {
2823 	/* Allocates the OGR structure for a given segment and copies current values from table OGR segment */
2824 	unsigned int col;
2825 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
2826 
2827 	gmtio_alloc_ogr_seg (GMT, S, G->n_aspatial);
2828 	for (col = 0; col < G->n_aspatial; col++) {
2829 		if (G->tvalue != NULL && G->tvalue[col])
2830 			SH->ogr->tvalue[col] = strdup (G->tvalue[col]);
2831 		if (G->dvalue != NULL && G->dvalue[col])
2832 			SH->ogr->dvalue[col] = G->dvalue[col];
2833 	}
2834 	SH->ogr->pol_mode = G->pol_mode;
2835 }
2836 
2837 /*! . */
gmtio_prep_ogr_output(struct GMT_CTRL * GMT,struct GMT_DATASET * D)2838 GMT_LOCAL int gmtio_prep_ogr_output (struct GMT_CTRL *GMT, struct GMT_DATASET *D) {
2839 
2840 	int object_ID, col, stop, n_reg, item;
2841 	uint64_t row, seg, seg1, seg2, k;
2842 	char buffer[GMT_BUFSIZ] = {""}, in_string[GMT_VF_LEN] = {""}, out_string[GMT_VF_LEN] = {""};
2843 	struct GMT_DATATABLE *T = NULL;
2844 	struct GMT_DATASET *M = NULL;
2845 	struct GMT_DATASEGMENT *S = NULL;
2846 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
2847 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL, *SH1 = NULL, *SH2 = NULL;
2848 	struct GMTAPI_DATA_OBJECT O;
2849 
2850 	/* When this function is called we have already registered the output destination.  This will normally
2851 	 * prevent us from register the data set separately in order to call GMT_gmtinfo.  We must temporarily
2852 	 * unregister the output, do our thing, then reregister again. */
2853 
2854 	n_reg = gmtlib_count_objects (GMT->parent, GMT_IS_DATASET, D->geometry, GMT_OUT, &object_ID);	/* Are there outputs registered already? */
2855 	if (n_reg == 1) {	/* Yes, must save and unregister, then reregister later */
2856 		if ((item = gmtlib_validate_id (GMT->parent, GMT_IS_DATASET, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET)
2857 			return (GMT->parent->error);
2858 		gmt_M_memcpy (&O, GMT->parent->object[item], 1, struct GMTAPI_DATA_OBJECT);
2859 		gmtlib_unregister_io (GMT->parent, object_ID, GMT_OUT);
2860 	}
2861 	else {	/* Cannot have registered more than one output for OGR data */
2862 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot specify more than one output file for OGR\n");
2863 		return (GMT_DIM_TOO_LARGE);
2864 	}
2865 
2866 	/* Determine w/e/s/n via GMT_gmtinfo */
2867 
2868 	if (GMT_Open_VirtualFile (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_IN|GMT_IS_REFERENCE, D, in_string) == GMT_NOTSET) {
2869 		return (GMT->parent->error);
2870 	}
2871 	if (GMT_Open_VirtualFile (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_OUT|GMT_IS_REFERENCE, NULL, out_string) == GMT_NOTSET) {
2872 		return (GMT->parent->error);
2873 	}
2874 	snprintf (buffer, GMT_BUFSIZ, "-C -fg -<%s ->%s --GMT_HISTORY=readonly", in_string, out_string);
2875 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Calling gmtinfo with args %s\n", buffer);
2876 	if (GMT_Call_Module (GMT->parent, "gmtinfo", GMT_MODULE_CMD, buffer) != GMT_OK) {	/* Get the extent via gmtinfo */
2877 		return (GMT->parent->error);
2878 	}
2879 	GMT_Close_VirtualFile (GMT->parent, in_string);
2880 	if ((M = GMT_Read_VirtualFile (GMT->parent, out_string)) == NULL) {
2881 		return (GMT->parent->error);
2882 	}
2883 	GMT_Close_VirtualFile (GMT->parent, out_string);
2884 
2885 	/* Time to reregister the original destination */
2886 
2887 	if ((object_ID = GMT_Register_IO (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_OUT, NULL, D)) == GMT_NOTSET) {
2888 		return (GMT->parent->error);
2889 	}
2890 	if ((item = gmtlib_validate_id (GMT->parent, GMT_IS_DATASET, object_ID, GMT_OUT, GMT_NOTSET)) == GMT_NOTSET) {
2891 		return (GMT->parent->error);
2892 	}
2893 	gmt_M_memcpy (GMT->parent->object[item], &O, 1, struct GMTAPI_DATA_OBJECT);	/* Restore what we had before */
2894 
2895 	T = D->table[0];
2896 	TH = gmt_get_DT_hidden (T);
2897 	TH->ogr = gmt_M_memory (GMT, NULL, 1, struct GMT_OGR);
2898 	snprintf (buffer, GMT_BUFSIZ, "%.8g/%.8g/%.8g/%.8g", M->table[0]->segment[0]->data[0][0], M->table[0]->segment[0]->data[1][0],
2899 	                                        M->table[0]->segment[0]->data[2][0], M->table[0]->segment[0]->data[3][0]);
2900 	if (GMT_Destroy_Data (GMT->parent, &M) != GMT_OK) {
2901 		return (GMT->parent->error);
2902 	}
2903 	TH->ogr->region = strdup (buffer);
2904 	TH->ogr->proj[1] = strdup ("\"-Jx1d --PROJ_ELLIPSOID=WGS84\"");
2905 	TH->ogr->proj[2] = strdup ("\"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs\"");
2906 	TH->ogr->proj[3] = strdup ("\"GEOGCS[\"GCS_WGS_1984\",DATUM[\"WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]\"");
2907 	TH->ogr->geometry = GMT->common.a.geometry;
2908 	TH->ogr->n_aspatial = GMT->common.a.n_aspatial;
2909 	if (TH->ogr->n_aspatial) {	/* Copy over the command-line settings */
2910 		TH->ogr->name = gmt_M_memory (GMT, NULL, TH->ogr->n_aspatial, char *);
2911 		TH->ogr->type = gmt_M_memory (GMT, NULL, TH->ogr->n_aspatial, enum GMT_enum_type);
2912 		TH->ogr->dvalue = gmt_M_memory (GMT, NULL, TH->ogr->n_aspatial, double);
2913 		for (k = 0; k < TH->ogr->n_aspatial; k++) {
2914 			TH->ogr->name[k] = strdup (GMT->common.a.name[k]);
2915 			TH->ogr->type[k] = GMT->common.a.type[k];
2916 		}
2917 		for (seg = 0; seg < T->n_segments; seg++) {	/* For each segment in the table */
2918 			S = T->segment[seg];
2919 			SH = gmt_get_DS_hidden (S);
2920 			gmtio_alloc_ogr_seg (GMT, S, TH->ogr->n_aspatial);	/* Copy over any feature-specific values */
2921 			for (k = 0; k < TH->ogr->n_aspatial; k++) {	/* For each column to turn into a constant aspatial value */
2922 				col = GMT->common.a.col[k];
2923 				if (col < 0) continue;	/* Multisegment header entry instead */
2924 				for (row = 1, stop = false; !stop && row < S->n_rows; ++row) {
2925 					if (!doubleAlmostEqualZero (S->data[col][row], S->data[col][row-1]))
2926 						stop = true;
2927 				}
2928 				if (stop) {
2929 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "The -a option specified a constant column but its contents vary!\n");
2930 					return (GMT_RUNTIME_ERROR);
2931 				}
2932 				else
2933 					SH->ogr->dvalue[k] = S->data[col][0];
2934 			}
2935 		}
2936 		/* OK, successfully passed the constant column tests, if any */
2937 		for (seg = col = 0; seg < T->n_segments; seg++) {	/* Free up columns now stored as aspatial values */
2938 			S = T->segment[seg];
2939 			SH = gmt_get_DS_hidden (S);
2940 			for (k = 0; k < TH->ogr->n_aspatial; k++)
2941 				if (GMT->common.a.col[k] > 0)
2942 					gmt_M_free (GMT, S->data[GMT->common.a.col[k]]);
2943 			for (k = col = 0; k < T->n_columns; k++) {
2944 				while (!S->data[k]) k++;	/* Next available column */
2945 				S->data[col] = S->data[k];	/* Update pointers */
2946 				SH->alloc_mode[col++] = SH->alloc_mode[k];	/* Update modes */
2947 			}
2948 			S->n_columns = col;	/* May have lost some columns now */
2949 		}
2950 		T->n_columns = D->n_columns = col;	/* May have lost some columns now */
2951 	}
2952 	if (TH->ogr->geometry == GMT_IS_POLYGON || TH->ogr->geometry == GMT_IS_MULTIPOLYGON) {	/* Must check consistency */
2953 		for (seg = 0; seg < T->n_segments; seg++) {	/* For each segment in the table */
2954 			if ((TH->ogr->geometry == GMT_IS_POLYGON || TH->ogr->geometry == GMT_IS_MULTIPOLYGON) &&
2955 			     gmt_polygon_is_open (GMT, T->segment[seg]->data[GMT_X], T->segment[seg]->data[GMT_Y], T->segment[seg]->n_rows)) {
2956 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "The -a option specified [M]POLY but open segments were detected!\n");
2957 				GMT_Destroy_Data (GMT->parent, &D[GMT_OUT]);
2958 				return (GMT_RUNTIME_ERROR);
2959 			}
2960 			gmtio_alloc_ogr_seg (GMT, T->segment[seg], TH->ogr->n_aspatial);	/* Copy over any feature-specific values */
2961 			SH = gmt_get_DS_hidden (T->segment[seg]);
2962 			SH->ogr->pol_mode = GMT_IS_PERIMETER;
2963 			gmt_set_seg_minmax (GMT, TH->ogr->geometry, 0, T->segment[seg]);	/* Make sure min/max are set per polygon */
2964 
2965 		}
2966 		/* OK, they are all polygons.  Determine any polygon holes: if a point is fully inside another polygon (not on the edge) */
2967 		for (seg1 = 0; seg1 < T->n_segments; seg1++) {	/* For each segment in the table */
2968 			SH1 = gmt_get_DS_hidden (T->segment[seg1]);
2969 			for (seg2 = seg1 + 1; seg2 < T->n_segments; seg2++) {	/* For each segment in the table */
2970 				SH2 = gmt_get_DS_hidden (T->segment[seg2]);
2971 				if (gmt_inonout (GMT, T->segment[seg1]->data[GMT_X][0], T->segment[seg1]->data[GMT_Y][0],
2972 				                 T->segment[seg2]) == GMT_INSIDE) SH1->ogr->pol_mode = GMT_IS_HOLE;
2973 				if (gmt_inonout (GMT, T->segment[seg2]->data[GMT_X][0], T->segment[seg2]->data[GMT_Y][0], T->segment[seg1]) == GMT_INSIDE)
2974 					SH2->ogr->pol_mode = GMT_IS_HOLE;
2975 			}
2976 		}
2977 	}
2978 	if (TH->ogr->geometry > GMT_IS_POINT && TH->ogr->geometry != GMT_IS_MULTIPOINT) {	/* Must check for Dateline crossings */
2979 		unsigned int n_split;
2980 		uint64_t n_segs = T->n_segments;
2981 		struct GMT_DATASEGMENT **L = NULL;
2982 
2983 		for (seg = 0; seg < T->n_segments; seg++) {	/* For each segment in the table */
2984 			if (!gmt_crossing_dateline (GMT, T->segment[seg])) continue;	/* GIS-safe feature! */
2985 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Feature %" PRIu64 " crosses the Dateline\n", seg);
2986 			if (!GMT->common.a.clip) continue;	/* Not asked to clip */
2987 			/* Here we must split into east and west part(s) */
2988 			if (TH->ogr->geometry == GMT_IS_POLYGON || TH->ogr->geometry == GMT_IS_MULTIPOLYGON) {	/* Clipping must add dateline segments */
2989 				/* Clip into two closed polygons.  Eventually, perhaps return more (eliminate bridges) */
2990 				n_split = gmt_split_poly_at_dateline (GMT, T->segment[seg], &L);
2991 			}
2992 			else {	/* Clipping just needs to add crossing points, unless already present */
2993 				/* Truncate into two or more line segments */
2994 				n_split = gmtlib_split_line_at_dateline (GMT, T->segment[seg], &L);
2995 			}
2996 			if (n_split == 0) continue;	/* Might have crossed dateline but had points exactly at 180 */
2997 			T->segment = gmt_M_memory (GMT, T->segment, n_segs + n_split - 1, struct GMT_DATASEGMENT *);	/* Allow more space for new segments */
2998 			gmt_free_segment (GMT, &(T->segment[seg]));	/* Delete the old one */
2999 			T->segment[seg] = L[0];			/* Hook in the first replacement */
3000 			for (k = 1; k < n_split; k++) T->segment[n_segs++] = L[k];	/* Add the remaining segments to the end */
3001 			gmt_M_free (GMT, L);
3002 		}
3003 		D->n_segments = T->n_segments = n_segs;	/* Update number of segments */
3004 
3005 	}
3006 	GMT->current.io.geo.range = GMT_IS_M180_TO_P180_RANGE;	/* Select the -180/180 output range format */
3007 	return (0);
3008 }
3009 
3010 /*! . */
gmtio_write_multilines(struct GMT_CTRL * GMT,FILE * fp,char * text,char * prefix)3011 GMT_LOCAL void gmtio_write_multilines (struct GMT_CTRL *GMT, FILE *fp, char *text, char *prefix) {
3012 	/* Optional title(s) or remarks provided; could be several lines separated by \n */
3013 	char p[GMT_BUFSIZ] = {""}, line[GMT_BUFSIZ] = {""};
3014 	unsigned int pos = 0, k = 0;
3015 
3016 	while (gmt_strtok (text, "\\", &pos, p)) {
3017 		snprintf (line, GMT_BUFSIZ, "# %7s : %s", prefix, &p[k]);
3018 		gmtlib_write_tableheader (GMT, fp, line);
3019 		k = 1;	/* Need k to skip the n in \n */
3020 	}
3021 }
3022 
3023 /*! . */
gmtio_adjust_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,uint64_t n_columns)3024 GMT_LOCAL void gmtio_adjust_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, uint64_t n_columns) {
3025 	/* Change the number of columns in this segment to n_columns (free or allocate as needed) */
3026 	uint64_t col;
3027 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
3028 	for (col = n_columns; col < S->n_columns; col++) gmt_M_free (GMT, S->data[col]);	/* Free up if n_columns < S->columns */
3029 	S->data = gmt_M_memory (GMT, S->data, n_columns, double *);
3030 	S->min = gmt_M_memory (GMT, S->min, n_columns, double);
3031 	S->max = gmt_M_memory (GMT, S->max, n_columns, double);
3032 	SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, n_columns, enum GMT_enum_alloc);
3033 	for (col = S->n_columns; col < n_columns; col++) {	/* Allocate new columns and initialize the min/max arrays */
3034 		S->min[col] = +DBL_MAX;
3035 		S->max[col] = -DBL_MAX;
3036 		S->data[col] = gmt_M_memory (GMT, NULL, S->n_rows, double);
3037 		SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
3038 	}
3039 	S->n_columns = n_columns;
3040 }
3041 
3042 /*! . */
gmtio_adjust_table(struct GMT_CTRL * GMT,struct GMT_DATATABLE * T,uint64_t n_columns)3043 GMT_LOCAL void gmtio_adjust_table (struct GMT_CTRL *GMT, struct GMT_DATATABLE *T, uint64_t n_columns) {
3044 	/* Let table have n_columns (so either deallocate or allocate columns). */
3045 	uint64_t seg;
3046 
3047 	T->min = gmt_M_memory (GMT, T->min, n_columns, double);
3048 	T->max = gmt_M_memory (GMT, T->max, n_columns, double);
3049 	for (seg = 0; seg < T->n_segments; seg++) gmtio_adjust_segment (GMT, T->segment[seg], n_columns);
3050 	T->n_columns = n_columns;	/* New number of n_columns */
3051 }
3052 
3053 /*! . */
gmtio_copy_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * Sout,struct GMT_DATASEGMENT * Sin)3054 GMT_LOCAL void gmtio_copy_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *Sout, struct GMT_DATASEGMENT *Sin) {
3055 	/* Duplicates the segment */
3056 	uint64_t col;
3057 	gmt_M_unused(GMT);
3058 	for (col = 0; col < Sin->n_columns; col++) gmt_M_memcpy (Sout->data[col], Sin->data[col], Sin->n_rows, double);
3059 	if (Sin->text) {	/* Must also duplicate text strings */
3060 		uint64_t row;
3061 		for (row = 0; row < Sin->n_rows; row++) Sout->text[row] = strdup (Sin->text[row]);
3062 	}
3063 	gmt_M_memcpy (Sout->min, Sin->min, Sin->n_columns, double);
3064 	gmt_M_memcpy (Sout->max, Sin->max, Sin->n_columns, double);
3065 	Sout->n_rows = Sin->n_rows;
3066 }
3067 
3068 /*! . */
gmtio_free_ogr_seg(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)3069 GMT_LOCAL void gmtio_free_ogr_seg (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
3070 	/* Frees the OGR structure for a given segment */
3071 	unsigned int k, n;
3072 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
3073 	n = (GMT->current.io.OGR) ? GMT->current.io.OGR->n_aspatial : GMT->common.a.n_aspatial;
3074 	if (n) {
3075 		for (k = 0; SH->ogr->tvalue && k < n; k++) gmt_M_str_free (SH->ogr->tvalue[k]);
3076 		gmt_M_free (GMT, SH->ogr->tvalue);
3077 		gmt_M_free (GMT, SH->ogr->dvalue);
3078 	}
3079 	gmt_M_free (GMT, SH->ogr);
3080 }
3081 
3082 /*! . */
gmtio_free_univector(struct GMT_CTRL * GMT,union GMT_UNIVECTOR * u,unsigned int type)3083 GMT_LOCAL void gmtio_free_univector (struct GMT_CTRL *GMT, union GMT_UNIVECTOR *u, unsigned int type) {
3084 	/* By taking a reference to the vector pointer we can set it to NULL when done */
3085 	if (!u) return;	/* Nothing to deallocate */
3086 	switch (type) {
3087 		case GMT_UCHAR:		gmt_M_free (GMT, u->uc1); break;
3088 		case GMT_CHAR:		gmt_M_free (GMT, u->sc1); break;
3089 		case GMT_USHORT:	gmt_M_free (GMT, u->ui2); break;
3090 		case GMT_SHORT:		gmt_M_free (GMT, u->si2); break;
3091 		case GMT_UINT:		gmt_M_free (GMT, u->ui4); break;
3092 		case GMT_INT:		gmt_M_free (GMT, u->si4); break;
3093 		case GMT_ULONG:		gmt_M_free (GMT, u->ui8); break;
3094 		case GMT_LONG:		gmt_M_free (GMT, u->si8); break;
3095 		case GMT_FLOAT:		gmt_M_free (GMT, u->f4);  break;
3096 		case GMT_DOUBLE:	gmt_M_free (GMT, u->f8);  break;
3097 	}
3098 }
3099 
3100 /*! . */
gmtio_null_univector(struct GMT_CTRL * GMT,union GMT_UNIVECTOR * u,unsigned int type)3101 GMT_LOCAL void gmtio_null_univector (struct GMT_CTRL *GMT, union GMT_UNIVECTOR *u, unsigned int type) {
3102 	/* Here we just set the type pointer to NULL as it was pointing to external memory */
3103 	gmt_M_unused(GMT);
3104 	if (!u) return;	/* Nothing to deal with */
3105 	switch (type) {
3106 		case GMT_UCHAR:	 u->uc1 = NULL; break;
3107 		case GMT_CHAR:	 u->sc1 = NULL; break;
3108 		case GMT_USHORT: u->ui2 = NULL; break;
3109 		case GMT_SHORT:	 u->si2 = NULL; break;
3110 		case GMT_UINT:	 u->ui4 = NULL; break;
3111 		case GMT_INT:	 u->si4 = NULL; break;
3112 		case GMT_ULONG:	 u->ui8 = NULL; break;
3113 		case GMT_LONG:	 u->si8 = NULL; break;
3114 		case GMT_FLOAT:	 u->f4  = NULL; break;
3115 		case GMT_DOUBLE: u->f8  = NULL; break;
3116 	}
3117 }
3118 
3119 /*! . */
gmtio_duplicate_univector(struct GMT_CTRL * GMT,union GMT_UNIVECTOR * u_out,union GMT_UNIVECTOR * u_in,unsigned int type,uint64_t n_rows)3120 GMT_LOCAL int gmtio_duplicate_univector (struct GMT_CTRL *GMT, union GMT_UNIVECTOR *u_out, union GMT_UNIVECTOR *u_in, unsigned int type, uint64_t n_rows) {
3121 	/* Allocate space for one univector according to data type */
3122 	gmt_M_unused(GMT);
3123 	switch (type) {
3124 		case GMT_UCHAR:  gmt_M_memcpy (u_out->uc1, u_in->uc1, n_rows, uint8_t);   break;
3125 		case GMT_CHAR:   gmt_M_memcpy (u_out->sc1, u_in->sc1, n_rows, int8_t);    break;
3126 		case GMT_USHORT: gmt_M_memcpy (u_out->ui2, u_in->ui2, n_rows, uint16_t);  break;
3127 		case GMT_SHORT:  gmt_M_memcpy (u_out->si2, u_in->si2, n_rows, int16_t);   break;
3128 		case GMT_UINT:   gmt_M_memcpy (u_out->ui4, u_in->ui4, n_rows, uint32_t);  break;
3129 		case GMT_INT:    gmt_M_memcpy (u_out->si4, u_in->si4, n_rows, int32_t);   break;
3130 		case GMT_ULONG:  gmt_M_memcpy (u_out->ui8, u_in->ui8, n_rows, uint64_t);  break;
3131 		case GMT_LONG:   gmt_M_memcpy (u_out->si8, u_in->si8, n_rows, int64_t);   break;
3132 		case GMT_FLOAT:  gmt_M_memcpy (u_out->f4,  u_in->f4,  n_rows, float);     break;
3133 		case GMT_DOUBLE: gmt_M_memcpy (u_out->f8,  u_in->f8,  n_rows, double);    break;
3134 	}
3135 	return (GMT_OK);
3136 }
3137 
3138 /*! Translates incoming ij (no padding) to a GMT ij (includes padding) for column-structured data */
gmtio_col_ij(struct GMT_Z_IO * r,struct GMT_GRID * G,uint64_t ij)3139 GMT_LOCAL uint64_t gmtio_col_ij (struct GMT_Z_IO *r, struct GMT_GRID *G, uint64_t ij) {
3140 
3141 	r->gmt_j = (unsigned int)(r->start_row + r->y_step * (ij % r->y_period));
3142 	r->gmt_i = (unsigned int)(r->start_col + r->x_step * (ij / r->y_period));
3143 
3144 	return (gmt_M_ijp (G->header, r->gmt_j, r->gmt_i));
3145 }
3146 
3147 /*! Translates incoming ij (no padding) to a GMT ij (includes padding) for row-structured data */
gmtio_row_ij(struct GMT_Z_IO * r,struct GMT_GRID * G,uint64_t ij)3148 GMT_LOCAL uint64_t gmtio_row_ij (struct GMT_Z_IO *r, struct GMT_GRID *G, uint64_t ij) {
3149 
3150 	r->gmt_j = (unsigned int)(r->start_row + r->y_step * (ij / r->x_period));
3151 	r->gmt_i = (unsigned int)(r->start_col + r->x_step * (ij % r->x_period));
3152 
3153 	return (gmt_M_ijp (G->header, r->gmt_j, r->gmt_i));
3154 }
3155 
3156 /*! . */
gmtio_bin_input(struct GMT_CTRL * GMT,FILE * fp,uint64_t * n,int * retval)3157 GMT_LOCAL void * gmtio_bin_input (struct GMT_CTRL *GMT, FILE *fp, uint64_t *n, int *retval) {
3158 	/* General binary read function which calls function pointed to by GMT->current.io.read_binary to handle
3159 	   actual reading (and possibly swabbing) */
3160 	unsigned int status;
3161 	uint64_t n_use, n_read;
3162 
3163 	GMT->current.io.status = GMT_IO_DATA_RECORD;
3164 	do {	/* Keep reading until (1) EOF, (2) got a segment record, or (3) a valid data record */
3165 		n_use = gmtio_n_cols_needed_for_gaps (GMT, *n);
3166 		gmtio_update_prev_rec (GMT, n_use);
3167 		if (gmtio_get_binary_input (GMT, fp, n_use)) { *retval = GMT_NOTSET; GMT->current.io.data_record_number_in_tbl[GMT_IN] = 0; return (NULL); }	/* EOF */
3168 		GMT->current.io.rec_no++;
3169 		GMT->current.io.data_record_number_in_set[GMT_IN]++;
3170 		GMT->current.io.data_record_number_in_tbl[GMT_IN]++;
3171 		GMT->current.io.data_record_number_in_seg[GMT_IN]++;
3172 		status = gmtlib_process_binary_input (GMT, n_use);
3173 		if (status == 1) { *retval = 0; return (NULL); }		/* A segment header */
3174 	} while (status == 2);	/* Continue reading when record is to be skipped */
3175 	n_read = (GMT->common.i.select) ? gmtio_bin_colselect (GMT) : *n;	/* We may use -i and select fewer of the input columns */
3176 
3177 	if (gmtlib_gap_detected (GMT)) { *retval = gmtlib_set_gap (GMT); return (&GMT->current.io.record); }
3178 	GMT->current.io.has_previous_rec = true;
3179 
3180 	*retval = (int)n_read;
3181 	return (&GMT->current.io.record);
3182 }
3183 
3184 /*! . */
gmtio_alloc_table(struct GMT_CTRL * GMT,struct GMT_DATATABLE * Tin,uint64_t n_columns,uint64_t n_rows)3185 GMT_LOCAL struct GMT_DATATABLE *gmtio_alloc_table (struct GMT_CTRL *GMT, struct GMT_DATATABLE *Tin, uint64_t n_columns, uint64_t n_rows) {
3186 	/* Allocate the new Table structure with same # of segments and rows/segment as input table.
3187 	 * However, n_columns is given separately and could differ.
3188 	 * If n_rows is > 0 we well override the Tin rows counts by using n_rows instead.  */
3189 	unsigned int hdr, smode;
3190 	uint64_t seg, nr;
3191 	struct GMT_DATATABLE *T = gmt_get_table (GMT);
3192 	struct GMT_DATATABLE_HIDDEN *TH = gmt_get_DT_hidden (T);
3193 
3194 	T->n_segments = TH->n_alloc = Tin->n_segments;	/* Same number of segments as input table */
3195 	T->n_headers  = Tin->n_headers;
3196 	T->n_columns  = n_columns;		/* Separately specified n_columns */
3197 	T->min = gmt_M_memory (GMT, NULL, n_columns, double);
3198 	T->max = gmt_M_memory (GMT, NULL, n_columns, double);
3199 	if (T->n_headers) {
3200 		T->header = gmt_M_memory (GMT, NULL, Tin->n_headers, char *);
3201 		for (hdr = 0; hdr < T->n_headers; hdr++) T->header[hdr] = strdup (Tin->header[hdr]);
3202 	}
3203 	T->segment = gmt_M_memory (GMT, NULL, Tin->n_segments, struct GMT_DATASEGMENT *);
3204 	for (seg = 0; seg < T->n_segments; seg++) {
3205 		nr = (n_rows) ? n_rows : Tin->segment[seg]->n_rows;
3206 		smode = (Tin->segment[seg]->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
3207 		T->segment[seg] = GMT_Alloc_Segment (GMT->parent, smode, nr, n_columns, Tin->segment[seg]->header, NULL);
3208 		T->n_records += nr;
3209 		if (Tin->segment[seg]->label)  T->segment[seg]->label = strdup (Tin->segment[seg]->label);
3210 	}
3211 	return (T);
3212 }
3213 
3214 /*! . */
gmtio_set_current_record(struct GMT_CTRL * GMT,char * text)3215 GMT_LOCAL void gmtio_set_current_record (struct GMT_CTRL *GMT, char *text) {
3216 	//while (*text && strchr ("#\t ", *text)) text++;	/* Skip header record indicator and leading whitespace [NO: messes up strsepzp count] */
3217 	while (*text && strchr ("#", *text)) text++;	/* Skip header record indicator */
3218 	if (*text)	/* Got some text to remember */
3219 		strncpy (GMT->current.io.curr_text, text, GMT_BUFSIZ-1);
3220 	else	/* Nutin' */
3221 		gmt_M_memset (GMT->current.io.curr_text, GMT_BUFSIZ, char);
3222 }
3223 
gmtio_get_type_name_index(unsigned int code)3224 GMT_LOCAL unsigned int gmtio_get_type_name_index (unsigned int code) {
3225 	unsigned int k;
3226 	switch (code) {
3227 		case GMT_IS_NAN:          k = 0;	break;
3228 		case GMT_IS_FLOAT:        k = 1;	break;
3229 		case GMT_IS_LAT:          k = 2;	break;
3230 		case GMT_IS_LON:          k = 3;	break;
3231 		case GMT_IS_GEO:          k = 4;	break;
3232 		case GMT_IS_RELTIME:      k = 5;	break;
3233 		case GMT_IS_ABSTIME:      k = 6;	break;
3234 		case GMT_IS_DURATION:     k = 7;	break;
3235 		case GMT_IS_DIMENSION:    k = 8;	break;
3236 		case GMT_IS_GEODIMENSION: k = 9;	break;
3237 		case GMT_IS_AZIMUTH:      k = 10;	break;
3238 		case GMT_IS_ANGLE:        k = 11;	break;
3239 		case GMT_IS_UNKNOWN:      k = 1;	break;
3240 		default: k = 0;	break;
3241 	}
3242 	return (k);
3243 }
3244 
gmtio_get_coltype_name_index(struct GMT_CTRL * GMT,unsigned int dir,unsigned int col)3245 GMT_LOCAL unsigned int gmtio_get_coltype_name_index (struct GMT_CTRL *GMT, unsigned int dir, unsigned int col) {
3246 	return (gmtio_get_type_name_index (gmt_M_type (GMT, dir, col)));
3247 }
3248 
gmtlib_maybe_abstime(struct GMT_CTRL * GMT,char * txt,bool * no_T)3249 bool gmtlib_maybe_abstime (struct GMT_CTRL *GMT, char *txt, bool *no_T) {
3250 	size_t k;
3251 	unsigned int n_dash, n_slash;
3252 	gmt_M_unused (GMT);
3253 	*no_T = false;	/* Until proven wrong */
3254 	if (strchr (txt, 'T')) return true;	/* Might be [<date>]T[<clock>], else if is likely text */
3255 	if (strchr (txt, 'e') || strchr (txt, 'E')) return false;	/* Check for exponentials since -0.5e-2 would trigger on two dashes */
3256 	/* Here there are no T, but perhaps somebody forgot to add T to 2004-10-19 or 1999/04/05 ? */
3257 	n_dash = n_slash = 0;
3258 	for (k = 0; k < strlen (txt); k++) {	/* Count slashes and dashes */
3259 		if (txt[k] == '-') n_dash++;
3260 		else if (txt[k] == '/') n_slash++;
3261 	}
3262 	if (((n_dash + n_slash) == 2) && (n_dash == 2 || n_slash == 2)) {	/* Time */
3263 		*no_T = true;
3264 		return true;	/* Might be yyyy/mm/dd or yyyy-mm-dd with missing trailing T */
3265 	}
3266 	return false;
3267 }
3268 
gmtio_physical_coltype(struct GMT_CTRL * GMT,unsigned int col)3269 GMT_LOCAL enum gmt_col_enum gmtio_physical_coltype (struct GMT_CTRL *GMT, unsigned int col) {
3270 	/* The users's -f settings apply to the logical columns and these are the same as
3271 	 * the physical columns except when -i is used.  This function takes the physical
3272 	 * column (we are scanning across the physical record) and returns the corresponding
3273 	 * logical column type. With no -i they are the same, otherwise not.
3274 	 */
3275 	if (GMT->common.i.select && GMT->common.i.n_cols) {	/* Logical record differs from physical */
3276 		unsigned int k;
3277 		for (k = 0; k < GMT->common.i.n_cols; k++) {
3278 			if (GMT->current.io.col[GMT_IN][k].col == col)	/* Found one of the physical columns that will be column = order */
3279 				return gmt_M_type (GMT, GMT_IN, GMT->current.io.col[GMT_IN][k].order);
3280 		}
3281 		/* Here we come when a physical column was not selected to be part of the logical column, so no -f is avialble.  We pass GMT_IS_UNKNOWN */
3282 		return GMT_IS_UNKNOWN;
3283 	}
3284 	return gmt_M_type (GMT, GMT_IN, col);	/* Physical == logical */
3285 }
3286 
gmtio_assign_col_type_if_notset(struct GMT_CTRL * GMT,unsigned int col,unsigned int type)3287 GMT_LOCAL void gmtio_assign_col_type_if_notset (struct GMT_CTRL *GMT, unsigned int col, unsigned int type) {
3288 	/* As gmtio_examine_current_record learns the content of each column, col, we wish to update
3289 	 * the column type so that parsing will work later.  However, there are some complications:
3290 	 * We are scanning the physical record, but if -i was used then the logical record (the one
3291 	 * we actually will return to a module) will differ, so the columns are not the same, and
3292 	 * the module may know more than we do and have already set what the column types should be,
3293 	 * at least for critical columns.  If so we are not allowed to change that even if we may know better.
3294 	 */
3295 	if (GMT->common.i.select && GMT->common.i.n_cols) {	/* Logical record differs from physical */
3296 		unsigned int k;
3297 		for (k = 0; k < GMT->common.i.n_cols; k++) {
3298 			if (GMT->current.io.col[GMT_IN][k].col == col) {	/* Found one of the physical columns that will be column = order */
3299 				if (!GMT->current.io.col_set[GMT_IN][GMT->current.io.col[GMT_IN][k].order])
3300 					gmt_set_column_type (GMT, GMT_IN, GMT->current.io.col[GMT_IN][k].order, type);
3301 				if (!GMT->current.io.col_set[GMT_OUT][GMT->current.io.col[GMT_IN][k].order])
3302 					gmt_set_column_type (GMT, GMT_OUT, GMT->current.io.col[GMT_IN][k].order, type);
3303 			}
3304 		}
3305 	}
3306 	else {	/* logical equal physical, so no column shopping */
3307 		if (!GMT->current.io.col_set[GMT_IN][col])
3308 			gmt_set_column_type (GMT, GMT_IN, col, type);
3309 		if (!GMT->current.io.col_set[GMT_OUT][col])
3310 			gmt_set_column_type (GMT, GMT_OUT, col, type);
3311 	}
3312 }
3313 
3314 /*! . */
gmtio_examine_current_record(struct GMT_CTRL * GMT,char * record,size_t * tpos,uint64_t * n_columns)3315 GMT_LOCAL unsigned int gmtio_examine_current_record (struct GMT_CTRL *GMT, char *record, size_t *tpos, uint64_t *n_columns) {
3316 	/* Examines this data record to determine the nature of the input.  There
3317 	 * are three different scenarios:
3318 	 * 1. Record only contains numerical columns: Set ncols and return GMT_READ_DATA [0].
3319 	 * 2. Record only contains text: Set ncols to 0 and return GMT_READ_TEXT [1].
3320 	 * 3. Record contains leading numerics followed by text: Set ncols and return GMT_READ_MIXED [2].
3321 	 * Note: The convoluted switch/test below reflects the fact that gmt_scanf_arg was set up
3322 	 * to parse command-line arguments.  For ABSTIME these must be in ISO format while for input
3323 	 * (which is what we are handling here) a huge variety of datetime strings are possible via
3324 	 * the FORMAT_DATE_IN, FORMAT_CLOCK_IN settings.
3325 	 */
3326 	unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, k, *type = NULL;
3327 	int got;
3328 	enum gmt_col_enum phys_col_type;
3329 	bool found_text = false, no_T = false;
3330 	char token[GMT_BUFSIZ], message[GMT_BUFSIZ] = {""};
3331 	double value;
3332 	static char *flavor[4] = {"", "Numerical only", "Text only", "Numerical with trailing text"};
3333 
3334 	type = gmt_M_memory (GMT, NULL, GMT_MAX_COLUMNS, unsigned int);
3335 	*tpos = pos;
3336 	while (!found_text && (gmt_strtok (record, GMT->current.io.scan_separators, &pos, token))) {
3337 		if ((phys_col_type = gmt_get_column_type (GMT, GMT_IN, col)) == GMT_IS_STRING) {	/* Explicit start of trailing text via -f<col>s */
3338 			found_text = true;	/* We stop right here, return flag as nan and hence n_columns will be col. */
3339 			got = GMT_IS_NAN;
3340 		}
3341 		else {	/* Auto-guess from examining the token */
3342 			phys_col_type = gmtio_physical_coltype (GMT, col);
3343 			if (phys_col_type == GMT_IS_ABSTIME || gmtlib_maybe_abstime (GMT, token, &no_T)) {	/* Might be ISO Absolute time; if not we got junk (and got -> GMT_IS_NAN) */
3344 				got = gmt_scanf (GMT, token, GMT_IS_ABSTIME, &value);
3345 			}
3346 			else	/* Let gmt_scanf_arg figure it out for us by passing UNKNOWN since ABSTIME has been dealt above */
3347 				got = gmt_scanf_arg (GMT, token, GMT_IS_UNKNOWN, false, &value);
3348 			if (got == GMT_IS_NAN) {	/* Parsing failed, which means we found our first non-number; but it could also be a valid NaN */
3349 				gmt_str_tolower (token);
3350 				if (strncmp (token, "nan", 3U))
3351 					found_text = true;
3352 				else
3353 					type[col++] = got = GMT_IS_FLOAT;
3354 			}
3355 			else	/* A successful numerical parsing */
3356 					type[col++] = got;
3357 		}
3358 		if (gmt_M_is_verbose (GMT, GMT_MSG_INFORMATION) && col <= 50) {	/* Tell user how we interpreted their first record, but not for excessively long records */
3359 			k = gmtio_get_type_name_index (got);
3360 			if (col>1) strcat (message, ",");
3361 			strncat (message, GMT_coltype_name[k], GMT_BUFSIZ-1);
3362 			if (col == 50) strcat (message, ",...");
3363 		}
3364 		*tpos = pos;
3365 	}
3366 	if (found_text && col == 0 && !GMT->common.i.select && phys_col_type != GMT_IS_STRING) {	/* Has to be a non-GMT header with just text starting in the first column - treat as header */
3367 		if (strncmp (GMT->init.module_name, "batch", 5U) && strncmp (GMT->init.module_name, "movie", 5U)) {	/* Make exception for these modules what often will just read text lines */
3368 			gmt_M_free (GMT, type);
3369 			return GMT_NOT_OUTPUT_OBJECT;
3370 		}
3371 	}
3372 	*n_columns = col;	/* Pass back the numerical column count */
3373 	if (GMT->common.i.end)	/* Asked for unspecified last column on input (e.g., -i3,2,5:), supply the missing last column number */
3374 		gmtlib_reparse_i_option (GMT, col);
3375 
3376 	if (found_text) {	/* Determine record type */
3377 		if (GMT->current.io.trailing_text[GMT_IN]) ret_val = (*n_columns) ? GMT_READ_MIXED : GMT_READ_TEXT;	/* Possibly update record type */
3378 	}
3379 	else	/* No trailing text found, reset tpos */
3380 		*tpos = 0;
3381 	if (GMT->current.io.OGR) {	/* A few decision specific to OGR files */
3382 		if ((ret_val = gmtio_select_all_ogr_if_requested (GMT))) {	/* Finalize choice of aspatial fields */
3383 			gmt_M_free (GMT, type);
3384 			return ret_val;
3385 		}
3386 		ret_val |= gmtio_reconsider_rectype (GMT);	/* Consider the nature of aspatial information */
3387 		ret_val |= GMT_READ_DATA;	/* Since all shapefiles/OGR files have spatial plus aspatial data */
3388 	}
3389 
3390 	/* Tell user how we interpreted their first record */
3391 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Source col types: (%s)\n", message);
3392 
3393 	for (k = 0; k < col; k++)	/* Check if we can utilize what we learned about each physical column */
3394 		gmtio_assign_col_type_if_notset (GMT, k, type[k]);
3395 	gmt_M_free (GMT, type);
3396 
3397 
3398 	if (gmt_M_is_verbose (GMT, GMT_MSG_DEBUG)) {	/* Tell user how we interpreted their first record */
3399 		static char *tt = " NYY";
3400 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "ASCII source scanned: Numerical columns: %" PRIu64 ", Trailing text: %c, Record type: %s\n",
3401 			*n_columns, tt[ret_val], flavor[ret_val]);
3402 		if (GMT->common.i.select && GMT->common.i.n_cols > 0) {	/* Made a selection with -i */
3403 			message[0] = '\0';
3404 			for (pos = 0; pos < GMT->common.i.n_cols; pos++) {
3405 				k = gmtio_get_coltype_name_index (GMT, GMT_IN, pos);
3406 				if (pos < 50) {
3407 					if (pos) strcat (message, ",");
3408 					strncat (message, GMT_coltype_name[k], GMT_BUFSIZ-1);
3409 					if (pos == 49) strcat (message, ",...");
3410 				}
3411 			}
3412 			if (GMT->current.io.trailing_text[GMT_IN]) {
3413 				if (pos) strcat (message, ",");
3414 				strcat (message, "String");
3415 			}
3416 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Selected col types: (%s)\n", message);
3417 		}
3418 		if (GMT->common.o.select) {
3419 			message[0] = '\0';
3420 			for (pos = 0; pos < GMT->common.o.n_cols; pos++) {
3421 				col = GMT->current.io.col[GMT_OUT][pos].col;
3422 				k = gmtio_get_coltype_name_index (GMT, GMT_OUT, col);
3423 				if (pos < 50) {
3424 					if (pos) strcat (message, ",");
3425 					strncat (message, GMT_coltype_name[k], GMT_BUFSIZ-1);
3426 					if (pos == 49) strcat (message, ",...");
3427 				}
3428 			}
3429 			if (GMT->current.io.trailing_text[GMT_OUT]) {
3430 				if (pos) strcat (message, ",");
3431 				strcat (message, "String");
3432 			}
3433 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Output col types: (%s)\n", message);
3434 		}
3435 	}
3436 
3437 	return (ret_val);
3438 }
3439 
gmtio_extract_trailing_text(struct GMT_CTRL * GMT,size_t start_of_text)3440 GMT_LOCAL void gmtio_extract_trailing_text (struct GMT_CTRL *GMT, size_t start_of_text) {
3441 	if (GMT->common.i.word) {	/* Need to extract a specific column from the trailing text */
3442 		char *word = NULL, *orig = strdup (&GMT->current.io.curr_text[start_of_text]), *trail = orig;
3443 		uint64_t col = 0;
3444 		while (col != GMT->common.i.w_col && (word = strsep (&trail, GMT_TOKEN_SEPARATORS)) != NULL) {
3445 			if (*word != '\0')	/* Skip empty strings */
3446 				col++;
3447 		}
3448 		if (word)
3449 			strncpy (GMT->current.io.curr_trailing_text, word, GMT_BUFSIZ-1);
3450 		else
3451 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Trailing text did not have %" PRIu64 " words - no trailing word read\n", GMT->common.i.w_col);
3452 		gmt_M_str_free (orig);
3453 	}
3454 	else	/* Get the whole enchilada */
3455 		strncpy (GMT->current.io.curr_trailing_text, &GMT->current.io.curr_text[start_of_text], GMT_BUFSIZ-1);
3456 }
3457 
gmtio_reached_EOF(struct GMT_CTRL * GMT)3458 GMT_LOCAL inline int gmtio_reached_EOF (struct GMT_CTRL *GMT) {
3459 	GMT->current.io.status = GMT_IO_EOF;
3460 	if (GMT->current.io.give_report && GMT->current.io.n_bad_records) {	/* Report summary and reset counters */
3461 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "This file had %" PRIu64 " data records with invalid x and/or y values\n",
3462 			GMT->current.io.n_bad_records);
3463 		GMT->current.io.n_bad_records = GMT->current.io.n_clean_rec = 0;
3464 		GMT->current.io.rec_no = GMT->current.io.rec_in_tbl_no = 0;
3465 	}
3466 	GMT->current.io.data_record_number_in_tbl[GMT_IN] = GMT->current.io.data_record_number_in_seg[GMT_IN] = 0;
3467 	GMT->current.io.has_previous_rec = false;
3468 
3469 	return (GMT_NOTSET);
3470 }
3471 
3472 /*! This is the lowest-most input function in GMT.  All ASCII table data are read via
3473  * gmt_ascii_input.  Changes here affect all programs that read such data. */
gmtio_ascii_input(struct GMT_CTRL * GMT,FILE * fp,uint64_t * n,int * status)3474 GMT_LOCAL void *gmtio_ascii_input (struct GMT_CTRL *GMT, FILE *fp, uint64_t *n, int *status) {
3475 	uint64_t pos, col_no = 0, col_pos, n_convert, n_ok = 0, kind, add, n_use = 0;
3476 	uint64_t n_cols_this_record = GMT_MAX_COLUMNS;
3477 	int64_t in_col;
3478 	size_t start_of_text;
3479 	bool done = false, bad_record, set_nan_flag = false, add_aspatial_to_expected = false;
3480 	char line[GMT_BUFSIZ] = {""}, *p = NULL, *token = NULL, *stringp = NULL;
3481 	double val;
3482 	static char * (*strscan) (char **, const char *, size_t *);	/* Pointer to strsepz or strsepzp */
3483 
3484 	/* gmtio_ascii_input will skip blank lines and shell comment lines which start
3485 	 * with #.  Fields may be separated by spaces, tabs, or commas.  The routine returns
3486 	 * the actual number of items read [or 0 for segment header and -1 for EOF]
3487 	 * If *n is passed as GMT_BUFSIZ it will be reset to the actual number of fields.
3488 	 * If gap checking is in effect and one of the checks involves a column beyond
3489 	 * the ones otherwise needed by the program we extend the reading so we may
3490 	 * examine the column needed in the gap test.
3491 	 * *status returns the number of fields read, 0 for header records, -1 for EOF.
3492 	 * We return NULL (headers or errors) or pointer to GMT->current.io.curr_rec.
3493 	 * If -i is in effect we must handle the modified output order as well as
3494 	 * check for repeated input column usage.
3495 	 */
3496 
3497 	GMT->current.io.status = 0;	/* Reset status before reading next record */
3498 	while (!done) {	/* Done becomes true when we successfully have read a data record */
3499 
3500 		/* First read until we get a non-blank, non-comment record, or reach EOF */
3501 
3502 		GMT->current.io.rec_no++;		/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
3503 		GMT->current.io.rec_in_tbl_no++;	/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
3504 		if (GMT->current.setting.io_header[GMT_IN] && GMT->current.io.rec_in_tbl_no <= GMT->current.setting.io_n_header_items) {	/* Must treat first io_n_header_items as headers */
3505 			gmt_fgets (GMT, line, GMT_BUFSIZ, fp);	/* Get the line */
3506 			if (GMT->common.h.mode == GMT_COMMENT_IS_RESET) continue;	/* Simplest way to replace headers on output is to ignore them on input */
3507 			gmtio_set_current_record (GMT, line);
3508 			GMT->current.io.status = GMT_IO_TABLE_HEADER;
3509 #if 0
3510 			GMT->current.setting.io_header[GMT_OUT] = true;	/* Turn on table headers on output PW: No! If we get here via -hi then no header output was requested */
3511 #endif
3512 			*status = 0;
3513 			return (NULL);
3514 		}
3515 
3516 		/* Here we are done with any header records implied by -h */
3517 		if (GMT->current.setting.io_blankline[GMT_IN]) {	/* Treat blank lines as segment markers, so only read a single line */
3518 			p = gmt_fgets (GMT, line, GMT_BUFSIZ, fp);
3519 			GMT->current.io.rec_no++, GMT->current.io.rec_in_tbl_no++;
3520 		}
3521 		else {	/* Default is to skip all blank lines until we get something else (or hit EOF) */
3522 			while ((p = gmt_fgets (GMT, line, GMT_BUFSIZ, fp)) && gmt_is_a_blank_line (line)) GMT->current.io.rec_no++, GMT->current.io.rec_in_tbl_no++;
3523 		}
3524 		if (!p) {	/* Ran out of records, which can happen if file ends in a comment record */
3525 			*status = gmtio_reached_EOF (GMT);
3526 			return (NULL);
3527 		}
3528 
3529 		/* First chop off trailing whitespace and commas */
3530 		gmt_strstrip (line, false); /* Eliminate DOS endings and trailing white space, add linefeed */
3531 
3532 		if (gmtio_ogr_parser (GMT, line)) continue;	/* If we parsed a GMT/OGR record we must go up to top of loop and get the next record */
3533 		if (strchr (GMT->current.setting.io_head_marker_in, line[0])) {	/* Got a file header, copy it and return */
3534 			if (GMT->common.h.mode == GMT_COMMENT_IS_RESET) continue;	/* Simplest way to replace headers on output is to ignore them on input */
3535 			gmtio_set_current_record (GMT, line);
3536 			GMT->current.io.status = GMT_IO_TABLE_HEADER;
3537 			*status = 0;
3538 			return (NULL);
3539 		}
3540 
3541 		if ((kind = gmtio_is_segment_header (GMT, line))) {	/* Got a segment header, take action and return */
3542 			GMT->current.io.status = GMT_IO_SEGMENT_HEADER;
3543 			gmt_set_segmentheader (GMT, GMT_OUT, true);	/* Turn on segment headers on output */
3544 			GMT->current.io.seg_no++;
3545 			GMT->current.io.segment_header[0] = '\0';
3546 			if (kind == 1) {
3547 				/* If OGR input the also read next 1-2 records to pick up metadata */
3548 				if (GMT->current.io.ogr == GMT_OGR_TRUE) {
3549 					int c;
3550 					if ((c = fgetc (fp)) == '#') {	/* Possibly, this record starts with a comment character # */
3551 						line[0] = (char)c;	/* Since we ate the # already we place it here manually */
3552 						gmt_fgets (GMT, &line[1], GMT_BUFSIZ-1, fp);	/* Start at position 1 since # placed already and required for gmtio_ogr_parser to work */
3553 						gmtio_ogr_parser (GMT, line);	/* Parsed a GMT/OGR record */
3554 						gmtio_build_text_from_ogr (GMT, NULL, GMT->current.io.segment_header);	/* Fill in the buffer for -D, -G, Z etc */
3555 						if (strstr (line, "@H")) strcat (GMT->current.io.segment_header, " -Ph");	/* Sometimes a @P or @H record instead */
3556 						/* May also have a second comment record with just @P or @ H so check for this case */
3557 						if ((c = fgetc (fp)) == '#') {	/* Possibly, this record starts with a comment character # */
3558 							line[0] = (char)c;	/* Since we ate the # already we place it here manually */
3559 							gmt_fgets (GMT, &line[1], GMT_BUFSIZ-1, fp);	/* Start at position 1 since # placed already and required for gmtio_ogr_parser to work */
3560 							gmtio_ogr_parser (GMT, line);	/* Parse a possible GMT/OGR record (just returns if no OGR data there) */
3561 							if (strstr (line, "@H")) strcat (GMT->current.io.segment_header, " -Ph");	/* Add the hole designation to the polygon option */
3562 						}
3563 						else	/* Not a comment record; put that character back on the stream and move on */
3564 							ungetc (c, fp);
3565 					}
3566 					else	/* Not a comment record; put that character back on the stream and move on */
3567 						ungetc (c, fp);
3568 				}
3569 				else
3570 					strncpy (GMT->current.io.segment_header, gmtio_trim_segheader (GMT, line), GMT_BUFSIZ-1);
3571 				/* Just save the header content, not the marker and leading whitespace */
3572 			}
3573 			/* else we got a segment break instead - and header was set to NULL */
3574 			GMT->current.io.data_record_number_in_seg[GMT_IN] = 0;
3575 			GMT->current.io.has_previous_rec = false;
3576 			*status = 0;
3577 			return (NULL);
3578 		}
3579 
3580 		/* Here we know we are processing a data record */
3581 
3582 		GMT->current.io.data_record_number_in_set[GMT_IN]++;
3583 		GMT->current.io.data_record_number_in_tbl[GMT_IN]++;
3584 		GMT->current.io.data_record_number_in_seg[GMT_IN]++;
3585 		if (gmtio_outside_in_row_range (GMT, *(GMT->common.q.rec))) {	/* Records is outside desired row range of interest */
3586 			if (!gmtio_is_segment_header (GMT, line))
3587 				continue;
3588 		}
3589 
3590 		if (GMT->common.a.active && GMT->current.io.ogr == GMT_OGR_FALSE) {	/* Cannot give -a and not be reading an OGR/GMT file */
3591 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Aspatial associations set with -a but input file is not in OGR/GMT format!\n");
3592 			return NULL;
3593 		}
3594 
3595 		if (GMT->common.e.active && gmtio_skip_record (GMT, GMT->common.e.select, line)) continue;	/* Fail a grep test */
3596 
3597 		if (GMT->current.io.first_rec) {	/* Learn from the 1st record what we can about the type of data record this is */
3598 			unsigned int type;
3599 			if ((type = gmtio_examine_current_record (GMT, line, &start_of_text, &n_cols_this_record)) == GMT_NOT_OUTPUT_OBJECT) {
3600 				GMT->current.io.status = GMT_IO_TABLE_HEADER;
3601 				return NULL;
3602 			}
3603 			else
3604 				GMT->current.io.record_type[GMT_IN] = type;
3605 			if (GMT->current.io.variable_in_columns) {	/* Never finalize # of fields since it can change from rec to rec */
3606 				*n = GMT_MAX_COLUMNS;
3607 				strscan = (GMT->current.io.record_type[GMT_IN] & GMT_READ_TEXT) ? &strsepzp : &strsepz;	/* Need zp scanner to detect trailing text */
3608 			}
3609 			else {	/* Happily parsed first record and learned a few things.  Now prevent this block from being executed again for the current data file */
3610 				GMT->current.io.first_rec = false;
3611 				if (GMT->current.io.max_cols_to_read) {	/* A hard column count is enforced at first record */
3612 					*n = GMT->current.io.max_cols_to_read;
3613 					if (GMT->current.io.max_cols_to_read == n_cols_this_record && start_of_text == 0) {
3614 						GMT->current.io.trailing_text[GMT_IN] = false;	/* Turn off reading text since none present */
3615 						GMT->current.io.curr_trailing_text[0] = '\0';
3616 						GMT->current.io.record.text = NULL;
3617 					}
3618 					GMT->current.io.record_type[GMT_IN] = (GMT->current.io.trailing_text[GMT_IN]) ? GMT_READ_MIXED : GMT_READ_DATA;	/* Since otherwise we fail to store the trailing text */
3619 					strscan = (GMT->current.io.trailing_text[GMT_IN]) ? &strsepzp : &strsepz;	/* Need zp scanner to detect anything beyond the fixed columns as trailing text */
3620 				}
3621 				else {	/* Set expected cols and the scanner based on what record type we detected */
3622 					if (start_of_text == 0) {	/* Turn off reading text since none present */
3623 						GMT->current.io.trailing_text[GMT_IN] = false;
3624 						GMT->current.io.curr_trailing_text[0] = '\0';
3625 						GMT->current.io.record.text = NULL;
3626 					}
3627 					else if (n_cols_this_record == 0 && start_of_text) {	/* All text */
3628 						GMT->current.io.input = &gmtlib_ascii_textinput;	/* Override and use ASCII text mode */
3629 						strcpy (GMT->current.io.curr_trailing_text, line);
3630 						GMT->current.io.record.text = GMT->current.io.curr_trailing_text;
3631 						GMT->current.io.record.data = NULL;
3632 						*n = 1ULL;			/* We always return 1 item as there are no columns */
3633 						*status = 1;
3634 						return (&GMT->current.io.record);
3635 					}
3636 					*n = (GMT->common.i.select) ? GMT->common.i.n_cols : n_cols_this_record;
3637 					strscan = (GMT->current.io.record_type[GMT_IN] & GMT_READ_TEXT) ? &strsepzp : &strsepz;	/* Need zp scanner to detect trailing text */
3638 					add_aspatial_to_expected = true;	/* Since counts does not include any aspatial additions */
3639 				}
3640 			}
3641 		}
3642 
3643 		n_use = gmtio_n_cols_needed_for_gaps (GMT, *n);	/* Gives the actual columns we need (which may > *n if gap checking is active; if gap check we also update prev_rec) */
3644 		gmtio_update_prev_rec (GMT, n_use);
3645 		if (GMT->current.io.variable_in_columns && n_cols_this_record < n_use) n_use = n_cols_this_record;
3646 
3647 		bad_record = set_nan_flag = false;	/* Initialize flags */
3648 		gmtio_set_current_record (GMT, line);	/* Keep copy of current record around */
3649 		col_no = pos = n_ok = 0;		/* Initialize counters */
3650 		in_col = -1;				/* Since we will increment right away inside the loop */
3651 		start_of_text = 0;			/* Position in current string record */
3652 		GMT->current.io.curr_trailing_text[0] = '\0';	/* Start with nuthin. */
3653 
3654 		stringp = line;
3655 		while (!bad_record && col_no < n_use && (token = strscan (&stringp, GMT->current.io.scan_separators, &start_of_text)) != NULL) {	/* Get one field at the time until we run out or have issues */
3656 			++in_col;	/* This is the actual column number in the input file */
3657 			if (GMT->common.i.select) {	/* Must do special column-based processing since the -i option was set */
3658 				if (GMT->current.io.col_skip[in_col]) continue;		/* Just skip and not even count this column */
3659 				col_pos = GMT->current.io.col[GMT_IN][col_no].order;	/* Which data column will receive this value */
3660 			}
3661 			else				/* Default column order */
3662 				col_pos = col_no;
3663 			n_convert = gmt_scanf (GMT, token, gmt_M_type (GMT, GMT_IN, col_pos), &val);
3664 			if (n_convert != GMT_IS_NAN && gmt_input_col_is_nan_proxy (GMT, val, col_pos))	/* Input matched no-data setting, so change to NaN */
3665 				n_convert = GMT_IS_NAN;
3666 			if (n_convert == GMT_IS_NAN) {	/* Got a NaN or it failed to decode the string */
3667 				if (GMT->current.setting.io_nan_records || !GMT->current.io.skip_if_NaN[col_pos]) {	/* This field (or all fields) can be NaN so we pass it on */
3668 					GMT->current.io.curr_rec[col_pos] = GMT->session.d_NaN;
3669 					n_ok++;	/* Since NaN is considered an OK result */
3670 				}
3671 				else	/* Cannot have NaN in this column, flag record as bad */
3672 					bad_record = true;
3673 				if (GMT->current.io.skip_if_NaN[col_pos]) set_nan_flag = true;	/* Flag that we found NaN in a column that means we should skip */
3674 				col_no++;		/* Count up number of columns found */
3675 			}
3676 			else {					/* Successful decode, assign the value to the input array */
3677 				if (GMT->current.io.cycle_col == col_pos)	/* Convert periodic times */
3678 					gmtlib_modulo_time_calculator (GMT, &val);
3679 				GMT->current.io.curr_rec[col_pos] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col_no], val);
3680 				if (col_pos == GMT_X && gmt_M_type (GMT, GMT_IN, col_pos) & GMT_IS_LON)	/* Must account for periodicity in 360 as per current rule */
3681 					gmtio_adjust_periodic_lon (GMT, &GMT->current.io.curr_rec[col_pos]);
3682 				col_no++;		/* Count up number of columns found */
3683 				n_ok++;
3684 				while (GMT->common.i.select && col_no < GMT->common.i.n_cols && GMT->current.io.col[GMT_IN][col_no].col == GMT->current.io.col[GMT_IN][col_no-1].col) {
3685 					/* This input column is requested more than once */
3686 					col_pos = GMT->current.io.col[GMT_IN][col_no].order;	/* The data column that will receive this value */
3687 					GMT->current.io.curr_rec[col_pos] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col_no], val);
3688 					col_no++;
3689 					n_ok++;
3690 				}
3691 			}
3692 		}
3693 		if (gmtio_outside_in_data_range (GMT, GMT->common.q.col))	/* Must skip this record as key column value is outside desired data-range */
3694 			continue;
3695 
3696 		if (start_of_text) {	/* Save pointer to start of trailing text portion of the record */
3697 			while (start_of_text < (GMT_BUFSIZ-1) && GMT->current.io.curr_text[start_of_text] && strchr (GMT->current.io.scan_separators, GMT->current.io.curr_text[start_of_text])) start_of_text++;	/* First wind to start of trailing text */
3698 			gmtio_extract_trailing_text (GMT, start_of_text);
3699 			GMT->current.io.record.text = GMT->current.io.curr_trailing_text;
3700 		}
3701 
3702 		if ((add = gmtio_assign_aspatial_cols (GMT))) {	/* We appended <add> columns given via aspatial OGR/GMT values */
3703 			col_no += add;
3704 			n_ok += add;
3705 			if (add_aspatial_to_expected) *n += add, n_use += add;	/* Only do this once */
3706 		}
3707 		if (bad_record) {	/* This record failed our test and had NaNs */
3708 			GMT->current.io.n_bad_records++;
3709 			if (GMT->current.io.give_report && (GMT->current.io.n_bad_records == 1)) {	/* Report 1st occurrence of bad record */
3710 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Encountered first invalid ASCII data record near/at line # %" PRIu64 "\n",
3711 				            GMT->current.io.rec_no);
3712 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Likely causes:\n");
3713 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "(1) Invalid x and/or y values, i.e. NaNs or garbage in text strings.\n");
3714 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "(2) Incorrect data type assumed if -J, -f are not set or set incorrectly.\n");
3715 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "(3) The -: switch is implied but not set.\n");
3716 			}
3717 		}
3718 		else if (GMT->current.io.skip_duplicates && GMT->current.io.has_previous_rec) {	/* Test to determine if we should skip repeated duplicate records with same x,y */
3719 			done = !(GMT->current.io.curr_rec[GMT_X] == GMT->current.io.prev_rec[GMT_X] &&
3720 			         GMT->current.io.curr_rec[GMT_Y] == GMT->current.io.prev_rec[GMT_Y]);	/* Yes, duplicate */
3721 		}
3722 		else
3723 			done = true;	/* Success, we can get out of this loop and return what we got */
3724 	}
3725 	GMT->current.io.status = (GMT->current.io.variable_in_columns || n_ok == n_use || *n == GMT_MAX_COLUMNS) ? 0 : GMT_IO_MISMATCH;	/* Hopefully set status to 0 (OK) */
3726 	if (*n == GMT_MAX_COLUMNS) *n = n_ok;							/* Update the number of expected fields */
3727 	if (gmt_M_rec_is_error (GMT))
3728 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Mismatch between actual (%d) and expected (%d) fields near line %" PRIu64 " in file %s\n",
3729 		            col_no, *n, GMT->current.io.rec_no, GMT->current.io.filename[GMT_IN]);
3730 
3731 	if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && col_no >= 2) {
3732 		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 */
3733 	}
3734 	else if (GMT->current.proj.inv_coordinates)
3735 		gmtio_adjust_projected (GMT);	/* Must apply inverse projection to get lon, lat */
3736 	if (gmtlib_gap_detected (GMT)) {	/* A gap between this an previous record was detected (see -g) so we set status and return 0 */
3737 		*status = gmtlib_set_gap (GMT);
3738 		return (&GMT->current.io.record);
3739 	}
3740 
3741 	//GMT->current.io.data_record_number_in_set[GMT_IN]++;	/* Got a valid data record (which is true even if it was a gap) */
3742 	*status = (int)n_ok;			/* Return the number of fields successfully read */
3743 	if (set_nan_flag) {
3744 		GMT->current.io.status |= GMT_IO_NAN;	/* Say we found NaNs */
3745 		return (&GMT->current.io.record);	/* Pass back pointer to data array */
3746 	}
3747 	GMT->current.io.has_previous_rec = true;
3748 	return ((GMT->current.io.status) ? NULL : &GMT->current.io.record);	/* Pass back pointer to data array */
3749 }
3750 
3751 /*! . */
gmtio_write_table(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,struct GMT_DATATABLE * table,bool use_GMT_io,unsigned int io_mode,unsigned int n_seg)3752 GMT_LOCAL int gmtio_write_table (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, struct GMT_DATATABLE *table, bool use_GMT_io, unsigned int io_mode, unsigned int n_seg) {
3753 	/* Writes an entire segment data set to file or wherever.
3754 	 * Specify io_mode == GMT_WRITE_SEGMENT or GMT_WRITE_TABLE_SEGMENT to write segments to individual files.
3755 	 * If dist is NULL we choose stdout. */
3756 
3757 	bool ASCII, close_file = false, append, was;
3758 	int save = 0;
3759 	unsigned int k;
3760 	uint64_t row = 0, seg, col;
3761 	int *fd = NULL;
3762 	char open_mode[4] = {""}, file[PATH_MAX] = {""}, tmpfile[PATH_MAX] = {""}, *out_file = tmpfile, *txt = NULL;
3763 	double *out = NULL;
3764 	FILE *fp = NULL;
3765 	struct GMT_DATASEGMENT *S = NULL;
3766 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
3767 	struct GMT_DATATABLE_HIDDEN *TH = gmt_get_DT_hidden (table);
3768 	int (*psave) (struct GMT_CTRL *, FILE *, uint64_t, double *, char *) = NULL;	/* Pointer to function writing tables */
3769 
3770 
3771 	if (TH->mode == GMT_WRITE_SKIP) return (0);	/* Skip this table */
3772 
3773 	append = (dest_type == GMT_IS_FILE && dest && ((char *)dest)[0] == '>');	/* Want to append to existing file */
3774 
3775 	if (use_GMT_io) {	/* Use GMT->current.io.info settings to determine if input is ASCII/binary, else it defaults to ASCII */
3776 		strcpy (open_mode, (append) ? GMT->current.io.a_mode : GMT->current.io.w_mode);
3777 		ASCII = !GMT->common.b.active[GMT_OUT];
3778 	}
3779 	else {			/* Force ASCII mode */
3780 		strcpy (open_mode, (append) ? "a" : "w");
3781 		ASCII = true;
3782 		GMT->current.io.output = GMT->session.output_ascii;	/* Override and use ASCII mode */
3783 	}
3784 	psave = GMT->current.io.output;		/* Save the previous pointer since we need to change it back at the end */
3785 
3786 	switch (dest_type) {
3787 		case GMT_IS_FILE:	/* dest is a file name */
3788 			if (!dest) {
3789 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Internal error: Pointer 'dest' cannot be NULL here\n");
3790 				return GMT_ARG_IS_NULL;
3791 			}
3792 			strncpy (file, dest, PATH_MAX-1);
3793 			if (io_mode < GMT_WRITE_SEGMENT) {	/* Only require one destination */
3794 				if ((fp = gmt_fopen (GMT, &file[append], open_mode)) == NULL) {
3795 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", &file[append]);
3796 					return GMT_ERROR_ON_FOPEN;
3797 				}
3798 				close_file = true;	/* We only close files we have opened here */
3799 			}
3800 			break;
3801 		case GMT_IS_STREAM:	/* Open file pointer given, just copy */
3802 			fp = (FILE *)dest;
3803 			if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
3804 			if (fp == GMT->session.std[GMT_OUT])
3805 				strcpy (file, "<stdout>");
3806 			else
3807 				strcpy (file, "<output stream>");
3808 			break;
3809 		case GMT_IS_FDESC:		/* Open file descriptor given, just convert to file pointer */
3810 			fd = dest;
3811 			if (fd && (fp = fdopen (*fd, open_mode)) == NULL) {
3812 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert file descriptor %d to stream in gmtio_write_table\n", *fd);
3813 				return GMT_ERROR_ON_FDOPEN;
3814 			}
3815 			else
3816 				close_file = true;	/* fdopen allocates memory */
3817 			if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
3818 			if (fp == GMT->session.std[GMT_OUT])
3819 				strcpy (file, "<stdout>");
3820 			else
3821 				strcpy (file, "<output file descriptor>");
3822 			break;
3823 		default:
3824 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Internal error: Unrecognized source type %d in gmtio_write_table\n", dest_type);
3825 			return GMT_NOT_A_VALID_METHOD;
3826 			break;
3827 	}
3828 	was = GMT->current.io.multi_segments[GMT_OUT];
3829 	if (io_mode < GMT_WRITE_SEGMENT) {
3830 		if (ASCII && GMT->current.setting.io_header[GMT_OUT]) {
3831 			for (k = 0; k < table->n_headers; k++) gmtlib_write_tableheader (GMT, fp, table->header[k]);	/* Write any existing header comments */
3832 			gmtlib_write_newheaders (GMT, fp, table->n_columns);	/* Write general header block */
3833 		}
3834 		if (TH->ogr) gmtlib_write_ogr_header (fp, TH->ogr);	/* Must write OGR/GMT header */
3835 		switch (GMT->current.setting.io_first_header) {
3836 			case GMT_FIRST_SEGHEADER_MAYBE:
3837 				GMT->current.io.multi_segments[GMT_OUT] = (n_seg > 1 || (table->n_segments == 1 && table->segment[0]->header));
3838 				break;
3839 			case GMT_FIRST_SEGHEADER_ALWAYS:
3840 				GMT->current.io.multi_segments[GMT_OUT] = true;
3841 				break;
3842 			case GMT_FIRST_SEGHEADER_NEVER:
3843 				GMT->current.io.multi_segments[GMT_OUT] = (n_seg >1);
3844 				break;
3845 		}
3846 	}
3847 
3848 	out = gmt_M_memory (GMT, NULL, table->n_columns, double);
3849 	for (seg = 0; seg < table->n_segments; seg++) {
3850 		S = table->segment[seg];
3851 		SH = gmt_get_DS_hidden (S);
3852 		if (SH->mode == GMT_WRITE_SKIP) continue;	/* Skip this segment */
3853 
3854 		if (io_mode >= GMT_WRITE_SEGMENT) {	/* Create separate file for each segment */
3855 			if (SH->file[GMT_OUT])
3856 				out_file = SH->file[GMT_OUT];
3857 			else if (io_mode == GMT_WRITE_TABLE_SEGMENT)	/* Build name with table id and seg # */
3858 				snprintf (tmpfile, PATH_MAX, file, TH->id, seg);
3859 			else					/* Build name with seg ids */
3860 				snprintf (tmpfile, PATH_MAX, file, SH->id);
3861 
3862 			if (close_file) gmt_fclose (GMT, fp);	/* Close the file since we opened it */
3863 			if ((fp = gmt_fopen (GMT, out_file, open_mode)) == NULL) {
3864 				gmt_M_free (GMT, out);
3865 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", out_file);
3866 				return GMT_ERROR_ON_FOPEN;
3867 			}
3868 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Writing data segment to file %s\n", out_file);
3869 			if (ASCII && GMT->current.setting.io_header[GMT_OUT]) {
3870 				for (k = 0; k < table->n_headers; k++)
3871 					gmtlib_write_tableheader (GMT, fp, table->header[k]);	/* Write any existing header comments */
3872 				gmtlib_write_newheaders (GMT, fp, table->n_columns);	/* Write general header block */
3873 			}
3874 		}
3875 		if (GMT->current.io.multi_segments[GMT_OUT]) {	/* Want to write segment headers */
3876 			if (!GMT->common.a.output && SH->ogr) gmtio_build_segheader_from_ogr (GMT, GMT_OUT, S);	/* We have access to OGR metadata */
3877 			if (S->header)
3878 				strncpy (GMT->current.io.segment_header, S->header, GMT_BUFSIZ-1);
3879 			else
3880 				GMT->current.io.segment_header[0] = '\0';
3881 			gmt_write_segmentheader (GMT, fp, S->n_columns);
3882 			if (SH->ogr && GMT->common.a.output) gmtio_write_ogr_segheader (GMT, fp, S);
3883 		}
3884 		if (SH->mode == GMT_WRITE_HEADER) continue;	/* Skip after writing segment header */
3885 		if (SH->range && SH->range != GMT->current.io.geo.range) {	/* Segment-specific formatting for longitudes */
3886 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "File %s Segment %d changed io.geo.range from %d to %d\n",
3887 			            out_file, (int)seg, GMT->current.io.geo.range, SH->range);
3888 			save = GMT->current.io.geo.range; GMT->current.io.geo.range = SH->range;
3889 		}
3890 		for (row = 0; row < S->n_rows; row++) {
3891 			txt = (S->text) ? S->text[row] : NULL;
3892 			for (col = 0; col < S->n_columns; col++) out[col] = S->data[col][row];
3893 			GMT->current.io.output (GMT, fp, S->n_columns, out, txt);
3894 		}
3895 		if (SH->range) GMT->current.io.geo.range = save; 	/* Restore formatting */
3896 		if (io_mode == GMT_WRITE_SEGMENT) gmt_fclose (GMT, fp);	/* Close the segment file */
3897 		if (ASCII && S->text)
3898 			GMT->current.io.output = psave;	/* Override and use ASCII mode */
3899 	}
3900 
3901 	if (close_file) gmt_fclose (GMT, fp);	/* Close the file since we opened it */
3902 	gmt_M_free (GMT, out);			/* Free up allocated memory */
3903 	if (!use_GMT_io) GMT->current.io.output = psave;	/* Restore former pointers and values */
3904 	GMT->current.io.multi_segments[GMT_OUT] = was;
3905 
3906 	return (0);	/* OK status */
3907 }
3908 
3909 /*! . */
gmtio_nc_input(struct GMT_CTRL * GMT,FILE * fp,uint64_t * n,int * retval)3910 GMT_LOCAL void * gmtio_nc_input (struct GMT_CTRL *GMT, FILE *fp, uint64_t *n, int *retval) {
3911 	/* netCDF tables contain information about the number of records, so we can use a
3912 	 * faster strategy: When file is opened, determine number of rows and columns and
3913 	 * preallocate all the column vectors.  Then, when we ask for the first data record
3914 	 * we read the entire data set.  We then simply return the values corresponding to
3915 	 * the current row.  Note some variables may be 2-D and we then consider them as a
3916 	 * stack of 1-D columns to loop over.
3917 	 */
3918 	int status;
3919 	uint64_t n_use = 0, col;
3920 	gmt_M_unused(fp);
3921 
3922 	GMT->current.io.status = GMT_IO_DATA_RECORD;
3923 	if (*n == GMT_MAX_COLUMNS) {	/* Set columns if not known yet */
3924 		*n = GMT->current.io.ncols;			/* Number of requested columns */
3925 	}
3926 	if (GMT->current.io.nrec == 0) {	/* First record, read the entire file and do all scalings */
3927 		uint64_t k, row;
3928 		int v;
3929 		size_t start[5], count[5];
3930 		n_use = gmtio_n_cols_needed_for_gaps (GMT, *n);	/* Specified number of output columns */
3931 		for (v = 0, col = 0; v < GMT->current.io.nvars && col < n_use; ++v) {	/* For each named variable v ... */
3932 			/* Copy info from current.io. t_index is generally {0,0,0,0,0}, count is generally {ndim,1,1,1,1}.
3933 			   For 2D array: count is {ndim,ncol,1,1,1} */
3934 			for (k = 0; k < 5; ++k) {
3935 				start[k] = GMT->current.io.t_index[v][k];
3936 				count[k] = GMT->current.io.count[v][k];
3937 			}
3938 			for (k = 0; k < GMT->current.io.count[v][1]; ++col, ++k) {	/* For each column in variable v [typically 1 unless 2-D array] */
3939 				start[1] = k, count[1] = 1;	/* Read only k'th column in 2-D array or the only column [k = 0] in 1-D array */
3940 				nc_get_vara_double (GMT->current.io.grpid[v], GMT->current.io.varid[v], start, count, GMT->hidden.mem_coord[col]);	/* Read column */
3941 				for (row = 0; row < GMT->current.io.ndim; ++row) {	/* Loop over all records (rows) to do scaling */
3942 					if (GMT->hidden.mem_coord[col][row] == GMT->current.io.missing_value[v])	/* Nan proxy detected */
3943 						GMT->hidden.mem_coord[col][row] = GMT->session.d_NaN;
3944 					else	/* Regular translation */
3945 						GMT->hidden.mem_coord[col][row] = GMT->hidden.mem_coord[col][row] * GMT->current.io.scale_factor[v] + GMT->current.io.add_offset[v];
3946 					GMT->hidden.mem_coord[col][row] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][v], GMT->hidden.mem_coord[col][row]);	/* Any additional user scalings */
3947 				}
3948 			}
3949 		}
3950 		/* OK, everything is read into memory; below we simply return the items from each array at the given record number */
3951 	}
3952 	else if (*n > GMT->current.io.ncols) {
3953 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtio_nc_input is asking for %d columns, but file has only %" PRIu64 "\n",
3954 		            *n, GMT->current.io.ncols);
3955 		GMT->current.io.status = GMT_IO_MISMATCH;
3956 	}
3957 	do {	/* Keep reading until (1) EOF, (2) got a segment record, or (3) a valid data record */
3958 		n_use = gmtio_n_cols_needed_for_gaps (GMT, *n);
3959 		gmtio_update_prev_rec (GMT, n_use);	/* Copy current record to previous record before getting the new current record */
3960 
3961 		if (GMT->current.io.nrec == GMT->current.io.ndim) {	/* Reading past last record means EOF for netCDF files */
3962 			GMT->current.io.status = GMT_IO_EOF;
3963 			*retval = GMT_NOTSET;
3964 			return (NULL);
3965 		}
3966 		/* Just get values from current row in the mem_coord arrays */
3967 		for (col = 0; col < GMT->current.io.ncols; ++col)
3968 			GMT->current.io.curr_rec[col] = GMT->hidden.mem_coord[col][GMT->current.io.nrec];
3969 		/* Increment record counters */
3970 		GMT->current.io.nrec++;
3971 		GMT->current.io.rec_no++;
3972 		GMT->current.io.data_record_number_in_set[GMT_IN]++;
3973 		GMT->current.io.data_record_number_in_tbl[GMT_IN]++;
3974 		GMT->current.io.data_record_number_in_seg[GMT_IN]++;
3975 		GMT->current.io.has_previous_rec = true;
3976 		status = gmtlib_process_binary_input (GMT, n_use);	/* Determine if a header record, data record, or record to skip */
3977 		if (status == 1) { *retval = 0; return (NULL); }	/* Found a segment header, meaning all columns were NaN */
3978 		if (gmtlib_gap_detected (GMT)) {	/* The -g is in effect and was triggered due to a user-specified gap criteria */
3979 			*retval = gmtlib_set_gap (GMT);
3980 			return (&GMT->current.io.record);
3981 		}
3982 		GMT->current.io.has_previous_rec = true;
3983 	} while (status == 2);	/* Continue reading when a record is to be skipped */
3984 
3985 	*retval = (int)*n;
3986 	return (&GMT->current.io.record);
3987 }
3988 
3989 /*! . */
gmtio_nc_inq_varid(int ncid,const char * varnm,int * grpid,int * varid)3990 GMT_LOCAL int gmtio_nc_inq_varid (int ncid, const char *varnm, int *grpid, int *varid) {
3991 /* Get group id and variable id of netCDF variable that includes the full group name.
3992  * If no group name is included, grpid is the ncid
3993  */
3994 	char *pstr;
3995 	char str[GMT_LEN64];
3996 	int status;
3997 
3998 	strcpy (str, varnm);
3999 	if ((pstr = strrchr (str, '/'))) {
4000 		pstr[0] = '\0';
4001 		status = nc_inq_grp_full_ncid (ncid, str, grpid);
4002 		if (status != NC_NOERR) return (status);
4003 		pstr++;
4004 	}
4005 	else {
4006 		*grpid = ncid;
4007 		pstr = str;
4008 	}
4009 	return (nc_inq_varid (*grpid, pstr, varid));
4010 }
4011 
4012 /*! . */
gmtio_nc_fopen(struct GMT_CTRL * GMT,const char * filename,const char * mode)4013 GMT_LOCAL FILE *gmtio_nc_fopen (struct GMT_CTRL *GMT, const char *filename, const char *mode) {
4014 /* Open a netCDF file for column I/O. Append ?var1/var2/... to indicate the requested columns.
4015  * Currently only reading is supported.
4016  * The routine returns a fake file pointer (in fact the netCDF file ID), but stores
4017  * all the relevant information in the GMT->current.io struct (ncid, ndim, nrec, varid, add_offset,
4018  * scale_factor, missing_value). Some of these are allocated here, and have to be
4019  * deallocated upon gmt_fclose.
4020  * Also assigns GMT->current.io.col_type[GMT_IN] based on the variable attributes.
4021  */
4022 
4023 	char file[PATH_MAX] = {""}, path[PATH_MAX] = {""};
4024 	char *pstr, *qstr;
4025 	int i, j, nvars, dimids[5] = {-1, -1, -1, -1, -1}, ndims, in, id;
4026 	size_t n, item[2];
4027 	size_t tmp_pointer; /* To avoid "cast from pointer to integer of different size" */
4028 	double t_value[5], dummy[2];
4029 	char varnm[20][GMT_LEN64], long_name[GMT_LEN256] = {""}, units[GMT_LEN256] = {""};
4030 	char varname[GMT_LEN64] = {""}, dimname[GMT_LEN64] = {""};
4031 	struct GMT_TIME_SYSTEM time_system;
4032 	bool by_value;
4033 
4034 	if (mode[0] != 'r') {
4035 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtio_nc_fopen does not support netCDF writing mode\n");
4036 		GMT->parent->error = GMT_NOT_A_VALID_DIRECTION;
4037 		return NULL;
4038 	}
4039 
4040 	gmt_M_memset (varnm, 20 * GMT_LEN64, char);
4041 
4042 	nvars = sscanf (filename,
4043 		"%[^?]?%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]",
4044 		file, varnm[0], varnm[1], varnm[2], varnm[3], varnm[4], varnm[5], varnm[6], varnm[7], varnm[8], varnm[9], varnm[10],
4045 		varnm[11], varnm[12], varnm[13], varnm[14], varnm[15], varnm[16], varnm[17], varnm[18], varnm[19]) - 1;
4046 	if (gmt_getdatapath (GMT, file, path, R_OK) == NULL) return (NULL);	/* No such file */
4047 	if (gmt_nc_open (GMT, path, NC_NOWRITE, &GMT->current.io.ncid)) return (NULL);
4048 	pstr = strrchr (filename, ',');
4049 	qstr = strchr (filename, '?');
4050 
4051 	/* In case there is a comma in the list of variables, or if the first variable is actually a group,
4052 	 * we re-read the variable list with comma separators.
4053 	 * When a group is missing, nc_iq_grp_ncid comes either back with error status, or with group id
4054 	 * identical to file id. Not clear why both can happen, but this check captures both. */
4055 	i = nc_inq_grp_ncid (GMT->current.io.ncid, varnm[0], &id);
4056 	if (pstr > qstr || (i == NC_NOERR && GMT->current.io.ncid != id)) {
4057 		nvars = sscanf (filename,
4058 			"%[^?]?%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]",
4059 			file, varnm[0], varnm[1], varnm[2], varnm[3], varnm[4], varnm[5], varnm[6], varnm[7], varnm[8], varnm[9], varnm[10],
4060 			varnm[11], varnm[12], varnm[13], varnm[14], varnm[15], varnm[16], varnm[17], varnm[18], varnm[19]) - 1;
4061 	}
4062 	else if (gmt_M_compat_check (GMT, 4)) {
4063 		if (nvars <= 0) nvars = sscanf (GMT->common.b.varnames,
4064 			"%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]",
4065 			varnm[0], varnm[1], varnm[2], varnm[3], varnm[4], varnm[5], varnm[6], varnm[7], varnm[8], varnm[9], varnm[10],
4066 			varnm[11], varnm[12], varnm[13], varnm[14], varnm[15], varnm[16], varnm[17], varnm[18], varnm[19]);
4067 	}
4068 	if (nvars <= 0)
4069 		nc_inq_nvars (GMT->current.io.ncid, &GMT->current.io.nvars);
4070 	else
4071 		GMT->current.io.nvars = nvars;
4072 	GMT->current.io.grpid = gmt_M_memory (GMT, NULL, GMT->current.io.nvars, int);
4073 	GMT->current.io.varid = gmt_M_memory (GMT, NULL, GMT->current.io.nvars, int);
4074 	GMT->current.io.scale_factor = gmt_M_memory (GMT, NULL, GMT->current.io.nvars, double);
4075 	GMT->current.io.add_offset = gmt_M_memory (GMT, NULL, GMT->current.io.nvars, double);
4076 	GMT->current.io.missing_value = gmt_M_memory (GMT, NULL, GMT->current.io.nvars, double);
4077 	GMT->current.io.ndim = GMT->current.io.nrec = 0;
4078 
4079 	for (i = 0; i < GMT->current.io.nvars; i++) {
4080 
4081 		/* Check for indices */
4082 		for (j = 0; j < 5; j++) GMT->current.io.t_index[i][j] = 0, GMT->current.io.count[i][j] = 1;
4083 		j = in = 0, by_value = false;
4084 		while (varnm[i][j] && varnm[i][j] != '(' && varnm[i][j] != '[') j++;
4085 		if (varnm[i][j] == '(') {
4086 			in = sscanf (&varnm[i][j+1], "%lf,%lf,%lf,%lf", &t_value[1], &t_value[2], &t_value[3], &t_value[4]);
4087 			varnm[i][j] = '\0';
4088 			by_value = true;
4089 		}
4090 		else if (varnm[i][j] == '[') {
4091 			in = sscanf (&varnm[i][j+1], "%" SCNuS ",%" SCNuS ",%" SCNuS ",%" SCNuS, &GMT->current.io.t_index[i][1],
4092 			             &GMT->current.io.t_index[i][2], &GMT->current.io.t_index[i][3], &GMT->current.io.t_index[i][4]);
4093 			varnm[i][j] = '\0';
4094 		}
4095 
4096 		/* Get variable ID and variable name */
4097 		if (nvars <= 0) {
4098 			GMT->current.io.grpid[i] = GMT->current.io.ncid;
4099 			GMT->current.io.varid[i] = i;
4100 		}
4101 		else {
4102 			if (gmt_M_err_fail (GMT, gmtio_nc_inq_varid (GMT->current.io.ncid, varnm[i], &GMT->current.io.grpid[i], &GMT->current.io.varid[i]), file)) {
4103 				GMT->parent->error = GMT_NOT_A_VALID_ID;
4104 				return NULL;
4105 			}
4106 		}
4107 		nc_inq_varname (GMT->current.io.grpid[i], GMT->current.io.varid[i], varname);
4108 
4109 		/* Check number of dimensions */
4110 		if (gmt_M_err_fail (GMT, nc_inq_varndims (GMT->current.io.grpid[i], GMT->current.io.varid[i], &ndims), file)) {
4111 			GMT->parent->error = GMT_GRDIO_BAD_DIM;
4112 			return NULL;
4113 		}
4114 		if (ndims > 5) {
4115 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "NetCDF variable %s has too many dimensions (%d)\n", varname, j);
4116 			GMT->parent->error = GMT_DIM_TOO_LARGE;
4117 			return NULL;
4118 		}
4119 		if (ndims - in < 1) {
4120 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "NetCDF variable %s has %" PRIuS " dimensions, cannot specify more than %d indices; ignoring remainder\n", varname, ndims, ndims-1);
4121 			for (j = in; j < ndims; j++) GMT->current.io.t_index[i][j] = 0;
4122 		}
4123 		if (ndims - in > 2)
4124 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "NetCDF variable %s has %" PRIuS " dimensions, showing only 2\n", varname, ndims);
4125 
4126 		/* Get information of the first two dimensions */
4127 		nc_inq_vardimid(GMT->current.io.grpid[i], GMT->current.io.varid[i], dimids);
4128 		nc_inq_dimlen(GMT->current.io.grpid[i], dimids[0], &n);
4129 		if (GMT->current.io.ndim != 0 && GMT->current.io.ndim != n) {
4130 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "NetCDF variable %s has different dimension (%" PRIuS ") from others (%" PRIuS ")\n",
4131 			            varname, n, GMT->current.io.ndim);
4132 			GMT->parent->error = GMT_RUNTIME_ERROR;
4133 			return NULL;
4134 		}
4135 		GMT->current.io.count[i][0] = GMT->current.io.ndim = n;
4136 		if (dimids[1] >= 0 && ndims - in > 1) {
4137 			nc_inq_dimlen(GMT->current.io.grpid[i], dimids[1], &n);
4138 		}
4139 		else
4140 			n = 1;
4141 		GMT->current.io.count[i][1] = n;
4142 		GMT->current.io.ncols += (int)n;
4143 
4144 		/* If selected by value instead of index */
4145 		for (j = 1; by_value && j <= in; j++) {
4146 			nc_inq_dim (GMT->current.io.grpid[i], dimids[j], dimname, &n);
4147 			nc_inq_varid (GMT->current.io.grpid[i], dimname, &id);
4148 			item[0] = 0, item[1] = n-1;
4149 			if (nc_get_att_double (GMT->current.io.grpid[i], id, "actual_range", dummy)) {
4150 				nc_get_var1_double (GMT->current.io.grpid[i], id, &item[0], &dummy[0]);
4151 				nc_get_var1_double (GMT->current.io.grpid[i], id, &item[1], &dummy[1]);
4152 			}
4153 			GMT->current.io.t_index[i][j] = lrint((t_value[j] - dummy[0]) / (dummy[1] - dummy[0]));
4154 		}
4155 
4156 		/* Get scales, offsets and missing values */
4157 		if (nc_get_att_double (GMT->current.io.grpid[i], GMT->current.io.varid[i], "scale_factor",
4158 		                       &GMT->current.io.scale_factor[i]))
4159 			GMT->current.io.scale_factor[i] = 1.0;
4160 		if (nc_get_att_double (GMT->current.io.grpid[i], GMT->current.io.varid[i], "add_offset",
4161 		                       &GMT->current.io.add_offset[i]))
4162 			GMT->current.io.add_offset[i] = 0.0;
4163 		if (nc_get_att_double (GMT->current.io.grpid[i], GMT->current.io.varid[i], "_FillValue",
4164 		                       &GMT->current.io.missing_value[i]) && nc_get_att_double (GMT->current.io.grpid[i], GMT->current.io.varid[i],
4165 		                       "missing_value", &GMT->current.io.missing_value[i]))
4166 		    GMT->current.io.missing_value[i] = GMT->session.d_NaN;
4167 
4168 		/* Scan for geographical or time units */
4169 		if (gmtlib_nc_get_att_text (GMT, GMT->current.io.grpid[i], GMT->current.io.varid[i], "long_name", long_name, GMT_LEN256)) long_name[0] = 0;
4170 		if (gmtlib_nc_get_att_text (GMT, GMT->current.io.grpid[i], GMT->current.io.varid[i], "units", units, GMT_LEN256)) units[0] = 0;
4171 		gmt_str_tolower (long_name); gmt_str_tolower (units);
4172 
4173 		if (gmt_M_type (GMT, GMT_IN, i) == GMT_IS_FLOAT)
4174 			{ /* Float type is preset, do not alter */ }
4175 		else if (!strcmp (long_name, "longitude") || strstr (units, "degrees_e"))
4176 			gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_LON);
4177 		else if (!strcmp (long_name, "latitude") || strstr (units, "degrees_n"))
4178 			gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_LAT);
4179 		else if (!strcmp (long_name, "time") || !strcmp (varname, "time")) {
4180 			gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_RELTIME);
4181 			gmt_M_memcpy (&time_system, &GMT->current.setting.time_system, 1, struct GMT_TIME_SYSTEM);
4182 			if (gmt_get_time_system (GMT, units, &time_system) || gmt_init_time_system_structure (GMT, &time_system))
4183 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Time units [%s] in NetCDF file not recognized, defaulting to %s.\n",
4184 				            units, GMT_SETTINGS_FILE);
4185 			/* Determine scale between data and internal time system, as well as the offset (in internal units) */
4186 			GMT->current.io.scale_factor[i] = GMT->current.io.scale_factor[i] * time_system.scale * GMT->current.setting.time_system.i_scale;
4187 			GMT->current.io.add_offset[i] *= time_system.scale;	/* Offset in seconds */
4188 			GMT->current.io.add_offset[i] += GMT_DAY2SEC_F * ((time_system.rata_die - GMT->current.setting.time_system.rata_die) +
4189 			                                 (time_system.epoch_t0 - GMT->current.setting.time_system.epoch_t0));
4190 			GMT->current.io.add_offset[i] *= GMT->current.setting.time_system.i_scale;	/* Offset in internal time units */
4191 		}
4192 		else if (gmt_M_type (GMT, GMT_IN, i) == GMT_IS_UNKNOWN)
4193 			gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_FLOAT);
4194 	}
4195 
4196 	GMT->current.io.input = gmtio_nc_input;
4197 	tmp_pointer = (size_t)(-GMT->current.io.ncid);
4198 
4199 	gmt_prep_tmp_arrays (GMT, GMT_IN, GMT->current.io.ndim, GMT->current.io.ncols);	/* Preallocate arrays for all netcdf vectors */
4200 
4201 	return ((FILE *)tmp_pointer);
4202 }
4203 
4204 /*! . */
gmtio_file_is_readable(struct GMT_CTRL * GMT,char * path)4205 GMT_LOCAL bool gmtio_file_is_readable (struct GMT_CTRL *GMT, char *path) {
4206 	/* Returns true if readable, otherwise give error and return false */
4207 	struct stat S;
4208 	int err = stat (path, &S);	/* Stat the path (which may not exist) */
4209 	if (err == 0 && S_ISDIR (S.st_mode)) return (false);	/* A directory is not a file */
4210 	if (!access (path, R_OK)) return (true);	/* Readable */
4211 	/* Get here when found, but not readable */
4212 	GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to read %s (permissions?)\n", path);
4213 	return (false);	/* Cannot read, give up */
4214 }
4215 
4216 #if 0 /* NOT USED ??? */
4217 GMT_LOCAL int gmtio_validate_aspatial (struct GMT_CTRL *GMT, struct GMT_OGR *G) {
4218 	unsigned int k;
4219 	if (GMT->current.io.ogr != GMT_OGR_TRUE) return (GMT_OK);	/* No point checking further since file is not GMT/OGR */
4220 	for (k = 0; k < GMT->common.a.n_aspatial; k++) if (gmt_get_ogr_id (G, GMT->common.a.name[k])) return (-1);
4221 	return (GMT_OK);
4222 }
4223 
4224 /* NOT USED ??? */
4225 GMT_LOCAL int gmtio_load_aspatial_values (struct GMT_CTRL *GMT, struct GMT_OGR *G) {
4226 	/* Uses the info in -a and OGR to replace values in the curr_rec array with aspatial values */
4227 
4228 	unsigned int k, n;
4229 	int id;
4230 	for (k = n = 0; k < GMT->common.a.n_aspatial; k++) {	/* For each item specified in -a */
4231 		if ((id = gmt_get_ogr_id (G, GMT->common.a.name[k])) == GMT_NOTSET) {
4232 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "No aspatial value found for column %s\n", GMT->common.a.name[k]);
4233 			return GMT_RUNTIME_ERROR;
4234 		}
4235 		switch (G->type[id]) {
4236 			case GMT_DOUBLE:
4237 			case GMT_FLOAT:
4238 			case GMT_ULONG:
4239 			case GMT_LONG:
4240 			case GMT_UINT:
4241 			case GMT_INT:
4242 			case GMT_USHORT:
4243 			case GMT_SHORT:
4244 			case GMT_UCHAR:
4245 			case GMT_CHAR:
4246 				GMT->current.io.curr_rec[GMT->common.a.col[k]] = atof (G->tvalue[id]);
4247 				break;
4248 			case GMT_DATETIME:
4249 				gmt_scanf_arg (GMT, G->tvalue[id], GMT_IS_ABSTIME, false, &GMT->current.io.curr_rec[GMT->common.a.col[k]]);
4250 				break;
4251 			default:	/* Do nothing (string) */
4252 				break;
4253 		}
4254 		n++;
4255 	}
4256 	return (n);
4257 }
4258 #endif
4259 
4260 /*----------------------------------------------------------|
4261  * Public functions that are part of the GMT Devel library  |
4262  *----------------------------------------------------------|
4263  */
4264 
4265 /*! . */
gmtlib_scanf_geodim(struct GMT_CTRL * GMT,char * s,double * val)4266 int gmtlib_scanf_geodim (struct GMT_CTRL *GMT, char *s, double *val) {
4267 	/* Try to decode a value from s and store
4268 	in val.  s is a regular float with optional
4269 	unit info, e.g., 8.5d or 7.5n.  If a valid unit
4270 	is found we convert the number to km.
4271 	We also skip any trailing modifiers like +<mods>, e.g.
4272 	vector specifications like 0.5i+jc+b+s
4273 
4274 	We return GMT_IS_FLOAT and pass val in km.
4275 	*/
4276 	char *p;
4277 	if (isalpha ((int)s[0]) || (s[1] == 0 && (s[0] == '-' || s[0] == '+'))) {	/* Probably a symbol character; quietly return 0 */
4278 		*val = 0.0;
4279 		return GMT_IS_FLOAT;
4280 	}
4281 
4282 	if ((p = strpbrk (s, GMT_LEN_UNITS))) {	/* Find location of first valid unit */
4283 		int c = p[0];	p[0] = '\0';	/* Chop off unit */
4284 		*val = atof (s);
4285 		p[0] = (char)c;	/* Restore unit */
4286 		switch (c) {
4287 			case 'd': *val *= GMT->current.proj.DIST_KM_PR_DEG; break;			/* arc degree */
4288 			case 'm': *val *= GMT->current.proj.DIST_KM_PR_DEG * GMT_MIN2DEG; break;	/* arc minutes */
4289 			case 's': *val *= GMT->current.proj.DIST_KM_PR_DEG * GMT_SEC2DEG; break;	/* arc seconds */
4290 			case 'e': *val *= 0.001; break;						/* meters */
4291 			case 'f': *val *= (0.001 * METERS_IN_A_FOOT); break;					/* feet */
4292 			case 'M': *val *= (0.001 * METERS_IN_A_MILE); break;					/* miles */
4293 			case 'n': *val *= (0.001 * METERS_IN_A_NAUTICAL_MILE); break;				/* nautical miles */
4294 			case 'u': *val *= (0.001 * METERS_IN_A_SURVEY_FOOT); break;				/* survey feet */
4295 		}
4296 	}
4297 	else	/* No unit, must assume default km */
4298 		*val = atof (s);
4299 
4300 	return (GMT_IS_FLOAT);
4301 }
4302 
4303 /*! . */
gmt_set_geographic(struct GMT_CTRL * GMT,unsigned int dir)4304 void gmt_set_geographic (struct GMT_CTRL *GMT, unsigned int dir) {
4305 	/* Eliminate lots of repeated statements to do this: */
4306 	gmt_set_column_type (GMT, dir, GMT_X, GMT_IS_LON);
4307 	gmt_set_column_type (GMT, dir, GMT_Y, GMT_IS_LAT);
4308 	if (dir == GMT_IN) {	/* Default spherical distance calculations are in meters (cannot fail) */
4309 		int mode = (GMT->common.j.active) ? GMT->common.j.mode : GMT_GREATCIRCLE;
4310 		(void) gmt_init_distaz (GMT, GMT_MAP_DIST_UNIT, mode, GMT_MAP_DIST);
4311 	}
4312 }
4313 
4314 /*! . */
gmt_set_cartesian(struct GMT_CTRL * GMT,unsigned int dir)4315 void gmt_set_cartesian (struct GMT_CTRL *GMT, unsigned int dir) {
4316 	/* Eliminate lots of repeated statements to do this: */
4317 	gmt_set_column_type (GMT, dir, GMT_X, GMT_IS_FLOAT);
4318 	gmt_set_column_type (GMT, dir, GMT_Y, GMT_IS_FLOAT);
4319 }
4320 
4321 /*! Handles non-proxy checking for input z values.  If the input value equals
4322  * the non_proxy then we return true so the value can be replaced by a NaN.
4323  */
gmt_input_col_is_nan_proxy(struct GMT_CTRL * GMT,double value,unsigned int col)4324 bool gmt_input_col_is_nan_proxy (struct GMT_CTRL *GMT, double value, unsigned int col) {
4325 	if (!GMT->common.d.active[GMT_IN]) return false;	/* Not active */
4326 	if (col < GMT->common.d.first_col[GMT_IN]) return false;	/* Not in column range */
4327 
4328 	if (GMT->common.d.is_zero[GMT_IN]) return doubleAlmostEqualZero (0.0, value);	/* Change to NaN if proxy value is ~zero */
4329 	return doubleAlmostEqual (GMT->common.d.nan_proxy[GMT_IN], value);		/* Change to NaN if value ~nan_proxy for a non-zero proxy */
4330 }
4331 
4332 /*! Appends one more metadata item to this OGR structure */
gmtlib_append_ogr_item(struct GMT_CTRL * GMT,char * name,enum GMT_enum_type type,struct GMT_OGR * S)4333 int gmtlib_append_ogr_item (struct GMT_CTRL *GMT, char *name, enum GMT_enum_type type, struct GMT_OGR *S) {
4334 	if (S == NULL) {
4335 		GMT_Report (GMT->parent, GMT_MSG_NORMAL, "gmtio_append_ogr_item: No GMT_OGR structure available\n");
4336 		return (GMT_PTR_IS_NULL);
4337 	}
4338 	S->n_aspatial++;
4339 	S->name = gmt_M_memory (GMT, S->name, S->n_aspatial, char *);
4340 	S->name[S->n_aspatial-1] = strdup (name);
4341 	S->type = gmt_M_memory (GMT, S->type, S->n_aspatial, enum GMT_enum_type);
4342 	S->type[S->n_aspatial-1] = type;
4343 	return (GMT_NOERROR);
4344 }
4345 
4346 //*! . */
gmtlib_write_ogr_header(FILE * fp,struct GMT_OGR * G)4347 void gmtlib_write_ogr_header (FILE *fp, struct GMT_OGR *G) {
4348 	/* Write out table-level OGR/GMT header metadata */
4349 	unsigned int k, col, type;
4350 	char *flavor = "egpw";
4351 
4352 	fprintf (fp, "# @VGMT1.0 @G");
4353 	if (G->geometry > GMT_IS_POLYGON) fprintf (fp, "MULTI");
4354 	if (G->geometry == GMT_IS_POINT || G->geometry == GMT_IS_MULTIPOINT) fprintf (fp, "POINT\n");
4355 	if (G->geometry == GMT_IS_LINESTRING || G->geometry == GMT_IS_MULTILINESTRING) fprintf (fp, "LINESTRING\n");
4356 	if (G->geometry == GMT_IS_POLYGON || G->geometry == GMT_IS_MULTIPOLYGON) fprintf (fp, "POLYGON\n");
4357 	fprintf (fp, "# @R%s\n", G->region);
4358 	for (k = 0; k < 4; k++) {
4359 		if (G->proj[k]) fprintf (fp, "# @J%c%s\n", flavor[k], G->proj[k]);
4360 	}
4361 	if (G->n_aspatial) {
4362 		fprintf (fp, "# @N%s", G->name[0]);
4363 		for (col = 1; col < G->n_aspatial; col++) fprintf (fp, "|%s", G->name[col]);
4364 		type = gmtio_type_index (G->type[0]);
4365 		fprintf (fp, "\n# @T%s", GMT_type[type]);
4366 		for (col = 1; col < G->n_aspatial; col++) {
4367 			type = gmtio_type_index (G->type[col]);
4368 			fprintf (fp, "|%s", GMT_type[type]);
4369 		}
4370 		fprintf (fp, "\n");
4371 	}
4372 	fprintf (fp, "# FEATURE_DATA\n");
4373 }
4374 
4375 /*! . */
gmtlib_write_tableheader(struct GMT_CTRL * GMT,FILE * fp,char * txt)4376 void gmtlib_write_tableheader (struct GMT_CTRL *GMT, FILE *fp, char *txt) {
4377 	/* Output ASCII segment header; skip if mode is binary.
4378 	 * We append a newline (\n) if not is present */
4379 
4380 	if (!GMT->current.setting.io_header[GMT_OUT]) return;	/* No output headers requested */
4381 	if (gmt_M_binary_header (GMT, GMT_OUT))		/* Must write a binary header */
4382 		gmtlib_io_binary_header (GMT, fp, GMT_OUT);
4383 	else if (!txt || !txt[0])				/* Blank header */
4384 		fprintf (fp, "%c\n", GMT->current.setting.io_head_marker_out);
4385 	else if (txt[0] == GMT->current.setting.io_seg_marker[GMT_OUT])
4386 		fprintf (fp, "%s\n", txt);
4387 	else {
4388 		size_t L = strlen (txt), k = 0;
4389 		fputc (GMT->current.setting.io_head_marker_out, fp);	/* Make sure we have # at start */
4390 		while (k < L && strchr ("#\t ", txt[k])) k++;	/* Skip header record indicator and leading whitespace */
4391 		if (k < L) fprintf (fp, " %s", &txt[k]);
4392 		if (txt[L-1] != '\n') fputc ('\n', fp);	/* Make sure we have \n at end */
4393 	}
4394 }
4395 
gmt_insert_tableheader(struct GMT_CTRL * GMT,struct GMT_DATATABLE * T,char * txt)4396 void gmt_insert_tableheader (struct GMT_CTRL *GMT, struct GMT_DATATABLE *T, char *txt) {
4397 	/* Add one more header record to the table */
4398 	T->header = gmt_M_memory (GMT, T->header, T->n_headers+1, char *);
4399 	T->header[T->n_headers++] = strdup (txt);
4400 }
4401 
4402 /* This version of fgets will check for input record truncation, that is,
4403  * the input record is longer than the given size.  Since calls to gmt_fgets
4404  * ASSUME they get a logical record, we will give a warning if truncation
4405  * occurs and read until we have consumed the linefeed, thus making the
4406  * i/o machinery ready for the next logical record.
4407  */
4408 
4409 /*! . */
gmt_fgets(struct GMT_CTRL * GMT,char * str,int size,FILE * stream)4410 char *gmt_fgets (struct GMT_CTRL *GMT, char *str, int size, FILE *stream) {
4411 	str[size-2] = '\0'; /* Set last but one record to 0 */
4412 	if (!fgets (str, size, stream))
4413 		return (NULL); /* Got nothing */
4414 
4415 	/* fgets will always set str[size-1] = '\0' if more data than str can handle is found.
4416 	 * Thus, we examine str[size-2].  If this is neither '\0' nor '\n' then we have only
4417 	 * read a portion of a logical record that is longer than size.
4418 	 */
4419 	if (!(str[size-2] == '\0' || str[size-2] == '\n')) {
4420 		/* Only got part of a record */
4421 		int c, n = 0;
4422 		/* Read char-by-char until newline is consumed */
4423 		while ((c = fgetc (stream)) != '\n' && c != EOF)
4424 			++n;
4425 		if (c == '\n')
4426 			/* We expect fgets to retain '\n', so add it */
4427 			str[size-2] = '\n';
4428 		else
4429 			/* EOF without '\n' */
4430 			--n;
4431 		/* This will report wrong lengths if last line has no '\n' but we don't care */
4432 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Long input record (%d bytes) was truncated to first %d bytes!\n", size+n, size-2);
4433 	}
4434 	return (str);
4435 }
4436 
4437 /*! . */
gmt_fclose(struct GMT_CTRL * GMT,FILE * stream)4438 int gmt_fclose (struct GMT_CTRL *GMT, FILE *stream) {
4439 	int err;
4440 	if (!stream || stream == NULL)  return (0);
4441 	/* First skip any stream related to the three Unix i/o descriptors */
4442 	if (stream == GMT->session.std[GMT_IN])  return (0);
4443 	if (stream == GMT->session.std[GMT_OUT]) return (0);
4444 	if (stream == GMT->session.std[GMT_ERR]) return (0);
4445 	if ((size_t)stream == (size_t)-GMT->current.io.ncid) {
4446 		/* Special treatment for netCDF files */
4447 		gmt_nc_close (GMT, GMT->current.io.ncid);
4448 		gmt_M_free (GMT, GMT->current.io.grpid);
4449 		gmt_M_free (GMT, GMT->current.io.varid);
4450 		gmt_M_free (GMT, GMT->current.io.add_offset);
4451 		gmt_M_free (GMT, GMT->current.io.scale_factor);
4452 		gmt_M_free (GMT, GMT->current.io.missing_value);
4453 		GMT->current.io.ncols = 0;
4454 		GMT->current.io.ncid = GMT->current.io.nvars = 0;
4455 		GMT->current.io.ndim = GMT->current.io.nrec = 0;
4456 		GMT->current.io.input = GMT->session.input_ascii;
4457 		gmtlib_free_tmp_arrays (GMT);	/* Free up pre-allocated vectors */
4458 		return (0);
4459 	}
4460 	/* Regular file */
4461 	err = fclose (stream);
4462 #ifdef HAVE_GDAL
4463 	if (GMT->current.io.tempfile[0] && !access (GMT->current.io.tempfile, F_OK)) {
4464 		/* Converted a shapefile to a temporary GMT/OGR file.  Now delete that file */
4465 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Remove temporary GMT/OGR file %s\n", GMT->current.io.tempfile);
4466 		gmt_remove_file (GMT, GMT->current.io.tempfile);
4467 		GMT->current.io.tempfile[0] = '\0';	/* Flag as no longer active */
4468 	}
4469 #endif
4470 	return (err);
4471 }
4472 
4473 /*! . */
gmt_skip_xy_duplicates(struct GMT_CTRL * GMT,bool mode)4474 void gmt_skip_xy_duplicates (struct GMT_CTRL *GMT, bool mode) {
4475 	/* Changes the status of the skip_duplicates setting */
4476 	/* PW: This is needed as some algorithms testing if a point is
4477 	 * inside or outside a polygon have trouble if there are
4478 	 * duplicate vertices in the polygon.  This option can
4479 	 * be set to true and such points will be skipped during
4480 	 * the data reading step. Mode = true or false */
4481 	GMT->current.io.skip_duplicates = mode;
4482 }
4483 
gmtlib_modulo_time_calculator(struct GMT_CTRL * GMT,double * val)4484 void gmtlib_modulo_time_calculator (struct GMT_CTRL *GMT, double *val) {
4485 	/* Only called if the -w<col>y|a|w|d|h|m|s|p option was given to select periodic temporal data.
4486 	 * Here, time_operator is the kind of period/treatment, and time_range is the period
4487 	 * length in current units.  If -R is set then we also handle wrapping.
4488 	 * Note: Below, items month, day_y (Julian day) and day_m all start at 1.  */
4489 	int period;
4490 	struct GMT_GCAL cal;
4491 	switch (GMT->current.io.cycle_operator) {
4492 		case GMT_CYCLE_SEC:	/* Return 0.000-0.999999 sec */
4493 			*val = fmod (*val, 1.0);
4494 			break;
4495 		case GMT_CYCLE_MIN:	/* Return 0.000-59.999999 seconds */
4496 			*val = fmod (*val, GMT_MIN2SEC_F);
4497 			break;
4498 		case GMT_CYCLE_HOUR:	/* Return 0.000-59.999999 minutes */
4499 			*val = fmod (*val, GMT_HR2SEC_F) * GMT_SEC2MIN;
4500 			break;
4501 		case GMT_CYCLE_DAY:	/* Return 0.000-23.999999 hours */
4502 			*val = fmod (*val, GMT_DAY2SEC_F) * GMT_SEC2HR;
4503 			break;
4504 		case GMT_CYCLE_WEEK:	/* Return 0.00000-6.9999999 days */
4505 			gmt_gcal_from_dt (GMT, *val, &cal);
4506 			*val = (GMT->current.setting.time_week_start) ? (GMT_WEEK2DAY_I + cal.day_w - GMT->current.setting.time_week_start) % GMT_WEEK2DAY_I : cal.day_w;
4507 			*val += cal.hour * GMT_HR2DAY + cal.min * GMT_MIN2DAY + cal.sec * GMT_SEC2DAY;
4508 			break;
4509 		case GMT_CYCLE_ANNUAL:	/* Return 0.000000-11.999999 months */
4510 			gmt_gcal_from_dt (GMT, *val, &cal);
4511 			period = gmtlib_gmonth_length (cal.year, cal.month);	/* Days in this month */
4512 			*val = cal.month - 1 + (cal.day_m - 1 + cal.hour * GMT_HR2DAY + cal.min * GMT_MIN2DAY + cal.sec * GMT_SEC2DAY) / period;
4513 			break;
4514 		case GMT_CYCLE_YEAR:	/* Return 0.00000-0.99999999 years */
4515 			gmt_gcal_from_dt (GMT, *val, &cal);
4516 			period = gmtlib_is_gleap (cal.year) ? 366 : 365;	/* Length of this year in days */
4517 			*val = (cal.day_y - 1 + cal.hour * GMT_HR2DAY + cal.min * GMT_MIN2DAY + cal.sec * GMT_SEC2DAY) / period;
4518 			break;
4519 		case GMT_CYCLE_CUSTOM:	/* Return 0.00000-0.99999999 for a custom cycle */
4520 			*val = fmod (*val - GMT->current.io.cycle_phase, GMT->current.io.cycle_period) / GMT->current.io.cycle_period;	/* Yields 0.000-0.999999 cycles */
4521 			break;
4522 	}
4523 	/* Handle wrapping around given range */
4524 	if (GMT->common.R.active[RSET]) {
4525 		if (*val > GMT->current.io.cycle_max)
4526 			*val -= GMT->current.io.cycle_range;
4527 		else if (*val < GMT->current.io.cycle_min)
4528 			*val += GMT->current.io.cycle_range;
4529 	}
4530 }
4531 
4532 /*! Determine if two points are "far enough apart" to constitute a data gap and thus "pen up" */
gmtlib_gap_detected(struct GMT_CTRL * GMT)4533 bool gmtlib_gap_detected (struct GMT_CTRL *GMT) {
4534 	uint64_t i;
4535 
4536 	if (!(GMT->common.g.active && GMT->current.io.has_previous_rec)) return (false);	/* Not active or on first point in a segment */
4537 	/* Here we must determine if any or all of the selected gap criteria [see gmtlib_set_gap_param] are met */
4538 	for (i = 0; i < GMT->common.g.n_methods; i++) {	/* Go through each criterion */
4539 		if ((GMT->common.g.get_dist[i] (GMT, GMT->common.g.col[i]) > GMT->common.g.gap[i]) != GMT->common.g.match_all)
4540 			return (!GMT->common.g.match_all);
4541 	}
4542 	return (GMT->common.g.match_all);
4543 }
4544 
4545 /*! . */
gmt_ascii_output_col(struct GMT_CTRL * GMT,FILE * fp,double x,uint64_t col)4546 int gmt_ascii_output_col (struct GMT_CTRL *GMT, FILE *fp, double x, uint64_t col) {
4547 	/* Formats x according to to output column number */
4548 	char text[GMT_LEN256] = {""};
4549 
4550 	gmt_ascii_format_col (GMT, text, x, GMT_OUT, col);
4551 	return (fprintf (fp, "%s", text));
4552 }
4553 
4554 /*! . */
gmtlib_set_gap(struct GMT_CTRL * GMT)4555 int gmtlib_set_gap (struct GMT_CTRL *GMT) {
4556 	/* Data gaps are special since there is no multiple-segment header flagging the gap; thus next time the record is already read */
4557 	GMT->current.io.status = GMT_IO_GAP;
4558 	GMT->current.io.seg_no++;
4559 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Data gap detected via -g; Segment header inserted near/at line # %" PRIu64 "\n",
4560 	            GMT->current.io.rec_no);
4561 	sprintf (GMT->current.io.segment_header, "Data gap detected via -g; Segment header inserted");
4562 	return (0);
4563 }
4564 
4565 /*! Enable/Disable multi-segment headers for either input or output */
gmt_set_segmentheader(struct GMT_CTRL * GMT,int direction,bool true_false)4566 void gmt_set_segmentheader (struct GMT_CTRL *GMT, int direction, bool true_false) {
4567 	GMT->current.io.multi_segments[direction] = true_false;
4568 }
4569 
4570 /*! Enable/Disable table headers for either input or output */
gmt_set_tableheader(struct GMT_CTRL * GMT,int direction,bool true_false)4571 void gmt_set_tableheader (struct GMT_CTRL *GMT, int direction, bool true_false) {
4572 	GMT->current.setting.io_header[direction] = true_false;
4573 }
4574 
4575 /*! . */
gmt_z_output(struct GMT_CTRL * GMT,FILE * fp,uint64_t n,double * data,char * txt)4576 int gmt_z_output (struct GMT_CTRL *GMT, FILE *fp, uint64_t n, double *data, char *txt) {
4577 	int err;
4578 	gmt_M_unused (txt);
4579 	if (gmt_skip_output (GMT, data, n)) return (GMT_NOTSET);	/* Record was skipped via -s[a|r] */
4580 	err = GMT->current.io.write_item (GMT, fp, n, data);
4581 	/* Cast below since the output functions are declared with uint64_t but cannot really exceed 4096... SHould change uint64_t to uint32_t */
4582 	return (err ? GMT_NOTSET : 0);	/* Return -1 if failed, else n items written */
4583 }
4584 
4585 /* gmt_z_input and gmt_z_output are used in grd2xyz/xyz2grd to fascilitate reading of one-col items via the general i/o machinery
4586  * Despite taking uint64_t *n we KNOW that this value is 1 and hence column is always GMT_X. */
4587 /*! . */
gmt_z_input(struct GMT_CTRL * GMT,FILE * fp,uint64_t * n,int * status)4588 void * gmt_z_input (struct GMT_CTRL *GMT, FILE *fp, uint64_t *n, int *status) {
4589 	if ((*status = GMT->current.io.read_item (GMT, fp, *n, GMT->current.io.curr_rec)) == GMT_DATA_READ_ERROR) {
4590 		GMT->current.io.status = GMT_IO_EOF;
4591 		return (NULL);
4592 	}
4593 	if (GMT->common.i.select)	/* We need to scale this single item */
4594 		GMT->current.io.curr_rec[GMT_X] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][GMT_X], GMT->current.io.curr_rec[GMT_X]);
4595 
4596 	return (&GMT->current.io.record);
4597 }
4598 
4599 /*! . */
gmtlib_process_binary_input(struct GMT_CTRL * GMT,uint64_t n_read)4600 int gmtlib_process_binary_input (struct GMT_CTRL *GMT, uint64_t n_read) {
4601 	/* Process a binary record to determine what kind of record it is. Return values:
4602 	 * 0 = regular record; 1 = segment header (all NaNs); 2 = skip this record
4603 	 * Also takes these optional actions:
4604 	 *   1) -:  Flips x and u
4605 	 *   2) Adjusts periodicity on longitudes
4606 	 *   3) Scales plot dimensions from prevailing unit to internal inches.
4607 	 *   4) Handles inverse projections if given projected coordinates.
4608 	*/
4609 	uint64_t col_no, n_NaN;
4610 	bool bad_record = false, set_nan_flag = false;
4611 	/* Here, GMT->current.io.curr_rec has been filled in by fread */
4612 
4613 	/* Determine if this was a segment header, and if so return */
4614 	for (col_no = n_NaN = 0; col_no < n_read; col_no++) {
4615 		if (!gmt_M_is_dnan (GMT->current.io.curr_rec[col_no])) {	/* Clean data */
4616 			if (gmt_input_col_is_nan_proxy (GMT, GMT->current.io.curr_rec[col_no], col_no))	/* Input matched no-data setting, so change to NaN */
4617 				GMT->current.io.curr_rec[col_no] = GMT->session.d_NaN;
4618 			else if (GMT->common.i.select)	/* Cannot check here, done in gmtio_bin_colselect instead when order is set */
4619 				continue;
4620 			else {	/* Still clean, so possibly adjust value and skip to next column */
4621 				switch (gmt_M_type (GMT, GMT_IN, col_no)) {
4622 					case GMT_IS_LON:	/* Must account for periodicity in 360 as per current rule */
4623 						gmtio_adjust_periodic_lon (GMT, &GMT->current.io.curr_rec[col_no]);
4624 						break;
4625 					case GMT_IS_LAT:
4626 						if (GMT->current.io.curr_rec[col_no] < -90.0 || GMT->current.io.curr_rec[col_no] > +90.0) {
4627 							GMT_Report (GMT->parent, GMT_MSG_WARNING, "Latitude (%g) at line # %" PRIu64 " exceeds -|+ 90! - set to NaN\n", GMT->current.io.curr_rec[col_no], GMT->current.io.rec_no);
4628 							GMT->current.io.curr_rec[col_no] = GMT->session.d_NaN;
4629 						}
4630 						break;
4631 					case GMT_IS_DIMENSION:	/* Convert to internal inches */
4632 						GMT->current.io.curr_rec[col_no] *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
4633 						break;
4634 					case GMT_IS_ABSTIME: case GMT_IS_RELTIME:	/* Possibly convert to periodic time */
4635 						if (GMT->current.io.cycle_operator && GMT->current.io.cycle_col == col_no)
4636 							gmtlib_modulo_time_calculator (GMT, &(GMT->current.io.curr_rec[col_no]));
4637 					default:	/* Nothing to do unless periodic */
4638 						if (GMT->current.io.cycle_operator && GMT->current.io.cycle_col == col_no)
4639 							gmtlib_modulo_time_calculator (GMT, &(GMT->current.io.curr_rec[col_no]));
4640 						break;
4641 				}
4642 				continue;
4643 			}
4644 		}
4645 		/* We end up here if we found a NaN */
4646 		if (!GMT->current.setting.io_nan_records && GMT->current.io.skip_if_NaN[col_no]) bad_record = true;	/* This field is not allowed to be NaN */
4647 		if (GMT->current.io.skip_if_NaN[col_no]) set_nan_flag = true;
4648 		n_NaN++;
4649 	}
4650 
4651 	if (!GMT->current.io.status && GMT->current.setting.n_bin_header_cols) {	/* Must have n_read NaNs to qualify as segment header (if enabled) */
4652 		if (n_read >= GMT->current.setting.n_bin_header_cols && n_NaN == n_read) {
4653 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Detected binary segment header near/at line # %" PRIu64 "\n", GMT->current.io.rec_no);
4654 			GMT->current.io.status = GMT_IO_SEGMENT_HEADER;
4655 			GMT->current.io.segment_header[0] = '\0';
4656 			gmt_set_segmentheader (GMT, GMT_OUT, true);	/* Turn on "-mo" */
4657 			GMT->current.io.seg_no++;
4658 			GMT->current.io.data_record_number_in_seg[GMT_IN] = 0;
4659 			return (1);	/* 1 means segment header */
4660 		}
4661 	}
4662 	if (gmtio_outside_in_row_range (GMT, *(GMT->common.q.rec))) return (2);	/* Record is outside desired row range of interest */
4663 
4664 	if (gmtio_outside_in_data_range (GMT, GMT->common.q.col)) return (2);	/* Must skip this record as key data value is outside desired data-range */
4665 
4666 	if (bad_record) {
4667 		GMT->current.io.n_bad_records++;
4668 		if (GMT->current.io.give_report && GMT->current.io.n_bad_records == 1) {	/* Report 1st occurrence */
4669 			GMT_Report (GMT->parent, GMT_MSG_WARNING,
4670 			            "Encountered first invalid binary data record near/at line # %" PRIu64 "\n", GMT->current.io.rec_no);
4671 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Likely causes:\n");
4672 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "(1) Invalid x and/or y values, i.e. NaNs.\n");
4673 		}
4674 		return (2);	/* 2 means skip this record and try again */
4675 	}
4676 	else if (GMT->current.io.skip_duplicates && GMT->current.io.has_previous_rec) {	/* Test to determine if we should skip duplicate records with same x,y */
4677 		if (GMT->current.io.curr_rec[GMT_X] == GMT->current.io.prev_rec[GMT_X] && GMT->current.io.curr_rec[GMT_Y] == GMT->current.io.prev_rec[GMT_Y]) return (2);	/* Yes, duplicate */
4678 	}
4679 	if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && n_read >= 2)
4680 		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 */
4681 	if (GMT->current.proj.inv_coordinates) gmtio_adjust_projected (GMT);	/* Must apply inverse projection to get lon, lat */
4682 
4683 	if (set_nan_flag) GMT->current.io.status |= GMT_IO_NAN;
4684 	return (0);	/* 0 means OK regular record */
4685 }
4686 
4687 /*! . */
gmtlib_nc_get_att_text(struct GMT_CTRL * GMT,int ncid,int varid,char * name,char * text,size_t textlen)4688 int gmtlib_nc_get_att_text (struct GMT_CTRL *GMT, int ncid, int varid, char *name, char *text, size_t textlen) {
4689 	/* This function is a replacement for nc_get_att_text that avoids overflow of text
4690 	 * ncid, varid, name, text	: as in nc_get_att_text
4691 	 * textlen			: maximum number of characters to copy to string text
4692 	 */
4693 	int status;
4694 	size_t attlen;
4695 	char *att = NULL;
4696 
4697 	status = nc_inq_attlen (ncid, varid, name, &attlen);
4698 	if (status != NC_NOERR) {
4699 		*text = '\0';
4700 		return status;
4701 	}
4702 	att = gmt_M_memory (GMT, NULL, attlen, char);
4703 	status = nc_get_att_text (ncid, varid, name, att);
4704 	if (status == NC_NOERR) {
4705 		attlen = MIN (attlen, textlen-1); /* attlen does not include terminating '\0') */
4706 		strncpy (text, att, attlen); /* Copy att to text */
4707 		text[attlen] = '\0'; /* Terminate string */
4708 	}
4709 	else
4710 		*text = '\0';
4711 	gmt_M_free (GMT, att);
4712 	return status;
4713 }
4714 
gmt_get_precision_width(struct GMT_CTRL * GMT,double x)4715 int gmt_get_precision_width (struct GMT_CTRL *GMT, double x) {
4716 	/* Here, x < 1 seconds and we want to determine an approximate precision with max truncation error of GMT_CONV4_LIMIT */
4717 	int k = -1;
4718 	double trunc_err, inv_x = 1.0 / x;
4719 	static double power_U[9] = {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};
4720 	static double power_D[9] = {1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9};
4721 
4722 	gmt_M_unused(GMT);
4723 	/* Here we compute |D-T|/D, where D is the incoming dt and T is the value recovered if printed using k decimals.
4724 	 * We want this relative truncation error to be < 10^-4 */
4725 	do {
4726 		k++;	/* First time k becomes 0 */
4727 		trunc_err = fabs (x - rint (x * power_U[k]) * power_D[k]) * inv_x;
4728 		// fprintf (stderr, "k = %d x = %g trunc_err = %g\n", k, x, trunc_err);
4729 	} while (k < 5 && trunc_err > GMT_CONV4_LIMIT);	/* Stop at microseconds if things go off the rails */
4730 	return (k + 1);
4731 }
4732 
gmt_check_abstime_format(struct GMT_CTRL * GMT,struct GMT_DATASET * D,uint64_t chunk)4733 void gmt_check_abstime_format (struct GMT_CTRL *GMT, struct GMT_DATASET *D, uint64_t chunk) {
4734 	/* Either just scan the first chunk items of the first segment.  If 0 we mean all */
4735 	bool abstime_found = false;
4736 	unsigned int col, row;
4737 	int w_max = 0, this_w;
4738 	double sub, sub_max = 0;
4739 	struct GMT_DATASEGMENT *S = NULL;
4740 
4741 	if (GMT->common.b.active[GMT_OUT]) return;	/* Nothing to do if using binary i/o */
4742 	if (D == NULL || D->table == NULL || D->table[0]->segment == NULL || D->table[0]->segment[0] == NULL) return;	/* Nothing to work with */
4743 	if (GMT->current.setting.time_system.unit != 's') return;	/* Current unit is not second */
4744 	if (strcmp (GMT->current.setting.format_clock_out, "hh:mm:ss")) return;	/* User has changed from default format - do nothing */
4745 	for (col = 0; !abstime_found && col < D->n_columns; col++)
4746 		if (GMT->current.io.col_type[GMT_OUT][col] == GMT_IS_ABSTIME) abstime_found = true;
4747 	if (!abstime_found) return;	/* No absolute time columns to worry about */
4748 
4749 	/* If we get here we have ASCII absolute time output and the default FORMAT_CLOCK_OUT remains in effect.
4750 	 * To assist the user we will scan the first segment's rows up to MIN (20, n_rows) and make our best guess
4751 	 * from that subset.  We will give information messages about our decision but not a warning. */
4752 
4753 	if (chunk == 0) chunk = UINTMAX_MAX;	/* Basically use all */
4754 	S = D->table[0]->segment[0];	/* The first segment */
4755 	for (col = 0; col < D->n_columns; col++) {
4756 		if (GMT->current.io.col_type[GMT_OUT][col] != GMT_IS_ABSTIME) continue;	/* Not an abstime column */
4757 		for (row = 0; row < MIN (S->n_rows, chunk); row++) {	/* Maximum 20 rows are examined */
4758 			sub = S->data[col][row] - floor (S->data[col][row]);	/* Fractional second */
4759 			if (sub > sub_max) sub_max = sub;
4760 			if (gmt_M_is_zero (sub)) continue;	/* Not checking zeros for width */
4761 			if ((this_w = gmt_get_precision_width (GMT, sub)) > w_max)
4762 				w_max = this_w;
4763 		}
4764 	}
4765 	if (w_max && (sub_max >= 1E-6)) {	/* Need to append w_max "x" to the default format */
4766 		strcat (GMT->current.setting.format_clock_out, ".");
4767 		while (w_max) {
4768 			strcat (GMT->current.setting.format_clock_out, "x");
4769 			w_max--;
4770 		}
4771 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "To prevent loss of time-series precision we have changed FORMAT_CLOCK_OUT to %s\n", GMT->current.setting.format_clock_out);
4772 		gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_out, &GMT->current.io.clock_output, 1);
4773 	}
4774 }
4775 
gmt_increase_abstime_format_precision(struct GMT_CTRL * GMT,unsigned int col,double dt)4776 void gmt_increase_abstime_format_precision (struct GMT_CTRL *GMT, unsigned int col, double dt) {
4777 	/* Adjust output format for absolute time if we need more precision in the seconds */
4778 	double sub;
4779 	int w;
4780 	if (GMT->current.io.col_type[GMT_OUT][col] != GMT_IS_ABSTIME) return;	/* Not an abstime column */
4781 	if (GMT->current.setting.time_system.unit != 's') return;	/* Current unit is not second */
4782 	if (strcmp (GMT->current.setting.format_clock_out, "hh:mm:ss")) return;	/* User has changed from default format - do nothing */
4783 	sub = dt - floor (dt);	/* Fractional second */
4784 	if (gmt_M_is_zero (sub)) return;	/* Not checking zeros for width */
4785 	w = gmt_get_precision_width (GMT, sub);	/* Get desired precision */
4786 	strcat (GMT->current.setting.format_clock_out, ".");
4787 	while (w) {
4788 		strcat (GMT->current.setting.format_clock_out, "x");
4789 		w--;
4790 	}
4791 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "To prevent loss of time-series precision we have changed FORMAT_CLOCK_OUT to %s\n", GMT->current.setting.format_clock_out);
4792 	gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_out, &GMT->current.io.clock_output, 1);
4793 }
4794 
4795 /*! . */
gmtlib_write_dataset(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,struct GMT_DATASET * D,bool use_GMT_io,int table)4796 int gmtlib_write_dataset (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, struct GMT_DATASET *D, bool use_GMT_io, int table) {
4797 	/* Writes an entire data set to file or stream */
4798 	unsigned int tbl, u_table, n_seg;
4799 	bool close_file = false;
4800 	int error, append = 0;
4801 	int *fd = NULL;
4802 	char file[PATH_MAX] = {""}, tmpfile[PATH_MAX] = {""}, open_mode[4] = {""}, *out_file = tmpfile;
4803 	FILE *fp = NULL;
4804 	struct GMT_DATASET_HIDDEN *DH = gmt_get_DD_hidden (D);
4805 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
4806 
4807 	if (dest_type == GMT_IS_FILE && dest && ((char *)dest)[0] == '>') append = 1;	/* Want to append to existing file */
4808 	if (use_GMT_io)	/* Use GMT->current.io.info settings to determine if input is ASCII/binary, else it defaults to ASCII */
4809 		strcpy (open_mode, (append) ? GMT->current.io.a_mode : GMT->current.io.w_mode);
4810 	else			/* Force ASCII mode */
4811 		strcpy (open_mode, (append) ? "a" : "w");
4812 	GMT->current.io.record_type[GMT_OUT] = D->type;
4813 
4814 	gmt_check_abstime_format (GMT, D, 20);	/* If some columns are absolute time destined for ASCII formatting, we may need to change FORMAT_CLOCK_OUT template */
4815 
4816 	/* Convert any destination type to stream */
4817 
4818 	switch (dest_type) {
4819 		case GMT_IS_FILE:	/* dest is a file name */
4820 			if (!dest) {
4821 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "'dest' pointer cannot be NULL here\n");
4822 				return (GMT_ARG_IS_NULL);
4823 			}
4824 			strncpy (file, dest, PATH_MAX-1);
4825 			if (DH->io_mode < GMT_WRITE_TABLE) {	/* Only need one destination */
4826 				if ((fp = gmt_fopen (GMT, &file[append], open_mode)) == NULL) {
4827 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", &file[append]);
4828 					return (GMT_ERROR_ON_FOPEN);
4829 				}
4830 				close_file = true;	/* We only close files we have opened here */
4831 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Write Data Table to file %s\n", &file[append]);
4832 			}
4833 			break;
4834 		case GMT_IS_STREAM:	/* Open file pointer given, just copy */
4835 			fp = (FILE *)dest;
4836 			if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
4837 			if (fp == GMT->session.std[GMT_OUT])
4838 				strcpy (file, "<stdout>");
4839 			else
4840 				strcpy (file, "<output stream>");
4841 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Write Data Table to %s\n", file);
4842 			break;
4843 		case GMT_IS_FDESC:		/* Open file descriptor given, just convert to file pointer */
4844 			fd = dest;
4845 			if (fd && (fp = fdopen (*fd, open_mode)) == NULL) {
4846 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert file descriptor %d to stream in gmtio_write_table\n", *fd);
4847 				return (GMT_ERROR_ON_FDOPEN);
4848 			}
4849 			else
4850 				close_file = true;	/* fdopen allocates memory */
4851 			if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
4852 			if (fp == GMT->session.std[GMT_OUT])
4853 				strcpy (file, "<stdout>");
4854 			else
4855 				strcpy (file, "<output file descriptor>");
4856 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Write Data Table to %s\n", file);
4857 			break;
4858 		default:
4859 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtio_write_table\n", dest_type);
4860 			return (GMT_NOT_A_VALID_METHOD);
4861 			break;
4862 	}
4863 
4864 	if (DH->io_mode == GMT_WRITE_OGR && gmtio_prep_ogr_output (GMT, D)) {	/* Must preprocess aspatial information and set metadata */
4865 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to prepare for OGR output formatting\n");
4866 		if (close_file) gmt_fclose (GMT, fp);
4867 		return (GMT_RUNTIME_ERROR);
4868 	}
4869 	for (tbl = 0; tbl < D->n_tables; tbl++) {
4870 		if (table != GMT_NOTSET && (u_table = table) != tbl) continue;	/* Selected a specific table */
4871 		GMT->current.io.data_record_number_in_tbl[GMT_OUT] = GMT->current.io.data_record_number_in_seg[GMT_OUT] = 0;
4872 		if (DH->io_mode > GMT_WRITE_TABLE) {	/* Write segments to separate files; must pass original file name in case a template */
4873 			if ((error = gmtio_write_table (GMT, dest, GMT_IS_FILE, D->table[tbl], use_GMT_io, DH->io_mode, 1))) {
4874 				if (close_file) gmt_fclose (GMT, fp);
4875 				return (error);
4876 			}
4877 		}
4878 		else if (DH->io_mode == GMT_WRITE_TABLE) {	/* Must write this table a its own file */
4879 			TH = gmt_get_DT_hidden (D->table[tbl]);
4880 			if (TH->file[GMT_OUT])
4881 				out_file = TH->file[GMT_OUT];
4882 			else
4883 				snprintf (tmpfile, PATH_MAX, file, TH->id);
4884 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Write Data Table to %s\n", out_file);
4885 			n_seg = (unsigned int)((GMT->current.io.skip_headers_on_outout) ? 1 : D->table[tbl]->n_segments);
4886 			if ((error = gmtio_write_table (GMT, out_file, GMT_IS_FILE, D->table[tbl], use_GMT_io, DH->io_mode, n_seg))) {
4887 				if (close_file) gmt_fclose (GMT, fp);
4888 				return (error);
4889 			}
4890 		}
4891 		else {	/* Write to stream we set up earlier */
4892 			n_seg = (unsigned int)((GMT->current.io.skip_headers_on_outout) ? 1 : D->n_segments);
4893 			if ((error = gmtio_write_table (GMT, fp, GMT_IS_STREAM, D->table[tbl], use_GMT_io, DH->io_mode, n_seg))) {
4894 				if (close_file) gmt_fclose (GMT, fp);
4895 				return (error);
4896 			}
4897 		}
4898 	}
4899 
4900 	if (close_file) gmt_fclose (GMT, fp);
4901 
4902 	return (0);	/* OK status */
4903 }
4904 
4905 /*! . */
gmt_fopen(struct GMT_CTRL * GMT,const char * filename,const char * mode)4906 FILE * gmt_fopen (struct GMT_CTRL *GMT, const char *filename, const char *mode) {
4907 	char path[PATH_MAX], *c = NULL;
4908 	FILE *fd = NULL;
4909 	unsigned int first = 0;
4910 	if (gmt_file_is_cache (GMT->parent, filename)) {	/* Must be a cache file */
4911 		first = gmt_download_file_if_not_found (GMT, filename, 0);
4912 	}
4913 
4914 	if (mode[0] != 'r')	/* Open file for writing (so cannot be netCDF) */
4915 		return (fopen (&filename[first], mode));
4916 	else if (GMT->common.b.active[GMT_IN]) {	/* Definitely not netCDF */
4917 		if ((c = gmt_getdatapath (GMT, &filename[first], path, R_OK)) == NULL) return NULL;
4918 		return (fopen (c, mode));
4919 	}
4920 	else if (gmt_M_compat_check (GMT, 4) && GMT->common.b.varnames[0])	/* Definitely netCDF */
4921 		return (gmtio_nc_fopen (GMT, &filename[first], mode));
4922 	else if (strchr (&filename[first], '?'))	/* Definitely netCDF */
4923 		return (gmtio_nc_fopen (GMT, &filename[first], mode));
4924 #ifdef WIN32
4925 	else if (!strcmp (&filename[first], "NUL"))	/* Special case of /dev/null under Windows */
4926 #else
4927 	else if (!strcmp (&filename[first], "/dev/null"))	/* The Unix null device; catch here to avoid gmtio_nc_fopen */
4928 #endif
4929 	{
4930 		if ((c = gmt_getdatapath(GMT, &filename[first], path, R_OK)) == NULL) return fd;
4931 		return (fopen (c, mode));
4932 	}
4933 	else {	/* Maybe netCDF */
4934 		fd = gmtio_nc_fopen (GMT, &filename[first], mode);
4935 		if (!fd) {	/* No, was not a netCDF file */
4936 			if ((c = gmt_getdatapath (GMT, &filename[first], path, R_OK)) != NULL) {	/* Got the file path */
4937 #ifdef HAVE_GDAL
4938 				char *ext = gmt_get_ext (c);	/* Get pointer to extension (or NULL if no extension) */
4939 				if (ext && mode[0] == 'r' && !strncmp (ext, "shp", 3U)) {	/* Got a shapefile for reading */
4940 					/* We will do a system call to ogr2ogr in order to read the shapefile */
4941 					char cmd[GMT_BUFSIZ+GMT_LEN256] = {""};
4942 					int error = 0;
4943 					if (GMT->parent->tmp_dir)	/* Make unique file in temp dir */
4944 						snprintf (GMT->current.io.tempfile, PATH_MAX, "%s/gmt_ogr_%d.gmt", GMT->parent->tmp_dir, (int)getpid());
4945 					else	/* Make unique file in current dir */
4946 						snprintf (GMT->current.io.tempfile, PATH_MAX, "gmt_ogr_%d.gmt", (int)getpid());
4947 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Convert %s to GMT/OGR file %s\n", c, GMT->current.io.tempfile);
4948 #if GDAL_VERSION_MAJOR >= 2
4949 					snprintf (cmd, GMT_BUFSIZ+GMT_LEN256, "ogr2ogr -mapFieldType Integer64=Integer -skipfailures -f \"OGR_GMT\" \"%s\" \"%s\"", GMT->current.io.tempfile, c);
4950 #else
4951 					snprintf (cmd, GMT_BUFSIZ+GMT_LEN256, "ogr2ogr -f \"GMT\" \"%s\" \"%s\"", GMT->current.io.tempfile, c);
4952 #endif
4953 					GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Running %s\n", cmd);
4954 					if ((error = system (cmd))) {
4955 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "System call [%s] FAILED with error %d.\n", cmd, error);
4956 						return NULL;
4957 					}
4958 					sprintf (GMT->current.io.filename[GMT_IN], "%s <converted from %s via ogr2ogr>", GMT->current.io.tempfile, c);
4959 					c = GMT->current.io.tempfile;	/* Open this temporary instead */
4960 				}
4961 #endif
4962                 		fd = fopen (c, mode);
4963 			}
4964 		}
4965 		return (fd);
4966 	}
4967 }
4968 
4969 /* Table I/O routines for ASCII and binary io */
4970 
4971 /*! Write verbose message about binary record i/o format */
gmtlib_io_banner(struct GMT_CTRL * GMT,unsigned int direction)4972 int gmtlib_io_banner (struct GMT_CTRL *GMT, unsigned int direction) {
4973 	static const char *gmt_direction[2] = {"Input", "Output"};
4974 	char *message = NULL, skip[GMT_LEN64] = {""};
4975 	char *letter = "-cuhHiIlLfditTn", s[2] = {0, 0};	/* letter order matches the type order in GMT_enum_type */
4976 	uint64_t col;
4977 	uint64_t n_bytes;
4978 	size_t alloc = GMT_LEN256, m_len = 0, len;
4979 
4980 #if 0
4981 	if (GMT->current.setting.verbose < GMT_MSG_WARNING) return GMT_OK;	/* Not in verbose mode anyway */
4982 #endif
4983 	if (!GMT->common.b.active[direction]) return GMT_OK;	/* Not using binary i/o */
4984 	if (GMT->common.b.type[direction] == 0) GMT->common.b.type[direction] = (direction == GMT_IN) ? 'd' : letter[GMT->current.setting.export_type];	/* Only happens for external interfaces */
4985 	if (GMT->common.b.ncol[direction] == 0) {		/* Number of columns not set yet - delay message */
4986 		if (direction == GMT_OUT) GMT->common.b.o_delay = true;
4987 		return GMT_OK;
4988 	}
4989 	if (direction == GMT_IN && GMT->common.i.select && GMT->common.b.ncol[GMT_IN] < GMT->common.i.n_actual_cols) {
4990 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Number of input columns set by -i exceeds those set by -bi!\n");
4991 		return GMT_DIM_TOO_LARGE;
4992 	}
4993 	if (direction == GMT_OUT && GMT->common.o.select && GMT->common.b.ncol[GMT_OUT] < GMT->common.o.n_cols) {
4994 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Number of output columns set by -o exceeds those set by -bo!\n");
4995 		return GMT_DIM_TOO_LARGE;
4996 	}
4997 	message = gmt_M_memory (GMT, NULL, alloc, char);
4998 	for (col = 0; col < GMT->common.b.ncol[direction]; col++) {	/* For each binary column of data */
4999 		if (GMT->current.io.fmt[direction][col].skip < 0) {	/* Must skip n_bytes BEFORE reading this column */
5000 			n_bytes = -GMT->current.io.fmt[direction][col].skip;
5001 			snprintf (skip, GMT_LEN64, "%" PRIu64 "x", n_bytes);
5002 			len = strlen (skip);
5003 			if ((m_len+len) >= alloc) {
5004 				alloc += GMT_LEN256;
5005 				message = gmt_M_memory (GMT, message, alloc, char);
5006 			}
5007 			strcat (message, skip);
5008 			m_len += len;
5009 		}
5010 		if (GMT->current.io.fmt[direction][col].type == 0) {	/* Still not set, use the default type */
5011 			if ((GMT->current.io.fmt[direction][col].type = gmt_get_io_type (GMT, GMT->common.b.type[direction])) == 0)
5012 				return GMT->parent->error;
5013 			if ((GMT->current.io.fmt[direction][col].io   = gmtlib_get_io_ptr (GMT, direction, GMT->common.b.swab[direction],
5014 			                                                              GMT->common.b.type[direction])) == NULL)
5015 				return GMT->parent->error;
5016 		}
5017 		s[0] = letter[GMT->current.io.fmt[direction][col].type];	/* Get data type code... */
5018 		if ((m_len+1) >= alloc) {
5019 			alloc += GMT_LEN256;
5020 			message = gmt_M_memory (GMT, message, alloc, char);
5021 		}
5022 		m_len++;
5023 		strcat (message, s);					/* ...and append to message */
5024 		if (GMT->current.io.fmt[direction][col].skip > 0) {	/* Must skip n_bytes AFTER reading this column */
5025 			n_bytes = GMT->current.io.fmt[direction][col].skip;
5026 			snprintf (skip, GMT_LEN64, "%" PRIu64 "x", n_bytes);
5027 			len = strlen (skip);
5028 			if ((m_len+len) >= alloc) {
5029 				alloc += GMT_LEN256;
5030 				message = gmt_M_memory (GMT, message, alloc, char);
5031 			}
5032 			strcat (message, skip);
5033 			m_len += len;
5034 		}
5035 	}
5036 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "%s %d columns via binary records using format %s\n",
5037 	            gmt_direction[direction], GMT->common.b.ncol[direction], message);
5038 	gmt_M_free (GMT, message);
5039 	return GMT_OK;
5040 }
5041 
5042 /*! . */
gmt_get_cols(struct GMT_CTRL * GMT,unsigned int direction)5043 uint64_t gmt_get_cols (struct GMT_CTRL *GMT, unsigned int direction) {
5044 	/* Return the number of columns currently in play in this direction.
5045 	 * This can be complicated.. For BINARY data:
5046 	 * On INPUT, a binary file has a known number of columns set via -bi. Internally,
5047 	 * we read in all those columns into gmt_current_rec.  However, if -i is used then we shuffle
5048 	 * the read values into the positions implied by -i. Further processing
5049 	 * is thus concerned with the possibly smaller number of columns than in the file.
5050 	 * So: n_cols is ncol[GMT_IN], but if -i was set then it is less (i.n_cols).
5051 	 *
5052 	 * On OUTPUT, a binary file has its number of columns set via -bo. Internally,
5053 	 * the number of columns we hold in our array might be much larger if -o is used,
5054 	 * as we then only write out those columns that are requested.
5055 	 * So: n_cols is ncol[GMT_OUT], but if -o it is same as input (see above for that!)
5056 	 *
5057 	 * For ASCII data it is the same except for on output, where we return the output
5058 	 * cols as set.
5059 	 */
5060 	uint64_t n_cols;
5061 	if (! (direction == GMT_IN || direction == GMT_OUT)) return (GMT_NOT_A_VALID_DIRECTION);
5062 
5063 	if (direction == GMT_IN) {
5064 		n_cols = (GMT->common.i.select) ? GMT->common.i.n_cols : GMT->common.b.ncol[GMT_IN];
5065 	}
5066 	else {
5067 		uint64_t in_n_cols = (GMT->common.i.select) ? GMT->common.i.n_cols : GMT->common.b.ncol[GMT_IN];
5068 		if (GMT->common.b.active[GMT_OUT])
5069 			n_cols = (GMT->common.o.select) ? in_n_cols : GMT->common.b.ncol[GMT_OUT];
5070 		else
5071 			n_cols = GMT->common.b.ncol[GMT_OUT];
5072 	}
5073 	return (n_cols);
5074 }
5075 
5076 /*! . */
gmt_set_cols(struct GMT_CTRL * GMT,unsigned int direction,uint64_t expected)5077 int gmt_set_cols (struct GMT_CTRL *GMT, unsigned int direction, uint64_t expected) {
5078 	/* Initializes the internal GMT->common.b.ncol[] settings.
5079 	 * direction is either GMT_IN or GMT_OUT.
5080 	 * expected is the expected or known number of columns.  Use 0 if not known.
5081 	 * For binary input or output the number of columns must be specified.
5082 	 * For ASCII output the number of columns must also be specified.
5083 	 * For ASCII input the i/o machinery will set this automatically so expected is ignored.
5084 	 * Programs that need to read an input record in order to determine how
5085 	 * many columns on output should call this function after returning the
5086 	 * first data record; otherwise, call it before registering the resource.
5087 	 */
5088 	static char *mode[2] = {"input", "output"};
5089 	int error;
5090 
5091 	if (! (direction == GMT_IN || direction == GMT_OUT)) return (GMT_NOT_A_VALID_DIRECTION);
5092 
5093 	if (direction == GMT_IN && GMT->common.b.ncol[direction]) {	/* Some more conditions when input n columns have been set (by -bi or by reading another file) */
5094 		if (GMT->common.b.ncol[direction] == expected) return (GMT_OK);	/* Already set to this value */
5095 		if (expected == 0) return (GMT_OK);	/* Nothing to do yet */
5096 		if (GMT->common.b.active[direction]) return (GMT_OK);	/* Already set once by -bi so cannot change that here */
5097 		/* If we get here we are ASCII input and we want to change the expected columns only */
5098 	}
5099 
5100 	if (expected == 0 && (direction == GMT_OUT || GMT->common.b.active[direction])) {
5101 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Number of numerical %s columns has been set to 0\n", mode[direction]);
5102 	}
5103 	/* Here we may set the number of data columns */
5104 	if (GMT->common.b.active[direction]) {	/* Must set uninitialized input/output pointers */
5105 		uint64_t col;
5106 		char type = (GMT->common.b.type[direction]) ? GMT->common.b.type[direction] : 'd';
5107 		for (col = 0; col < expected; col++) {
5108 			if (!GMT->current.io.fmt[direction][col].io) {
5109 				if ((GMT->current.io.fmt[direction][col].io = gmtlib_get_io_ptr (GMT, direction, GMT->common.b.swab[direction], type)) == NULL)
5110 					return GMT->parent->error;
5111 				if ((GMT->current.io.fmt[direction][col].type = gmt_get_io_type (GMT, type)) == 0)
5112 					return GMT->parent->error;
5113 			}
5114 		}
5115 		GMT->common.b.ncol[direction] = expected;
5116 	}
5117 	else {	/* ascii */
5118 		GMT->common.b.ncol[direction] = (direction == GMT_IN && expected == 0) ? GMT_MAX_COLUMNS : expected;
5119 		if (direction == GMT_IN) GMT->current.io.max_cols_to_read = (unsigned int)expected;
5120 	}
5121 	if (direction == GMT_OUT && GMT->common.b.o_delay) {	/* Issue delayed message (see gmtlib_io_banner) */
5122 		if ((error = gmtlib_io_banner (GMT, direction)))
5123 			return error;
5124 		GMT->common.b.o_delay = false;
5125 	}
5126 	if (direction == GMT_IN && expected && GMT->common.i.select && GMT->common.i.n_actual_cols > expected)
5127 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Number of %s columns required [%" PRIu64 "] is less that implied by -i [%" PRIu64 "]\n",
5128 		            mode[GMT_IN], expected, GMT->common.i.n_actual_cols);
5129 	return (GMT_OK);
5130 }
5131 
5132 /*! . */
gmtlib_getuserpath(struct GMT_CTRL * GMT,const char * stem,char * path)5133 char *gmtlib_getuserpath (struct GMT_CTRL *GMT, const char *stem, char *path) {
5134 	/* stem is the name of the file, e.g., gmt.conf
5135 	 * path is the full path to the file in question
5136 	 * Returns full pathname if a workable path was found
5137 	 * Looks for file stem in the temporary directory (if defined),
5138 	 * current directory, home directory and $GMT->session.USERDIR (default ~/.gmt[/cache])
5139 	 */
5140 
5141 	/* If a full path is given, we only look for that file directly */
5142 
5143 #ifdef WIN32
5144 	if (stem[0] == '/' || stem[1] == ':')
5145 #else
5146 	if (stem[0] == '/')
5147 #endif
5148 	{
5149 		if (!access (stem, R_OK)) return (strcpy (path, stem));	/* Yes, found it */
5150 		return (NULL);	/* No file found, give up */
5151 	}
5152 
5153 	/* In isolation mode (when GMT->session.TMPDIR is defined), we first look there */
5154 
5155 	if (GMT->session.TMPDIR) {
5156 		sprintf (path, "%s/%s", GMT->session.TMPDIR, stem);
5157 		if (!access (path, R_OK)) return (path);
5158 	}
5159 
5160 	/* Then look in the current working directory */
5161 
5162 
5163 	if (!access (stem, R_OK)) {
5164 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found file %s\n", stem);
5165 		return (strcpy (path, stem));	/* Yes, found it */
5166 	}
5167 
5168 	/* If still not found, see if there is a file in the GMT_{HOME,USER}DIR directories */
5169 
5170 	if (GMT->session.HOMEDIR) {
5171 		sprintf (path, "%s/%s", GMT->session.HOMEDIR, stem);
5172 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Look for file %s\n", path);
5173 		if (!access (path, R_OK)) {
5174 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found file %s\n", path);
5175 			return (path);
5176 		}
5177 	}
5178 	if (GMT->session.USERDIR) {
5179 		if (strstr (stem, ".SRTMGL1.")) /* Special srtm1 subdirs */
5180 			sprintf (path, "%s/server/srtm1/%s", GMT->session.USERDIR, stem);
5181 		else if (strstr (stem, ".SRTMGL3.")) /* Special srtm3 subdirs */
5182 			sprintf (path, "%s/server/srtm3/%s", GMT->session.USERDIR, stem);
5183 		else {
5184 			sprintf (path, "%s/%s", GMT->session.USERDIR, stem);
5185 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Look for file %s\n", path);
5186 			if (!access (path, R_OK)) {
5187 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found file %s\n", path);
5188 				return (path);
5189 			}
5190 			sprintf (path, "%s/server/%s", GMT->session.USERDIR, stem);
5191 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Look for file %s\n", path);
5192 			if (!access (path, R_OK)) {
5193 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found file %s\n", path);
5194 				return (path);
5195 			}
5196 		}
5197 	}
5198 	if (GMT->session.CACHEDIR) {
5199 		sprintf (path, "%s/%s", GMT->session.CACHEDIR, stem);
5200 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Look for file %s\n", path);
5201 		if (!access (path, R_OK)) {
5202 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found file %s\n", path);
5203 			return (path);
5204 		}
5205 	}
5206 
5207 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Could not find file %s\n", stem);
5208 	return (NULL);	/* No file found, give up */
5209 }
5210 
5211 /*! . */
gmt_getdatapath(struct GMT_CTRL * GMT,const char * stem,char * path,int mode)5212 char *gmt_getdatapath (struct GMT_CTRL *GMT, const char *stem, char *path, int mode) {
5213 	/* stem is the name of the file, e.g., grid.img
5214 	 * path is the full path to the file in question
5215 	 * Returns full pathname if a workable path was found
5216 	 * Looks for file stem in current directory, $GMT_{USER,DATA,CACHE}DIR and server dir.
5217 	 * If the dir ends in / we traverse recursively [not under Windows].
5218 	 */
5219 	unsigned int d, pos;
5220 	int t_data;
5221 	size_t L;
5222 	bool found = false;
5223 	char *udir[4] = {GMT->session.USERDIR, GMT->session.DATADIR, GMT->session.CACHEDIR, NULL}, dir[PATH_MAX];
5224 	char path_separator[2] = {',', '\0'}, serverdir[PATH_MAX] = {""};
5225 #ifdef HAVE_DIRENT_H_
5226 	size_t N;
5227 #endif /* HAVE_DIRENT_H_ */
5228 
5229 	/* First look in the current working directory */
5230 
5231 	if (!access (stem, F_OK)) {	/* Yes, found it */
5232 		if (mode == F_OK || gmtio_file_is_readable (GMT, (char *)stem)) {	/* Yes, found it or can read it */
5233 			strcpy (path, stem);
5234 			if (mode == R_OK) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found readable file %s\n", path);
5235 			return (path);
5236 		}
5237 		return (NULL);	/* Cannot read, give up */
5238 	}
5239 
5240 	/* If we got here and a full path is given, we give up ... unless it is one of those /vsi.../ files */
5241 	if (stem[0] == '/') {
5242 #ifdef HAVE_GDAL
5243 		if (gmtlib_found_url_for_gdal ((char *)stem))
5244 			return ((char *)stem);			/* With GDAL all the /vsi-stuff is given existence credit */
5245 		else
5246 			return (NULL);
5247 #else
5248 		return (NULL);
5249 #endif
5250 	}
5251 
5252 #ifdef WIN32
5253 	if (stem[1] == ':') return (NULL);
5254 #endif
5255 
5256 	/* Not found, see if there is a file in the GMT_{USER,DATA,CACHE}DIR directories [if set] */
5257 
5258 	snprintf (serverdir, PATH_MAX, "%s/server", GMT->session.USERDIR);	udir[3] = serverdir;
5259 
5260 	for (d = 0; d < 4; d++) {	/* Loop over USER, DATA and CACHE dirs */
5261 		if (!udir[d]) continue;	/* This directory was not set */
5262 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Look for file %s in %s\n", stem, udir[d]);
5263 		found = false;	pos = 0;
5264 		while (!found && (gmt_strtok (udir[d], path_separator, &pos, dir))) {
5265 			L = strlen (dir);
5266 
5267 #ifdef HAVE_DIRENT_H_
5268 			if (dir[L-1] == '/' || (gmt_M_compat_check (GMT, 4) && dir[L-1] == '*')) {	/* Must search recursively from this dir */
5269 				N = (dir[L-1] == '/') ? L - 1 : L - 2;
5270 				strncpy (path, dir, N);	path[N] = 0;
5271 				found = gmtio_traverse_dir (stem, path);
5272 			}
5273 			else {
5274 #endif /* HAVE_DIRENT_H_ */
5275 				sprintf (path, "%s/%s", dir, stem);
5276 				found = (!access (path, F_OK));
5277 #ifdef HAVE_DIRENT_H_
5278 			}
5279 #endif /* HAVE_DIRENT_H_ */
5280 		}
5281 		if (found && gmtio_file_is_readable (GMT, path)) {
5282 			if (mode == R_OK) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found readable file %s\n", path);
5283 			return (path);	/* Yes, can read it */
5284 		}
5285 	}
5286 
5287 	/* Check special case of a local tile */
5288 	if ((t_data = gmt_file_is_a_tile (GMT->parent, stem, GMT_LOCAL_DIR)) != GMT_NOTSET) {
5289 		snprintf (path, PATH_MAX, "%s%s%s%s", GMT->session.USERDIR, GMT->parent->remote_info[t_data].dir, GMT->parent->remote_info[t_data].file, stem);
5290 		found = (!access (path, F_OK));
5291 		if (found && gmtio_file_is_readable (GMT, path)) {
5292 			if (mode == R_OK) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found readable file %s\n", path);
5293 			return (path);	/* Yes, can read it */
5294 		}
5295 	}
5296 
5297 	/* Finally, try any subdirectory under the server; For GMT >= 6.1 these are server/dir/subdir so we go as far deep as 2 */
5298 	if (udir[3]) {
5299 		char **subdir = gmtlib_get_dirs (GMT, udir[3]), **subsubdir = NULL;
5300 		if (subdir == NULL) return (NULL);	/* No dirs found, give up */
5301 		d = 0;
5302 		while (!found && subdir[d]) {	/* Look through planetary subdirectories under /server (e.g. /server/earth) */
5303 			sprintf (path, "%s/%s/", udir[3], subdir[d]);
5304 			if ((subsubdir = gmtlib_get_dirs (GMT, path))) {	/* Now look in any sub-subdirs (e.g., /server/earth/earth_relief) */
5305 				unsigned int s = 0;
5306 				while (!found && subsubdir[s]) {
5307 					sprintf (path, "%s/%s/%s/%s", udir[3], subdir[d], subsubdir[s], stem);
5308 					found = (!access (path, F_OK));
5309 					s++;
5310 				}
5311 				gmtlib_free_dir_list (GMT, &subsubdir);
5312 			}
5313 			d++;
5314 		}
5315 		gmtlib_free_dir_list (GMT, &subdir);
5316 	}
5317 	if (found && gmtio_file_is_readable (GMT, path)) {	/* Yes, can read it */
5318 		if (mode == R_OK) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found readable file %s\n", path);
5319 		return (path);
5320 	}
5321 
5322 	return (NULL);	/* No file found, give up */
5323 }
5324 
5325 /*! . */
gmt_getsharepath(struct GMT_CTRL * GMT,const char * subdir,const char * stem,const char * suffix,char * path,int mode)5326 char *gmt_getsharepath (struct GMT_CTRL *GMT, const char *subdir, const char *stem, const char *suffix, char *path, int mode) {
5327 	/* stem is the prefix of the file, e.g., mysymbol for mysymbol.def
5328 	 * subdir is an optional subdirectory name in the $GMT_SHAREDIR directory.
5329 	 * suffix is an optional suffix to append to name
5330 	 * path is the full path to the file in question
5331 	 * Returns full pathname if a workable path was found
5332 	 * Looks for file stem in current directory, $GMT_USERDIR (default ~/.gmt), $GMT_SHAREDIR/subdir and $GMT_SHAREDIR.
5333 	 */
5334 
5335 	/* First look in the current working directory */
5336 
5337 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 0. Will try to find subdir=%s stem = %s suffix=%s\n", subdir, stem, suffix);
5338 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 1. gmt_getsharepath trying current dir\n");
5339 	sprintf (path, "%s%s", stem, suffix);
5340 	if (!access (path, mode)) return (path);	/* Yes, found it in current directory */
5341 
5342 	/* Do not continue when full pathname is given */
5343 
5344 	if (stem[0] == '/') return (NULL);
5345 #ifdef WIN32
5346 	if (stem[0] && stem[1] == ':') return (NULL);
5347 #endif
5348 
5349 	/* Not found, see if there is a file in the user's GMT_USERDIR (~/.gmt) directory */
5350 
5351 	if (GMT->session.USERDIR) {
5352 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 2. gmt_getsharepath trying USERDIR %s\n", GMT->session.USERDIR);
5353 		/* Try to get file from $GMT_USERDIR */
5354 		sprintf (path, "%s/%s%s", GMT->session.USERDIR, stem, suffix);
5355 		if (!access (path, mode)) return (path);
5356 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 3. gmt_getsharepath trying USERDIR subdir %s/%s\n", GMT->session.USERDIR, subdir);
5357 		/* Try to get file from $GMT_USERDIR/subdir */
5358 		sprintf (path, "%s/%s/%s%s", GMT->session.USERDIR, subdir, stem, suffix);
5359 		if (!access (path, mode)) return (path);
5360 	}
5361 
5362 	/* Try to get file from $GMT_SHAREDIR/subdir */
5363 
5364 	if (subdir) {
5365 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 4. gmt_getsharepath trying SHAREDIR subdir %s/%s\n", GMT->session.SHAREDIR, subdir);
5366 		sprintf (path, "%s/%s/%s%s", GMT->session.SHAREDIR, subdir, stem, suffix);
5367 		if (!access (path, R_OK)) return (path);
5368 	}
5369 
5370 	/* Lastly try to get file from $GMT_SHAREDIR */
5371 
5372 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 5. gmt_getsharepath trying SHAREDIR %s\n", GMT->session.SHAREDIR);
5373 	sprintf (path, "%s/%s%s", GMT->session.SHAREDIR, stem, suffix);
5374 	if (!access (path, R_OK)) return (path);
5375 
5376 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT: 6. gmt_getsharepath failed\n");
5377 	return (NULL);	/* No file found, give up */
5378 }
5379 
5380 /*! Copy num characters from a char string without triggering a Coverity issue about
5381     "Buffer not null terminated" */
gmt_strncpy(char * dest,const char * source,size_t num)5382 char *gmt_strncpy (char *dest, const char *source, size_t num) {
5383 	/* This function is meant to be used only when Coverity annoying and insistently acuses of
5384 	   "Buffer not null terminated". We have several of those cases where strings are not NULL terminated. */
5385 	strncpy(dest, source, num-1);
5386 	dest[num-1] = source[num-1];
5387 	return dest;
5388 }
5389 
gmtlib_valid_filemodifiers(struct GMT_CTRL * GMT)5390 char *gmtlib_valid_filemodifiers (struct GMT_CTRL *GMT) {
5391 	/* Returns a single string with all unique valid file modifiers.
5392 	 * We do this based on the individua modifier constants and assemble
5393 	 * one with the unique entries on the fly. */
5394 	char count[GMT_LEN128], *m = NULL;
5395 	static char string[GMT_LEN16];
5396 	unsigned int k, q;
5397 	gmt_M_unused (GMT);
5398 	gmt_M_memset (count, GMT_LEN128, char);
5399 	m = GMT_GRIDFILE_MODIFIERS;
5400 	for (k = 0; k < strlen (m); k++)
5401 		count[(int)m[k]]++;
5402 	m = GMT_CPTFILE_MODIFIERS;
5403 	for (k = 0; k < strlen (m); k++)
5404 		count[(int)m[k]]++;
5405 	for (k = q = 0; k < GMT_LEN128; k++)
5406 		if (count[k]) string[q++] = k;
5407 	string[q] = '\0';
5408 	return ((char *)string);
5409 }
5410 
gmt_get_filename(struct GMTAPI_CTRL * API,const char * filename,const char * mods)5411 char *gmt_get_filename (struct GMTAPI_CTRL *API, const char* filename, const char *mods) {
5412 	/* Need to strip off any valid, trailing modifiers and netCDF specifications that may be part of filename */
5413 	char file[PATH_MAX] = {""}, *c = NULL, *clean_file = NULL;
5414 
5415 	if (strstr (filename, "/=tiled_"))	/* Special list with remote tiles, use exactly as is */
5416 		strncpy (file, filename, PATH_MAX-1);
5417 	else	/* Strip off netCDF3-D grid extensions to make sure we get a valid file name */
5418 		sscanf (filename, "%[^=?]", file);
5419 	if (file[0] == '\0')
5420 		return NULL;	/* It happens for example when parsing grdmath args and it finds an isolated  "=" */
5421 	if (mods) {	/* Given modifiers to chop off if they are valid */
5422 		char *f = NULL;
5423 		/* If recognized file extension for grids (.grd, .nc) or cpt (.cpt) we must look after those */
5424 		if ((f = gmt_strrstr (file, ".grd")) || (f = gmt_strrstr (file, GMT_CPT_EXTENSION)) || (f = gmt_strrstr (file, ".nc")))
5425 			c = gmtlib_last_valid_file_modifier (API, f, mods);
5426 		else
5427 			c = gmtlib_last_valid_file_modifier (API, file, mods);
5428 
5429 		if (c == NULL)	/* Modifier free file name */
5430 			return (strdup (file));
5431 		c[0] = '\0';	/* Begone with you */
5432 	}
5433 	clean_file = strdup (file);
5434 
5435 	GMT_Report (API, GMT_MSG_DEBUG, "gmt_get_filename: In: %s Out: %s\n", filename, clean_file);
5436 
5437 	return (clean_file);
5438 }
5439 
5440 /*! Like access but also checks the GMT_*DIR places */
gmt_access(struct GMT_CTRL * GMT,const char * filename,int mode)5441 int gmt_access (struct GMT_CTRL *GMT, const char* filename, int mode) {
5442 	char file[PATH_MAX] = {""}, *cleanfile = NULL;
5443 	unsigned int first = 0;
5444 	int err, k_data;
5445 	struct stat S;
5446 
5447 	if (!filename || !filename[0]) return (GMT_NOTSET);		/* No file given */
5448 	if (gmt_M_file_is_memory (filename)) return (0);	/* Memory location always exists */
5449 	if (gmt_file_is_cache (GMT->parent, filename))			/* Must be a cache file */
5450 		first = gmt_download_file_if_not_found (GMT, filename, 0);
5451 
5452 	if ((cleanfile = gmt_get_filename (GMT->parent, &filename[first], gmtlib_valid_filemodifiers (GMT))) == NULL) return (GMT_NOTSET);	/* Likely not a valid filename */
5453 	strncpy (file, cleanfile, PATH_MAX-1);
5454 	gmt_M_str_free (cleanfile);
5455 	if (mode == W_OK)
5456 		return (access (file, mode));	/* When writing, only look in current directory */
5457 	err = stat (file, &S);	/* Stat the argument */
5458 	if (!err && S_ISDIR (S.st_mode))	/* Path exists, but it is a directory */
5459 		return (GMT_NOTSET);
5460 	if (mode == R_OK || mode == F_OK) {	/* Look in special directories when reading or just checking for existence */
5461 		char path[PATH_MAX] = {""};
5462 		if ((k_data = gmt_remote_no_extension (GMT->parent, filename)) != GMT_NOTSET)	/* A remote @filename_xxm|s grid without extension */
5463 			strcat (file, GMT->parent->remote_info[k_data].ext);	/* Must supply the .extension */
5464 		return (gmt_getdatapath (GMT, file, path, mode) ? 0 : GMT_NOTSET);
5465 	}
5466 	/* If we get here then mode is bad (X_OK)? */
5467 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT: Bad mode (%d) passed to gmt_access\n", mode);
5468 	return (GMT_NOTSET);
5469 }
5470 
5471 /*! . */
gmt_get_ogr_id(struct GMT_OGR * G,char * name)5472 int gmt_get_ogr_id (struct GMT_OGR *G, char *name) {
5473 	unsigned int k;
5474 	for (k = 0; k < G->n_aspatial; k++) if (!strcmp (name, G->name[k])) return (k);
5475 	return (GMT_NOTSET);
5476 }
5477 
5478 /*! . */
gmtlib_ascii_textinput(struct GMT_CTRL * GMT,FILE * fp,uint64_t * n,int * status)5479 void * gmtlib_ascii_textinput (struct GMT_CTRL *GMT, FILE *fp, uint64_t *n, int *status) {
5480 	bool more = true;
5481 	char line[GMT_BUFSIZ] = {""}, *p = NULL;
5482 
5483 	/* gmtlib_ascii_textinput will read one text line and return it, setting
5484 	 * header or segment flags in the process.
5485 	 */
5486 
5487 	while (more) {
5488 		/* First read until we get a non-blank, non-comment record, or reach EOF */
5489 
5490 		GMT->current.io.rec_no++;		/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
5491 		GMT->current.io.rec_in_tbl_no++;	/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
5492 		while ((p = gmt_fgets (GMT, line, GMT_BUFSIZ, fp)) && gmtio_ogr_parser (GMT, line)) {	/* Exits loop when we successfully have read a data record */
5493 			GMT->current.io.rec_no++;		/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
5494 			GMT->current.io.rec_in_tbl_no++;	/* Counts up, regardless of what this record is (data, junk, segment header, etc) */
5495 		}
5496 		/* Here we come once any OGR headers have been parsed and we have a real (non-OGR header) record */
5497 		if (GMT->current.setting.io_header[GMT_IN] && GMT->current.io.rec_in_tbl_no <= GMT->current.setting.io_n_header_items) {	/* Must treat first io_n_header_items as headers */
5498 			if (GMT->common.h.mode == GMT_COMMENT_IS_RESET) continue;	/* Simplest way to replace headers on output is to ignore them on input */
5499 			gmtio_set_current_record (GMT, line);
5500 			GMT->current.io.status = GMT_IO_TABLE_HEADER;
5501 			*status = 0;
5502 			return (NULL);
5503 		}
5504 		/* Here we are done with any header records implied by -h */
5505 		if (!p) {	/* Ran out of records */
5506 			GMT->current.io.status = GMT_IO_EOF;
5507 			*n = 0ULL;
5508 			*status = GMT_NOTSET;
5509 			return (NULL);
5510 		}
5511 		if (strchr (GMT->current.setting.io_head_marker_in, line[0])) {	/* Got a file header, take action and return */
5512 			if (GMT->common.h.mode == GMT_COMMENT_IS_RESET) continue;	/* Simplest way to replace headers on output is to ignore them on input */
5513 			gmtio_set_current_record (GMT, line);
5514 			GMT->current.io.status = GMT_IO_TABLE_HEADER;
5515 			*n = 1ULL;
5516 			*status = 0;
5517 			return (NULL);
5518 		}
5519 
5520 		if (line[0] == GMT->current.setting.io_seg_marker[GMT_IN]) {	/* Got a segment header, take action and return */
5521 			GMT->current.io.status = GMT_IO_SEGMENT_HEADER;
5522 			gmt_set_segmentheader (GMT, GMT_OUT, true);	/* Turn on segment headers on output */
5523 			GMT->current.io.seg_no++;
5524 			/* Just save the header content, not the marker and leading whitespace */
5525 			strncpy (GMT->current.io.segment_header, gmtio_trim_segheader (GMT, line), GMT_BUFSIZ-1);
5526 			*n = 1ULL;
5527 			*status = 0;
5528 			return (NULL);
5529 		}
5530 		if (!(GMT->common.e.active && gmtio_skip_record (GMT, GMT->common.e.select, line)))	/* Fail a grep test */
5531 			more = false;	/* Got a valid record */
5532 	}
5533 
5534 	/* Normal data record */
5535 
5536 	/* First chop off trailing whitespace and commas */
5537 
5538 	gmt_strstrip (line, false); /* Eliminate DOS endings and trailing white space */
5539 
5540 	gmtio_set_current_record (GMT, line);
5541 	strcpy (GMT->current.io.curr_trailing_text, GMT->current.io.curr_text);
5542 	GMT->current.io.record.text = GMT->current.io.curr_trailing_text;
5543 
5544 	GMT->current.io.status = GMT_IO_DATA_RECORD;
5545 	GMT->current.io.data_record_number_in_set[GMT_IN]++;	/* Got a valid text record */
5546 	*n = 1ULL;			/* We always return 1 item as there are no columns */
5547 	*status = 1;
5548 	return (&GMT->current.io.record);
5549 }
5550 
5551 /*! Returns true if we should skip this line (because it is blank) */
gmt_is_a_blank_line(char * line)5552 bool gmt_is_a_blank_line (char *line) {
5553 	unsigned int i = 0;
5554 	while (line[i] && (line[i] == ' ' || line[i] == '\t')) i++;	/* Wind past leading whitespace or tabs */
5555 	if (line[i] == '\n' || line[i] == '\r' || line[i] == '\0') return (true);
5556 	return (false);
5557 }
5558 
5559 /*! . */
gmt_skip_output(struct GMT_CTRL * GMT,double * cols,uint64_t n_cols)5560 bool gmt_skip_output (struct GMT_CTRL *GMT, double *cols, uint64_t n_cols) {
5561 	/* Consult the -s[<cols>][+a][+r] setting and the <cols> values to determine if this record should be output */
5562 	uint64_t c, n_nan;
5563 
5564 	if (n_cols > GMT_MAX_COLUMNS) {
5565 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Number of output data columns (%d) exceeds limit (GMT_MAX_COLUMNS = %d)\n", n_cols, GMT_MAX_COLUMNS);
5566 		return (true);	/* Skip record since we cannot access that many columns */
5567 	}
5568 	/* Increment record numbers before output */
5569 	GMT->current.io.data_record_number_in_set[GMT_OUT]++;
5570 	GMT->current.io.data_record_number_in_tbl[GMT_OUT]++;
5571 	GMT->current.io.data_record_number_in_seg[GMT_OUT]++;
5572 	if (GMT->common.q.mode == GMT_RANGE_ROW_OUT) {	/* Limit output based on data-record range(s) */
5573 		if (gmtio_outside_out_row_range (GMT, *(GMT->common.q.rec))) return (true);		/* Not in a valid row range for output */
5574 	}
5575 	else if (GMT->common.q.mode == GMT_RANGE_DATA_OUT) {	/* Limit output based on data value range(s) */
5576 		if (gmtio_outside_out_data_range (GMT, GMT->common.q.col, cols)) return (true);	/* Not in a valid data range for output */
5577 	}
5578 	if (!GMT->common.s.active) return (false);	/* Normal case; No -s set, just output the record */
5579 	n_nan = 0;	/* None so far */
5580 	if (GMT->current.setting.io_nan_mode & GMT_IO_NAN_ANY) {	/* -s[<cols>]+a[+r]: Determine if one or more NaNs are found in given columns */
5581 		for (c = 0; n_nan == 0 && c < GMT->current.io.io_nan_ncols; c++) {	/* Check each of the specified columns set via -s<cols> [2] */
5582 			if (GMT->current.io.io_nan_col[c] >= n_cols) continue;	/* Input record does not have this column */
5583 			if (gmt_M_is_dnan (cols[GMT->current.io.io_nan_col[c]])) n_nan = GMT->current.io.io_nan_ncols;	/* Finding one is enough to flag the entire record */
5584 		}
5585 	}
5586 	else {	/* No +a, so must check if all selected columns equal NaNs */
5587 		for (c = 0; c < GMT->current.io.io_nan_ncols; c++) {	/* Check each of the specified columns set via -s<cols> [2] */
5588 			if (GMT->current.io.io_nan_col[c] >= n_cols) continue;	/* Input record does not have this column */
5589 			if (gmt_M_is_dnan (cols[GMT->current.io.io_nan_col[c]])) n_nan++;	/* Count the NaN-columns found */
5590 		}
5591 	}
5592 	if (GMT->current.setting.io_nan_mode & GMT_IO_NAN_KEEP && n_nan < GMT->current.io.io_nan_ncols)  return (true);	/* Skip record if -s+r and not enough NaNs found */
5593 	if (GMT->current.setting.io_nan_mode & GMT_IO_NAN_SKIP && n_nan == GMT->current.io.io_nan_ncols) return (true);	/* Skip record if -s and NaNs-test is satisfied */
5594 	return (false);	/* No match, output record */
5595 }
5596 
5597 /*! . */
gmtlib_set_bin_io(struct GMT_CTRL * GMT)5598 void gmtlib_set_bin_io (struct GMT_CTRL *GMT) {
5599 	/* Make sure we point to binary input functions after processing -b option */
5600 	if (GMT->common.b.active[GMT_IN]) {
5601 		GMT->current.io.input = &gmtio_bin_input;
5602 		strcpy (GMT->current.io.r_mode, "rb");
5603 	}
5604 	if (GMT->common.b.active[GMT_OUT]) {
5605 		GMT->current.io.output = &gmtio_bin_output;
5606 		strcpy (GMT->current.io.w_mode, "wb");
5607 		strcpy (GMT->current.io.a_mode, "ab+");
5608 	}
5609 }
5610 
5611 /*! . */
gmt_format_abstime_output(struct GMT_CTRL * GMT,double dt,char * text)5612 void gmt_format_abstime_output (struct GMT_CTRL *GMT, double dt, char *text) {
5613 	char date[GMT_LEN16] = {""}, tclock[GMT_LEN16] = {""};
5614 
5615 	gmt_format_calendar (GMT, date, tclock, &GMT->current.io.date_output, &GMT->current.io.clock_output, false, 1, dt);
5616 	if (date[0] == '\0')	/* No date wanted hence don't use T */
5617 		sprintf (text, "%s", tclock);
5618 	else if (tclock[0] == '\0')	/* No clock wanted hence don't use T */
5619 		sprintf (text, "%s", date);
5620 	else	/* ISO format */
5621 		sprintf (text, "%sT%s", date, tclock);
5622 }
5623 
5624 #define N_T_UNITS 5
5625 /* Using average year and month here for durations */
5626 static double tcount[N_T_UNITS] = {31557600.0, 2629800.0, 86400.0, 3600.0, 60.0};
5627 static char *tformat[N_T_UNITS] = {"%uY","%2.2uM", "%2.2uD", "%2.2uH", "%2.2uM"};
5628 
5629 /*! . */
gmtio_format_duration_output(struct GMT_CTRL * GMT,double dt,char * text)5630 GMT_LOCAL void gmtio_format_duration_output (struct GMT_CTRL *GMT, double dt, char *text) {
5631 	unsigned int k, k0 = N_T_UNITS, n[N_T_UNITS];
5632 	char item[GMT_LEN16] = {""};
5633 	double sec;	/* Get seconds */
5634 	text[0] = 'P';	/* The ISO duration designator */
5635 	if (dt == 0.0) {	/* Zero duration */
5636 		text[1] = '0';	text[2] = 0;
5637 		return;
5638 	}
5639 	sec = dt * GMT->current.setting.time_system.scale;	/* Get seconds */
5640 	for (k = 0; k < N_T_UNITS; k++) {	/* Add up number of time units */
5641 		if ((n[k] = (unsigned int)floor (sec / tcount[k]))) {
5642 			sec -= n[k] * tcount[k];
5643 			if (k0 == N_T_UNITS) k0 = k;
5644 		}
5645 	}
5646 	while (k0 < N_T_UNITS) {	/* For remaining units... */
5647 		snprintf (item, GMT_LEN16, tformat[k0], n[k0]);
5648 		strcat (text, item);
5649 		k0++;
5650 	}
5651 	if (sec > 0.0) {	/* Also add seconds */
5652 		if (GMT->current.io.clock_output.n_sec_decimals)
5653 			snprintf (item, GMT_LEN16, "%0*.*fS", GMT->current.io.clock_output.n_sec_decimals+3, GMT->current.io.clock_output.n_sec_decimals, sec);
5654 		else
5655 			snprintf (item, GMT_LEN16, "%2.2uS", urint (sec));
5656 		strcat (text, item);
5657 	}
5658 }
5659 
5660 /*! . */
gmt_ascii_format_col(struct GMT_CTRL * GMT,char * text,double x,unsigned int direction,uint64_t col)5661 void gmt_ascii_format_col (struct GMT_CTRL *GMT, char *text, double x, unsigned int direction, uint64_t col) {
5662 	/* Format based on column position in in or out direction */
5663 	if (gmt_M_is_dnan (x)) {	/* NaN, just write it as a string */
5664 		sprintf (text, "NaN");
5665 		return;
5666 	}
5667 	switch (gmt_M_type (GMT, direction, col)) {
5668 		case GMT_IS_LON:
5669 			gmtio_format_geo_output (GMT, false, x, text);
5670 			break;
5671 		case GMT_IS_LAT:
5672 			gmtio_format_geo_output (GMT, true, x, text);
5673 			break;
5674 		case GMT_IS_ABSTIME:
5675 			gmt_format_abstime_output (GMT, x, text);
5676 			break;
5677 		case GMT_IS_DURATION:
5678 			gmtio_format_duration_output (GMT, x, text);
5679 			break;
5680 		default:	/* Floating point */
5681 			if (GMT->current.io.o_format[col])	/* Specific to this column */
5682 				sprintf (text, GMT->current.io.o_format[col], x);
5683 			else	/* Use the general float format */
5684 				sprintf (text, GMT->current.setting.format_float_out, x);
5685 			break;
5686 	}
5687 }
5688 
gmt_ascii_format_one(struct GMT_CTRL * GMT,char * text,double x,unsigned int type)5689 void gmt_ascii_format_one (struct GMT_CTRL *GMT, char *text, double x, unsigned int type) {
5690 	if (gmt_M_is_dnan (x)) {
5691 		sprintf (text, "NaN");
5692 		return;
5693 	}
5694 	switch (type) {
5695 		case GMT_IS_LON:
5696 			gmtio_format_geo_output (GMT, false, x, text);
5697 			break;
5698 		case GMT_IS_LAT:
5699 			gmtio_format_geo_output (GMT, true, x, text);
5700 			break;
5701 		case GMT_IS_ABSTIME:
5702 			gmt_format_abstime_output (GMT, x, text);
5703 			break;
5704 		default:
5705 			sprintf (text, GMT->current.setting.format_float_out, x);
5706 			break;
5707 	}
5708 }
5709 
gmt_ascii_format_inc(struct GMT_CTRL * GMT,char * text,double x,unsigned int type)5710 void gmt_ascii_format_inc (struct GMT_CTRL *GMT, char *text, double x, unsigned int type) {
5711 	char unit;
5712 	double d_inc = GMT_DEG2SEC_F * x;
5713 	unsigned int inc = urint (d_inc);
5714 	if ((type & GMT_IS_GEO) == 0 || fabs (d_inc - inc) > GMT_CONV6_LIMIT) {	/* Cartesian or not a clear multiple of arc seconds */
5715 		sprintf (text, GMT->current.setting.format_float_out, x);
5716 		return;
5717 	}
5718 
5719 	unit = 's';
5720 	if (inc >= 60 && (inc % 60) == 0) {	/* Arc minutes perhaps */
5721 		inc /= 60;
5722 		unit = 'm';
5723 	}
5724 	if (inc >= 60 && (inc % 60) == 0) {	/* Arc seconds perhaps */
5725 		inc /= 60;
5726 		unit = 'd';
5727 	}
5728 	sprintf (text, "%d%c", inc, unit);
5729 }
5730 
5731 /*! . */
gmtio_init_io_columns(struct GMT_CTRL * GMT,unsigned int dir)5732 GMT_LOCAL void gmtio_init_io_columns (struct GMT_CTRL *GMT, unsigned int dir) {
5733 	/* Initialize (reset) information per column which may have changed due to -i -o */
5734 	unsigned int i;
5735 	for (i = 0; i < GMT_MAX_COLUMNS; i++) GMT->current.io.col[dir][i].col = GMT->current.io.col[dir][i].order = i;	/* Default order */
5736 	if (dir == GMT_OUT) return;
5737 	for (i = 0; i < GMT_MAX_COLUMNS; i++) GMT->current.io.col_skip[i] = false;	/* Consider all input columns */
5738 }
5739 
5740 /*! . */
gmtlib_io_init(struct GMT_CTRL * GMT)5741 void gmtlib_io_init (struct GMT_CTRL *GMT) {
5742 	/* No need to memset the structure to NULL as this is done initlally.
5743 	 * The assignments here are done once per GMT session as gmtlib_io_init is called
5744 	 * from gmt_begin.  Some variables may change later due to --PAR=value parsing.
5745 	 * gmtlib_io_init must be called before parsing of defaults. */
5746 
5747 	unsigned int i;
5748 
5749 	GMT->common.q.rec = &(GMT->current.io.data_record_number_in_set[GMT_IN]);	/* Default assignment for -q counter */
5750 
5751 	/* Pointer assignment for default ASCII input functions */
5752 
5753 	GMT->current.io.input  = GMT->session.input_ascii = &gmtio_ascii_input;
5754 	GMT->current.io.output = &gmtio_ascii_output;
5755 
5756 	GMT->current.io.ogr_parser = &gmtio_ogr_header_parser;		/* Parse OGR header records to start with */
5757 
5758 	GMT->current.io.scan_separators = GMT_TOKEN_SEPARATORS;		/* Characters that may separate columns in ascii records */
5759 
5760 	/* Assign non-zero/NULL initial values */
5761 
5762 	GMT->current.io.give_report = true;
5763 	GMT->current.io.seg_no = GMT->current.io.rec_no = GMT->current.io.rec_in_tbl_no = 0;	/* These gets incremented so 1 means 1st record */
5764 	GMT->current.io.warn_geo_as_cartesion = true;	/* Not yet read geographic data while in Cartesian mode so we want to warn if we find it */
5765 	GMT->current.io.trailing_text[GMT_IN] = GMT->current.io.trailing_text[GMT_OUT] = true;	/* Default reads and writes any trailing text */
5766 	GMT->current.setting.io_seg_marker[GMT_IN] = GMT->current.setting.io_seg_marker[GMT_OUT] = '>';
5767 	strcpy (GMT->current.io.r_mode, "r");
5768 	strcpy (GMT->current.io.w_mode, "w");
5769 	strcpy (GMT->current.io.a_mode, "a+");
5770 	for (i = 0; i < 4; i++) {
5771 		GMT->current.io.date_input.item_order[i] = GMT->current.io.date_input.item_pos[i] = -1;
5772 		GMT->current.io.date_output.item_order[i] = GMT->current.io.date_output.item_pos[i] = -1;
5773 	}
5774 	for (i = 0; i < 3; i++) {
5775 		GMT->current.io.clock_input.order[i] = GMT->current.io.clock_output.order[i] = GMT->current.io.geo.order[i] = -1;
5776 	}
5777 	strcpy (GMT->current.io.clock_input.ampm_suffix[0],  "am");
5778 	strcpy (GMT->current.io.clock_output.ampm_suffix[0], "am");
5779 	strcpy (GMT->current.io.clock_input.ampm_suffix[1],  "pm");
5780 	strcpy (GMT->current.io.clock_output.ampm_suffix[1], "pm");
5781 
5782 	gmtio_init_io_columns (GMT, GMT_IN);	/* Set default input column order */
5783 	gmtio_init_io_columns (GMT, GMT_OUT);	/* Set default output column order */
5784 	for (i = 0; i < 2; i++) GMT->current.io.skip_if_NaN[i] = true;								/* x/y must be non-NaN */
5785 	for (i = 0; i < 2; i++) gmt_set_column_type (GMT, GMT_IO, i, GMT_IS_UNKNOWN);	/* Must be told [or find out] what x/y are */
5786 	for (i = 2; i < GMT_MAX_COLUMNS; i++) gmt_set_column_type (GMT, GMT_IO, i, GMT_IS_FLOAT);	/* Other columns default to floats */
5787 	gmt_M_memset (GMT->current.io.col_set[GMT_X], GMT_MAX_COLUMNS, char);	/* This is the initial state of input columns - all available to be changed by modules */
5788 	gmt_M_memset (GMT->current.io.col_set[GMT_Y], GMT_MAX_COLUMNS, char);	/* This is the initial state of output columns - all available to be changed by modules */
5789 	gmt_M_memset (GMT->current.io.curr_rec, GMT_MAX_COLUMNS, double);	/* Initialize current and previous records to zero */
5790 	gmt_M_memset (GMT->current.io.prev_rec, GMT_MAX_COLUMNS, double);
5791 	GMT->current.io.record.data = GMT->current.io.curr_rec;
5792 	/* Time periodicity column */
5793 	GMT->current.io.cycle_col = GMT_NOTSET;
5794 }
5795 
5796 /*! Routine will temporarily suspend any -b, -i, -g, h selections for secondary inputs */
gmt_disable_bghio_opts(struct GMT_CTRL * GMT)5797 void gmt_disable_bghio_opts (struct GMT_CTRL *GMT) {
5798 	/* Temporarily turn off any -i, -h selections */
5799 	GMT->common.i.select = false;
5800 	GMT->common.o.select = false;
5801 	GMT->current.setting.io_header_orig = GMT->current.setting.io_header[GMT_IN];
5802 	GMT->current.setting.io_header[GMT_IN] = false;
5803 	GMT->common.g.active = false;	/* Turn this off (if set) for now */
5804 	/* Then deal with primary binary input selection */
5805 	if (GMT->common.b.active[GMT_IN]) {	/* Secondary file input requires ASCII */
5806 		GMT->common.b.active[GMT_IN] = false;
5807 		GMT->common.b.bin_primary = true;
5808 		GMT->current.io.input = &gmtio_ascii_input;
5809 	}
5810 }
5811 
5812 /*! Routine will re-enable any suspended -b, -i, -g, -h selections */
gmt_reenable_bghio_opts(struct GMT_CTRL * GMT)5813 void gmt_reenable_bghio_opts (struct GMT_CTRL *GMT) {
5814 	/* Turn on again any -i, -h selections */
5815 	GMT->common.i.select = GMT->common.i.orig;
5816 	GMT->common.o.select = GMT->common.o.orig;
5817 	GMT->current.setting.io_header[GMT_IN] = GMT->current.setting.io_header_orig;
5818 	GMT->common.g.active = GMT->common.g.selected;	/* Turn this back on (if set) */
5819 	if (GMT->common.b.bin_primary) {	/* Switch back to primary i/o mode which was binary */
5820 		GMT->common.b.active[GMT_IN] = true;
5821 		GMT->common.b.bin_primary = false;
5822 		GMT->current.io.input = &gmtio_bin_input;
5823 	}
5824 }
5825 
5826 /*! . */
gmt_lon_range_adjust(unsigned int range,double * lon)5827 void gmt_lon_range_adjust (unsigned int range, double *lon) {
5828 	switch (range) {	/* Adjust to the desired range */
5829 		case GMT_IS_0_TO_P360_RANGE:		/* Make 0 <= lon <= 360 */
5830 			while ((*lon) < 0.0) (*lon) += 360.0;
5831 			while ((*lon) > 360.0) (*lon) -= 360.0;
5832 			break;
5833 		case GMT_IS_0_TO_P360:		/* Make 0 <= lon < 360 */
5834 			while ((*lon) < 0.0) (*lon) += 360.0;
5835 			while ((*lon) >= 360.0) (*lon) -= 360.0;
5836 			break;
5837 		case GMT_IS_M360_TO_0_RANGE:		/* Make -360 <= lon <= 0 */
5838 			while ((*lon) < -360.0) (*lon) += 360.0;
5839 			while ((*lon) > 0) (*lon) -= 360.0;
5840 			break;
5841 		case GMT_IS_M360_TO_0:		/* Make -360 < lon <= 0 */
5842 			while ((*lon) <= -360.0) (*lon) += 360.0;
5843 			while ((*lon) > 0) (*lon) -= 360.0;
5844 			break;
5845 		case GMT_IS_M180_TO_P180_RANGE:	/* Make -180 <= lon <= +180 */
5846 			while ((*lon) < -180.0) (*lon) += 360.0;
5847 			while ((*lon) > 180.0) (*lon) -= 360.0;
5848 			break;
5849 		case GMT_IS_M180_TO_P180:	/* Make -180 <= lon < +180 [Special case where +180 is not desired] */
5850 			while ((*lon) < -180.0) (*lon) += 360.0;
5851 			while ((*lon) >= 180.0) (*lon) -= 360.0;
5852 			break;
5853 		case GMT_IS_M180_TO_P270_RANGE:	/* Make -180 <= lon < +270 [Special case for GSHHG only] */
5854 			while ((*lon) < -180.0) (*lon) += 360.0;
5855 			while ((*lon) >= 270.0) (*lon) -= 360.0;
5856 			break;
5857 		default:	/* Do nothing */
5858 			break;
5859 	}
5860 }
5861 
5862 /*! . */
gmt_quad_reset(struct GMT_CTRL * GMT,struct GMT_QUAD * Q,uint64_t n_items)5863 void gmt_quad_reset (struct GMT_CTRL *GMT, struct GMT_QUAD *Q, uint64_t n_items) {
5864 	/* Allocate and initialize the QUAD struct needed to find min/max of a set of longitudes */
5865 	uint64_t i;
5866 
5867 	gmt_M_unused(GMT);
5868 	gmt_M_memset (Q, n_items, struct GMT_QUAD);	/* Set all to NULL/0 */
5869 	for (i = 0; i < n_items; i++) {
5870 		Q[i].min[0] = Q[i].min[1] = +DBL_MAX;
5871 		Q[i].max[0] = Q[i].max[1] = -DBL_MAX;
5872 		Q[i].range[0] = GMT_IS_M180_TO_P180_RANGE;
5873 		Q[i].range[1] = GMT_IS_0_TO_P360_RANGE;
5874 	}
5875 }
5876 
5877 /*! . */
gmt_quad_init(struct GMT_CTRL * GMT,uint64_t n_items)5878 struct GMT_QUAD * gmt_quad_init (struct GMT_CTRL *GMT, uint64_t n_items) {
5879 	/* Allocate an initialize the QUAD struct needed to find min/max of longitudes */
5880 	struct GMT_QUAD *Q = gmt_M_memory (GMT, NULL, n_items, struct GMT_QUAD);
5881 
5882 	gmt_quad_reset (GMT, Q, n_items);
5883 
5884 	return (Q);
5885 }
5886 
5887 /*! . */
gmt_quad_add(struct GMT_CTRL * GMT,struct GMT_QUAD * Q,double x)5888 void gmt_quad_add (struct GMT_CTRL *GMT, struct GMT_QUAD *Q, double x) {
5889 	/* Update quad array for this longitude x */
5890 	unsigned int way, quad_no;
5891 	gmt_M_unused(GMT);
5892 	if (gmt_M_is_dnan (x)) return;	/* Cannot handle a NaN */
5893 	for (way = 0; way < 2; way++) {
5894 		gmt_lon_range_adjust (Q->range[way], &x);	/* Set -180/180, then 0-360 range */
5895 		Q->min[way] = MIN (x, Q->min[way]);
5896 		Q->max[way] = MAX (x, Q->max[way]);
5897 	}
5898 	quad_no = urint (floor (x / 90.0));	/* Now x is 0-360; this yields quadrants 0-3 */
5899 	if (quad_no == 4) quad_no = 0;		/* When x == 360.0 */
5900 	Q->quad[quad_no] = true;		/* Our x fell in this quadrant */
5901 }
5902 
5903 /*! . */
gmt_quad_finalize(struct GMT_CTRL * GMT,struct GMT_QUAD * Q)5904 unsigned int gmt_quad_finalize (struct GMT_CTRL *GMT, struct GMT_QUAD *Q) {
5905 	/* Finalize longitude range settings */
5906 	uint64_t n_quad;
5907 	unsigned int way;
5908 
5909 	n_quad = Q->quad[0] + Q->quad[1] + Q->quad[2] + Q->quad[3];		/* How many quadrants had data */
5910 	if (Q->quad[0] && Q->quad[3])		/* Longitudes on either side of Greenwich only, must use -180/+180 notation */
5911 		way = 0;
5912 	else if (Q->quad[1] && Q->quad[2])	/* Longitudes on either side of the date line, must user 0/360 notation */
5913 		way = 1;
5914 	else if (n_quad == 2 && ((Q->quad[0] && Q->quad[2]) || (Q->quad[1] && Q->quad[3])))	/* Funny quadrant gap, pick shortest longitude extent */
5915 		way = ((Q->max[0] - Q->min[0]) < (Q->max[1] - Q->min[1])) ? 0 : 1;
5916 	else					/* Either will do, use default settings */
5917 		way = (GMT->current.io.geo.range == GMT_IS_0_TO_P360_RANGE) ? 1 : 0;
5918 	/* Final adjustments */
5919 	if (Q->min[way] > Q->max[way]) Q->min[way] -= 360.0;
5920 	if (Q->min[way] < 0.0 && Q->max[way] < 0.0) Q->min[way] += 360.0, Q->max[way] += 360.0;
5921 	return (way);
5922 }
5923 
gmtio_update_west_east_limits(struct GMT_CTRL * GMT,double * W,double * E,double w,double e)5924 GMT_LOCAL void gmtio_update_west_east_limits (struct GMT_CTRL *GMT, double *W, double *E, double w, double e) {
5925 	/* Longitudes are tricky and we must find total extent by adding one new segment at the time.
5926 	 * Extend the previous W/E extent with the new w/e to minimize final longitude range */
5927 	double shift, best_shift = 0.0, range, range_max = DBL_MAX, WW = *W, EE = *E;
5928 	int k;
5929 	if (*W == *E) {	/* Initialization */
5930 		*W = w;	*E = e;
5931 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Longitude range initialized to %g/%g\n", *W, *E);
5932 		return;
5933 	}
5934 	for (k = -1; k <= +1; k++) {	/* Try to shift 2nd range by -360, 0, and +360 */
5935 		shift = k * 360.0;
5936 		range = MAX (*E, e + shift) - MIN (*W, w + shift);
5937 		if (range < range_max) {	/* This is a tighter fit */
5938 			best_shift = shift;
5939 			range_max = range;
5940 		}
5941 	}
5942 	*W = MIN (*W, w + best_shift);
5943 	*E = MAX (*E, e + best_shift);
5944 	/* Ensure w < e and fits inside usual limits */
5945 	if (*W > *E) *W -= 360.0;
5946 	if (*W < 0.0 && *E < 0.0) { *W += 360.0; *E += 360.0;}
5947 	else if (*E > 360.0) { *W -= 360.0; *E -= 360.0;}
5948 	if ((*E - *W) > 360.0) { *W = 0.0; *E = 360.0;}
5949 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Longitude range %g/%g + %g/%g = %g/%g\n", WW, EE, w, e, *W, *E);
5950 }
5951 
gmtio_compare_center(const void * p1,const void * p2)5952 GMT_LOCAL int gmtio_compare_center (const void *p1, const void *p2) {
5953 	const struct GMT_RANGE *a = p1, *b = p2;
5954 	if (a->center < b->center) return (-1);
5955 	if (a->center > b->center) return (+1);
5956 	return (0);
5957 }
5958 
gmt_find_range(struct GMT_CTRL * GMT,struct GMT_RANGE * Z,uint64_t n_items,double * west,double * east)5959 void gmt_find_range (struct GMT_CTRL *GMT, struct GMT_RANGE *Z, uint64_t n_items, double *west, double *east) {
5960 	/* Need to sort Z on center longitude, then build final range incrementally and return answer via west/east */
5961 	uint64_t k;
5962 	for (k = 0; k < n_items; k++) {	/* Find middle point, and force 0-360 range */
5963 		Z[k].center = 0.5 * (Z[k].east + Z[k].west);
5964 		if (Z[k].center < 0.0) Z[k].center += 360.0;
5965 	}
5966 	qsort (Z, n_items, sizeof (struct GMT_RANGE), gmtio_compare_center);
5967 	*west = *east = 0.0;	/* Initialized to have no range yet */
5968 	for (k = 0; k < n_items; k++)
5969 		gmtio_update_west_east_limits (GMT, west, east, Z[k].west, Z[k].east);
5970 }
5971 
5972 /*! . */
gmtlib_get_lon_minmax(struct GMT_CTRL * GMT,double * lon,uint64_t n_rows,double * min,double * max)5973 void gmtlib_get_lon_minmax (struct GMT_CTRL *GMT, double *lon, uint64_t n_rows, double *min, double *max) {
5974 	/* Return the min/max longitude in array lon using clever quadrant checking. */
5975 	bool all_negative = true;
5976 	unsigned int way;
5977 	uint64_t row;
5978 	struct GMT_QUAD *Q = gmt_quad_init (GMT, 1);	/* Allocate and initialize one QUAD structure */
5979 
5980 	/* We must keep separate min/max for both Dateline and Greenwich conventions */
5981 	for (row = 0; row < n_rows; row++) {
5982 		if (lon[row] > 0.0) all_negative = false;
5983 		gmt_quad_add (GMT, Q, lon[row]);
5984 	}
5985 
5986 	/* Finalize longitude range settings */
5987 	way = gmt_quad_finalize (GMT, Q);
5988 	*min = Q->min[way];		*max = Q->max[way];
5989 	if (all_negative && *min >= 0.0 && *max > 0.0) {	/* Shift the min/max to negative longitudes */
5990 		*min -= 360.0;	*max -= 360.0;
5991 	}
5992 	gmt_M_free (GMT, Q);
5993 }
5994 
5995 /*! . */
gmt_eliminate_lon_jumps(struct GMT_CTRL * GMT,double * lon,uint64_t n_rows)5996 void gmt_eliminate_lon_jumps (struct GMT_CTRL *GMT, double *lon, uint64_t n_rows) {
5997 	/* Eliminate longitude jumps in array lon using clever quadrant checking. */
5998 	unsigned int way;
5999 	uint64_t row;
6000 	struct GMT_QUAD *Q = gmt_quad_init (GMT, 1);	/* Allocate and initialize one QUAD structure */
6001 
6002 	/* We must keep separate min/max for both Dateline and Greenwich conventions */
6003 	for (row = 0; row < n_rows; row++) gmt_quad_add (GMT, Q, lon[row]);
6004 
6005 	/* Finalize longitude range settings */
6006 	way = gmt_quad_finalize (GMT, Q);
6007 	for (row = 0; row < n_rows; row++) gmt_lon_range_adjust (Q->range[way], &lon[row]);
6008 
6009 	gmt_M_free (GMT, Q);
6010 }
6011 
gmtlib_determine_pole(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n)6012 int gmtlib_determine_pole (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n) {
6013 	/* Determines if a polygon is a polar cap and if so, which one:
6014 	 *  0 : Not a polar cap.
6015 	 * -1 : A south polar cap going clockwise
6016 	 * -2 : A south polar cap going counter-clockwise
6017 	 * +1 : A north polar cap going clockwise
6018 	 * +2 : A north polar cap going counter-clockwise.
6019 	 * -99: Returned if there is an error (no points)
6020 	 * Here, we tolerate open polygons and reuse 1st point to close it in the calculation.
6021 	 */
6022 	bool touched_N = false, touched_S = false, open = false;
6023 	uint64_t row, n_unique, last_point = 0, next;
6024 	int type = 0, n_360;
6025 	double dlon, lon_sum = 0.0, lat_sum = 0.0, lat_S = 90.0, lat_N = -90.0;
6026 	static char *pole[5] = {"south (CCW)", "south (CW)", "no", "north (CW)", "north (CCW)"};
6027 
6028 	if (n < 3) return -99;	/* Cannot be a polygon that was given */
6029 	if (gmt_polygon_is_open (GMT, lon, lat, n)) {	/* No repeat last = first point so reuse first */
6030 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Calling gmtlib_determine_pole on an open polygon\n");
6031 		n_unique = n;	/* Need to loop over all input points and then go to point 0 for last leg */
6032 		open = true;
6033 		last_point = n - 1;
6034 	}
6035 	else	/* Stop 1 short since last point is the duplicate first point */
6036 		n_unique = n - 1;
6037 	for (row = 0; row < n_unique; row++) {	/* Add up angular increments between vertices */
6038 		next = (open && row == last_point) ? 0 : row + 1;
6039 		gmt_M_set_delta_lon (lon[row], lon[next], dlon);	/* Handles the 360 jump cases */
6040 		lon_sum += dlon;
6041 		lat_sum += lat[row];
6042 		if (doubleAlmostEqual (lat[row], +90.0)) touched_N = true;
6043 		else if (doubleAlmostEqual (lat[row], -90.0)) touched_S = true;
6044 		if (lat[row] < lat_S) lat_S = lat[row];
6045 		if (lat[row] > lat_N) lat_N = lat[row];
6046 	}
6047 	n_360 = irint (lon_sum / 360.0);	/* This is either -1, 0, or +1 since lon_sum is either -360, 0, +360 plus some noise */
6048 	if (n_360) {	/* test is true if contains a pole; adjust rectangular bounds and set pole flag accordingly */
6049 		dlon = (n_360 > 0) ? 2.0 : 1.0;			/* 2 for CCW, 1 for CW paths */
6050 		type = irint (copysign (dlon, lat_sum));	/* Here, either -2, -1 or +1, +2 */
6051 		if (type < 0 && touched_N && lat_S > -90.0) type = -type;
6052 		else if (type > 0 && touched_S && lat_N < 90.0) type = -type;
6053 	}
6054     if (touched_N && touched_S) type = 0;   /* Cannot contain both poles */
6055 	else if (type == 0 && touched_N) type = 1;	/* Cuts through the N pole */
6056 	else if (type == 0 && touched_S) type = -1;	/* Cuts through the S pole */
6057 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtlib_determine_pole: N = %" PRIu64 " Multiples of 360: %d  Residual: %g Polygon contains %s pole.\n", n, n_360, lon_sum - n_360 * 360.0, pole[type+2]);
6058 	return (type);
6059 }
6060 
6061 /*! . */
gmt_set_seg_polar(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)6062 void gmt_set_seg_polar (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
6063 	/* Must check if polygon is a polar cap.  We use the idea that the sum of
6064  	 * the change in angle along the polygon is either -/+360 (if pole is inside)
6065 	 * or 0 if outside.  The sign (not used here) gives the handedness of the polygon.
6066 	 * Which pole (S or N) is determined by computng the average latitude and
6067 	 * assuming the pole is in the heimsphere most visited.  This may not be
6068 	 * true of course. */
6069 	int answer;
6070 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
6071 
6072 	if ((gmt_M_type (GMT, GMT_IN, GMT_X) & GMT_IS_GEO) == 0 || S->n_columns < 2) return;	/* No can do */
6073 	if ((answer = gmtlib_determine_pole (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)) == -99) return;	/* No good */
6074 	if (answer) {	/* true if contains a pole; adjust rectangular bounds and set pole flag */
6075 		SH->pole = (answer < 0) ? -1 : +1;
6076 		S->min[GMT_X] = 0.0;	S->max[GMT_X] = 360.0;
6077 		if (SH->pole == -1) SH->lat_limit = S->min[GMT_Y], S->min[GMT_Y] = -90.0;
6078 		else if (SH->pole == +1) SH->lat_limit = S->max[GMT_Y], S->max[GMT_Y] = +90.0;
6079 	}
6080 	else	/* So, 0 means not polar */
6081 		SH->pole = 0;
6082 }
6083 
6084 /*! . */
gmtlib_geo_to_dms(double val,int n_items,double fact,int * d,int * m,int * s,int * ix)6085 bool gmtlib_geo_to_dms (double val, int n_items, double fact, int *d, int *m,  int *s,  int *ix) {
6086 	/* Convert floating point degrees to dd:mm[:ss][.xxx].  Returns true if d = 0 and val is negative */
6087 	bool minus;
6088 	int isec, imin;
6089 	double sec, fsec, min, fmin, step;
6090 
6091 	minus = (val < 0.0);
6092 	step = (fact == 0.0) ? GMT_CONV8_LIMIT : 0.5 / fact;  	/* Precision desired in seconds (or minutes); else just deal with roundoff */
6093 
6094 	if (n_items == 3) {		/* Want dd:mm:ss[.xxx] format */
6095 		sec = GMT_DEG2SEC_F * fabs (val) + step;	/* Convert to seconds */
6096 		isec = irint (floor (sec));			/* Integer seconds */
6097 		fsec = sec - (double)isec;  			/* Leftover fractional second */
6098 		*d = isec / GMT_DEG2SEC_I;			/* Integer degrees */
6099 		isec -= ((*d) * GMT_DEG2SEC_I);			/* Left-over seconds in the last degree */
6100 		*m = isec / GMT_MIN2SEC_I;			/* Integer minutes */
6101 		isec -= ((*m) * GMT_MIN2SEC_I);			/* Leftover seconds in the last minute */
6102 		*s = isec;					/* Integer seconds */
6103 		*ix = irint (floor (fsec * fact));		/* Fractional seconds scaled to integer */
6104 	}
6105 	else if (n_items == 2) {		/* Want dd:mm[.xxx] format */
6106 		min = GMT_DEG2MIN_F * fabs (val) + step;	/* Convert to minutes */
6107 		imin = irint (floor (min));			/* Integer minutes */
6108 		fmin = min - (double)imin;  			/* Leftover fractional minute */
6109 		*d = imin / GMT_DEG2MIN_I;			/* Integer degrees */
6110 		imin -= ((*d) * GMT_DEG2MIN_I);			/* Left-over seconds in the last degree */
6111 		*m = imin;					/* Integer minutes */
6112 		*s = 0;						/* No seconds */
6113 		*ix = irint (floor (fmin * fact));		/* Fractional minutes scaled to integer */
6114 	}
6115 	else {		/* Want dd[.xxx] format */
6116 		min = fabs (val) + step;			/* Convert to degrees */
6117 		imin = irint (floor (min));			/* Integer degrees */
6118 		fmin = min - (double)imin;  			/* Leftover fractional degree */
6119 		*d = imin;					/* Integer degrees */
6120 		*m = 0;						/* Integer minutes */
6121 		*s = 0;						/* No seconds */
6122 		*ix = irint (floor (fmin * fact));		/* Fractional degrees scaled to integer */
6123 	}
6124 	if (minus) {	/* OK, change sign, but watch for *d = 0 */
6125 		if (*d)	/* Non-zero degree term is easy */
6126 			*d = -(*d);
6127 		else	/* Cannot change 0 to -0, so pass flag back to calling function */
6128 			return (true);
6129 	}
6130 	return (false);
6131 }
6132 
6133 /*! . */
gmt_add_to_record(struct GMT_CTRL * GMT,char * record,double val,uint64_t col,unsigned int way,unsigned int sep)6134 void gmt_add_to_record (struct GMT_CTRL *GMT, char *record, double val, uint64_t col, unsigned int way, unsigned int sep) {
6135 	/* formats and appends val to the record texts string; way is GMT_IN|GMT_OUT
6136 	 * If sep is 1 we prepend col separator.
6137 	 * If sep is 2 we append col separator
6138 	 * If sep is 1|2 do both [0 means no separator].
6139 	 * if sep > 10 we init record then remove 10 from sep.
6140 	 */
6141 	char word[GMT_LEN64] = {""};
6142 	gmt_ascii_format_col (GMT, word, val, way, col);
6143 	if (sep >= 10) {	/* Initialize new record */
6144 		record[0] = '\0';
6145 		sep -= 10;
6146 	}
6147 	if (sep & 1) strcat (record, GMT->current.setting.io_col_separator);
6148 	strcat (record, word);
6149 	if (sep & 2) strcat (record, GMT->current.setting.io_col_separator);
6150 }
6151 
6152 /*! . */
gmt_cat_to_record(struct GMT_CTRL * GMT,char * record,char * word,unsigned int way,unsigned int sep)6153 void gmt_cat_to_record (struct GMT_CTRL *GMT, char *record, char *word, unsigned int way, unsigned int sep) {
6154 	/* appends val to the record text string; way is GMT_IN|GMT_OUT
6155 	 * If sep is 1 we prepend col separator.
6156 	 * If sep is 2 we append col separator
6157 	 * If sep is 1|2 do both [0 means no separator].
6158 	 * if sep > 10 we init record then remove 10 from sep.
6159 	 */
6160 	gmt_M_unused(way);
6161 	if (sep >= 10) {	/* Initialize new record */
6162 		record[0] = '\0';
6163 		sep -= 10;
6164 	}
6165 	if (sep & 1) strcat (record, GMT->current.setting.io_col_separator);
6166 	strcat (record, word);
6167 	if (sep & 2) strcat (record, GMT->current.setting.io_col_separator);
6168 }
6169 
6170 /*! . */
gmt_write_segmentheader(struct GMT_CTRL * GMT,FILE * fp,uint64_t n_cols)6171 void gmt_write_segmentheader (struct GMT_CTRL *GMT, FILE *fp, uint64_t n_cols) {
6172 	/* Output ASCII or binary segment header.
6173 	 * ASCII header is expected to contain newline (\n) */
6174 
6175 	uint64_t col;
6176 
6177 	if (!GMT->current.io.multi_segments[GMT_OUT]) return;	/* No output segments requested */
6178 	GMT->current.io.data_record_number_in_seg[GMT_OUT] = 0;	/* Reset counter */
6179 	if (GMT->common.b.active[GMT_OUT]) {			/* Binary native file uses all NaNs */
6180 		for (col = 0; col < n_cols; col++) GMT->current.io.output (GMT, fp, 1, &GMT->session.d_NaN, NULL);
6181 		return;
6182 	}
6183 	/* Here we are doing ASCII */
6184 	if (GMT->current.setting.io_blankline[GMT_OUT])	/* Write blank line to indicate segment break */
6185 		fprintf (fp, "\n");
6186 	else if (GMT->current.setting.io_nanline[GMT_OUT]) {	/* Write NaN record to indicate segment break */
6187 		if (GMT->common.d.active[GMT_OUT]) {	/* Ah, but NaNs are to be replaced */
6188 			gmt_ascii_output_col (GMT, fp, GMT->common.d.nan_proxy[GMT_OUT], GMT_Z);
6189 			for (col = 1 ; col < MAX (2,n_cols); col++) {
6190 				fprintf (fp, "%s", GMT->current.setting.io_col_separator);
6191 				gmt_ascii_output_col (GMT, fp, GMT->common.d.nan_proxy[GMT_OUT], GMT_Z);
6192 			}
6193 			fprintf (fp, "\n");
6194 		}
6195 		else {
6196 			for (col = 1 ; col < MAX (2,n_cols); col++)
6197 				fprintf (fp, "NaN%s", GMT->current.setting.io_col_separator);
6198 			fprintf (fp, "NaN\n");
6199 		}
6200 	}
6201 	else if (!GMT->current.io.segment_header[0])		/* No header; perhaps via binary input with NaN-headers */
6202 		fprintf (fp, "%c\n", GMT->current.setting.io_seg_marker[GMT_OUT]);
6203 	else
6204 		fprintf (fp, "%c %s\n", GMT->current.setting.io_seg_marker[GMT_OUT], GMT->current.io.segment_header);
6205 }
6206 
6207 /*! . */
gmtlib_io_binary_header(struct GMT_CTRL * GMT,FILE * fp,unsigned int dir)6208 void gmtlib_io_binary_header (struct GMT_CTRL *GMT, FILE *fp, unsigned int dir) {
6209 	uint64_t k;
6210 	char c = ' ';
6211 	if (dir == GMT_IN) {	/* Use fread since we don't know if input is a stream or a file */
6212 		size_t nr = 0;
6213 		for (k = 0; k < GMT->current.setting.io_n_header_items; k++) nr += gmt_M_fread (&c, sizeof (char), 1U, fp);
6214 	}
6215 	else {
6216 		for (k = 0; k < GMT->current.setting.io_n_header_items; k++) gmt_M_fwrite (&c, sizeof (char), 1U, fp);
6217 	}
6218 }
6219 
6220 /*! . */
gmt_byteswap_file(struct GMT_CTRL * GMT,FILE * outfp,FILE * infp,const SwapWidth swapwidth,const uint64_t offset,const uint64_t length)6221 bool gmt_byteswap_file (struct GMT_CTRL *GMT, FILE *outfp, FILE *infp, const SwapWidth swapwidth, const uint64_t offset, const uint64_t length) {
6222 	/* read from *infp and write byteswapped data to *ofp
6223 	 * swap only 'length' bytes beginning at 'offset' bytes
6224 	 * if 'length == 0' swap until EOF */
6225 	uint64_t bytes_read = 0;
6226 	size_t nbytes, chunk, extrabytes = 0;
6227 	static const size_t chunksize = 0x1000000; /* 16 MiB */
6228 	char *buffer, message[GMT_LEN256] = {""};
6229 
6230 	/* length must be a multiple SwapWidth */
6231 	if ( length%swapwidth != 0 ) {
6232 		snprintf (message, GMT_LEN256, "%s: error: length must be a multiple of %u bytes.\n", __func__, swapwidth);
6233 		GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6234 		return false;
6235 	}
6236 
6237 	/* allocate buffer on stack to improve disk i/o */
6238 	buffer = malloc (chunksize);
6239 	if (buffer == NULL) {
6240 		snprintf (message, GMT_LEN256, "%s: error: cannot malloc %" PRIuS " bytes.\n", __func__, chunksize);
6241 		GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6242 		return false;
6243 	}
6244 
6245 	/* skip offset bytes at beginning of infp */
6246 	while ( bytes_read < offset ) {
6247 		chunk = chunksize < offset - bytes_read ? chunksize : offset - bytes_read;
6248 		nbytes = fread (buffer, sizeof (char), chunk, infp);
6249 		if (nbytes == 0) {
6250 			if (feof (infp)) {
6251 				/* EOF */
6252 #ifdef DEBUG_BYTESWAP
6253 				snprintf (message, GMT_LEN256, "%s: EOF encountered at %" PRIu64
6254 						" (before offset at %" PRIu64 ")\n", __func__, bytes_read, offset);
6255 				GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6256 #endif
6257 				GMT->current.io.status = GMT_IO_EOF;
6258 				gmt_M_str_free (buffer);
6259 				return true;
6260 			}
6261 			snprintf (message, GMT_LEN256, "%s: error reading stream while skipping.\n", __func__);
6262 			GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6263 			gmt_M_str_free (buffer);
6264 			return false;
6265 		}
6266 		bytes_read += nbytes;
6267 		/* write buffer */
6268 		if (gmtio_fwrite_check (GMT, buffer, sizeof (char), nbytes, outfp)) {
6269 			gmt_M_str_free (buffer);
6270 			return false;
6271 		}
6272 	}
6273 #ifdef DEBUG_BYTESWAP
6274 	if (bytes_read) {
6275 		snprintf (message, GMT_LEN256, "%s: %" PRIu64 " bytes skipped at beginning.\n", __func__, bytes_read);
6276 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, message);
6277 	}
6278 #endif
6279 
6280 	/* start swapping bytes */
6281 	while ( length == 0 || bytes_read < offset + length ) {
6282 		uint64_t bytes_left = length - bytes_read + offset;
6283 		chunk = (length == 0 || bytes_left > chunksize) ? chunksize : bytes_left;
6284 		nbytes = fread (buffer, sizeof (char), chunk, infp);
6285 		if (nbytes == 0) {
6286 			if (feof (infp)) {
6287 				/* EOF */
6288 #ifdef DEBUG_BYTESWAP
6289 				snprintf (message, GMT_LEN256, "%s: %" PRIu64 " bytes swapped.\n", __func__, bytes_read - offset - extrabytes);
6290 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, message);
6291 #endif
6292 				if (extrabytes != 0) {
6293 					snprintf (message, GMT_LEN256, "%s: The last %" PRIuS " bytes were ignored during swapping.\n",
6294 							__func__, extrabytes);
6295 					GMT_Report (GMT->parent, GMT_MSG_WARNING, message);
6296 				}
6297 				GMT->current.io.status = GMT_IO_EOF;
6298 				gmt_M_str_free (buffer);
6299 				return true;
6300 			}
6301 			snprintf (message, GMT_LEN256, "%s: error reading stream while swapping.\n", __func__);
6302 			GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6303 			gmt_M_str_free (buffer);
6304 			return false;
6305 		}
6306 		bytes_read += nbytes;
6307 #ifdef DEBUG_BYTESWAP
6308 		snprintf (message, GMT_LEN256, "%s: read %" PRIuS " bytes into buffer of size %" PRIuS ".\n",
6309 				__func__, nbytes, chunksize);
6310 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, message);
6311 #endif
6312 
6313 		/* nbytes must be a multiple of SwapWidth */
6314 		extrabytes = nbytes % swapwidth;
6315 		if (extrabytes != 0) {
6316 			/* this can only happen on EOF, ignore extra bytes while swapping. */
6317 			snprintf (message, GMT_LEN256, "%s: Read buffer contains %" PRIuS " bytes which are "
6318 					"not aligned with the swapwidth of %" PRIuS " bytes.\n",
6319 					__func__, nbytes, extrabytes);
6320 			GMT_Report (GMT->parent, GMT_MSG_WARNING, message);
6321 			nbytes -= extrabytes;
6322 		}
6323 
6324 		/* swap bytes in buffer */
6325 		switch (swapwidth) {
6326 			case Int16len:
6327 				gmtio_swap_uint16 (buffer, nbytes);
6328 				break;
6329 			case Int32len:
6330 				gmtio_swap_uint32 (buffer, nbytes);
6331 				break;
6332 			case Int64len:
6333 			default:
6334 				gmtio_swap_uint64 (buffer, nbytes);
6335 				break;
6336 		}
6337 
6338 		/* restore nbytes */
6339 		nbytes += extrabytes;
6340 
6341 		/* write buffer */
6342 		if (gmtio_fwrite_check (GMT, buffer, sizeof (char), nbytes, outfp)) {
6343 			gmt_M_str_free (buffer);
6344 			return false;
6345 		}
6346 	}
6347 #ifdef DEBUG_BYTESWAP
6348 	snprintf (message, GMT_LEN256, "%s: %" PRIu64 " bytes swapped.\n", __func__, bytes_read - offset);
6349 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, message);
6350 #endif
6351 
6352 	/* skip to EOF */
6353 	while ( true ) {
6354 		nbytes = fread (buffer, sizeof (char), chunksize, infp);
6355 		if (nbytes == 0) {
6356 			if (feof (infp)) {
6357 				/* EOF */
6358 #ifdef DEBUG_BYTESWAP
6359 				snprintf (message, GMT_LEN256, "%s: %" PRIu64 " bytes nbytes until EOF.\n",
6360 						__func__, bytes_read - offset - length);
6361 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, message);
6362 #endif
6363 				break;
6364 			}
6365 			snprintf (message, GMT_LEN256, "%s: error reading stream while skipping to EOF.\n", __func__);
6366 			GMT_Report (GMT->parent, GMT_MSG_ERROR, message);
6367 			gmt_M_str_free (buffer);
6368 			return false;
6369 		}
6370 		bytes_read += nbytes;
6371 		/* write buffer */
6372 		if (gmtio_fwrite_check (GMT, buffer, sizeof (char), nbytes, outfp)) {
6373 			gmt_M_str_free (buffer);
6374 			return false;
6375 		}
6376 	}
6377 
6378 	GMT->current.io.status = GMT_IO_EOF;
6379 	gmt_M_str_free (buffer);
6380 	return true;
6381 }
6382 
6383 /*! . */
gmt_parse_z_io(struct GMT_CTRL * GMT,char * txt,struct GMT_PARSE_Z_IO * z)6384 int gmt_parse_z_io (struct GMT_CTRL *GMT, char *txt, struct GMT_PARSE_Z_IO *z) {
6385 	int value;
6386 	unsigned int i, k = 0, start;
6387 
6388 	if (!txt) return (GMT_PARSE_ERROR);	/* Must give a non-NULL argument */
6389 	if (!txt[0]) return (0);		/* Default -ZTLa */
6390 
6391 	for (start = 0; !z->not_grid && txt[start] && start < 2; start++) {	/* Loop over the first 2 flags unless dataset is not a grid */
6392 
6393 		switch (txt[start]) {
6394 
6395 			/* These 4 cases will set the format orientation for input */
6396 
6397 			case 'T':
6398 			case 'B':
6399 			case 'L':
6400 			case 'R':
6401 				z->format[k++] = txt[start];
6402 				break;
6403 			default:
6404 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Z: Must begin with [TBLR][TBLR]!\n");
6405 				return (GMT_PARSE_ERROR);
6406 				break;
6407 		}
6408 	}
6409 
6410 	for (i = start; txt[i]; i++) {	/* Loop over remaining flags */
6411 
6412 		switch (txt[i]) {
6413 
6414 			/* Set this if file is periodic, is grid registered, but repeating column or row is missing from input */
6415 
6416 			case 'x':
6417 				z->repeat[GMT_X] = true;	break;
6418 			case 'y':
6419 				z->repeat[GMT_Y] = true;	break;
6420 
6421 			/* Optionally skip the given number of bytes before reading data */
6422 
6423 			case 's':
6424 				i++;
6425 				if (txt[i]) {	/* Read the byte count for skipping */
6426 					value = atoi (&txt[i]);
6427 					if (value < 0) {
6428 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Z: Skip must be positive\n");
6429 						return (GMT_PARSE_ERROR);
6430 					}
6431 					z->skip = value;
6432 					while (txt[i] && isdigit ((int)txt[i])) i++;
6433 					i--;
6434 				}
6435 				break;
6436 
6437 			case 'w':
6438 				z->swab = (k_swap_in | k_swap_out); 	break;	/* Default is swap both input and output when selected */
6439 
6440 			/* Set read pointer depending on data format */
6441 
6442 			case 'A': /* ASCII (next regular float (%lg) from the stream) */
6443 			case 'a': /* ASCII (1 per record) */
6444 			case 'c': /* Binary int8_t */
6445 			case 'u': /* Binary uint8_t */
6446 			case 'h': /* Binary int16_t */
6447 			case 'H': /* Binary uint16_t */
6448 			case 'i': /* Binary int32_t */
6449 			case 'I': /* Binary uint32_t */
6450 			case 'l': /* Binary int64_t */
6451 			case 'L': /* Binary uint64_t */
6452 			case 'f': /* Binary 4-byte float */
6453 			case 'd': /* Binary 8-byte double */
6454 				z->type = txt[i];
6455 				break;
6456 
6457 			default:
6458 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Z: %c not a valid modifier!\n", txt[i]);
6459 				return (GMT_PARSE_ERROR);
6460 				break;
6461 		}
6462 	}
6463 
6464 	return (0);
6465 }
6466 
6467 /*! . */
gmt_get_io_type(struct GMT_CTRL * GMT,char type)6468 int gmt_get_io_type (struct GMT_CTRL *GMT, char type) {
6469 	int t = -1;
6470 	switch (type) {
6471 		/* Set read pointer depending on data format */
6472 		case 'a': case 'A':          break; /* ASCII */
6473 		case 'c': t = GMT_CHAR;   break; /* Binary int8_t */
6474 		case 'u': t = GMT_UCHAR;  break; /* Binary uint8_t */
6475 		case 'h': t = GMT_SHORT;  break; /* Binary int16_t */
6476 		case 'H': t = GMT_USHORT; break; /* Binary uint16_t */
6477 		case 'i': t = GMT_INT;    break; /* Binary int32_t */
6478 		case 'I': t = GMT_UINT;   break; /* Binary uint32_t */
6479 		case 'l': t = GMT_LONG;   break; /* Binary int64_t */
6480 		case 'L': t = GMT_ULONG;  break; /* Binary uint64_t */
6481 		case 'f': t = GMT_FLOAT;  break; /* Binary 4-byte float */
6482 		case 'd': t = GMT_DOUBLE; break; /* Binary 8-byte double */
6483 		default:
6484 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Valid data type not set [%c]!\n", type);
6485 			GMT->parent->error = GMT_NOT_A_VALID_TYPE;
6486 			break;
6487 	}
6488 	return (t+1);	/* Since 0 means not set */
6489 }
6490 
6491 /*! . */
gmtlib_get_io_ptr(struct GMT_CTRL * GMT,int direction,enum GMT_swap_direction swap,char type)6492 p_to_io_func gmtlib_get_io_ptr (struct GMT_CTRL *GMT, int direction, enum GMT_swap_direction swap, char type) {
6493 	/* Return pointer to read or write function for this data type */
6494 	/* swap is 0 for no swap, 1 for swap input, 2 for swap output, 3 for swap both */
6495 	p_to_io_func p = NULL;
6496 
6497 	switch (type) {	/* Set read pointer depending on data format */
6498 		case 'd':	/* Binary 8-byte double */
6499 			if (direction == GMT_IN)
6500 				p = (swap & k_swap_in) ? &gmtio_d_read_swab : &gmtio_d_read;
6501 			else
6502 				p = (swap & k_swap_out) ? &gmtio_d_write_swab : &gmtio_d_write;
6503 			break;
6504 		case 'A':	/* ASCII with more than one per record */
6505 			p = (direction == GMT_IN) ? &gmtio_A_read : &gmtio_a_write;
6506 			break;
6507 		case 'a':	/* ASCII */
6508 			p = (direction == GMT_IN) ? &gmtio_a_read : &gmtio_a_write;
6509 			break;
6510 		case 'c':	/* Binary int8_t */
6511 			p = (direction == GMT_IN) ? &gmtio_c_read : &gmtio_c_write;
6512 			break;
6513 		case 'u':	/* Binary uint8_t */
6514 			p = (direction == GMT_IN) ? &gmtio_u_read : &gmtio_u_write;
6515 			break;
6516 		case 'h':	/* Binary int16_t */
6517 			if (direction == GMT_IN)
6518 				p = (swap & k_swap_in) ? &gmtio_h_read_swab : &gmtio_h_read;
6519 			else
6520 				p = (swap & k_swap_out) ? &gmtio_h_write_swab : &gmtio_h_write;
6521 			break;
6522 		case 'H':	/* Binary uint16_t */
6523 			if (direction == GMT_IN)
6524 				p = (swap & k_swap_in) ? &gmtio_H_read_swab : &gmtio_H_read;
6525 			else
6526 				p = (swap & k_swap_out) ? &gmtio_H_write_swab : &gmtio_H_write;
6527 			break;
6528 		case 'i':	/* Binary int32_t */
6529 			if (direction == GMT_IN)
6530 				p = (swap & k_swap_in) ? &gmtio_i_read_swab : &gmtio_i_read;
6531 			else
6532 				p = (swap & k_swap_out) ? &gmtio_i_write_swab : &gmtio_i_write;
6533 			break;
6534 		case 'I':	/* Binary uint32_t */
6535 			if (direction == GMT_IN)
6536 				p = (swap & k_swap_in) ? &gmtio_I_read_swab : &gmtio_I_read;
6537 			else
6538 				p = (swap & k_swap_out) ? &gmtio_I_write_swab : &gmtio_I_write;
6539 			break;
6540 		case 'l':	/* Binary int64_t */
6541 			if (direction == GMT_IN)
6542 				p = (swap & k_swap_in) ? &gmtio_l_read_swab : &gmtio_l_read;
6543 			else
6544 				p = (swap & k_swap_out) ? &gmtio_l_write_swab : &gmtio_l_write;
6545 			break;
6546 		case 'L':	/* Binary uint64_t */
6547 			if (direction == GMT_IN)
6548 				p = (swap & k_swap_in) ? &gmtio_L_read_swab : &gmtio_L_read;
6549 			else
6550 				p = (swap & k_swap_out) ? &gmtio_L_write_swab : &gmtio_L_write;
6551 			break;
6552 		case 'f':	/* Binary 4-byte float */
6553 			if (direction == GMT_IN)
6554 				p = (swap & k_swap_in) ? &gmtio_f_read_swab : &gmtio_f_read;
6555 			else
6556 				p = (swap & k_swap_out) ? &gmtio_f_write_swab : &gmtio_f_write;
6557 			break;
6558 		case 'x':
6559 			break;	/* Binary skip */
6560 
6561 		default:
6562 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "%c not a valid data type!\n", type);
6563 			GMT->parent->error = GMT_NOT_A_VALID_TYPE;
6564 			return NULL;
6565 			break;
6566 	}
6567 
6568 	return (p);
6569 }
6570 
6571 /*! . */
gmt_init_z_io(struct GMT_CTRL * GMT,char format[],bool repeat[],enum GMT_swap_direction swab,off_t skip,char type,struct GMT_Z_IO * r)6572 int gmt_init_z_io (struct GMT_CTRL *GMT, char format[], bool repeat[], enum GMT_swap_direction swab, off_t skip, char type, struct GMT_Z_IO *r) {
6573 	bool first = true;
6574 	unsigned int k;
6575 
6576 	gmt_M_memset (r, 1, struct GMT_Z_IO);
6577 
6578 	for (k = 0; k < 2; k++) {	/* Loop over the two format flags */
6579 		switch (format[k]) {
6580 			/* These 4 cases will set the format orientation for input */
6581 			case 'T':
6582 				if (first) r->format = GMT_IS_ROW_FORMAT;
6583 				r->y_step = 1;	first = false;	break;
6584 			case 'B':
6585 				if (first) r->format = GMT_IS_ROW_FORMAT;
6586 				r->y_step = -1;	first = false;	break;
6587 			case 'L':
6588 				if (first) r->format = GMT_IS_COL_FORMAT;
6589 				r->x_step = 1;	first = false;	break;
6590 			case 'R':
6591 				if (first) r->format = GMT_IS_COL_FORMAT;
6592 				r->x_step = -1;	first = false;	break;
6593 			default:
6594 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Z: %c not a valid format specifier!\n", format[k]);
6595 				return GMT_PARSE_ERROR;
6596 				break;
6597 		}
6598 	}
6599 
6600 	if (!strchr ("AacuhHiIlLfd", type)) {
6601 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Z: %c not a valid data type!\n", type);
6602 		return GMT_NOT_A_VALID_TYPE;
6603 	}
6604 
6605 	r->x_missing = (repeat[GMT_X]) ? 1 : 0;	r->y_missing = (repeat[GMT_Y]) ? 1 : 0;
6606 	r->skip = skip;			r->swab = swab;
6607 	r->binary = (strchr ("Aa", type)) ? false : true;
6608 	if ((GMT->current.io.read_item  = gmtlib_get_io_ptr (GMT, GMT_IN,  swab, type)) == NULL)	/* Set read pointer depending on data format */
6609 		return (GMT->parent->error);
6610 	if ((GMT->current.io.write_item = gmtlib_get_io_ptr (GMT, GMT_OUT, swab, type)) == NULL)	/* Set write pointer depending on data format */
6611 		return (GMT->parent->error);
6612 	GMT->common.b.type[GMT_IN] = GMT->common.b.type[GMT_OUT] = type;		/* Since -b is not setting this */
6613 	if (r->binary) {	/* Use the binary modes (which only matters under Windoze)  */
6614 		strcpy (GMT->current.io.r_mode, "rb");
6615 		strcpy (GMT->current.io.w_mode, "wb");
6616 		strcpy (GMT->current.io.a_mode, "ab+");
6617 	}
6618 	return (GMT_OK);
6619 }
6620 
6621 /*! . */
gmt_set_z_io(struct GMT_CTRL * GMT,struct GMT_Z_IO * r,struct GMT_GRID * G)6622 int gmt_set_z_io (struct GMT_CTRL *GMT, struct GMT_Z_IO *r, struct GMT_GRID *G) {
6623 	/* THIS SHOULD NOT BE FATAL!
6624 	if ((r->x_missing || r->y_missing) && G->header->registration == GMT_GRID_PIXEL_REG) return (GMT_GRDIO_RI_NOREPEAT);
6625 	*/
6626 	gmt_M_unused(GMT);
6627 	r->start_col = ((r->x_step == 1) ? 0 : G->header->n_columns - 1 - r->x_missing);
6628 	r->start_row = ((r->y_step == 1) ? r->y_missing : G->header->n_rows - 1);
6629 	r->get_gmt_ij = (r->format == GMT_IS_COL_FORMAT) ? gmtio_col_ij : gmtio_row_ij;
6630 	r->x_period = G->header->n_columns - r->x_missing;
6631 	r->y_period = G->header->n_rows - r->y_missing;
6632 	r->n_expected = ((uint64_t)r->x_period) * ((uint64_t)r->y_period);
6633 	return (GMT_NOERROR);
6634 }
6635 
6636 /*! . */
gmt_check_z_io(struct GMT_CTRL * GMT,struct GMT_Z_IO * r,struct GMT_GRID * G)6637 void gmt_check_z_io (struct GMT_CTRL *GMT, struct GMT_Z_IO *r, struct GMT_GRID *G) {
6638 	/* Routine to fill in the implied periodic row or column that was missing.
6639 	 * We must allow for padding in G->data */
6640 
6641 	unsigned int col, row;
6642 	uint64_t k, k_top, k_bot;
6643 	gmt_M_unused(GMT);
6644 
6645 	if (r->x_missing) for (row = 0, k = gmt_M_ijp (G->header, row, 0); row < G->header->n_rows; row++, k += G->header->mx) G->data[k+G->header->n_columns-1] = G->data[k];
6646 	if (r->y_missing) for (col = 0, k_top = gmt_M_ijp (G->header, 0, 0), k_bot = gmt_M_ijp (G->header, G->header->n_rows-1, 0); col < G->header->n_columns; col++) G->data[col+k_top] = G->data[col+k_bot];
6647 }
6648 
6649 /*! . */
gmtlib_clock_C_format(struct GMT_CTRL * GMT,char * form,struct GMT_CLOCK_IO * S,unsigned int mode)6650 int gmtlib_clock_C_format (struct GMT_CTRL *GMT, char *form, struct GMT_CLOCK_IO *S, unsigned int mode) {
6651 	/* Determine the order of H, M, S in input and output clock strings,
6652 	 * as well as the number of decimals in output seconds (if any), and
6653 	 * if a 12- or 24-hour clock is used.
6654 	 * mode is 0 for input, 1 for output, and 2 for plot output.
6655 	 */
6656 
6657 	S->skip = false;
6658 	if (mode && strlen (form) == 1 && form[0] == '-') {	/* Do not want clock output or plotted */
6659 		S->skip = true;
6660 		return GMT_NOERROR;
6661 	}
6662 
6663 	/* Get the order of year, month, day or day-of-year in input/output formats for dates */
6664 
6665 	if (gmtio_get_hms_order (GMT, form, S)) return GMT_PARSE_ERROR;
6666 
6667 	/* Craft the actual C-format to use for input/output clock strings */
6668 
6669 	if (S->order[0] >= 0) {	/* OK, at least hours is needed */
6670 		char fmt[GMT_LEN64] = {""};
6671 		if (S->compact)
6672 			snprintf (S->format, GMT_LEN64, "%%d");
6673 		else
6674 			(mode) ? snprintf (S->format, GMT_LEN64, "%%02d") : snprintf (S->format, GMT_LEN64, "%%2d");
6675 		if (S->order[1] >= 0) {	/* Need minutes too*/
6676 			if (S->delimiter[0][0]) strcat (S->format, S->delimiter[0]);
6677 			(mode) ? snprintf (fmt, GMT_LEN64, "%%02d") : snprintf (fmt, GMT_LEN64, "%%2d");
6678 			strcat (S->format, fmt);
6679 			if (S->order[2] >= 0) {	/* .. and seconds */
6680 				if (S->delimiter[1][0]) strcat (S->format, S->delimiter[1]);
6681 				if (mode) {	/* Output format */
6682 					snprintf (fmt, GMT_LEN64, "%%02d");
6683 					strcat (S->format, fmt);
6684 					if (S->n_sec_decimals) {	/* even add format for fractions of second */
6685 						snprintf (fmt, GMT_LEN64, ".%%%d.%dd", S->n_sec_decimals, S->n_sec_decimals);
6686 						strcat (S->format, fmt);
6687 					}
6688 				}
6689 				else {		/* Input format */
6690 					sprintf (fmt, "%%lf");
6691 					strcat (S->format, fmt);
6692 				}
6693 			}
6694 		}
6695 		if (mode && S->twelve_hr_clock) {	/* Finally add %s for the am, pm string */
6696 			sprintf (fmt, "%%s");
6697 			strcat (S->format, fmt);
6698 		}
6699 	}
6700 	return (GMT_NOERROR);
6701 }
6702 
6703 /*! . */
gmtlib_date_C_format(struct GMT_CTRL * GMT,char * form,struct GMT_DATE_IO * S,unsigned int mode)6704 int gmtlib_date_C_format (struct GMT_CTRL *GMT, char *form, struct GMT_DATE_IO *S, unsigned int mode) {
6705 	/* Determine the order of Y, M, D, J in input and output date strings.
6706 	 * mode is 0 for input, 1 for output, and 2 for plot output.
6707 	 */
6708 
6709 	int k, ywidth;
6710 	bool no_delim, watch;
6711 	char fmt[GMT_LEN64] = {""};
6712 
6713 	S->skip = false;
6714 	if (mode && strlen (form) == 1 && form[0] == '-') {	/* Do not want date output or plotted */
6715 		S->skip = true;
6716 		return GMT_NOERROR;
6717 	}
6718 
6719 	/* Get the order of year, month, day or day-of-year in input/output formats for dates */
6720 
6721 	GMT->parent->error = GMT_NOERROR;
6722 	watch = gmtio_get_ymdj_order (GMT, form, S);
6723 	if (GMT->parent->error) return (GMT->parent->error);
6724 
6725 	S->watch = (watch && mode == 0);
6726 
6727 	/* Craft the actual C-format to use for i/o date strings */
6728 
6729 	no_delim = !(S->delimiter[0][0] || S->delimiter[1][0]);	/* true for things like yyyymmdd */
6730 	ywidth = (no_delim) ? 4 : 4+(!mode);			/* 4 or 5, depending on values */
6731 	if (S->item_order[0] >= 0 && S->iso_calendar) {	/* ISO Calendar string: At least one item is needed */
6732 		k = (S->item_order[0] == 0 && !S->Y2K_year) ? ywidth : 2;
6733 		if (S->mw_text && S->item_order[0] == 1)	/* Prepare for "Week ##" format */
6734 			snprintf (S->format, GMT_LEN64, "%%s %%02d");
6735 		else if (S->compact)			/* Numerical formatting of week or year without leading zeros */
6736 			sprintf (S->format, "%%d");
6737 		else					/* Numerical formatting of week or year  */
6738 			(mode) ? snprintf (S->format, GMT_LEN64, "%%%d.%dd", k, k) : snprintf (S->format, GMT_LEN64, "%%%dd", k);
6739 		if (S->item_order[1] >= 0) {	/* Need another item */
6740 			if (S->delimiter[0][0]) strcat (S->format, S->delimiter[0]);
6741 			if (S->mw_text && S->item_order[0] == 1) {	/* Prepare for "Week ##" format */
6742 				sprintf (fmt, "%%s ");
6743 				strcat (S->format, fmt);
6744 			}
6745 			else
6746 				strcat (S->format, "W");
6747 			if (S->compact)
6748 				sprintf (fmt, "%%d");
6749 			else
6750 				(mode) ? sprintf (fmt, "%%02d") : sprintf (fmt, "%%2d");
6751 			strcat (S->format, fmt);
6752 			if (S->item_order[2] >= 0) {	/* and ISO day of week */
6753 				if (S->delimiter[1][0]) strcat (S->format, S->delimiter[1]);
6754 				sprintf (fmt, "%%1d");
6755 				strcat (S->format, fmt);
6756 			}
6757 		}
6758 	}
6759 	else if (S->item_order[0] >= 0) {			/* Gregorian Calendar string: At least one item is needed */
6760 		k = (S->item_order[0] == 0 && !S->Y2K_year) ? ywidth : 2;
6761 		if (S->item_order[0] == 3) k = 3;	/* Day of year */
6762 		if (S->mw_text && S->item_order[0] == 1) {	/* Prepare for "Monthname" format */
6763 			if (mode == 0) {
6764 				if (no_delim)
6765 					sprintf (S->format, "%%3s");
6766 				else
6767 					snprintf (S->format, GMT_LEN64, "%%[^%s]", S->delimiter[0]);
6768 			}
6769 			else
6770 				sprintf (S->format, "%%s");
6771 		}
6772 		else if (S->compact)			/* Numerical formatting of month or year w/o leading zeros */
6773 			sprintf (S->format, "%%d");
6774 		else					/* Numerical formatting of month or year */
6775 			(mode) ? snprintf (S->format, GMT_LEN64, "%%%d.%dd", k, k) : snprintf (S->format, GMT_LEN64, "%%%dd", k);
6776 		if (S->item_order[1] >= 0) {	/* Need more items */
6777 			if (S->delimiter[0][0]) strcat (S->format, S->delimiter[0]);
6778 			k = (S->item_order[1] == 0 && !S->Y2K_year) ? ywidth : 2;
6779 			if (S->item_order[1] == 3) k = 3;	/* Day of year */
6780 			if (S->mw_text && S->item_order[1] == 1) {	/* Prepare for "Monthname" format */
6781 				if (mode == 0) {
6782 					if (no_delim)
6783 						sprintf (fmt, "%%3s");
6784 					else
6785 						snprintf (fmt, GMT_LEN64, "%%[^%s]", S->delimiter[1]);
6786 				}
6787 				else sprintf (fmt, "%%s");
6788 			}
6789 			else if (S->compact && !S->Y2K_year)		/* Numerical formatting of month or 4-digit year w/o leading zeros */
6790 				sprintf (fmt, "%%d");
6791 			else
6792 				(mode) ? snprintf (fmt, GMT_LEN64, "%%%d.%dd", k, k) : snprintf (fmt, GMT_LEN64, "%%%dd", k);
6793 			strcat (S->format, fmt);
6794 			if (S->item_order[2] >= 0) {	/* .. and even more */
6795 				if (S->delimiter[1][0]) strcat (S->format, S->delimiter[1]);
6796 				k = (S->item_order[2] == 0 && !S->Y2K_year) ? ywidth : 2;
6797 				if (S->mw_text && S->item_order[2] == 1)	/* Prepare for "Monthname" format */
6798 					sprintf (fmt, "%%s");
6799 				else if (S->compact)			/* Numerical formatting of month or year w/o leading zeros */
6800 					sprintf (fmt, "%%d");
6801 				else
6802 					(mode) ? snprintf (fmt, GMT_LEN64, "%%%d.%dd", k, k) : snprintf (fmt, GMT_LEN64, "%%%dd", k);
6803 				strcat (S->format, fmt);
6804 			}
6805 		}
6806 	}
6807 	return GMT_NOERROR;
6808 }
6809 
6810 /*! . */
gmtlib_geo_C_format(struct GMT_CTRL * GMT)6811 int gmtlib_geo_C_format (struct GMT_CTRL *GMT) {
6812 	/* Determine the output of geographic location formats. */
6813 
6814 	struct GMT_GEO_IO *S = &GMT->current.io.geo;
6815 
6816 	if (GMT->current.setting.format_geo_out[0] == '\0') return GMT_RUNTIME_ERROR;	/* Gave nothing */
6817 
6818 	if (gmtio_get_dms_order (GMT, GMT->current.setting.format_geo_out, S)) return GMT_PARSE_ERROR;	/* Get the order of degree, min, sec in output formats */
6819 
6820 	if (S->no_sign) return (GMT_IO_BAD_PLOT_DEGREE_FORMAT);
6821 
6822 	if (S->decimal) {	/* Plain decimal degrees */
6823 		 /* here we depend on FORMAT_FLOAT_OUT begin set.  This will not be true when FORMAT_GEO_MAP is parsed but will be
6824 		  * handled at the end of gmt_begin.  For gmtset and --PAR later we will be OK as well. */
6825 		if (!GMT->current.setting.format_float_out[0]) return (GMT_NOERROR); /* Quietly return and deal with this later in gmt_begin */
6826 		sprintf (S->x_format, "%s", GMT->current.setting.format_float_out);
6827 		sprintf (S->y_format, "%s", GMT->current.setting.format_float_out);
6828 	}
6829 	else {			/* Some form of dd:mm:ss */
6830 		char fmt[GMT_LEN64] = {""};
6831 		sprintf (S->x_format, "%%03d");
6832 		sprintf (S->y_format, "%%02d");
6833 		if (S->order[1] >= 0) {	/* Need minutes too */
6834 			strcat (S->x_format, S->delimiter[0]);
6835 			strcat (S->y_format, S->delimiter[0]);
6836 			sprintf (fmt, "%%02d");
6837 			strcat (S->x_format, fmt);
6838 			strcat (S->y_format, fmt);
6839 		}
6840 		if (S->order[2] >= 0) {	/* .. and seconds */
6841 			strcat (S->x_format, S->delimiter[1]);
6842 			strcat (S->y_format, S->delimiter[1]);
6843 			sprintf (fmt, "%%02d");
6844 			strcat (S->x_format, fmt);
6845 			strcat (S->y_format, fmt);
6846 		}
6847 		if (S->n_sec_decimals) {	/* even add format for fractions of second (or minutes or degrees) */
6848 			snprintf (fmt, GMT_LEN64, ".%%%d.%dd", S->n_sec_decimals, S->n_sec_decimals);
6849 			strcat (S->x_format, fmt);
6850 			strcat (S->y_format, fmt);
6851 		}
6852 		/* Finally add %s for the W,E,S,N string (or NULL) */
6853 		sprintf (fmt, "%%s");
6854 		strcat (S->x_format, fmt);
6855 		strcat (S->y_format, fmt);
6856 	}
6857 	return (GMT_NOERROR);
6858 }
6859 
6860 /*! . */
gmtlib_plot_C_format(struct GMT_CTRL * GMT)6861 int gmtlib_plot_C_format (struct GMT_CTRL *GMT) {
6862 	unsigned int i, j, length;
6863 	struct GMT_GEO_IO *S = &GMT->current.plot.calclock.geo;
6864 
6865 	/* Determine the plot geographic location formats. */
6866 
6867 	if (GMT->current.setting.format_geo_map[0] == '\0') return GMT_RUNTIME_ERROR;	/* Gave nothing */
6868 
6869 	for (i = 0; i < 3; i++) for (j = 0; j < 2; j++) gmt_M_memset (GMT->current.plot.format[i][j], GMT_LEN256, char);
6870 
6871 	if (gmtio_get_dms_order (GMT, GMT->current.setting.format_geo_map, S)) return GMT_PARSE_ERROR;	/* Get the order of degree, min, sec in output formats */
6872 
6873 	if (S->decimal) {	/* Plain decimal degrees */
6874 		int len;
6875 		 /* Here we depend on FORMAT_FLOAT_OUT being set.  This will not be true when FORMAT_GEO_MAP is parsed but will be
6876 		  * handled at the end of gmt_begin.  For gmtset and --PAR later we will be OK as well. */
6877 		if (!GMT->current.setting.format_float_out[0]) return GMT_NOERROR; /* Quietly return and deal with this later in gmt_begin */
6878 
6879 		len = sprintf (S->x_format, "%s", GMT->current.setting.format_float_out);
6880 		      sprintf (S->y_format, "%s", GMT->current.setting.format_float_out);
6881 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6882 		{	/* But we want the degree symbol appended */
6883 			S->x_format[len] = (char)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol];
6884 			S->y_format[len] = (char)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol];
6885 			S->x_format[len+1] = S->y_format[len+1] = '\0';
6886 		}
6887 		strcat (S->x_format, "%s");
6888 		strcat (S->y_format, "%s");
6889 	}
6890 	else {			/* Must cover all the 6 forms of dd[:mm[:ss]][.xxx] */
6891 		char fmt[GMT_LEN256] = {""};
6892 
6893 		/* Level 0: degrees only. index 0 is integer degrees, index 1 is [possibly] fractional degrees */
6894 
6895 		sprintf (GMT->current.plot.format[0][0], "%%d");		/* ddd */
6896 		if (S->order[1] == GMT_NOTSET && S->n_sec_decimals > 0) /* ddd.xxx format */
6897 			snprintf (GMT->current.plot.format[0][1], GMT_LEN64, "%%d.%%%d.%dd", S->n_sec_decimals, S->n_sec_decimals);
6898 		else						/* ddd format */
6899 			sprintf (GMT->current.plot.format[0][1], "%%d");
6900 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6901 		{	/* But we want the degree symbol appended */
6902 			snprintf (fmt, GMT_LEN256, "%c", (int)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol]);
6903 			strcat (GMT->current.plot.format[0][0], fmt);
6904 			strcat (GMT->current.plot.format[0][1], fmt);
6905 		}
6906 
6907 		/* Level 1: degrees and minutes only. index 0 is integer minutes, index 1 is [possibly] fractional minutes  */
6908 
6909 		sprintf (GMT->current.plot.format[1][0], "%%d");	/* ddd */
6910 		sprintf (GMT->current.plot.format[1][1], "%%d");
6911 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6912 		{	/* We want the degree symbol appended */
6913 			sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol]);
6914 			strcat (GMT->current.plot.format[1][0], fmt);
6915 			strcat (GMT->current.plot.format[1][1], fmt);
6916 		}
6917 		strcat (GMT->current.plot.format[1][0], "%02d");
6918 		if (S->order[2] == GMT_NOTSET && S->n_sec_decimals > 0) /* ddd:mm.xxx format */
6919 			snprintf (fmt, GMT_LEN256, "%%02d.%%%d.%dd", S->n_sec_decimals, S->n_sec_decimals);
6920 		else						/* ddd:mm format */
6921 			sprintf (fmt, "%%02d");
6922 		strcat (GMT->current.plot.format[1][1], fmt);
6923 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6924 		{	/* We want the minute symbol appended */
6925 			if (GMT->current.setting.map_degree_symbol == gmt_colon)
6926 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_colon]);
6927 			else
6928 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_squote]);
6929 			strcat (GMT->current.plot.format[1][0], fmt);
6930 			strcat (GMT->current.plot.format[1][1], fmt);
6931 		}
6932 
6933 		/* Level 2: degrees, minutes, and seconds. index 0 is integer seconds, index 1 is [possibly] fractional seconds  */
6934 
6935 		sprintf (GMT->current.plot.format[2][0], "%%d");
6936 		sprintf (GMT->current.plot.format[2][1], "%%d");
6937 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6938 		{	/* We want the degree symbol appended */
6939 			sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol]);
6940 			strcat (GMT->current.plot.format[2][0], fmt);
6941 			strcat (GMT->current.plot.format[2][1], fmt);
6942 		}
6943 		strcat (GMT->current.plot.format[2][0], "%02d");
6944 		strcat (GMT->current.plot.format[2][1], "%02d");
6945 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6946 		{	/* We want the minute symbol appended */
6947 			if (GMT->current.setting.map_degree_symbol == gmt_colon)
6948 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_colon]);
6949 			else
6950 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_squote]);
6951 			strcat (GMT->current.plot.format[2][0], fmt);
6952 			strcat (GMT->current.plot.format[2][1], fmt);
6953 		}
6954 		strcat (GMT->current.plot.format[2][0], "%02d");
6955 		if (S->n_sec_decimals > 0)			 /* ddd:mm:ss.xxx format */
6956 			snprintf (fmt, GMT_LEN256, "%%d.%%%d.%dd", S->n_sec_decimals, S->n_sec_decimals);
6957 		else						/* ddd:mm:ss format */
6958 			sprintf (fmt, "%%02d");
6959 		strcat (GMT->current.plot.format[2][1], fmt);
6960 		if (GMT->current.setting.map_degree_symbol != gmt_none)
6961 		{	/* We want the second symbol appended */
6962 			if (GMT->current.setting.map_degree_symbol == gmt_colon)
6963 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_colon]);
6964 			else
6965 				sprintf (fmt, "%c", (int)GMT->current.setting.ps_encoding.code[gmt_dquote]);
6966 			strcat (GMT->current.plot.format[2][0], fmt);
6967 			strcat (GMT->current.plot.format[2][1], fmt);
6968 		}
6969 
6970 		/* Finally add %s for the [leading space]W,E,S,N char (or NULL) */
6971 
6972 		for (i = 0; i < 3; i++) for (j = 0; j < 2; j++) {
6973 			length = (unsigned int)MAX (1, strlen (GMT->current.plot.format[i][j])) - 1;
6974 			if (GMT->current.plot.format[i][j][length] == ':') GMT->current.plot.format[i][j][length] = '\0';	/* Chop off a trailing colon */
6975 			strcat (GMT->current.plot.format[i][j], "%s");
6976 		}
6977 	}
6978 	return GMT_NOERROR;
6979 }
6980 
6981 /*! . */
gmt_scanf(struct GMT_CTRL * GMT,char * s,unsigned int expectation,double * val)6982 int gmt_scanf (struct GMT_CTRL *GMT, char *s, unsigned int expectation, double *val) {
6983 	/* Called with s pointing to a char string, expectation
6984 	indicating what is known/required/expected about the
6985 	format of the string.  Attempts to decode the string to
6986 	find a double value.  Upon success, loads val and
6987 	returns type found.  Upon failure, does not touch val,
6988 	and returns GMT_IS_NAN.  Expectations permitted on call
6989 	are
6990 		GMT_IS_FLOAT	we expect an uncomplicated float.
6991 	*/
6992 
6993 	int type;
6994 	char calstring[GMT_LEN64] = {""}, clockstring[GMT_LEN64] = {""}, *p = NULL;
6995 	double x;
6996 	int64_t rd;
6997 	size_t callen, clocklen;
6998 
6999 	if (s[0] == '\"') {	/* Must handle double-quoted items */
7000 		callen = strlen (s) - 1;
7001 		if (s[callen] == '\"') { s[callen] = '\0'; s++;}	/* Strip off trailing quote and advance pointer over the first */
7002 	}
7003 	if (s[0] == 'T') {	/* Numbers cannot start with letters except for clocks, e.g., T07:0 */
7004 		if ((int)s[1] < 0 || !isdigit((int)s[1])) return (GMT_IS_NAN);	/* Clocks must have T followed by digit, e.g., T07:0 otherwise junk */
7005 	}
7006 	else if (!gmtio_is_pi (s) && isalpha ((int)s[0])) return (GMT_IS_NAN);	/* Numbers cannot start with letters */
7007 
7008 	switch (expectation) {
7009 		case GMT_IS_GEO: case GMT_IS_LON: case GMT_IS_LAT:
7010 			/* True if either a lat or a lon is expected  */
7011 			return (gmtio_scanf_geo (s, val));
7012 			break;
7013 
7014 	 	case GMT_IS_FLOAT:
7015 			/* True if no special format is expected or allowed  */
7016 			return (gmt_scanf_float (GMT, s, val));
7017 			break;
7018 
7019 	 	case GMT_IS_DIMENSION:
7020 			/* True if units might be appended, e.g. 8.4i  */
7021 			return (gmtio_scanf_dim (GMT, s, val));
7022 			break;
7023 
7024 	 	case GMT_IS_GEODIMENSION:
7025 			/* True if geo-distance units might be appended, e.g. 8.4d  */
7026 			return (gmtlib_scanf_geodim (GMT, s, val));
7027 			break;
7028 
7029 		case GMT_IS_RELTIME:
7030 			/* True if we expect to read a float with no special
7031 			   formatting (except for an optional trailing 't'), and then
7032 			   assume it is relative time in user's units since epoch.  */
7033 #if 0			/* PW: Never accept <number>t in data records.  This is only valid for gmt_scanf_arg */
7034 			callen = strlen (s) - 1;
7035 			if (s[callen] == 't') s[callen] = '\0';
7036 #endif
7037 			if ((gmt_scanf_float (GMT, s, val)) == GMT_IS_NAN) return (GMT_IS_NAN);
7038 			return (GMT_IS_ABSTIME);
7039 			break;
7040 
7041 	 	case GMT_IS_ABSTIME:
7042 			/* True when we expect to read calendar and/or
7043 			   clock strings in user-specified formats.  If both
7044 			   are present, they must be in the form
7045 			   <calendar_string>T<clock_string>.
7046 			   If only a calendar string is present, then either
7047 			   <calendar_string> or <calendar_string>T are valid.
7048 			   If only a clock string is present, then it must
7049 			   be preceded by a T:  T<clock_string>, and the time
7050 			   will be treated as if on day one of our calendar.  */
7051 			callen = strlen (s);
7052 			if (callen < 2) return (GMT_IS_NAN);	/* Maybe should be more than 2  */
7053 
7054 			if (s[callen-1] == 'T') {	/* Got <date>T with no <clock> */
7055 				clocklen = 0;
7056 				if (GMT->current.io.date_input.watch)	/* Watch for shit like 2013-23-OCT */
7057 					strncpy (calstring, s, callen);
7058 				else
7059 					strncpy (calstring, s, callen-1);
7060 			}
7061 			else if (s[0] == 'T') {	/* Got T<clock> presumably, with no <date> */
7062 				strncpy (clockstring, &s[1], GMT_LEN64-1);
7063 				clocklen = callen - 1;
7064 				callen = 0;
7065 			}
7066 			else if ((p = strrchr (s, 'T'))) {	/* There is a T in there (but could be stuff like 2012-OCT-20 with no trailing T) */
7067 				char *p2 = NULL;
7068 				/* Watch for shit like 2013-OCT-23 with no trailing T */
7069 				if (GMT->current.io.date_input.mw_text && GMT->current.io.date_input.delimiter[0][0] && (p2 = strrchr (s, GMT->current.io.date_input.delimiter[0][0])) > p) {
7070 					/* Got a delimiter after that T, so assume it is a T in a name instead */
7071 					strncpy (calstring, s, GMT_LEN64-1);
7072 					clocklen = 0;
7073 				}
7074 				else {	/* Regular <date>T<clock> */
7075 					*p = ' ';	/* Temporarily replace T with space */
7076 					sscanf (s, "%s %s", calstring, clockstring);
7077 					clocklen = strlen (clockstring);
7078 					callen = strlen (calstring);
7079 				}
7080 			}
7081 			else {	/* There is no trailing T.  Put all of s in calstring.  */
7082 				clocklen = 0;
7083 				strncpy (calstring, s, GMT_LEN64-1);
7084 			}
7085 			x = 0.0;	/* Default to 00:00:00 if no clock is given */
7086 			if (clocklen && gmtio_scanf_clock (GMT, clockstring, &x)) return (GMT_IS_NAN);
7087 			rd = GMT->current.time.today_rata_die;	/* Default to today if no date is given */
7088 			if (callen && gmtio_scanf_calendar (GMT, calstring, &rd)) return (GMT_IS_NAN);
7089 			*val = gmt_rdc2dt (GMT, rd, x);
7090 			if (GMT->current.setting.time_is_interval) {	/* Must truncate and center on time interval */
7091 				gmtlib_moment_interval (GMT, &GMT->current.time.truncate.T, *val, true);	/* Get the current interval */
7092 				if (GMT->current.time.truncate.direction) {	/* Actually need midpoint of previous interval... */
7093 					x = GMT->current.time.truncate.T.dt[0] - 0.5 * (GMT->current.time.truncate.T.dt[1] - GMT->current.time.truncate.T.dt[0]);
7094 					gmtlib_moment_interval (GMT, &GMT->current.time.truncate.T, x, true);	/* Get the current interval */
7095 				}
7096 				/* Now get half-point of interval */
7097 				*val = 0.5 * (GMT->current.time.truncate.T.dt[1] + GMT->current.time.truncate.T.dt[0]);
7098 			}
7099 			return (GMT_IS_ABSTIME);
7100 			break;
7101 
7102 	 	case GMT_IS_ARGTIME:
7103 			return (gmt_scanf_argtime (GMT, s, val));
7104 			break;
7105 
7106 		case  GMT_IS_UNKNOWN:
7107 			/* True if we don't know but must try both geographic or float formats  */
7108 			type = gmtio_scanf_geo (s, val);
7109 			if ((type == GMT_IS_LON) && GMT->current.io.warn_geo_as_cartesion) {
7110 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT: Longitude input data detected and successfully converted but will be considered Cartesian coordinates.\n");
7111 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT: If you need longitudes to be processed as periodic in 360 degrees then you must use -fg.\n");
7112 				GMT->current.io.warn_geo_as_cartesion = false;	/* OK, done with the warning */
7113 			}
7114 			return (type);
7115 			break;
7116 
7117 		default:
7118 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT_LOGIC_BUG: gmt_scanf() called with invalid expectation.\n");
7119 			return (GMT_IS_NAN);
7120 	}
7121 }
7122 
7123 /*! . */
gmtio_n_trailing_chars(struct GMT_CTRL * GMT,char * text)7124 GMT_LOCAL unsigned int gmtio_n_trailing_chars (struct GMT_CTRL *GMT, char *text) {
7125 	/* Try to determine if there are trailing text that is not a valid unit for a number.
7126 	 * We do not try to scan for leading chars since Jan-01-2001T might be caught. */
7127 	unsigned int n = 0, p = 0, n_periods = 0;	/* n is number of trailing characters after last digit (or period) */
7128 	int k, last;
7129 	gmt_M_unused (GMT);
7130 	if (!text || !text[0]) return 0;
7131 	k = last = (int)strlen(text) - 1;
7132 	while (k >= 0 && !isdigit (text[k])) {	/* Count how many non-digits at the end of this text */
7133 		if (text[k] == '.') p = k, n_periods++;	/* But be aware of a trailing single period in a number before unit (e.g., 30.k) */
7134 		k--, n++;	/* Count trailing letters */
7135 	}
7136 	if (n_periods == 1 && p && isdigit (text[p-1]) && n <= 2)	/* May be OK if a valid unit (or nothing) afterwards and a digit before */
7137 		n--;	/* Let the period be part of the number */
7138 	if (n == 1 && !strchr (GMT_LEN_UNITS GMT_DIM_UNITS GMT_WESN_UNITS, text[last]))	/* Single trailing text letter but not recognized as a unit */
7139 		n = 2;	/* To ensure it is seen as text */
7140 	return (n);
7141 }
7142 
7143 /*! . */
gmt_scanf_arg(struct GMT_CTRL * GMT,char * s,unsigned int expectation,bool cmd,double * val)7144 int gmt_scanf_arg (struct GMT_CTRL *GMT, char *s, unsigned int expectation, bool cmd, double *val) {
7145 	/* Version of gmt_scanf used for cpt & command line arguments only (not data records).
7146 	 * It differs from gmt_scanf in that if the expectation is GMT_IS_UNKNOWN it will
7147 	 * check to see if the argument is (1) an absolute time string, (2) a geographical
7148 	 * location string, or if not (3) a floating point string, etc.  We carefully check
7149 	 * to detect free-form strings which should result in a NaN.
7150 	 * When cmd is false then we disallow the check for trailing DdGg meaning geographic
7151 	 * since that is only valid on command line and in a data record the "d" mans degree.
7152 	 */
7153 
7154 	unsigned int got;
7155 
7156 	if (s == NULL) {	/* Cannot do anything here */
7157 		*val = GMT->session.d_NaN;
7158 		return GMT_IS_NAN;
7159 	}
7160 
7161 	if (expectation == GMT_IS_UNKNOWN) {	/* Expectation for this item was not set - must be determined if possible */
7162 		size_t len = strlen (s);
7163 		if (len == 0) {	/* Cannot do anything here */
7164 			*val = GMT->session.d_NaN;
7165 			return GMT_IS_NAN;
7166 		}
7167 		if (len > 1) {		/* Arguments of at least 2 characters can be many things */
7168 			char c = s[len-1];	/* Trailing letter */
7169 			unsigned int nt = (cmd) ? 0 : gmtio_n_trailing_chars (GMT, s);
7170 			if ((s[0] == 'T' && isdigit (s[1])) || strchr (s, 'T'))	/* Found a T in the argument - must be Absolute time or it will fail as junk */
7171 				expectation = GMT_IS_ARGTIME;
7172 			else if (gmtio_is_pi (s))	/* Found a "pi" specification - will try scanning as float */
7173 				expectation = GMT_IS_FLOAT;
7174 			else if (c == 't')		/* Found trailing t - assume Relative time */
7175 				expectation = GMT_IS_ARGTIME;
7176 			else if (nt > 1 || gmt_not_numeric (GMT, s)) {	/* No number has 2 or more letters at the end, or other junk, so return as NaN */
7177 				*val = GMT->session.d_NaN;
7178 				return GMT_IS_NAN;
7179 			}
7180 			else if (!((s[0] >= 0 && isdigit (s[0])) || s[0] == '-' || s[0] == '+' || s[0] == '.')) {	/* All other numbers must be [-|+][<num>[.]<num>][<end>] */
7181 				*val = GMT->session.d_NaN;
7182 				return GMT_IS_NAN;	/* Cannot be a number so return as NaN */
7183 			}
7184 			else if (strchr ("WE", c))		/* Found trailing W or E - assume Geographic longitudes */
7185 				expectation = GMT_IS_LON;
7186 			else if (strchr ("SN", c))		/* Found trailing S or N - assume Geographic latitudes */
7187 				expectation = GMT_IS_LAT;
7188 			else if (cmd && strchr ("DdGg", c))	/* Found trailing G or D - assume Geographic coordinate */
7189 				expectation = GMT_IS_GEO;
7190 			else if (strchr (s, ':'))		/* Found a : in the argument - assume Geographic coordinates */
7191 				expectation = GMT_IS_GEO;
7192 			else if (strchr (GMT_DIM_UNITS, c))	/* Found a trailing dimension unit (c|i|p) */
7193 				expectation = GMT_IS_DIMENSION;
7194 			else if (strchr (GMT_LEN_UNITS, c))	/* Found a trailing geo-length unit (d|m|s|e|f|k|M|n|u) */
7195 				expectation = GMT_IS_GEODIMENSION;
7196 			else 					/* Found nothing - assume floating point */
7197 				expectation = GMT_IS_FLOAT;
7198 		}
7199 		else if (isdigit (s[0]))				/* Found a 1-char digit */
7200 			expectation = GMT_IS_FLOAT;
7201 		else {	/* Must be a single letter not digit */
7202 			*val = GMT->session.d_NaN;
7203 			return GMT_IS_NAN;	/* Cannot be a number so return as NaN */
7204 		}
7205 	}
7206 
7207 	/* OK, here we have an expectation, now call gmt_scanf */
7208 
7209 	got = gmt_scanf (GMT, s, expectation, val);
7210 	return (got == GMT_IS_NAN) ? got : expectation;	/* Want to return the actual expectation here since it may be used upstream, unless parsing failed */
7211 }
7212 
7213 /*! . */
gmt_set_seg_minmax(struct GMT_CTRL * GMT,unsigned int geometry,unsigned int n_cols,struct GMT_DATASEGMENT * S)7214 void gmt_set_seg_minmax (struct GMT_CTRL *GMT, unsigned int geometry, unsigned int n_cols, struct GMT_DATASEGMENT *S) {
7215 	/* Determine the min/max values for each column in the segment.
7216 	 * If n_cols > 0 then we only update the first n_cols */
7217 	uint64_t row, col;
7218 
7219 	/* In case the creation of the segment did not allocate min/max do it now */
7220 	if (!S->min) S->min = gmt_M_memory (GMT, NULL, S->n_columns, double);
7221 	if (!S->max) S->max = gmt_M_memory (GMT, NULL, S->n_columns, double);
7222 	if (S->n_rows == 0) return;	/* Nothing more we can do */
7223 	if (n_cols == 0) n_cols = (unsigned int)S->n_columns;	/* Set number of columns to work on */
7224 	for (col = 0; col < n_cols; col++) {
7225 		if (gmt_M_type (GMT, GMT_IN, col) == GMT_IS_LON) /* Requires separate quandrant assessment */
7226 			gmtlib_get_lon_minmax (GMT, S->data[col], S->n_rows, &(S->min[col]), &(S->max[col]));
7227 		else {	/* Simple Cartesian-like arrangement */
7228 			S->min[col] = S->max[col] = S->data[col][0];
7229 			for (row = 1; row < S->n_rows; row++) {
7230 				if (S->data[col][row] < S->min[col]) S->min[col] = S->data[col][row];
7231 				if (S->data[col][row] > S->max[col]) S->max[col] = S->data[col][row];
7232 			}
7233 		}
7234 	}
7235 	if (geometry & GMT_IS_POLY)
7236 		gmt_set_seg_polar (GMT, S);
7237 }
7238 
7239 /*! . */
gmt_set_tbl_minmax(struct GMT_CTRL * GMT,unsigned int geometry,struct GMT_DATATABLE * T)7240 void gmt_set_tbl_minmax (struct GMT_CTRL *GMT, unsigned int geometry, struct GMT_DATATABLE *T) {
7241 	/* Update the min/max of all segments and the entire table */
7242 	uint64_t seg, col;
7243 	struct GMT_DATASEGMENT *S = NULL;
7244 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
7245 
7246 	if (!T) return;	/* No table given */
7247 	if (!T->n_columns) return;	/* No columns given */
7248 	if (!T->min) T->min = gmt_M_memory (GMT, NULL, T->n_columns, double);
7249 	if (!T->max) T->max = gmt_M_memory (GMT, NULL, T->n_columns, double);
7250 	for (col = 0; col < T->n_columns; col++) {	/* Initialize */
7251 		T->min[col] = DBL_MAX;
7252 		T->max[col] = -DBL_MAX;
7253 	}
7254 	T->n_records = 0;
7255 	for (seg = 0; seg < T->n_segments; seg++) {
7256 		S = T->segment[seg];
7257 		SH = gmt_get_DS_hidden (S);
7258 		if (gmt_parse_segment_item (GMT, S->header, "-Ph", NULL))
7259 			SH->pol_mode = GMT_IS_HOLE;
7260 		/* If this is a hole then set link from previous segment to this one */
7261 		if (seg && gmt_polygon_is_hole (GMT, S)) {
7262 			struct GMT_DATASEGMENT_HIDDEN *SH2 = gmt_get_DS_hidden (T->segment[seg-1]);
7263 			SH2->next = S;
7264 		}
7265 		gmt_set_seg_minmax (GMT, geometry, 0, S);
7266 		if (S->n_rows == 0) continue;
7267 		for (col = 0; col < T->n_columns; col++) {
7268 			if (S->min[col] < T->min[col]) T->min[col] = S->min[col];
7269 			if (S->max[col] > T->max[col]) T->max[col] = S->max[col];
7270 		}
7271 		T->n_records += S->n_rows;
7272 	}
7273 }
7274 
7275 /*! . */
gmt_set_dataset_minmax(struct GMT_CTRL * GMT,struct GMT_DATASET * D)7276 void gmt_set_dataset_minmax (struct GMT_CTRL *GMT, struct GMT_DATASET *D) {
7277 	uint64_t tbl, col;
7278 	struct GMT_DATATABLE *T = NULL;
7279 	if (!D) return;	/* No dataset given */
7280 	if (!D->n_columns) return;	/* No columns given */
7281 	if (!D->min) D->min = gmt_M_memory (GMT, NULL, D->n_columns, double);
7282 	if (!D->max) D->max = gmt_M_memory (GMT, NULL, D->n_columns, double);
7283 	for (col = 0; col < D->n_columns; col++) {	/* Initialize */
7284 		D->min[col] = DBL_MAX;
7285 		D->max[col] = -DBL_MAX;
7286 	}
7287 	D->n_records = D->n_segments = 0;
7288 	for (tbl = 0; tbl < D->n_tables; tbl++) {
7289 		T = D->table[tbl];
7290 		gmt_set_tbl_minmax (GMT, D->geometry, T);
7291 		for (col = 0; col < D->n_columns; col++) {
7292 			if (T->min[col] < D->min[col]) D->min[col] = T->min[col];
7293 			if (T->max[col] > D->max[col]) D->max[col] = T->max[col];
7294 		}
7295 		D->n_records += T->n_records;
7296 		D->n_segments += T->n_segments;
7297 	}
7298 }
7299 
7300 /*! . */
gmt_parse_segment_header(struct GMT_CTRL * GMT,char * header,struct GMT_PALETTE * P,bool * use_fill,struct GMT_FILL * fill,struct GMT_FILL * def_fill,bool * use_pen,struct GMT_PEN * pen,struct GMT_PEN * def_pen,unsigned int def_outline,struct GMT_OGR_SEG * G)7301 int gmt_parse_segment_header (struct GMT_CTRL *GMT, char *header, struct GMT_PALETTE *P, bool *use_fill, struct GMT_FILL *fill, struct GMT_FILL *def_fill,  bool *use_pen, struct GMT_PEN *pen, struct GMT_PEN *def_pen, unsigned int def_outline, struct GMT_OGR_SEG *G) {
7302 	/* Scan header for occurrences of valid GMT options.
7303 	 * The possibilities are:
7304 	 * Fill: -G<fill>	Use the new fill and turn filling ON
7305 	 *	 -G-		Turn filling OFF
7306 	 *	 -G		Revert to default fill [none if not set on command line]
7307 	 * Pens: -W<pen>	Use the new pen and turn outline ON
7308 	 *	 -W-		Turn outline OFF
7309 	 *	 -W		Revert to default pen [current.map_default_pen if not set on command line]
7310 	 * z:	-Z<zval>	Obtain fill via cpt lookup using this z value
7311 	 *	-ZNaN		Get the NaN color from the CPT
7312 	 *	-t<transp>		Apply this transparency to both fill and pen
7313 	 *
7314 	 * header is the text string to process
7315 	 * P is the color palette used for the -Z option
7316 	 * use_fill is set to true, false or left alone if no change
7317 	 * fill is the fill structure to use after this function returns
7318 	 * def_fill holds the default fill (if any) to use if -G is found
7319 	 * use_pen is set to true, false or left alone if no change
7320 	 * pen is the pen structure to use after this function returns
7321 	 * def_pen holds the default pen (if any) to use if -W is found
7322 	 * def_outline holds the default outline setting (true/false)
7323 	 *
7324 	 * The function returns the sum of the following return codes:
7325 	 * 0 = No fill, no outline
7326 	 * 1 = Revert to default fill or successfully parsed a -G<fill> option
7327 	 * 2 = Encountered a -Z option to set the fill based on a CPT
7328 	 * 4 = Encountered a -W<pen> option
7329 	 * For OGR/GMT files similar parsing occurs in that aspatial values assigned to the magic
7330 	 * columns D, G, L, W, Z are used in the same fashion.
7331 	 */
7332 
7333 	unsigned int processed = 0, change = 0, col, ogr_col;
7334 	bool got_transparency = false;
7335 	char line[GMT_BUFSIZ] = {""}, *txt = NULL;
7336 	double z, transparency = 0.0;
7337 	struct GMT_FILL test_fill;
7338 	struct GMT_PEN test_pen;
7339 
7340 	if (GMT->common.a.active) {	/* Use aspatial data instead */
7341 #if 0 /* Just for testing OGR stuff */
7342 		for (col = 0; col < GMT->current.io.OGR->n_aspatial; col++)
7343 			fprintf (stderr, "OGR %d: N = %s T = %d\n", col, GMT->current.io.OGR->name[col], GMT->current.io.OGR->type[col]);
7344 		for (col = 0; col < GMT->common.a.n_aspatial; col++)
7345 			fprintf (stderr, "a: %d: C = %d O = %d, N = %s\n",
7346 			         col, GMT->common.a.col[col], GMT->common.a.ogr[col], GMT->common.a.name[col]);
7347 		for (col = 0; col < G->n_aspatial; col++)
7348 			fprintf (stderr, "G: %d: V = %s\n", col, G->tvalue[col]);
7349 #endif
7350 		for (col = 0; col < GMT->common.a.n_aspatial; col++) {
7351 			if (GMT->common.a.col[col] >= 0) continue;	/* Skip regular data column fillers */
7352 			if (!G && !GMT->current.io.OGR->tvalue) continue;	/* Nothing set yet */
7353 			if (!G && !GMT->current.io.OGR->dvalue) continue;	/* Nothing set yet */
7354 			ogr_col = GMT->common.a.ogr[col];
7355 			txt = (G) ? G->tvalue[ogr_col] : GMT->current.io.OGR->tvalue[col];
7356 			z = (G) ? G->dvalue[ogr_col] : GMT->current.io.OGR->dvalue[col];
7357 			switch (GMT->common.a.col[col]) {
7358 				case GMT_IS_G:
7359 					if (gmt_getfill (GMT, txt, fill)) {
7360 						*use_fill = true;
7361 						change = 1;
7362 						processed++;	/* Processed one option */
7363 					}
7364 					break;
7365 				case GMT_IS_W:
7366 					if (gmt_getpen (GMT, txt, pen)) {
7367 						*use_pen = true;
7368 						change |= 4;
7369 					}
7370 					break;
7371 				case GMT_IS_Z:
7372 					if (P->categorical & GMT_CPT_CATEGORICAL_KEY)
7373 						gmt_get_fill_from_key (GMT, P, txt, fill);
7374 					else
7375 						gmt_get_fill_from_z (GMT, P, z, fill);
7376 					*use_fill = true;
7377 					change |= 2;
7378 					processed++;	/* Processed one option */
7379 					break;
7380 			}
7381 		}
7382 		return (change);
7383 	}
7384 
7385 	/* Standard GMT multisegment parsing */
7386 
7387 	if (!header || !header[0]) return (0);
7388 
7389 	if (gmt_parse_segment_item (GMT, header, "-t", line)) {	/* Found a potential -t option for transparency */
7390 		transparency = atof (line) * 0.01;
7391 		got_transparency = true;	/* Must apply to any fill and pen settings */
7392 	}
7393 	if (gmt_parse_segment_item (GMT, header, "-G", line)) {	/* Found a potential -G option */
7394 		test_fill = *def_fill;
7395 		if (line[0] == '-') {	/* Turn fill OFF */
7396 			fill->rgb[0] = fill->rgb[1] = fill->rgb[2] = -1.0, fill->use_pattern = false;
7397 			*use_fill = false;
7398 			processed++;	/* Processed one option */
7399 		}
7400 		else if (!line[0] || line[0] == '+') {	/* Revert to default fill */
7401 			*fill = *def_fill;
7402 			*use_fill = (def_fill->use_pattern || def_fill->rgb[0] != -1.0);
7403 			if (*use_fill) change = 1;
7404 			processed++;	/* Processed one option */
7405 		}
7406 		else if (!gmt_getfill (GMT, line, &test_fill)) {	/* Successfully processed a -G<fill> option */
7407 			*fill = test_fill;
7408 			if (got_transparency) fill->rgb[3] = transparency;
7409 			*use_fill = true;
7410 			change = 1;
7411 			processed++;	/* Processed one option */
7412 		}
7413 		/* Failure is OK since -Gjunk may appear in text strings - we then do nothing (hence no else clause) */
7414 	}
7415 	if (P && gmt_parse_segment_item (GMT, header, "-Z", line)) {	/* Found a potential -Z option to set symbol r/g/b via cpt-lookup */
7416 		if(!strncmp (line, "NaN", 3U))	{	/* Got -ZNaN */
7417 			gmt_get_fill_from_z (GMT, P, GMT->session.d_NaN, fill);
7418 			if (got_transparency) fill->rgb[3] = transparency;
7419 			*use_fill = true;
7420 			change |= 2;
7421 			processed++;	/* Processed one option */
7422 		}
7423 		else {
7424 			if (P->categorical & GMT_CPT_CATEGORICAL_KEY)
7425 				gmt_get_fill_from_key (GMT, P, line, fill);
7426 			else if (sscanf (line, "%lg", &z) == 1)
7427 				gmt_get_fill_from_z (GMT, P, z, fill);
7428 			if (got_transparency) fill->rgb[3] = transparency;
7429 			*use_fill = true;
7430 			change |= 2;
7431 			processed++;	/* Processed one option */
7432 		}
7433 		/* Failure is OK since -Zjunk may appear in text strings - we then do nothing (hence no else clause) */
7434 	}
7435 
7436 	if (processed == 2) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Segment header has both -G and -Z options\n");	/* Giving both -G and -Z is a problem */
7437 
7438 	if (gmt_parse_segment_item (GMT, header, "-W", line)) {	/* Found a potential -W option */
7439 		test_pen = *def_pen;	/* Set test pen to the default, may be overruled later */
7440 		if (line[0] == '-') {	/* Turn outline OFF */
7441 			*pen = *def_pen;	/* Set pen to default */
7442 			*use_pen = false;
7443 		}
7444 		else if (!line[0] || line[0] == '+') {	/* Revert to default pen/outline */
7445 			*pen = *def_pen;	/* Set pen to default */
7446 			*use_pen = def_outline;
7447 			if (def_outline) change |= 4;
7448 		}
7449 		else if (!gmt_getpen (GMT, line, &test_pen)) {
7450 			*pen = test_pen;
7451 			if (got_transparency) pen->rgb[3] = transparency;
7452 			*use_pen = true;
7453 			change |= 4;
7454 		}
7455 		/* Failure is OK since -W may appear in text strings (hence no else clause) */
7456 	}
7457 	return (change);
7458 }
7459 
7460 /*! . */
gmt_extract_label(struct GMT_CTRL * GMT,char * line,char * label,struct GMT_OGR_SEG * G)7461 void gmt_extract_label (struct GMT_CTRL *GMT, char *line, char *label, struct GMT_OGR_SEG *G) {
7462 	/* Pull out the label in a -L<label> option in a segment header w./w.o. quotes.
7463  	 * If G is not NULL we use it (OGR stuff) instead. */
7464 	bool done;
7465 	unsigned int i = 0, k, j, j0;
7466 	char *p = NULL, q[2] = {'\"', '\''};
7467 
7468 	if (G && G->tvalue && G->tvalue[0]) { strcpy (label, G->tvalue[0]); return ;}	/* Had an OGR segment label */
7469 	if (gmt_parse_segment_item (GMT, line, "-L", label)) return;	/* Found -L */
7470 
7471 	label[0] = '\0';	/* Remove previous label */
7472 	if (!line || !line[0]) return;	/* Line is empty */
7473 	while (line[i] && (line[i] == ' ' || line[i] == '\t')) i++;	/* Bypass whitespace */
7474 
7475 	for (k = 0, done = false; k < 2; k++) {
7476 		if ((p = strchr (&line[i], q[k]))) {	/* Gave several double/single-quoted words as label */
7477 			for (j0 = j = i + 1; line[j] != q[k]; j++);
7478 			if (line[j] == q[k]) {	/* Found the matching quote */
7479 				strncpy (label, &line[j0], j-j0);
7480 				label[j-j0] = '\0';
7481 				done = true;
7482 			}
7483 			else {			/* Missing the matching quote */
7484 				sscanf (&line[i], "%s", label);
7485 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Label (%s) not terminated by matching quote\n", label);
7486 			}
7487 		}
7488 	}
7489 	if (!done) sscanf (&line[i], "%s", label);
7490 }
7491 
7492 /*! . */
gmt_parse_segment_item(struct GMT_CTRL * GMT,char * in_string,char * pattern,char * out_string)7493 bool gmt_parse_segment_item (struct GMT_CTRL *GMT, char *in_string, char *pattern, char *out_string) {
7494 	/* Scans the in_string for the occurrence of an option switch (e.g, -L) and
7495 	 * if found, extracts the argument and returns it via out_string.  Function
7496 	 * return true if the pattern was found and false otherwise.
7497 	 * out_string must be allocated and have space for the copying */
7498 	char *t = NULL;
7499 	size_t k;
7500 	gmt_M_unused(GMT);
7501 	if (!in_string || !pattern) return (false);	/* No string or pattern passed */
7502 	if (!(t = strstr (in_string, pattern))) return (false);	/* Option not present */
7503 	if (!out_string) return (true);	/* If NULL is passed as out_string then we just return true if we find the option */
7504 	out_string[0] = '\0';	/* Reset string to empty before we try to set it below */
7505 	k = (size_t)t - (size_t)in_string; /* Position of pattern in in_string */
7506 	if (k && !(in_string[k-1] == ' ' || in_string[k-1] == '\t')) return (false);	/* Option not first or preceded by whitespace */
7507 	t += 2;	/* Position of the argument */
7508 	if (t[0] == '\"')	/* Double quoted argument, must scan from next character until terminal quote */
7509 		sscanf (++t, "%[^\"]", out_string);
7510 	else if (t[0] == '\'')	/* Single quoted argument, must scan from next character until terminal quote */
7511 		sscanf (++t, "%[^\']", out_string);
7512 	else	/* Scan until next white space; stop also when there is leading white space, indicating no argument at all! */
7513 		sscanf (t, "%[^ \t]", out_string);
7514 	return (true);
7515 }
7516 
7517 /*! . */
gmt_duplicate_ogr_seg(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S_to,struct GMT_DATASEGMENT * S_from)7518 void gmt_duplicate_ogr_seg (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S_to, struct GMT_DATASEGMENT *S_from) {
7519 	/* Allocates the OGR structure for a given segment and copies current values from table OGR segment */
7520 	unsigned int col;
7521 	struct GMT_DATASEGMENT_HIDDEN *SH_to = gmt_get_DS_hidden (S_to), *SH_from = gmt_get_DS_hidden (S_from);
7522 
7523 	if (!SH_from->ogr) return;	/* No data */
7524 	gmtio_alloc_ogr_seg (GMT, S_to, SH_from->ogr->n_aspatial);
7525 	for (col = 0; col < SH_from->ogr->n_aspatial; col++) {
7526 		if (SH_from->ogr->tvalue[col]) SH_to->ogr->tvalue[col] = strdup (SH_from->ogr->tvalue[col]);
7527 		SH_to->ogr->dvalue[col] = SH_from->ogr->dvalue[col];
7528 	}
7529 	SH_to->ogr->pol_mode = SH_from->ogr->pol_mode;
7530 }
7531 
7532 /*! . */
gmtlib_write_newheaders(struct GMT_CTRL * GMT,FILE * fp,uint64_t n_cols)7533 void gmtlib_write_newheaders (struct GMT_CTRL *GMT, FILE *fp, uint64_t n_cols) {
7534 	/* Common ASCII header records added on output */
7535 	if (GMT->common.b.active[GMT_OUT]) return;		/* No output headers for binary files */
7536 	if (!GMT->current.setting.io_header[GMT_OUT]) return;	/* No output headers requested, so don't bother */
7537 	if (GMT->common.h.title) {	/* Optional title(s) provided; could be several lines separated by \n */
7538 		gmtio_write_multilines (GMT, fp, GMT->common.h.title, "Title");
7539 	}
7540 
7541 	if (GMT->common.h.multi_segment) {	/* A multi-segment record */
7542 		gmtlib_write_tableheader (GMT, fp, gmtlib_create_header_item (GMT->parent, GMT_COMMENT_IS_MULTISEG, GMT->common.h.multi_segment));
7543 		return;
7544 	}
7545 
7546 	/* Always write command line */
7547 	gmtlib_write_tableheader (GMT, fp, gmtlib_create_header_item (GMT->parent, GMT_COMMENT_IS_COMMAND | GMT_COMMENT_IS_OPTION, GMT->current.options));
7548 	if (GMT->common.h.remark) {	/* Optional remark(s) provided; could be several lines separated by \n */
7549 		gmtio_write_multilines (GMT, fp, GMT->common.h.remark, "Remark");
7550 	}
7551 	if (GMT->common.h.add_colnames) {	/* Want output comment with column names */
7552 		if (GMT->common.h.colnames)	/* Optional column names already provided */
7553 			gmtlib_write_tableheader (GMT, fp, GMT->common.h.colnames);
7554 		else if (n_cols) {	/* Generate names col1[0], col2[1] etc */
7555 			uint64_t col, first = 1;
7556 			char record[GMT_BUFSIZ] = {""}, txt[GMT_LEN64] = {""};
7557 			if (n_cols >= 2) {	/* Place x and y first */
7558 				gmt_set_xycolnames (GMT, record);
7559 				first++;
7560 			}
7561 			else
7562 				sprintf (record, "col1[0]");
7563 			for (col = first; col < n_cols; col++) {
7564 				snprintf (txt, GMT_LEN64, "\tcol%" PRIu64 "[%" PRIu64 "]", col+1, col);
7565 				strcat (record, txt);
7566 			}
7567 			gmtlib_write_tableheader (GMT, fp, record);
7568 		}
7569 	}
7570 }
7571 
7572 /*! . */
gmt_set_xycolnames(struct GMT_CTRL * GMT,char * string)7573 void gmt_set_xycolnames (struct GMT_CTRL *GMT, char *string) {
7574 	char *xy[2][2] = {{"x", "y"}, {"lon", "lat"}};
7575 	unsigned int mode = (gmt_M_is_geographic (GMT, GMT_OUT)) ? 1 : 0;
7576 	unsigned int ix = (GMT->current.setting.io_lonlat_toggle[GMT_OUT]) ? 1 : 0, iy;
7577 	iy = 1 - ix;
7578 	sprintf (string, "%s[0]\t%s[1]", xy[mode][ix], xy[mode][iy]);
7579 }
7580 
gmtio_finalize_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)7581 GMT_LOCAL void gmtio_finalize_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
7582 	/* Reallocate arrays to fit current segment size */
7583 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
7584 	if (S->n_rows > SH->n_alloc) {
7585 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtio_finalize_segment: Internal error: S->n_rows > SH->n_alloc!!!");
7586 		return;
7587 	}
7588 	if (S->n_rows < SH->n_alloc) {
7589 		uint64_t col;
7590 		for (col = 0; col < S->n_columns; col++)
7591 			S->data[col] = gmt_M_memory (GMT, S->data[col], S->n_rows, double);
7592 		if (S->text) S->text = gmt_M_memory (GMT, S->text, S->n_rows, char *);
7593 		SH->n_alloc = S->n_rows;
7594 	}
7595 }
7596 
gmtio_finalize_table(struct GMT_CTRL * GMT,struct GMT_DATATABLE * T)7597 GMT_LOCAL void gmtio_finalize_table (struct GMT_CTRL *GMT, struct GMT_DATATABLE *T) {
7598 	/* Reallocate size of segments to fit current counts */
7599 	uint64_t seg;
7600 	struct GMT_DATATABLE_HIDDEN *TH = gmt_get_DT_hidden (T);
7601 	if (T->n_segments > TH->n_alloc) {
7602 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtio_finalize_table: Internal error: T->n_segments > TH->n_alloc!!!");
7603 		return;
7604 	}
7605 	if (T->n_segments < TH->n_alloc) {
7606 		for (seg = T->n_segments; seg < TH->n_alloc; seg++)
7607 			gmt_free_segment (GMT, &(T->segment[seg]));
7608 		T->segment = gmt_M_memory (GMT, T->segment, T->n_segments, struct GMT_DATASEGMENT *);
7609 		TH->n_alloc = T->n_segments;
7610 	}
7611 	for (seg = 0; seg < T->n_segments; seg++) gmtio_finalize_segment (GMT, T->segment[seg]);
7612 }
7613 
gmtlib_finalize_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * D)7614 void gmtlib_finalize_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *D) {
7615 	/* Reallocate size of items to fit current counts */
7616 	uint64_t tbl;
7617 	struct GMT_DATASET_HIDDEN *DH = gmt_get_DD_hidden (D);
7618 	if (D->n_tables > DH->n_alloc) {
7619 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtlib_finalize_dataset: Internal error: D->n_tables > DH->alloc!!!");
7620 		return;
7621 	}
7622 	if (D->n_tables < DH->n_alloc) {
7623 		D->table = gmt_M_memory (GMT, D->table, D->n_tables, struct GMT_DATATABLE *);
7624 		DH->n_alloc = D->n_tables;
7625 	}
7626 	for (tbl = 0; tbl < D->n_tables; tbl++) gmtio_finalize_table (GMT, D->table[tbl]);
7627 }
7628 
7629 /*! . */
gmt_adjust_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * D,uint64_t n_columns)7630 void gmt_adjust_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *D, uint64_t n_columns) {
7631 	/* Adjust existing data set structure to have n_columns instead.  This may
7632 	 * involve shrinking (deallocation) or growing (allocation) of columns.
7633 	 */
7634 	uint64_t tbl;
7635 
7636 	for (tbl = 0; tbl < D->n_tables; tbl++) gmtio_adjust_table (GMT, D->table[tbl], n_columns);
7637 	D->n_columns = n_columns;
7638 }
7639 
7640 /*! . */
gmt_alloc_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,uint64_t n_rows,uint64_t n_columns,unsigned int mode,bool first)7641 int gmt_alloc_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, uint64_t n_rows, uint64_t n_columns, unsigned int mode, bool first) {
7642 	/* (re)allocates memory for a segment of given dimensions.
7643 	 * If first is true then the segment arrays for data/text are allocated and min/max arrays set
7644 	 */
7645 	uint64_t col;
7646 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
7647 	if (first && n_columns) {	/* First time we allocate the number of columns needed */
7648 		S->data = gmt_M_memory (GMT, S->data, n_columns, double *);
7649 		S->min = gmt_M_memory (GMT, S->min, n_columns, double);
7650 		S->max = gmt_M_memory (GMT, S->max, n_columns, double);
7651 		for (col = 0; col < n_columns; col++) {	/* Initialize the min/max array */
7652 			S->min[col] = +DBL_MAX;
7653 			S->max[col] = -DBL_MAX;
7654 		}
7655 		S->n_columns = n_columns;
7656 		SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, n_columns, enum GMT_enum_alloc);
7657 	}
7658 	if (!first && S->n_columns != n_columns) {	/* Error */
7659 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_alloc_segment: Cannot reallocate the number of columns in an existing segment");
7660 		return 1;
7661 	}
7662 	S->n_rows  = n_rows;
7663 	if (n_rows) {	/* Allocate or reallocate column arrays */
7664 		for (col = 0; col < n_columns; col++) {
7665 			if ((S->data[col] = gmt_M_memory (GMT, S->data[col], n_rows, double)) == NULL) {
7666 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_alloc_segment: Unable to reallocate data column %" PRIu64 " to new length %" PRIu64 "\n", col, n_rows);
7667 				return 1;
7668 			}
7669 			SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
7670 		}
7671 		if (mode & GMT_WITH_STRINGS) {	/* Also allocate the string pointers */
7672 			if ((S->text = gmt_M_memory (GMT, S->text, n_rows, char *)) == NULL) {
7673 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_alloc_segment: Unable to reallocate string array new length %" PRIu64 "\n", n_rows);
7674 				return 1;
7675 			}
7676 		}
7677 		SH->n_alloc = n_rows;
7678 	}
7679 	return (GMT_OK);
7680 }
7681 
7682 /*! . */
gmtlib_assign_segment(struct GMT_CTRL * GMT,unsigned int direction,struct GMT_DATASEGMENT * S,uint64_t n_rows,uint64_t n_columns)7683 void gmtlib_assign_segment (struct GMT_CTRL *GMT, unsigned int direction, struct GMT_DATASEGMENT *S, uint64_t n_rows, uint64_t n_columns) {
7684 	/* Allocates and memcpy over vectors from GMT->hidden.mem_coord.
7685   	 * If n_rows > GMT_INITIAL_MEM_ROW_ALLOC then we pass the arrays and reset the tmp arrays to NULL
7686 	 */
7687 	uint64_t col;
7688 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
7689 	if (n_rows == 0) return;	/* Nothing to do */
7690 	/* First allocate struct member arrays */
7691 	S->data = gmt_M_memory (GMT, S->data, n_columns, double *);
7692 	S->min  = gmt_M_memory (GMT, S->min, n_columns, double);
7693 	S->max  = gmt_M_memory (GMT, S->max, n_columns, double);
7694 	SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, n_columns, enum GMT_enum_alloc);
7695 
7696 	if (n_rows > GMT_INITIAL_MEM_ROW_ALLOC) {	/* Large segment, just pass allocated pointers and start over with new tmp vectors later */
7697 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtlib_assign_segment: Pass %" PRIu64 " large arrays with length = %"
7698 		            PRIu64 " off and get new tmp arrays\n", n_columns, n_rows);
7699 		for (col = 0; col < n_columns; col++) {	/* Initialize the min/max array */
7700 			if (n_rows < GMT->hidden.mem_rows)
7701 				GMT->hidden.mem_coord[col] = gmt_M_memory (GMT, GMT->hidden.mem_coord[col], n_rows, double);	/* Trim back */
7702 			S->data[col] = GMT->hidden.mem_coord[col];	/* Pass the pointer */
7703 			SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
7704 			GMT->hidden.mem_coord[col] = NULL;		/* Null this out to start over for next segment */
7705 		}
7706 		if (GMT->current.io.record_type[direction] & GMT_READ_TEXT) {
7707 			if (n_rows < GMT->hidden.mem_rows)
7708 				GMT->hidden.mem_txt = gmt_M_memory (GMT, GMT->hidden.mem_txt, n_rows, char *);	/* Trim back */
7709 			S->text = GMT->hidden.mem_txt;	/* Pass the pointer */
7710 			GMT->hidden.mem_txt = NULL;		/* Null this out to start over for next segment */
7711 		}
7712 		GMT->hidden.mem_cols = 0;	/* Flag that we need to reallocate new temp arrays for next segment, if any */
7713 	}
7714 	else {	/* Small segments, allocate and memcpy, leave tmp array as is for further use */
7715 		for (col = 0; col < n_columns; col++) {	/* Initialize the min/max array */
7716 			S->data[col] = gmt_M_memory (GMT, S->data[col], n_rows, double);
7717 			gmt_M_memcpy (S->data[col], GMT->hidden.mem_coord[col], n_rows, double);
7718 			SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
7719 		}
7720 		if (GMT->current.io.record_type[direction] & GMT_READ_TEXT) {
7721 			uint64_t row;
7722 			S->text = gmt_M_memory (GMT, S->text, n_rows, char *);
7723 			for (row = 0; row < n_rows; row++) {
7724 				S->text[row] = GMT->hidden.mem_txt[row];
7725 				GMT->hidden.mem_txt[row] = NULL;
7726 			}
7727 		}
7728 	}
7729 	S->n_rows = n_rows;
7730 	S->n_columns = n_columns;
7731 }
7732 
7733 /*! . */
gmtlib_assign_vector(struct GMT_CTRL * GMT,uint64_t n_rows,uint64_t col)7734 double *gmtlib_assign_vector (struct GMT_CTRL *GMT, uint64_t n_rows, uint64_t col) {
7735 	/* Allocates and memcpy over vectors from GMT->hidden.mem_coord.
7736   	 * If n_rows > GMT_INITIAL_MEM_ROW_ALLOC then we pass the arrays and reset the tmp arrays to NULL.
7737 	 */
7738 	double *vector = NULL;
7739 	if (n_rows == 0) return NULL;	/* Nothing to do */
7740 
7741 	if (n_rows > GMT_INITIAL_MEM_ROW_ALLOC) {	/* Large segment, just pass allocated pointers and start over with new tmp vectors later */
7742 		if (n_rows < GMT->hidden.mem_rows)
7743 			GMT->hidden.mem_coord[col] = gmt_M_memory (GMT, GMT->hidden.mem_coord[col], n_rows, double);	/* Trim back */
7744 		vector = GMT->hidden.mem_coord[col];	/* Pass the pointer */
7745 		GMT->hidden.mem_coord[col] = NULL;	/* Null this out to start over for next segment */
7746 		GMT->hidden.mem_cols = 0;	/* Flag that we need to reallocate new temp arrays for next segment, if any */
7747 	}
7748 	else {	/* Small segments, allocate and memcpy, leave tmp array as is for further use */
7749 		vector = gmt_M_memory (GMT, NULL, n_rows, double);
7750 		gmt_M_memcpy (vector, GMT->hidden.mem_coord[col], n_rows, double);
7751 	}
7752 	return (vector);
7753 }
7754 
gmt_get_dataset(struct GMT_CTRL * GMT)7755 struct GMT_DATASET * gmt_get_dataset (struct GMT_CTRL *GMT) {
7756 	struct GMT_DATASET *D = NULL;
7757 	struct GMT_DATASET_HIDDEN *DH = NULL;
7758 	D = gmt_M_memory (GMT, NULL, 1, struct GMT_DATASET);
7759 	D->hidden = DH = gmt_M_memory (GMT, NULL, 1, struct GMT_DATASET_HIDDEN);
7760 	DH->alloc_mode = GMT_ALLOC_INTERNALLY;	/* So GMT_* modules can free this memory */
7761 	DH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level */
7762 	return (D);
7763 }
7764 
gmt_get_table(struct GMT_CTRL * GMT)7765 struct GMT_DATATABLE * gmt_get_table (struct GMT_CTRL *GMT) {
7766 	struct GMT_DATATABLE *T = NULL;
7767 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
7768 	T = gmt_M_memory (GMT, NULL, 1, struct GMT_DATATABLE);
7769 	T->hidden = TH = gmt_M_memory (GMT, NULL, 1, struct GMT_DATATABLE_HIDDEN);
7770 	TH->alloc_mode = GMT_ALLOC_INTERNALLY;	/* So GMT_* modules can free this memory */
7771 	TH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level */
7772 	return (T);
7773 }
7774 
gmt_get_segment(struct GMT_CTRL * GMT,uint64_t n_columns)7775 struct GMT_DATASEGMENT * gmt_get_segment (struct GMT_CTRL *GMT, uint64_t n_columns) {
7776 	uint64_t col;
7777 	struct GMT_DATASEGMENT *S = NULL;
7778 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
7779 	S = gmt_M_memory (GMT, NULL, 1, struct GMT_DATASEGMENT);
7780 	S->hidden = SH = gmt_M_memory (GMT, NULL, 1, struct GMT_DATASEGMENT_HIDDEN);
7781 	if (n_columns)
7782 		SH->alloc_mode = gmt_M_memory (GMT, NULL, n_columns, enum GMT_enum_alloc);
7783 	for (col = 0; col < n_columns; col++)
7784 		SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;	/* So GMT_* modules can free this memory */
7785 	return (S);
7786 }
7787 
7788 /*! . */
gmt_create_table(struct GMT_CTRL * GMT,uint64_t n_segments,uint64_t n_rows,uint64_t n_columns,unsigned int mode,bool alloc_only)7789 struct GMT_DATATABLE * gmt_create_table (struct GMT_CTRL *GMT, uint64_t n_segments, uint64_t n_rows, uint64_t n_columns, unsigned int mode, bool alloc_only) {
7790 	/* Allocate the new Table structure given the specified dimensions.
7791 	 * If n_rows == 0 it means we don't have any data yet.
7792 	 * If n_columns == 0 it means we don't know that dimension yet.
7793 	 * If alloc_only is true then we do NOT set the corresponding counters (i.e., n_segments),
7794 	 * else these are set to reflect the new sizes. */
7795 	uint64_t seg;
7796 	bool alloc = (n_columns || mode & GMT_WITH_STRINGS);
7797 	struct GMT_DATATABLE *T = NULL;
7798 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
7799 
7800 	T = gmt_get_table (GMT);
7801 	TH = gmt_get_DT_hidden (T);
7802 	if (!alloc_only) T->n_segments = n_segments;
7803 	if (!alloc_only) T->n_records = n_segments * n_rows;
7804 	TH->n_alloc = n_segments;
7805 	if (n_columns) {
7806 		T->min = gmt_M_memory (GMT, NULL, n_columns, double);
7807 		T->max = gmt_M_memory (GMT, NULL, n_columns, double);
7808 	}
7809 	T->n_columns = n_columns;
7810 	if (n_segments) {
7811 		T->segment = gmt_M_memory (GMT, NULL, n_segments, struct GMT_DATASEGMENT *);
7812 		for (seg = 0; alloc && seg < n_segments; seg++) {
7813 			T->segment[seg] = gmt_get_segment (GMT, n_columns);
7814 			gmt_alloc_segment (GMT, T->segment[seg], n_rows, n_columns, mode, true);
7815 			if (alloc_only) T->segment[seg]->n_rows = 0;
7816 		}
7817 	}
7818 
7819 	return (T);
7820 }
7821 
7822 /*! . */
gmtlib_create_dataset(struct GMT_CTRL * GMT,uint64_t n_tables,uint64_t n_segments,uint64_t n_rows,uint64_t n_columns,unsigned int geometry,unsigned int mode,bool alloc_only)7823 struct GMT_DATASET * gmtlib_create_dataset (struct GMT_CTRL *GMT, uint64_t n_tables, uint64_t n_segments, uint64_t n_rows, uint64_t n_columns, unsigned int geometry, unsigned int mode, bool alloc_only) {
7824 	/* Create an empty data set structure with the required number of empty tables, all set to hold n_segments, each with n_rows holding n_columns.
7825 	 * if mode & GMT_WITH_STRINGS we also allocate arrays to hold trailing text.
7826 	 * If alloc_only is true then we leave the counters (n_records, n_rows) unchanged, else these
7827 	 * are updated to reflect the new allocated sizes. */
7828 	uint64_t tbl;
7829 	struct GMT_DATASET *D = NULL;
7830 	struct GMT_DATASET_HIDDEN *DH = NULL;
7831 
7832 	D = gmt_get_dataset (GMT);
7833 	DH = gmt_get_DD_hidden (D);
7834 	if (n_columns) {
7835 		D->min = gmt_M_memory (GMT, NULL, n_columns, double);
7836 		D->max = gmt_M_memory (GMT, NULL, n_columns, double);
7837 	}
7838 	D->n_columns = n_columns;
7839 	D->geometry = geometry;
7840 	D->type = (mode & GMT_WITH_STRINGS) ? ((n_columns == 0) ? GMT_READ_TEXT : GMT_READ_MIXED) : GMT_READ_DATA;
7841 	if (n_tables) D->table = gmt_M_memory (GMT, NULL, n_tables, struct GMT_DATATABLE *);
7842 	DH->n_alloc = D->n_tables = n_tables;
7843 	if (!alloc_only) D->n_segments = D->n_tables * n_segments;
7844 	if (!alloc_only) D->n_records = D->n_segments * n_rows;
7845 	for (tbl = 0; tbl < n_tables; tbl++)
7846 		if ((D->table[tbl] = gmt_create_table (GMT, n_segments, n_rows, n_columns, mode, alloc_only)) == NULL)
7847 			return (NULL);
7848 	DH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
7849 
7850 	return (D);
7851 }
7852 
7853 /*! . */
gmtlib_read_table(struct GMT_CTRL * GMT,void * source,unsigned int source_type,bool greenwich,unsigned int * geometry,unsigned int * data_type,bool use_GMT_io)7854 struct GMT_DATATABLE * gmtlib_read_table (struct GMT_CTRL *GMT, void *source, unsigned int source_type, bool greenwich, unsigned int *geometry, unsigned int *data_type, bool use_GMT_io) {
7855 	/* Reads an entire data set into a single table in memory with any number of segments */
7856 
7857 	bool ASCII, close_file = false, header = true, no_segments, first_seg = true, poly, this_is_poly = false;
7858 	bool pol_check, check_geometry;
7859 	int status;
7860 	uint64_t n_expected_fields, n_returned = 0;
7861 	uint64_t n_read = 0, row = 0, seg = 0, col, n_poly_seg = 0, k;
7862 	size_t n_head_alloc = GMT_TINY_CHUNK;
7863 	char open_mode[4] = {""}, file[PATH_MAX] = {""}, line[GMT_LEN64] = {""};
7864 	double d;
7865 	struct GMT_RECORD *In = NULL;
7866 	FILE *fp = NULL;
7867 	struct GMT_DATATABLE *T = NULL;
7868 	struct GMT_DATASEGMENT *S = NULL;
7869 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
7870 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
7871 	void * (*psave) (struct GMT_CTRL *, FILE *, uint64_t *, int *) = NULL;	/* Pointer to function reading tables */
7872 
7873 	if (use_GMT_io) {	/* Use GMT->current.io.info settings to determine if input is ASCII/binary, else it defaults to ASCII */
7874 		if (GMT->common.b.active[GMT_IN]) {	/* May return fewer than # of binary columns if -i was set */
7875 			n_returned = (GMT->common.i.select) ? GMT->common.i.n_cols : GMT->common.b.ncol[GMT_IN];
7876 			n_expected_fields = GMT->common.b.ncol[GMT_IN];
7877 		}
7878 		else
7879 			n_expected_fields = GMT_MAX_COLUMNS;
7880 		strcpy (open_mode, GMT->current.io.r_mode);
7881 		ASCII = !GMT->common.b.active[GMT_IN];
7882 	}
7883 	else {			/* Force ASCII mode */
7884 		n_expected_fields = GMT_MAX_COLUMNS;	/* GMT->current.io.input will return the number of columns */
7885 		strcpy (open_mode, "r");
7886 		ASCII = true;
7887 		psave = GMT->current.io.input;			/* Save the previous pointer since we need to change it back at the end */
7888 		GMT->current.io.input = GMT->session.input_ascii;	/* Override and use ASCII mode */
7889 	}
7890 
7891 #ifdef SET_IO_MODE
7892 	if (!ASCII) gmt_setmode (GMT, GMT_IN);
7893 #endif
7894 
7895 	if (ASCII && *geometry == GMT_IS_TEXT) {
7896 		psave = GMT->current.io.input;			/* Save the previous pointer since we need to change it back at the end */
7897 		GMT->current.io.input = &gmtlib_ascii_textinput;	/* Override and use ASCII text mode */
7898 		GMT->current.io.record_type[GMT_IN] = *data_type = GMT_READ_TEXT;
7899 	}
7900 
7901 	/* Determine input source */
7902 
7903 	if (source_type == GMT_IS_FILE) {	/* source is a file name */
7904 		strncpy (file, source, PATH_MAX-1);
7905 		if ((fp = gmt_fopen (GMT, file, open_mode)) == NULL) {
7906 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", file);
7907 			if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
7908 			return (NULL);
7909 		}
7910 		close_file = true;	/* We only close files we have opened here */
7911 	}
7912 	else if (source_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
7913 		fp = (FILE *)source;
7914 		if (fp == NULL) fp = GMT->session.std[GMT_IN];	/* Default input */
7915 		if (fp == GMT->session.std[GMT_IN])
7916 			strcpy (file, "<stdin>");
7917 		else
7918 			strcpy (file, "<input stream>");
7919 	}
7920 	else if (source_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
7921 		int *fd = source;
7922 		if (fd && (fp = fdopen (*fd, open_mode)) == NULL) {
7923 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert file descriptor %d to stream in gmtlib_read_table\n", *fd);
7924 			if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
7925 			return (NULL);
7926 		}
7927 		if (fd == NULL) fp = GMT->session.std[GMT_IN];	/* Default input */
7928 		if (fp == GMT->session.std[GMT_IN])
7929 			strcpy (file, "<stdin>");
7930 		else
7931 			strcpy (file, "<input file descriptor>");
7932 	}
7933 	else {
7934 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtlib_read_table\n", source_type);
7935 		if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
7936 		return (NULL);
7937 	}
7938 
7939 	/* Skip binary header first, if binary and there is a header given in # of bytes */
7940 	if (GMT->common.b.active[GMT_IN]) {
7941 		if (GMT->current.setting.io_n_header_items) {
7942 			gmtlib_io_binary_header (GMT, fp, GMT_IN);
7943 			header = false;	/* No more binary header */
7944 		}
7945 	}
7946 
7947 	if (GMT->current.io.record.data == NULL) *data_type = GMT_READ_TEXT;
7948 	In = GMT->current.io.input (GMT, fp, &n_expected_fields, &status);	/* Get first record */
7949 	n_read++;
7950 	if (gmt_M_rec_is_eof(GMT)) {
7951 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "File %s is empty!\n", file);
7952 		if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
7953 		return (NULL);
7954 	}
7955 
7956 	pol_check = check_geometry = ((*geometry & GMT_IS_POLY) && (*geometry & GMT_IS_LINE));	/* Have to determine if these are closed polygons or not */
7957 	poly = (((*geometry & GMT_IS_POLY) || *geometry == GMT_IS_MULTIPOLYGON) && (*geometry & GMT_IS_LINE) == 0);	/* To enable polar cap assessment in i/o */
7958 
7959 	if (GMT->current.io.ogr == GMT_OGR_TRUE) {	/* Reading an OGR file so we can set the geometry, and possibly poly */
7960 		if (GMT->current.io.OGR == NULL) {
7961 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "OGR parsing incomplete (is file missing OGR statements?) - abort\n");
7962 			GMT->parent->error = GMT_DATA_READ_ERROR;
7963 			return (NULL);
7964 		}
7965 		poly = (GMT->current.io.OGR->geometry == GMT_IS_POLYGON || GMT->current.io.OGR->geometry == GMT_IS_MULTIPOLYGON);
7966 		*geometry = GMT->current.io.OGR->geometry;
7967 		if (*geometry > GMT_IS_MULTI)
7968 			*geometry -= GMT_IS_MULTI;
7969 	}
7970 
7971 	/* Allocate the Table structure with GMT_CHUNK segments, but none has any rows or columns */
7972 
7973 	T = gmt_create_table (GMT, GMT_CHUNK, 0U, 0U, 0U, false);
7974 	TH = gmt_get_DT_hidden (T);
7975 
7976 	TH->file[GMT_IN] = strdup (file);
7977 	if (header) T->header = gmt_M_memory (GMT, NULL, n_head_alloc, char *);
7978 
7979 	while (status >= 0 && !gmt_M_rec_is_eof (GMT)) {	/* Not yet EOF */
7980 		if (header) {	/* Only true at start of an ASCII file */
7981 			while ((GMT->current.setting.io_header[GMT_IN] && n_read <= GMT->current.setting.io_n_header_items) ||
7982 			        gmt_M_rec_is_table_header (GMT)) { /* Process headers */
7983 				for (k = 0; GMT->current.io.curr_text[k] == ' '; k++);
7984 				T->header[T->n_headers] = strdup (&GMT->current.io.curr_text[k]);
7985 				T->n_headers++;
7986 				if (T->n_headers == n_head_alloc) {
7987 					n_head_alloc <<= 1;
7988 					T->header = gmt_M_memory (GMT, T->header, n_head_alloc, char *);
7989 				}
7990 				In = GMT->current.io.input (GMT, fp, &n_expected_fields, &status);
7991 				n_read++;
7992 			}
7993 			if (T->n_headers)
7994 				T->header = gmt_M_memory (GMT, T->header, T->n_headers, char *);
7995 			else {	/* No header records found */
7996 				gmt_M_free (GMT, T->header);
7997 				T->header = NULL;
7998 			}
7999 			header = false;	/* Done processing header block; other comments are GIS/OGR encoded comments */
8000 		}
8001 
8002 		if (gmt_M_rec_is_eof (GMT)) continue;	/* Got EOF after headers */
8003 
8004 		no_segments = !gmt_M_rec_is_segment_header (GMT);	/* Not a multi-segment file.  We then assume file has only one segment */
8005 
8006 		while (no_segments || (gmt_M_rec_is_segment_header (GMT) && !gmt_M_rec_is_eof (GMT))) {
8007 			/* To use different line-distances for each segment, place the distance in the segment header */
8008 			if (first_seg || T->segment[seg]->n_rows > 0) {
8009 				if (!first_seg) seg++;	/* Only advance segment if last had any points */
8010 				T->segment[seg] = gmt_get_segment (GMT, T->n_columns);
8011 				first_seg = false;
8012 			}
8013 			n_read++;
8014 			S = T->segment[seg];
8015 			SH = gmt_get_DS_hidden (S);
8016 			if (ASCII && !no_segments) {	/* Only ASCII files can have info stored in multi-seg header records */
8017 				if (gmt_parse_segment_item (GMT, GMT->current.io.segment_header, "-D", line)) {	/* Found a potential -D<dist> option in the header */
8018 					if (sscanf (line, "%lg", &d) == 1) SH->dist = d;	/* If readable, assign it to dist, else leave as zero */
8019 				}
8020 			}
8021 			/* Segment initialization */
8022 			row = 0;
8023 			if (!no_segments) {	/* Read data if we read a segment header up front, but guard against headers which sets in = NULL */
8024 				while (!gmt_M_rec_is_eof (GMT) && (In = GMT->current.io.input (GMT, fp, &n_expected_fields, &status)) == NULL) n_read++;
8025 			}
8026 			if (GMT->current.io.record_type[GMT_IN] == GMT_READ_TEXT)
8027 				S->n_columns = 0;	/* No numerical data */
8028 			else
8029 				S->n_columns = (n_returned) ? n_returned : n_expected_fields;	/* This is where number of columns are determined */
8030 			no_segments = false;	/* This has now served its purpose */
8031 		}
8032 		if (gmt_M_rec_is_eof (GMT)) continue;	/* At EOF; get out of this loop */
8033 		if (ASCII && !no_segments) {	/* Only ASCII files can have info stored in multi-seg header record */
8034 			char buffer[GMT_BUFSIZ] = {""};
8035 			if (strlen (GMT->current.io.segment_header)) {
8036 				S->header = strdup (GMT->current.io.segment_header);
8037 				if (gmt_parse_segment_item (GMT, GMT->current.io.segment_header, "-L", buffer))
8038 					S->label = strdup (buffer);
8039 			}
8040 			if (GMT->current.io.ogr == GMT_OGR_TRUE) gmtio_copy_ogr_seg (GMT, S, GMT->current.io.OGR);	/* Copy over any feature-specific values */
8041 		}
8042 
8043 		if (poly && S->n_columns < 2) {
8044 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "File %s does not have at least 2 columns required for polygons (found %d)\n",
8045 			            file, S->n_columns);
8046 			if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
8047 			return (NULL);
8048 		}
8049 
8050 		while (! (GMT->current.io.status & (GMT_IO_SEGMENT_HEADER | GMT_IO_GAP | GMT_IO_EOF))) {	/* Keep going until false or find a new segment header */
8051 			if (GMT->current.io.status & GMT_IO_MISMATCH) {
8052 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Mismatch between actual (%d) and expected (%d) fields near line %" PRIu64 " in file %s\n",
8053 				            status, n_expected_fields, n_read, file);
8054 				if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
8055 				return NULL;
8056 			}
8057 
8058 			gmt_prep_tmp_arrays (GMT, GMT_IN, row, S->n_columns);	/* Init or reallocate tmp read vectors */
8059 			for (col = 0; col < S->n_columns; col++) {
8060 				GMT->hidden.mem_coord[col][row] = In->data[col];
8061 				if (gmt_M_type (GMT, GMT_IN, col) & GMT_IS_LON) {	/* Must handle greenwich/dateline alignments */
8062 					if (greenwich && GMT->hidden.mem_coord[col][row] > 180.0) GMT->hidden.mem_coord[col][row] -= 360.0;
8063 					if (!greenwich && GMT->hidden.mem_coord[col][row] < 0.0)  GMT->hidden.mem_coord[col][row] += 360.0;
8064 				}
8065 			}
8066 			if (GMT->current.io.record_type[GMT_IN] & GMT_READ_TEXT) {
8067 				if (GMT->current.io.record.text) GMT->hidden.mem_txt[row] = strdup (GMT->current.io.record.text);
8068 				*data_type = GMT_READ_MIXED;
8069 			}
8070 
8071 			row++;
8072 			In = GMT->current.io.input (GMT, fp, &n_expected_fields, &status);
8073 			while (gmt_M_rec_is_table_header (GMT)) In = GMT->current.io.input (GMT, fp, &n_expected_fields, &status);	/* Just wind past other comments */
8074 			n_read++;
8075 		}
8076 
8077 		if (n_expected_fields < 2) {
8078 			pol_check = check_geometry = poly = false;
8079 			*geometry = GMT_IS_NONE;
8080 		}
8081 		if (pol_check) this_is_poly = (row > 2 && !gmt_polygon_is_open (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], row));	/* true if this segment is closed polygon */
8082 		if (this_is_poly) n_poly_seg++;
8083 		if (check_geometry) {	/* Determine if dealing with closed polygons or lines based on first segment only */
8084 			if (this_is_poly) poly = true;
8085 			check_geometry = false;	/* Done with one-time checking */
8086 			*geometry = (poly) ? GMT_IS_POLY : GMT_IS_LINE;	/* Update the geometry setting */
8087 		}
8088 		if (poly) {	/* If file contains a polygon then we must close it if needed */
8089 			if (gmt_M_type (GMT, GMT_IN, GMT_X) & GMT_IS_GEO) {	/* Must check for polar cap */
8090 				double dlon = GMT->hidden.mem_coord[GMT_X][0] - GMT->hidden.mem_coord[GMT_X][row-1];
8091 				if (!((fabs (dlon) == 0.0 || fabs (dlon) == 360.0) && GMT->hidden.mem_coord[GMT_Y][0] == GMT->hidden.mem_coord[GMT_Y][row-1])) {
8092 					gmt_prep_tmp_arrays (GMT, GMT_IN, row, S->n_columns);	/* Maybe reallocate tmp read vectors */
8093 					for (col = 0; col < S->n_columns; col++) GMT->hidden.mem_coord[col][row] = GMT->hidden.mem_coord[col][0];
8094 					row++;	/* Explicitly close polygon */
8095 					GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Explicitly closed open geographic polygon in file %s, segment %" PRIu64 "\n", file, seg);
8096 				}
8097 			}
8098 			else if (gmt_polygon_is_open (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], row)) {	/* Cartesian closure */
8099 				gmt_prep_tmp_arrays (GMT, GMT_IN, row, S->n_columns);	/* Init or update tmp read vectors */
8100 				for (col = 0; col < S->n_columns; col++) GMT->hidden.mem_coord[col][row] = GMT->hidden.mem_coord[col][0];
8101 				row++;	/* Explicitly close polygon */
8102 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Explicitly closed open Cartesian polygon in file %s, segment %" PRIu64 "\n", file, seg);
8103 			}
8104 			if (gmt_parse_segment_item (GMT, S->header, "-Ph", NULL)) SH->pol_mode = GMT_IS_HOLE;
8105 			/* If this is a hole then set link from previous segment to this one */
8106 			if (seg && gmt_polygon_is_hole (GMT, S)) {
8107 				struct GMT_DATASEGMENT_HIDDEN *SH2 = gmt_get_DS_hidden (T->segment[seg-1]);
8108 				SH2->next = S;
8109 			}
8110 		}
8111 
8112 		if (row == 0) {	/* Empty segment; we delete to avoid problems downstream in applications */
8113 			gmt_M_free (GMT, T->segment[seg]);
8114 			seg--;	/* Go back to where we were */
8115 		}
8116 		else {	/* OK to populate segment and increment counters */
8117 			gmtlib_assign_segment (GMT, GMT_IN, T->segment[seg], row, T->segment[seg]->n_columns);	/* Allocate and place arrays into segment */
8118 			gmt_set_seg_minmax (GMT, *geometry, 0, T->segment[seg]);	/* Set min/max */
8119 			T->n_records += row;		/* Total number of records so far */
8120 			SH->id = seg;	/* Internal segment number */
8121 		}
8122 		/* Reallocate to free up some memory */
8123 
8124 		if (seg == (TH->n_alloc-1)) {	/* Need to allocate more segments */
8125 			size_t n_old_alloc = TH->n_alloc;
8126 			TH->n_alloc <<= 1;
8127 			T->segment = gmt_M_memory (GMT, T->segment, TH->n_alloc, struct GMT_DATASEGMENT *);
8128 			gmt_M_memset (&(T->segment[n_old_alloc]), TH->n_alloc - n_old_alloc, struct GMT_DATASEGMENT *);	/* Set to NULL */
8129 		}
8130 
8131 		/* If a gap was detected, forget about it now, so we can use the data for the next segment */
8132 
8133 		GMT->current.io.status -= (GMT->current.io.status & GMT_IO_GAP);
8134 	}
8135 	if (close_file) gmt_fclose (GMT, fp);
8136 	if (!use_GMT_io) GMT->current.io.input = psave;	/* Restore previous setting */
8137 
8138 	if (first_seg) {	/* Never saw any segment or data records */
8139 		gmt_free_table (GMT, T);
8140 		return (NULL);
8141 	}
8142 	if (T->segment[seg]->n_rows == 0) {	/* Last segment was empty; we delete to avoid problems downstream in applications */
8143 		gmt_M_free (GMT, T->segment[seg]->hidden);
8144 		gmt_M_free (GMT, T->segment[seg]);
8145 		if (seg == 0) {	/* Happens when we just read 1 segment header with no data */
8146 			gmt_free_table (GMT, T);
8147 			return (NULL);
8148 		}
8149 	}
8150 	else
8151 		seg++;
8152 	if (check_geometry && poly && n_poly_seg != seg) {
8153 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Table contains mix of polygons (%" PRIu64 ") and lines (%" PRIu64 ")\n",
8154 		            n_poly_seg, n_poly_seg - seg);
8155 	}
8156 	T->segment = gmt_M_memory (GMT, T->segment, seg, struct GMT_DATASEGMENT *);
8157 	T->n_segments = seg;
8158 	T->n_columns = T->segment[0]->n_columns;
8159 	/* Determine table min,max values */
8160 	T->min = gmt_M_memory (GMT, NULL, T->n_columns, double);
8161 	T->max = gmt_M_memory (GMT, NULL, T->n_columns, double);
8162 	for (col = 0; col < T->n_columns; col++) {T->min[col] = DBL_MAX; T->max[col] = -DBL_MAX;}
8163 	for (seg = 0; seg < T->n_segments; seg++) {
8164 		S = T->segment[seg];
8165 		SH = gmt_get_DS_hidden (S);
8166 		for (col = 0; col < T->n_columns; col++) {
8167 			T->min[col] = MIN (T->min[col], S->min[col]);
8168 			T->max[col] = MAX (T->max[col], S->max[col]);
8169 		}
8170 		if (SH->pole) {T->min[GMT_X] = 0.0; T->max[GMT_X] = 360.0;}
8171 	}
8172 
8173 	return (T);
8174 }
8175 
8176 /*! . */
gmt_duplicate_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * Sin)8177 struct GMT_DATASEGMENT * gmt_duplicate_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *Sin) {
8178 	/* Duplicates the segment */
8179 	uint64_t col;
8180 	struct GMT_DATASEGMENT *Sout = GMT_Alloc_Segment (GMT->parent, GMT_IS_DATASET, Sin->n_rows, Sin->n_columns, Sin->header, NULL);
8181 	for (col = 0; col < Sin->n_columns; col++) gmt_M_memcpy (Sout->data[col], Sin->data[col], Sin->n_rows, double);
8182 	return (Sout);
8183 }
8184 
8185 /* gmt_realloc_dataset as not yet been tested and is not used anywhere !!!!!!!!!!!!!!!! */
8186 /*! . */
gmt_realloc_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * D,uint64_t dim[])8187 unsigned int gmt_realloc_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *D, uint64_t dim[]) {
8188 	/* Reallocate this dataset given desired dimensions in dim.  Note: a dim of 0 means no change.
8189 	 * If the new dimension(s) are smaller than before then we free memory, else we allocate. */
8190 	uint64_t tbl, seg, col, n_seg, n_col, n_row;
8191 	unsigned int mode = 0;
8192 	struct GMT_DATATABLE *T = NULL;
8193 	struct GMT_DATASEGMENT *S;
8194 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
8195 
8196 	if (D == NULL) return GMT_NOERROR;
8197 	if (D->table[0]->segment[0]->text) mode = GMT_WITH_STRINGS;
8198 	/* 1. First we eliminate anything no longer wanted */
8199 	if (dim[GMT_TBL] && dim[GMT_TBL] < D->n_tables) {	/* Remove unneeded tables and reallocate array */
8200 		for (tbl = dim[GMT_TBL]; tbl < D->n_tables; tbl++)
8201 			gmt_free_table (GMT, D->table[tbl]);
8202 		D->table = gmt_M_memory (GMT, D->table, dim[GMT_TBL], struct GMT_DATATABLE *);
8203 		D->n_tables = dim[GMT_TBL];
8204 	}
8205 	/* Here, any tables not needed have been removed */
8206 	for (tbl = 0; tbl < D->n_tables; tbl++) {	/* Loop over the tables we have kept */
8207 		T = D->table[tbl];	/* Short-cut */
8208 		n_seg = (dim[GMT_SEG] == 0) ? T->n_segments : dim[GMT_SEG];
8209 		n_col = (dim[GMT_COL] == 0) ? T->n_columns  : dim[GMT_COL];
8210 		if (dim[GMT_SEG] && dim[GMT_SEG] < T->n_segments) {	/* Remove unneeded segments and reallocate array for this table */
8211 			for (seg = dim[GMT_SEG]; seg < T->n_segments; seg++)
8212 				gmt_free_segment (GMT, &(T->segment[seg]));
8213 			T->segment = gmt_M_memory (GMT, T->segment, dim[GMT_SEG], struct GMT_DATASEGMENT *);
8214 			T->n_segments = dim[GMT_SEG];
8215 			/* This table now has max dim[SEG] segments but might have fewer */
8216 		}
8217 		else if (dim[GMT_SEG] > T->n_segments) {	/* Want to add more segments */
8218 			T->segment = gmt_M_memory (GMT, T->segment, dim[GMT_SEG], struct GMT_DATASEGMENT *);
8219 			for (seg = T->n_segments; seg < dim[GMT_SEG]; seg++) {
8220 				S = T->segment[seg];	/* Short-cut */
8221 				n_row = (dim[GMT_ROW] == 0) ? S->n_rows : dim[GMT_ROW];
8222 				T->segment[seg] = gmt_get_segment (GMT, n_col);
8223 				gmt_alloc_segment (GMT, T->segment[seg], n_row, n_col, mode, false);
8224 				S->n_rows = n_row;
8225 			}
8226 		}
8227 
8228 		for (seg = 0; seg < T->n_segments; seg++) {	/* Check each of the segments of this table */
8229 			S = T->segment[seg];	/* Short-cut */
8230 			SH = gmt_get_DS_hidden (S);
8231 			n_row = (dim[GMT_ROW] == 0) ? S->n_rows : dim[GMT_ROW];
8232 			if (dim[GMT_COL] && dim[GMT_COL] < T->n_columns) {	/* Remove unneeded columns and reallocate array of columns */
8233 				for (col = dim[GMT_COL]; col < T->n_columns; col++)
8234 					gmt_M_free (GMT, S->data[col]);
8235 				S->n_columns = dim[GMT_COL];
8236 				S->data = gmt_M_memory (GMT, S->data, dim[GMT_COL], double *);
8237 				S->min = gmt_M_memory (GMT, S->min, dim[GMT_COL], double *);
8238 				S->max = gmt_M_memory (GMT, S->max, dim[GMT_COL], double *);
8239 				SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, dim[GMT_COL], enum GMT_enum_alloc);
8240 				if (seg == 0) {	/* Only do this once for the table */
8241 					T->min = gmt_M_memory (GMT, T->min, dim[GMT_COL], double *);
8242 					T->max = gmt_M_memory (GMT, T->max, dim[GMT_COL], double *);
8243 					if (tbl == 0) {/* Only do this once for the dataset */
8244 						D->min = gmt_M_memory (GMT, D->min, dim[GMT_COL], double *);
8245 						D->max = gmt_M_memory (GMT, D->max, dim[GMT_COL], double *);
8246 					}
8247 				}
8248 			}
8249 			else if (dim[GMT_COL] > T->n_columns) {	/* Add new columns and reallocate arrays */
8250 				S->data = gmt_M_memory (GMT, S->data, dim[GMT_COL], double *);
8251 				S->min = gmt_M_memory (GMT, S->min, dim[GMT_COL], double *);
8252 				S->max = gmt_M_memory (GMT, S->max, dim[GMT_COL], double *);
8253 				SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, dim[GMT_COL], enum GMT_enum_alloc);
8254 				for (col = T->n_columns; col < dim[GMT_COL]; col++) {	/* Allocate the new data arrays */
8255 					S->data[col] = gmt_M_memory (GMT, S->data[col], n_row, double);
8256 					SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
8257 				}
8258 			}
8259 			if (dim[GMT_ROW] && dim[GMT_ROW] < S->n_rows) {	/* Remove unneeded rows by reallocating arrays */
8260 				for (col = dim[GMT_COL]; col < S->n_columns; col++)
8261 					S->data[col] = gmt_M_memory (GMT, S->data[col], dim[GMT_ROW], double);
8262 				if (S->text)
8263 					S->text = gmt_M_memory (GMT, S->text, dim[GMT_ROW], char *);
8264 				S->n_rows = dim[GMT_ROW];
8265 			}
8266 		}
8267 		T->n_columns = S->n_columns;	/* This has either changed or not */
8268 	}
8269 
8270 	/* 2. Now we can expand anything that should grow */
8271 	if (dim[GMT_TBL] > D->n_tables) {	/* Add new tables and reallocate array */
8272 		D->table = gmt_M_memory (GMT, D->table, dim[GMT_TBL], struct GMT_DATATABLE *);
8273 		for (tbl = D->n_tables; tbl < dim[GMT_TBL]; tbl++) {
8274 			if ((D->table[tbl] = gmt_create_table (GMT, n_seg, n_row, n_col, mode, false)) == NULL)
8275 				return (GMT_MEMORY_ERROR);
8276 		}
8277 		D->n_tables = dim[GMT_TBL];
8278 	}
8279 
8280 	gmt_set_dataset_minmax (GMT, D);	/* Update all stats */
8281 
8282 	return (GMT_NOERROR);
8283 }
8284 
8285 /*! . */
gmt_alloc_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,uint64_t n_rows,uint64_t n_columns,unsigned int mode)8286 struct GMT_DATASET * gmt_alloc_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, uint64_t n_rows, uint64_t n_columns, unsigned int mode) {
8287 	/* Allocate new dataset structure with same # of tables, segments and rows/segment as input data set.
8288 	 * However, n_columns is given separately and could differ.  Also, if n_rows > 0 we let that override the segment row counts.
8289 	 * We copy over headers and segment headers.
8290 	 * mode controls how the new dataset is to be allocated;
8291 	 * mode = GMT_ALLOC_NORMAL means we replicate the number of tables and the layout of the Din dataset
8292 	 * mode = GMT_ALLOC_VERTICAL means we concatenate all the tables in Din into a single table for Dout
8293 	 * mode = GMT_ALLOC_HORIZONTAL means we base the Dout size only on the first Din table
8294 	 *	(# of segments, # of rows/segment) because tables will be pasted horizontally and not vertically.
8295 	 * mode may also have bitflag GMT_ALLOC_VIA_ICOLS which then complicates the duplication by having to go via -i
8296 	 */
8297 	unsigned int hdr, smode;
8298 	size_t len;
8299 	uint64_t nr, tbl, seg, n_seg, seg_in_tbl;
8300 	struct GMT_DATASET *D = gmt_get_dataset (GMT);
8301 	struct GMT_DATASET_HIDDEN *DH = gmt_get_DD_hidden (D);
8302 	struct GMT_DATATABLE *T = NULL;
8303 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
8304 
8305 	if (mode & GMT_ALLOC_VIA_ICOLS)	/* Not used here but in gmt_duplicate_dataset */
8306 		mode -= GMT_ALLOC_VIA_ICOLS;
8307 	D->n_columns = (n_columns) ? n_columns : Din->n_columns;
8308 	D->geometry = Din->geometry;
8309 	D->type = Din->type;
8310 	D->min = gmt_M_memory (GMT, NULL, D->n_columns, double);
8311 	D->max = gmt_M_memory (GMT, NULL, D->n_columns, double);
8312 	if (mode) {	/* Pack everything into a single table */
8313 		DH->n_alloc = D->n_tables = 1;
8314 		if (mode == GMT_ALLOC_VERTICAL)
8315 			for (tbl = n_seg = 0; tbl < Din->n_tables; tbl++) n_seg += Din->table[tbl]->n_segments;
8316 		else	/* mode == GMT_ALLOC_HORIZONTAL */
8317 			n_seg = Din->table[0]->n_segments;
8318 		D->table = gmt_M_memory (GMT, NULL, 1, struct GMT_DATATABLE *);
8319 		T = D->table[0] = gmt_get_table (GMT);
8320 		TH = gmt_get_DT_hidden (T);
8321 
8322 		/* As for file headers we concatenate the headers from all tables */
8323 		T->n_headers  = Din->table[0]->n_headers;
8324 		if (T->n_headers) T->header = gmt_M_memory (GMT, NULL, T->n_headers, char *);
8325 		for (hdr = 0; hdr < T->n_headers; hdr++) {	/* Concatenate headers */
8326 			for (tbl = len = 0; tbl < Din->n_tables; tbl++) if (Din->table[tbl]->header) len += (strlen (Din->table[tbl]->header[hdr]) + 2);
8327 			T->header[hdr] = calloc (len, sizeof (char));
8328 			strncpy (T->header[hdr], Din->table[0]->header[hdr], len);
8329 			if (Din->n_tables > 1) gmt_chop (T->header[hdr]);	/* Remove newline */
8330 			for (tbl = 1; tbl < Din->n_tables; tbl++) {	/* Now go across tables to paste */
8331 				if (tbl < (Din->n_tables - 1)) gmt_chop (Din->table[tbl]->header[hdr]);
8332 				strcat (T->header[hdr], "\t");
8333 				if (Din->table[tbl]->header) strcat (T->header[hdr], Din->table[tbl]->header[hdr]);
8334 			}
8335 		}
8336 
8337 		D->n_segments = T->n_segments = TH->n_alloc = n_seg;
8338 		T->n_columns = D->n_columns;
8339 		T->segment = gmt_M_memory (GMT, NULL, n_seg, struct GMT_DATASEGMENT *);
8340 		T->min = gmt_M_memory (GMT, NULL, D->n_columns, double);
8341 		T->max = gmt_M_memory (GMT, NULL, D->n_columns, double);
8342 		for (seg = tbl = seg_in_tbl = 0; seg < D->n_segments; seg++) {
8343 			if (seg == Din->table[tbl]->n_segments) { tbl++; seg_in_tbl = 0; }	/* Go to next table */
8344 			nr = (n_rows) ? n_rows : Din->table[tbl]->segment[seg_in_tbl]->n_rows;
8345 			smode = (Din->table[tbl]->segment[seg_in_tbl]->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
8346 			T->segment[seg] = GMT_Alloc_Segment (GMT->parent, smode, nr, D->n_columns, NULL, NULL);
8347 			if (mode != GMT_ALLOC_HORIZONTAL && Din->table[tbl]->segment[seg_in_tbl]->header)
8348 				T->segment[seg]->header = strdup (Din->table[tbl]->segment[seg_in_tbl]->header);
8349 			D->n_records += nr;
8350 			seg_in_tbl++;
8351 		}
8352 	}
8353 	else {	/* Just copy over the same dataset layout except for columns */
8354 		DH->n_alloc  = D->n_tables = Din->n_tables;		/* Same number of tables as input dataset */
8355 		D->n_segments  = Din->n_segments;	/* Same number of segments as input dataset */
8356 		D->n_records  = Din->n_records;		/* Same number of records as input dataset */
8357 		D->table = gmt_M_memory (GMT, NULL, D->n_tables, struct GMT_DATATABLE *);
8358 		for (tbl = 0; tbl < D->n_tables; tbl++) {
8359 			D->table[tbl] = gmtio_alloc_table (GMT, Din->table[tbl], D->n_columns, n_rows);
8360 		}
8361 	}
8362 	DH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level. */
8363 	DH->alloc_mode = GMT_ALLOC_INTERNALLY;		/* So GMT_* modules can free this memory. */
8364 	DH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
8365 	return (D);
8366 }
8367 
gmtio_duplicate_dataset_cols(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,struct GMT_DATASET * D)8368 GMT_LOCAL void gmtio_duplicate_dataset_cols (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, struct GMT_DATASET *D) {
8369 	/* D has required dimensions but we need to use -i to populate with data from Din */
8370 	uint64_t tbl, seg, col, row;
8371 	unsigned int col_pos_out, col_pos_in;
8372 	struct GMT_DATASEGMENT *S, *Sin;
8373 
8374 	for (tbl = 0; tbl < Din->n_tables; tbl++) {
8375 		for (seg = 0; seg < Din->table[tbl]->n_segments; seg++) {
8376 			Sin = Din->table[tbl]->segment[seg];
8377 			S = D->table[tbl]->segment[seg];
8378 			for (col = 0; col < S->n_columns; col++) {	/* Dummy loop over the number of output columns but not their order */
8379 				col_pos_out = gmtlib_pick_in_col_number (GMT, (unsigned int)col, &col_pos_in);
8380 				if (GMT->current.io.col[GMT_IN][col].convert) {	/* Cannot do a straight copy */
8381 					for (row = 0; row < Sin->n_rows; row++) {
8382 						S->data[col_pos_out][row] = gmt_M_convert_col (GMT->current.io.col[GMT_IN][col], Sin->data[col_pos_in][row]);
8383 					}
8384 				}
8385 				else	/* Can copy the lot */
8386 					gmt_M_memcpy (S->data[col_pos_out], Sin->data[col_pos_in], Sin->n_rows, double);
8387 			}
8388 		}
8389 	}
8390 }
8391 
8392 /*! . */
gmt_duplicate_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,unsigned int mode,unsigned int * geometry)8393 struct GMT_DATASET *gmt_duplicate_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, unsigned int mode, unsigned int *geometry) {
8394 	/* Make an exact replica, return geometry if not NULL */
8395 	uint64_t tbl, seg;
8396 	struct GMT_DATASET *D = NULL;
8397 	struct GMT_DATATABLE_HIDDEN *TH = NULL, *TinH = NULL;
8398 
8399 	if (mode & GMT_ALLOC_VIA_ICOLS && GMT->common.i.select) {
8400 		/* Cannot copy segments but must go via -i */
8401 		D = gmt_alloc_dataset (GMT, Din, 0, GMT->common.i.n_cols, mode);
8402 		gmtio_duplicate_dataset_cols (GMT, Din, D);
8403 		if (geometry)
8404 			*geometry = D->geometry;
8405 		return (D);
8406 	}
8407 	/* Regular duplication */
8408 	D = gmt_alloc_dataset (GMT, Din, 0, Din->n_columns, mode);
8409 	gmt_M_memcpy (D->min, Din->min, Din->n_columns, double);
8410 	gmt_M_memcpy (D->max, Din->max, Din->n_columns, double);
8411 	for (tbl = 0; tbl < Din->n_tables; tbl++) {
8412 		TH   = gmt_get_DT_hidden (D->table[tbl]);
8413 		TinH = gmt_get_DT_hidden (Din->table[tbl]);
8414 		for (unsigned int k = 0; k < 2; k++)
8415 			if (TinH->file[k]) TH->file[k] = strdup (TinH->file[k]);
8416 		for (seg = 0; seg < Din->table[tbl]->n_segments; seg++) {
8417 			gmtio_copy_segment (GMT, D->table[tbl]->segment[seg], Din->table[tbl]->segment[seg]);
8418 		}
8419 		gmt_M_memcpy (D->table[tbl]->min, Din->table[tbl]->min, Din->table[tbl]->n_columns, double);
8420 		gmt_M_memcpy (D->table[tbl]->max, Din->table[tbl]->max, Din->table[tbl]->n_columns, double);
8421 	}
8422 	if (geometry)
8423 		*geometry = D->geometry;
8424 
8425 	return (D);
8426 }
8427 
gmtlib_change_out_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET * D)8428 void gmtlib_change_out_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET *D) {
8429 	/* Given the -o option, rearrange the columns of the dataset.
8430 	 * This is needed by the API when returning a dataset by reference
8431 	 * as the normal -o path for printing is not taken. */
8432 	uint64_t tbl, seg, col, src_col;
8433 	unsigned int *used = NULL;
8434 	bool extend = false, adjust = false;
8435 	double **temp = NULL;
8436 	struct GMT_DATATABLE *T = NULL;
8437 	struct GMT_DATASEGMENT *S;
8438 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
8439 
8440 	if (!GMT->common.o.select) return;	/* Nutin' to do */
8441 	temp = gmt_M_memory (GMT, NULL, D->n_columns, double *);
8442 	used = gmt_M_memory (GMT, NULL, D->n_columns, unsigned int);
8443 	if (GMT->common.o.n_cols > D->n_columns)	/* Must allocate more columns first */
8444 		extend = true;
8445 	if (GMT->common.o.n_cols != D->n_columns)	/* Must free/realloc some columns afterwards */
8446 		adjust = true;
8447 	for (tbl = 0; tbl < D->n_tables; tbl++) {
8448 		T = D->table[tbl];	/* Current atble */
8449 		for (seg = 0; seg < T->n_segments; seg++) {
8450 			S = T->segment[seg];	/* Current segment */
8451 			SH = gmt_get_DS_hidden (S);
8452 			if (extend)	{	/* Allocate more arrays first */
8453 				S->data = gmt_M_memory (GMT, S->data, GMT->common.o.n_cols, double *);
8454 				SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, GMT->common.o.n_cols, enum GMT_enum_alloc);
8455 				for (col = D->n_columns; col < GMT->common.o.n_cols; col++) {
8456 					S->data[col] = gmt_M_memory (GMT, NULL, S->n_rows, double);
8457 					SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
8458 				}
8459 			}
8460 			for (col = 0; col < S->n_columns; col++)	/* Keep pointers to original arrays */
8461 				temp[col] = S->data[col];
8462 			gmt_M_memset (used, S->n_columns, unsigned int);	/* Reset used counters */
8463 			gmt_M_memset (S->data, S->n_columns, double *);	/* Reset pointers */
8464 			/* During the first pass we can set pointers only once per source column */
8465 			for (col = 0; col < GMT->common.o.n_cols; col++) {	/* These are final output columns */
8466 				src_col = GMT->current.io.col[GMT_OUT][col].col;	/* Corresponding original column */
8467 				if (used[src_col] == 0)	/* Can set pointer the first time */
8468 					S->data[col] = temp[src_col];
8469 				used[src_col]++;	/* Used this column one more time */
8470 			}
8471 			/* During second pass we must allocate space for any repeated columns */
8472 			for (col = 0; col < GMT->common.o.n_cols; col++) {	/* These are again final output columns */
8473 				if (S->data[col]) continue;	/* Set this one via pointer in pass one */
8474 				S->data[col] = gmt_M_memory (GMT, NULL, S->n_rows, double);
8475 				src_col = GMT->current.io.col[GMT_OUT][col].col;	/* Corresponding original column */
8476 				gmt_M_memcpy (S->data[col], temp[src_col], S->n_rows, double);	/* Duplicate contents in second pass */
8477 			}
8478 			for (col = 0; col < S->n_columns; col++) {		/* Free the unused columns */
8479 				if (used[col] == 0) gmt_M_free (GMT, temp[col]);
8480 			}
8481 			if (adjust) {	/* Modify length of min/max arrays */
8482 				S->min  = gmt_M_memory (GMT, S->min, GMT->common.o.n_cols, double);
8483 				S->max  = gmt_M_memory (GMT, S->max, GMT->common.o.n_cols, double);
8484 				if (!extend) {
8485 					S->data = gmt_M_memory (GMT, S->data, GMT->common.o.n_cols, double *);
8486 					SH->alloc_mode = gmt_M_memory (GMT, SH->alloc_mode, GMT->common.o.n_cols, enum GMT_enum_alloc);
8487 				}
8488 			}
8489 			S->n_columns = GMT->common.o.n_cols;	/* New column count */
8490 		}
8491 		if (adjust) {	/* Modify length of table min/max arrays */
8492 			T->min = gmt_M_memory (GMT, T->min, GMT->common.o.n_cols, double);
8493 			T->max = gmt_M_memory (GMT, T->max, GMT->common.o.n_cols, double);
8494 		}
8495 		T->n_columns = GMT->common.o.n_cols;	/* New column count */
8496 	}
8497 	gmt_M_free (GMT, temp);
8498 	gmt_M_free (GMT, used);
8499 	if (adjust) {	/* Modify length of dataset min/max arrays */
8500 		D->min = gmt_M_memory (GMT, D->min, GMT->common.o.n_cols, double);
8501 		D->max = gmt_M_memory (GMT, D->max, GMT->common.o.n_cols, double);
8502 	}
8503 	D->n_columns = GMT->common.o.n_cols;	/* New column count */
8504 	gmt_set_dataset_minmax (GMT, D);		/* Update column stats */
8505 }
8506 
gmtio_free_segment_text(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)8507 GMT_LOCAL void gmtio_free_segment_text (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
8508 	/* Frees any array of trailing text items */
8509 	uint64_t row;
8510 	if (S->text == NULL) return;	/* No text */
8511 	for (row = 0; row < S->n_rows; row++)
8512 		gmt_M_str_free (S->text[row]);
8513 	gmt_M_free (GMT, S->text);
8514 }
8515 
8516 /*! . */
gmt_free_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT ** S)8517 void gmt_free_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT **S) {
8518 	/* Free memory allocated by gmtlib_read_table */
8519 
8520 	unsigned int k;
8521 	uint64_t col;
8522 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
8523 	struct GMT_DATASEGMENT *segment = *S;
8524 	if (!segment) return;	/* Do not try to free NULL pointer */
8525 	SH = gmt_get_DS_hidden (segment);
8526 	for (col = 0; col < segment->n_columns; col++) {
8527 		if (SH->alloc_mode[col] == GMT_ALLOC_INTERNALLY)	/* Free data GMT allocated */
8528 			gmt_M_free (GMT, segment->data[col]);
8529 	}
8530 	gmt_M_free (GMT, segment->data);
8531 	gmt_M_free (GMT, segment->min);
8532 	gmt_M_free (GMT, segment->max);
8533 	gmt_M_str_free ( segment->label);
8534 	gmt_M_str_free ( segment->header);
8535 	for (k = 0; k < 2; k++) gmt_M_str_free (SH->file[k]);
8536 	if (SH->ogr) gmtio_free_ogr_seg (GMT, segment);	/* OGR metadata */
8537 	gmtio_free_segment_text (GMT, segment);
8538 	gmt_M_free (GMT, SH->alloc_mode);
8539 	gmt_M_free (GMT, segment->hidden);
8540 	gmt_M_free (GMT, segment);
8541 	*S = NULL;
8542 }
8543 
8544 /*! . */
gmt_free_table(struct GMT_CTRL * GMT,struct GMT_DATATABLE * table)8545 void gmt_free_table (struct GMT_CTRL *GMT, struct GMT_DATATABLE *table) {
8546 	unsigned int k;
8547 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
8548 	if (!table) return;		/* Do not try to free NULL pointer */
8549 	TH = gmt_get_DT_hidden (table);
8550 	if (TH->alloc_mode == GMT_ALLOC_EXTERNALLY) return;	/* Not ours to free */
8551 	if (table->n_headers) {
8552 		for (k = 0; k < table->n_headers; k++) gmt_M_str_free (table->header[k]);
8553 		gmt_M_free (GMT, table->header);
8554 	}
8555 	gmt_M_free (GMT, table->min);
8556 	gmt_M_free (GMT, table->max);
8557 	for (k = 0; k < 2; k++) gmt_M_str_free (TH->file[k]);
8558 	gmtlib_free_ogr (GMT, &(TH->ogr), 1);
8559 	if (table->segment) {	/* Free segments */
8560 		uint64_t seg;
8561 		for (seg = 0; seg < table->n_segments; seg++) gmt_free_segment (GMT, &(table->segment[seg]));
8562 		gmt_M_free (GMT, table->segment);
8563 	}
8564 	gmt_M_free (GMT, table->hidden);
8565 	gmt_M_free (GMT, table);
8566 }
8567 
gmtlib_free_dataset_misc(struct GMT_CTRL * GMT,struct GMT_DATASET * data)8568 void gmtlib_free_dataset_misc (struct GMT_CTRL *GMT, struct GMT_DATASET *data) {
8569 	/* This takes pointer to data array and thus can return it as NULL */
8570 	unsigned int k;
8571 	struct GMT_DATASET_HIDDEN *DH = NULL;
8572 	if (!data) return;	/* Do not try to free NULL pointer */
8573 	DH = gmt_get_DD_hidden (data);
8574 	gmt_M_free (GMT, data->min);
8575 	gmt_M_free (GMT, data->max);
8576 	gmt_M_free (GMT, data->table);
8577 	for (k = 0; k < 2; k++) gmt_M_str_free (DH->file[k]);
8578 	gmt_M_free (GMT, data->hidden);
8579 }
8580 
8581 /*! . */
gmtlib_free_dataset_ptr(struct GMT_CTRL * GMT,struct GMT_DATASET * data)8582 void gmtlib_free_dataset_ptr (struct GMT_CTRL *GMT, struct GMT_DATASET *data) {
8583 	/* This takes pointer to data array and thus can return it as NULL */
8584 	uint64_t tbl;
8585 	if (!data) return;	/* Do not try to free NULL pointer */
8586 	for (tbl = 0; tbl < data->n_tables; tbl++)
8587 		gmt_free_table (GMT, data->table[tbl]);
8588 	gmtlib_free_dataset_misc (GMT, data);
8589 }
8590 
8591 /*! . */
gmt_free_dataset(struct GMT_CTRL * GMT,struct GMT_DATASET ** data)8592 void gmt_free_dataset (struct GMT_CTRL *GMT, struct GMT_DATASET **data) {
8593 	/* This takes pointer to data array and thus can return it as NULL */
8594 	gmtlib_free_dataset_ptr (GMT, *data);
8595 	gmt_M_free (GMT, *data);
8596 }
8597 
gmtio_get_image(struct GMT_CTRL * GMT)8598 GMT_LOCAL struct GMT_IMAGE *gmtio_get_image (struct GMT_CTRL *GMT) {
8599 	struct GMT_IMAGE *I = gmt_M_memory (GMT, NULL, 1, struct GMT_IMAGE);
8600 	I->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_IMAGE_HIDDEN);
8601 	return (I);
8602 }
8603 
8604 /*! . */
gmtlib_create_image(struct GMT_CTRL * GMT)8605 struct GMT_IMAGE *gmtlib_create_image (struct GMT_CTRL *GMT) {
8606 	/* Allocates space for a new image container. */
8607 	struct GMT_IMAGE_HIDDEN *IH = NULL;
8608 	struct GMT_IMAGE *I = gmtio_get_image (GMT);
8609 	IH = gmt_get_I_hidden (I);
8610 	I->header = gmt_get_header (GMT);
8611 	IH->alloc_mode = GMT_ALLOC_INTERNALLY;		/* Memory can be freed by GMT. */
8612 	IH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level. */
8613 	IH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
8614 	gmt_grd_init (GMT, I->header, NULL, false); /* Set default values */
8615 #ifdef HAVE_GDAL
8616 	if (GMT->current.gdal_read_in.O.mem_layout[0])
8617 		gmt_strncpy (I->header->mem_layout, GMT->current.gdal_read_in.O.mem_layout, 4);	/* Set the current memory layout */
8618 	else
8619 #endif
8620 		gmt_strncpy (I->header->mem_layout, GMT_IMAGE_LAYOUT, 4);	/* Set the default array memory layout */
8621 	GMT_Set_Index (GMT->parent, I->header, GMT_IMAGE_LAYOUT);
8622 	return (I);
8623 }
8624 
8625 /*! . */
gmtlib_duplicate_image(struct GMT_CTRL * GMT,struct GMT_IMAGE * I,unsigned int mode)8626 struct GMT_IMAGE *gmtlib_duplicate_image (struct GMT_CTRL *GMT, struct GMT_IMAGE *I, unsigned int mode) {
8627 	/* Duplicates an entire image, including data if requested.
8628 	 * For the purpose of alloc/duplicate, items like colormap and color_interp is
8629 	 * considered part of the header and is always duplicated. */
8630 	struct GMT_IMAGE *Inew = NULL;
8631 	struct GMT_GRID_HEADER *save = NULL;
8632 	struct GMT_IMAGE_HIDDEN *IH = NULL;
8633 
8634 	Inew = gmtlib_create_image (GMT);
8635 	save = Inew->header;
8636 	IH = Inew->hidden;
8637 	gmt_M_memcpy (Inew, I, 1, struct GMT_IMAGE);	/* Copy everything, but this also messes with header/data pointers */
8638 	Inew->header = save;	/* Reset to correct header pointer */
8639 	Inew->hidden = IH;	/* Reset to correct hidden pointer */
8640 	Inew->data = NULL;	/* Reset to NULL data pointer */
8641 	Inew->colormap = NULL;	/* Reset to NULL colormap pointer */
8642 	Inew->color_interp = NULL;	/* Reset to NULL pointer */
8643 	Inew->alpha = NULL;	/* Reset to NULL alpha pointer */
8644 	Inew->x = NULL;		/* Reset to NULL x pointer */
8645 	Inew->y = NULL;		/* Reset to NULL y pointer */
8646 	gmt_copy_gridheader (GMT, Inew->header, I->header);
8647 	if (I->colormap) {	/* Also deal with the colormap for indexed images. If found we duplicate */
8648 		size_t nc = I->n_indexed_colors * 4 + 1;
8649 		if (I->n_indexed_colors > 2000)		/* If colormap is Mx4 or has encoded the alpha color */
8650 			nc = (uint64_t)(floor(I->n_indexed_colors / 1000.0)) * 4 + 1;
8651 		Inew->colormap = gmt_M_memory (GMT, NULL, nc, int);
8652 		gmt_M_memcpy (Inew->colormap, I->colormap, nc, int);
8653 		if (I->color_interp) Inew->color_interp = I->color_interp;
8654 	}
8655 
8656 	if ((mode & GMT_DUPLICATE_DATA) || (mode & GMT_DUPLICATE_ALLOC)) {	/* Also allocate and possibly duplicate data array */
8657 		Inew->data = gmt_M_memory_aligned (GMT, NULL, I->header->size * I->header->n_bands, char);
8658 		if (mode & GMT_DUPLICATE_DATA) gmt_M_memcpy (Inew->data, I->data, I->header->size * I->header->n_bands, char);
8659 		if (I->alpha) {	/* Also deal with the alpha layer */
8660 			Inew->alpha = gmt_M_memory_aligned (GMT, NULL, I->header->size, unsigned char);
8661 			if (mode & GMT_DUPLICATE_DATA) gmt_M_memcpy (Inew->alpha, I->alpha, I->header->size, unsigned char);
8662 		}
8663 		Inew->x = gmt_grd_coord (GMT, Inew->header, GMT_X);	/* Get array of x coordinates */
8664 		Inew->y = gmt_grd_coord (GMT, Inew->header, GMT_Y);	/* Get array of y coordinates */
8665 	}
8666 	return (Inew);
8667 }
8668 
8669 /*! . */
gmtlib_free_image_ptr(struct GMT_CTRL * GMT,struct GMT_IMAGE * I,bool free_image)8670 void gmtlib_free_image_ptr (struct GMT_CTRL *GMT, struct GMT_IMAGE *I, bool free_image) {
8671 	/* Free contents of image pointer */
8672 	struct GMT_IMAGE_HIDDEN *IH = NULL;
8673 	if (!I) return;	/* Nothing to deallocate */
8674 	IH = gmt_get_I_hidden (I);
8675 	if (free_image && I->data) {
8676 		if (IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8677 			gmt_M_free_aligned (GMT, I->data);
8678 	}
8679 	if (free_image && I->alpha) {
8680 		if (IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8681 			gmt_M_free_aligned (GMT, I->alpha);
8682 	}
8683 	if (I->x && I->y && free_image) {
8684 		if (IH->alloc_mode == GMT_ALLOC_INTERNALLY) {
8685 			gmt_M_free (GMT, I->x);
8686 			gmt_M_free (GMT, I->y);
8687 		}
8688 		I->x = I->y = NULL;	/* This will remove reference to external memory since gmt_M_free would not have been called */
8689 	}
8690 	if (I->header) {	/* Free the header structure and anything allocated by it */
8691 		struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
8692 		if (I->header->ProjRefPROJ4 && IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8693 			gmt_M_str_free (I->header->ProjRefPROJ4);
8694 		if (I->header->ProjRefWKT && IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8695 			gmt_M_str_free(I->header->ProjRefWKT);
8696 		if (HH->pocket && IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8697 			gmt_M_str_free (HH->pocket);
8698 		gmt_M_free (GMT, HH);
8699 		gmt_M_free (GMT, I->header);
8700 	}
8701 	if (I->colormap && IH->alloc_mode == GMT_ALLOC_INTERNALLY)
8702 		gmt_M_free (GMT, I->colormap);
8703 
8704 	gmt_M_free (GMT, I->hidden);
8705 }
8706 
8707 /*! . */
gmtlib_free_image(struct GMT_CTRL * GMT,struct GMT_IMAGE ** I,bool free_image)8708 void gmtlib_free_image (struct GMT_CTRL *GMT, struct GMT_IMAGE **I, bool free_image) {
8709 	/* By taking a reference to the image pointer we can set it to NULL when done */
8710 	gmtlib_free_image_ptr (GMT, *I, free_image);
8711 	gmt_M_free (GMT, *I);
8712 }
8713 
8714 /*! . */
gmt_create_vector(struct GMT_CTRL * GMT,uint64_t n_columns,unsigned int direction)8715 struct GMT_VECTOR *gmt_create_vector (struct GMT_CTRL *GMT, uint64_t n_columns, unsigned int direction) {
8716 	/* Allocates space for a new vector container.  No space allocated for the vectors themselves */
8717 	uint64_t col;
8718 	struct GMT_VECTOR *V = NULL;
8719 	struct GMT_VECTOR_HIDDEN *VH = NULL;
8720 	gmt_M_unused(direction);
8721 
8722 	if ((V = gmt_M_memory (GMT, NULL, 1U, struct GMT_VECTOR)) == NULL)
8723 		return_null (GMT, GMT_MEMORY_ERROR);
8724 	VH = V->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_VECTOR_HIDDEN);
8725 	if (n_columns) V->data = gmt_M_memory_aligned (GMT, NULL, n_columns, union GMT_UNIVECTOR);
8726 	if (n_columns) V->type = gmt_M_memory (GMT, NULL, n_columns, enum GMT_enum_type);
8727 	if (n_columns) VH->alloc_mode = gmt_M_memory (GMT, NULL, n_columns, enum GMT_enum_alloc);
8728 	V->n_columns = n_columns;
8729 	/* We expect external memory for input and GMT-allocated memory on output */
8730 	for (col = 0; col < n_columns; col++)
8731 		VH->alloc_mode[col] = (direction == GMT_IN) ? GMT_ALLOC_EXTERNALLY : GMT_ALLOC_INTERNALLY;
8732 	VH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level */
8733 	VH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
8734 
8735 	return (V);
8736 }
8737 
8738 /*! . */
gmtlib_alloc_vectors(struct GMT_CTRL * GMT,struct GMT_VECTOR * V,uint64_t n_alloc)8739 int gmtlib_alloc_vectors (struct GMT_CTRL *GMT, struct GMT_VECTOR *V, uint64_t n_alloc) {
8740 	/* Allocate space for each column according to data type; V->n_rows is not touched */
8741 	uint64_t col;
8742 	int error;
8743 	struct GMT_VECTOR_HIDDEN *VH = NULL;
8744 
8745 	if (!V) return (GMT_PTR_IS_NULL);			/* Nothing to allocate to */
8746 	if (V->n_columns == 0) return (GMT_PTR_IS_NULL);	/* No columns specified */
8747 	if (n_alloc == 0) return (GMT_N_ROWS_NOT_SET);		/* No rows specified */
8748 	if (!V->data) return (GMT_PTR_IS_NULL);			/* Array of columns have not been allocated */
8749 	VH = gmt_get_V_hidden (V);
8750 	for (col = 0; col < V->n_columns; col++) {
8751 		if ((error = gmtlib_alloc_univector (GMT, &V->data[col], V->type[col], n_alloc)) != GMT_NOERROR) return (error);
8752 		VH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
8753 	}
8754 	return (GMT_NOERROR);
8755 }
8756 
8757 /*! . */
gmtlib_duplicate_vector(struct GMT_CTRL * GMT,struct GMT_VECTOR * V_in,unsigned int mode)8758 struct GMT_VECTOR *gmtlib_duplicate_vector (struct GMT_CTRL *GMT, struct GMT_VECTOR *V_in, unsigned int mode) {
8759 	/* Duplicates a vector container - optionally allocates and duplicates the data vectors */
8760 	struct GMT_VECTOR *V = NULL;
8761 	unsigned int col;
8762 	int error;
8763 	if ((V = gmt_create_vector (GMT, V_in->n_columns, GMT_IN)) == NULL)
8764 		return_null (GMT, GMT_MEMORY_ERROR);
8765 	for (col = 0; col < V_in->n_columns; col++)	/* Set same data type for all vectors */
8766 		V->type[col] = V_in->type[col];
8767 	if ((mode & GMT_DUPLICATE_DATA) || (mode & GMT_DUPLICATE_ALLOC)) {	/* Also allocate and possibly duplicate data array */
8768 		if ((error = gmtlib_alloc_vectors (GMT, V, V_in->n_rows)) != GMT_NOERROR)
8769 			return_null (GMT, error);
8770 		if (mode & GMT_DUPLICATE_DATA) {
8771 			for (col = 0; col < V_in->n_columns; col++)
8772 				gmtio_duplicate_univector (GMT, &V->data[col], &V_in->data[col], V->type[col], V_in->n_rows);
8773 			if (V_in->text) {	/* Also duplicate string array */
8774 				uint64_t row;
8775 				struct GMT_VECTOR_HIDDEN *VH = gmt_get_V_hidden (V);
8776 				V->text = gmt_M_memory (GMT, NULL, V_in->n_rows, char *);
8777 				for (row = 0; row < V_in->n_rows; row++)
8778 					V->text[row] = strdup (V_in->text[row]);
8779 				VH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
8780 			}
8781 		}
8782 	}
8783 	return (V);
8784 }
8785 
8786 /*! . */
gmtlib_alloc_univector(struct GMT_CTRL * GMT,union GMT_UNIVECTOR * u,unsigned int type,uint64_t n_rows)8787 int gmtlib_alloc_univector (struct GMT_CTRL *GMT, union GMT_UNIVECTOR *u, unsigned int type, uint64_t n_rows) {
8788 	/* Allocate space for one univector according to data type */
8789 	int error = GMT_OK;
8790 	switch (type) {
8791 		case GMT_UCHAR:  u->uc1 = gmt_M_memory (GMT, u->uc1, n_rows, uint8_t);   if (u->uc1 == NULL) error = GMT_MEMORY_ERROR; break;
8792 		case GMT_CHAR:   u->sc1 = gmt_M_memory (GMT, u->sc1, n_rows, int8_t);    if (u->sc1 == NULL) error = GMT_MEMORY_ERROR; break;
8793 		case GMT_USHORT: u->ui2 = gmt_M_memory (GMT, u->ui2, n_rows, uint16_t);  if (u->ui2 == NULL) error = GMT_MEMORY_ERROR; break;
8794 		case GMT_SHORT:  u->si2 = gmt_M_memory (GMT, u->si2, n_rows, int16_t);   if (u->si2 == NULL) error = GMT_MEMORY_ERROR; break;
8795 		case GMT_UINT:   u->ui4 = gmt_M_memory (GMT, u->ui4, n_rows, uint32_t);  if (u->ui4 == NULL) error = GMT_MEMORY_ERROR; break;
8796 		case GMT_INT:    u->si4 = gmt_M_memory (GMT, u->si4, n_rows, int32_t);   if (u->si4 == NULL) error = GMT_MEMORY_ERROR; break;
8797 		case GMT_ULONG:  u->ui8 = gmt_M_memory (GMT, u->ui8, n_rows, uint64_t);  if (u->ui8 == NULL) error = GMT_MEMORY_ERROR; break;
8798 		case GMT_LONG:   u->si8 = gmt_M_memory (GMT, u->si8, n_rows, int64_t);   if (u->si8 == NULL) error = GMT_MEMORY_ERROR; break;
8799 		case GMT_FLOAT:  u->f4  = gmt_M_memory (GMT, u->f4,  n_rows, float);     if (u->f4  == NULL) error = GMT_MEMORY_ERROR; break;
8800 		case GMT_DOUBLE: u->f8  = gmt_M_memory (GMT, u->f8,  n_rows, double);    if (u->f8  == NULL) error = GMT_MEMORY_ERROR; break;
8801 	}
8802 	return (error);
8803 }
8804 
8805 /* Free the individual strings in text[] that were allocated by strdup */
gmtio_free_text_array(uint64_t n,char ** text)8806 GMT_LOCAL void gmtio_free_text_array (uint64_t n, char **text) {
8807 	uint64_t row;
8808 	for (row = 0; row < n; row++) gmt_M_str_free (text[row]);
8809 }
8810 
8811 /*! . */
gmtlib_free_vector_ptr(struct GMT_CTRL * GMT,struct GMT_VECTOR * V,bool free_vector)8812 void gmtlib_free_vector_ptr (struct GMT_CTRL *GMT, struct GMT_VECTOR *V, bool free_vector) {
8813 	/* By taking a reference to the vector pointer we can set it to NULL when done */
8814 	/* free_vector = false means the vectors are not to be freed but the data array itself will be */
8815 	struct GMT_VECTOR_HIDDEN *VH = NULL;
8816 	if (!V) return;	/* Nothing to deallocate */
8817 	/* Only free V->data if allocated by GMT AND free_vector is true */
8818 	VH = gmt_get_V_hidden (V);
8819 	if (V->data && free_vector) {
8820 		uint64_t col;
8821 		for (col = 0; col < V->n_columns; col++) {
8822 			if (VH->alloc_mode[col] == GMT_ALLOC_INTERNALLY) gmtio_free_univector (GMT, &(V->data[col]), V->type[col]);
8823 			gmtio_null_univector (GMT, &(V->data[col]), V->type[col]);
8824 		}
8825 	}
8826 	if (V->text && free_vector && VH->alloc_mode_text == GMT_ALLOC_INTERNALLY) {
8827 		gmtio_free_text_array (V->n_rows, V->text);
8828 		gmt_M_free (GMT, V->text);
8829 	}
8830 	if (V->n_headers) {
8831 		for (unsigned int k = 0; k < V->n_headers; k++) gmt_M_str_free (V->header[k]);
8832 		gmt_M_free (GMT, V->header);
8833 	}
8834 	gmt_M_free (GMT, V->data);	/* Sometimes we free a V that has nothing allocated so must check */
8835 	gmt_M_free (GMT, V->type);
8836 	gmt_M_free (GMT, VH->alloc_mode);
8837 	gmt_M_free (GMT, V->hidden);
8838 }
8839 
8840 /*! . */
gmt_free_vector(struct GMT_CTRL * GMT,struct GMT_VECTOR ** V,bool free_vector)8841 void gmt_free_vector (struct GMT_CTRL *GMT, struct GMT_VECTOR **V, bool free_vector) {
8842 	/* By taking a reference to the vector pointer we can set it to NULL when done */
8843 	/* free_vector = false means the vectors are not to be freed but the data array itself will be */
8844 	gmtlib_free_vector_ptr (GMT, *V, free_vector);
8845 	gmt_M_free (GMT, *V);
8846 }
8847 
8848 /*! . */
gmtlib_create_matrix(struct GMT_CTRL * GMT,uint64_t layers,unsigned int direction,int flag)8849 struct GMT_MATRIX * gmtlib_create_matrix (struct GMT_CTRL *GMT, uint64_t layers, unsigned int direction, int flag) {
8850 	/* Allocates space for a new matrix container. */
8851 	struct GMT_MATRIX *M = NULL;
8852 	struct GMT_MATRIX_HIDDEN *MH = NULL;
8853 	M = gmt_M_memory (GMT, NULL, 1, struct GMT_MATRIX);
8854 	MH = M->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_MATRIX_HIDDEN);
8855 	/* We expect external memory for input and GMT-allocated memory on output */
8856 	MH->alloc_mode = (direction == GMT_IN) ? GMT_ALLOC_EXTERNALLY : GMT_ALLOC_INTERNALLY;
8857 	MH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level. */
8858 	MH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
8859 	M->n_layers = (layers) ? layers : 1;		/* Default to 1 if not set */
8860 	switch (flag) {
8861 		case GMT_IS_ROW_FORMAT:	M->shape = GMT_IS_ROW_FORMAT;  break;	/* row-major */
8862 		case GMT_IS_COL_FORMAT:	M->shape = GMT_IS_COL_FORMAT;  break;	/* col-major */
8863 		default: 		M->shape = GMT->parent->shape; break;	/* Default layout (row vs column) selected by GMT_Create_Session [row-major] */
8864 	}
8865 	return (M);
8866 }
8867 
8868 /*! . */
gmtlib_duplicate_matrix(struct GMT_CTRL * GMT,struct GMT_MATRIX * M_in,unsigned int mode)8869 struct GMT_MATRIX * gmtlib_duplicate_matrix (struct GMT_CTRL *GMT, struct GMT_MATRIX *M_in, unsigned int mode) {
8870 	/* Duplicates a matrix container - optionally duplicates the data array */
8871 	struct GMT_MATRIX *M = NULL;
8872 	M = gmt_M_memory (GMT, NULL, 1, struct GMT_MATRIX);
8873 	gmt_M_memcpy (M, M_in, 1, struct GMT_MATRIX);
8874 	gmt_M_memset (&M->data, 1, union GMT_UNIVECTOR);
8875 	if ((mode & GMT_DUPLICATE_DATA) || (mode & GMT_DUPLICATE_ALLOC)) {	/* Also allocate and possibly duplicate data array */
8876 		size_t size = M->n_rows * M->n_columns;
8877 		if (gmtlib_alloc_univector (GMT, &(M->data), M->type, size)) {
8878 			gmt_M_free (GMT, M);
8879 			return (NULL);
8880 		}
8881 		if (mode & GMT_DUPLICATE_DATA) {
8882 			gmtio_duplicate_univector (GMT, &M->data, &M_in->data, M->type, size);
8883 			if (M_in->text) {	/* Also duplicate string array */
8884 				uint64_t row;
8885 				struct GMT_MATRIX_HIDDEN *MH = gmt_get_M_hidden (M);
8886 				M->text = gmt_M_memory (GMT, NULL, M_in->n_rows, char *);
8887 				for (row = 0; row < M_in->n_rows; row++)
8888 					M->text[row] = strdup (M_in->text[row]);
8889 				MH->alloc_mode_text = GMT_ALLOC_INTERNALLY;
8890 			}
8891 		}
8892 	}
8893 	return (M);
8894 }
8895 
8896 /*! . */
gmtlib_free_matrix_ptr(struct GMT_CTRL * GMT,struct GMT_MATRIX * M,bool free_matrix)8897 void gmtlib_free_matrix_ptr (struct GMT_CTRL *GMT, struct GMT_MATRIX *M, bool free_matrix) {
8898 	/* Free everything but the struct itself  */
8899 	struct GMT_MATRIX_HIDDEN *MH = NULL;
8900 	enum GMT_enum_alloc alloc_mode;
8901 	if (!M) return;	/* Nothing to deallocate */
8902 	/* Only free M->data if allocated by GMT AND free_matrix is true */
8903 	MH = gmt_get_M_hidden (M);
8904 	if (&(M->data) && free_matrix) {
8905 		if (MH->alloc_mode == GMT_ALLOC_INTERNALLY) gmtio_free_univector (GMT, &(M->data), M->type);
8906 		gmtio_null_univector (GMT, &(M->data), M->type);
8907 	}
8908 	if (M->text && free_matrix && MH->alloc_mode_text == GMT_ALLOC_INTERNALLY) {
8909 		gmtio_free_text_array (M->n_rows, M->text);
8910 		gmt_M_free (GMT, M->text);
8911 	}
8912 	if (M->n_headers) {
8913 		for (unsigned int k = 0; k < M->n_headers; k++) gmt_M_str_free (M->header[k]);
8914 		gmt_M_free (GMT, M->header);
8915 	}
8916 	alloc_mode = MH->alloc_mode;
8917 	gmt_M_free (GMT, M->hidden);
8918 }
8919 
gmtlib_free_matrix(struct GMT_CTRL * GMT,struct GMT_MATRIX ** M,bool free_matrix)8920 void gmtlib_free_matrix (struct GMT_CTRL *GMT, struct GMT_MATRIX **M, bool free_matrix) {
8921 	/* By taking a reference to the matrix pointer we can set it to NULL when done */
8922 	gmtlib_free_matrix_ptr (GMT, *M, free_matrix);
8923 	gmt_M_free (GMT, *M);
8924 }
8925 
8926 /*!  m input matrix
8927 	n_rows number of rows of \a m
8928 	n_columns number of columns of \a m
8929 
8930    Performs in-place transposition of matrix m.
8931    Modified from http://programmers.stackexchange.com/questions/271713/transpose-a-matrix-without-a-buffering-one
8932 */
8933 
gmtlib_union_transpose(struct GMT_CTRL * GMT,union GMT_UNIVECTOR * m,const uint64_t n_rows,const uint64_t n_columns,unsigned int type)8934 void gmtlib_union_transpose(struct GMT_CTRL *GMT, union GMT_UNIVECTOR *m, const uint64_t n_rows, const uint64_t n_columns, unsigned int type) {
8935 	/* In-place transpose of rectangular matrix m */
8936 	union GMT_UNIVECTOR tmp;
8937 	int error = GMT_OK;
8938 
8939 	switch (type) {
8940 		case GMT_UCHAR:  tmp.uc1 = gmt_M_memory(GMT, NULL, 1, uint8_t);   if (tmp.uc1 == NULL) error = GMT_MEMORY_ERROR; break;
8941 		case GMT_CHAR:   tmp.sc1 = gmt_M_memory(GMT, NULL, 1, int8_t);    if (tmp.sc1 == NULL) error = GMT_MEMORY_ERROR; break;
8942 		case GMT_USHORT: tmp.ui2 = gmt_M_memory(GMT, NULL, 1, uint16_t);  if (tmp.ui2 == NULL) error = GMT_MEMORY_ERROR; break;
8943 		case GMT_SHORT:  tmp.si2 = gmt_M_memory(GMT, NULL, 1, int16_t);   if (tmp.si2 == NULL) error = GMT_MEMORY_ERROR; break;
8944 		case GMT_UINT:   tmp.ui4 = gmt_M_memory(GMT, NULL, 1, uint32_t);  if (tmp.ui4 == NULL) error = GMT_MEMORY_ERROR; break;
8945 		case GMT_INT:    tmp.si4 = gmt_M_memory(GMT, NULL, 1, int32_t);   if (tmp.si4 == NULL) error = GMT_MEMORY_ERROR; break;
8946 		case GMT_ULONG:  tmp.ui8 = gmt_M_memory(GMT, NULL, 1, uint64_t);  if (tmp.ui8 == NULL) error = GMT_MEMORY_ERROR; break;
8947 		case GMT_LONG:   tmp.si8 = gmt_M_memory(GMT, NULL, 1, int64_t);   if (tmp.si8 == NULL) error = GMT_MEMORY_ERROR; break;
8948 		case GMT_FLOAT:   tmp.f4 = gmt_M_memory(GMT, NULL, 1, float);     if (tmp.f4 == NULL)  error = GMT_MEMORY_ERROR; break;
8949 		case GMT_DOUBLE:  tmp.f8 = gmt_M_memory(GMT, NULL, 1, double);    if (tmp.f8 == NULL)  error = GMT_MEMORY_ERROR; break;
8950 		default:          tmp.f8 = NULL;	/* To shut up the compiler about the "potentially uninitialized local variable 'tmp' used" */
8951 	}
8952 	if (error) {
8953 		GMT_Report(GMT, GMT_MSG_ERROR, "gmtlib_union_transpose: Failure to allocate memory.\n");
8954 		return;
8955 	}
8956 
8957 	for (uint64_t start = 0; start <= n_columns * n_rows - 1; ++start) {
8958 		uint64_t next = start, i = 0;
8959 		do {
8960 			++i; next = (next % n_rows) * n_columns + next / n_rows;
8961 		} while (next > start);
8962 		if (next >= start && i != 1) {
8963 			switch (type) {
8964 				case GMT_DOUBLE:	*tmp.f8  =  m->f8[start];	break;
8965 				case GMT_FLOAT:		*tmp.f4  =  m->f4[start];	break;
8966 				case GMT_ULONG:		*tmp.ui8 = m->ui8[start];	break;
8967 				case GMT_LONG:		*tmp.si8 = m->si8[start];	break;
8968 				case GMT_UINT:		*tmp.ui4 = m->ui4[start];	break;
8969 				case GMT_INT:		*tmp.si4 = m->si4[start];	break;
8970 				case GMT_USHORT:	*tmp.ui2 = m->ui2[start];	break;
8971 				case GMT_SHORT:		*tmp.si2 = m->si2[start];	break;
8972 				case GMT_UCHAR:		*tmp.uc1 = m->uc1[start];	break;
8973 				case GMT_CHAR:		*tmp.sc1 = m->sc1[start];	break;
8974 			}
8975 			next = start;
8976 			do {
8977 				i = (next % n_rows) * n_columns + next / n_rows;
8978 				switch (type) {
8979 					case GMT_DOUBLE:	m->f8[next]  = (i == start) ? *tmp.f8  :  m->f8[i];	break;
8980 					case GMT_FLOAT:		m->f4[next]  = (i == start) ? *tmp.f4  :  m->f4[i];	break;
8981 					case GMT_ULONG:		m->ui8[next] = (i == start) ? *tmp.ui8 : m->ui8[i];	break;
8982 					case GMT_LONG:		m->si8[next] = (i == start) ? *tmp.si8 : m->si8[i];	break;
8983 					case GMT_UINT:		m->ui4[next] = (i == start) ? *tmp.ui4 : m->ui4[i];	break;
8984 					case GMT_INT:		m->si4[next] = (i == start) ? *tmp.si4 : m->si4[i];	break;
8985 					case GMT_USHORT:	m->ui2[next] = (i == start) ? *tmp.ui2 : m->ui2[i];	break;
8986 					case GMT_SHORT:		m->si2[next] = (i == start) ? *tmp.si2 : m->si2[i];	break;
8987 					case GMT_UCHAR:		m->uc1[next] = (i == start) ? *tmp.uc1 : m->uc1[i];	break;
8988 					case GMT_CHAR:		m->sc1[next] = (i == start) ? *tmp.sc1 : m->sc1[i];	break;
8989 				}
8990 				next = i;
8991 			} while (next > start);
8992 		}
8993 	}
8994 	gmtio_free_univector (GMT, &(tmp), type);
8995 
8996 }
8997 
gmt_convert_double(struct GMT_CTRL * GMT,char * text,double * value)8998 int gmt_convert_double (struct GMT_CTRL *GMT, char *text, double *value) {
8999 	/* Convert text to floating point number and return an error if it failed and set value to NaN.
9000 	 * Eventually, all options passing in floating point numbers should be validated with this function.
9001 	 * Numbers such as dimensions with trailing units are handled by other functions. */
9002 	char *endptr = NULL;
9003 	int error;
9004 	if (text == NULL || text[0] == '\0') return GMT_NOTSET;	/* Not given anything */
9005 
9006     *value = strtod (text, &endptr);
9007 	if ((*endptr == '\0') || (isspace(*endptr) != 0)) {
9008         error = GMT_NOERROR;
9009 	}
9010     else {
9011 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert %s to floating point as it contains invalid characters (%s).\n", text, endptr);
9012         *value = GMT->session.d_NaN;
9013         error = GMT_PARSE_ERROR;
9014     }
9015     return (error);
9016 }
9017 
9018 /*! . */
gmt_not_numeric(struct GMT_CTRL * GMT,char * text)9019 bool gmt_not_numeric (struct GMT_CTRL *GMT, char *text) {
9020 	/* true if text cannot represent a valid number  However,
9021 	 * false does not therefore mean we have a valid number because
9022 	 * <date>T<clock> representations may use all kinds
9023 	 * of punctuations or letters according to the various format
9024 	 * settings in gmt.conf.  Here we just rule out things
9025 	 * that we are sure of. */
9026 
9027 	unsigned int i, k, n_digits = 0, n_period = 0, period = 0, n_plus = 0, n_minus = 0;
9028 	static char *valid = "0123456789-+.:WESNT" GMT_LEN_UNITS GMT_DIM_UNITS;
9029 	gmt_M_unused(GMT);
9030 	if (!text) return (true);		/* NULL pointer */
9031 	if (!strlen (text)) return (true);	/* Blank string */
9032 	if (isalpha ((int)text[0])) return (true);	/* Numbers cannot start with letters */
9033 	i = (int)text[0];
9034 	if (!(text[0] == '+' || text[0] == '-' || text[0] == '.' || (i <= 255 && isdigit(i)) )) return (true);	/* Numbers must be [+|-][.][<digits>] */
9035 	for (i = 0; text[i]; i++) {	/* Check each character */
9036 		/* First check for ASCII values that should never appear in any number */
9037 		if (!strchr (valid, text[i])) return (true);	/* Found a char not among valid letters */
9038 		if (isdigit ((int)text[i])) n_digits++;
9039 		if (text[i] == '.') {
9040 			n_period++;
9041 			period = i;
9042 		}
9043 		if (text[i] == '+') n_plus++;
9044 		if (text[i] == '-') n_minus++;
9045 	}
9046 	if (n_digits == 0 || n_period > 1 || (n_plus + n_minus) > 2) return (true);
9047 	if (n_period) {	/* Check if we have filename.ext with ext having no numbers */
9048 		for (i = period + 1, n_digits = k = 0; text[i]; i++, k++) if (isdigit ((int)text[i])) n_digits++;
9049 		if (k > 0 && n_digits == 0) return (true);	/* Probably a file */
9050 	}
9051 	return (false);	/* This may in fact be numeric */
9052 }
9053 
gmt_is_float(struct GMT_CTRL * GMT,char * text)9054 bool gmt_is_float (struct GMT_CTRL *GMT, char *text) {
9055 	/* Returns true if text is a valid floating point number.
9056 	 * Only called if we know text is not longitude or time, etc. */
9057 	int len;
9058 	double dummy = 0.0;
9059 	gmt_M_unused(GMT);
9060 
9061 	if (sscanf (text, "%lf %n", &dummy, &len) == 1 && len == (int)strlen(text))
9062 		return true;
9063 	else
9064 		return false;
9065 }
9066 
9067 /*! . */
gmtlib_conv_text2datarec(struct GMT_CTRL * GMT,char * record,unsigned int ncols,double * out,unsigned int * ptext)9068 unsigned int gmtlib_conv_text2datarec (struct GMT_CTRL *GMT, char *record, unsigned int ncols, double *out, unsigned int *ptext) {
9069 	/* Used when we read text records and need to obtain doubles */
9070 	/* Convert the first ncols fields in the record string to numbers that we
9071 	 * store in GMT->current.io.curr_rec, which is what normal GMT_DATASET processing do.
9072 	 * We stop if we run out of fields or fail to convert.  */
9073 
9074 	unsigned int k = 0, pos = 0;
9075 	char p[GMT_BUFSIZ];
9076 
9077 	while (k < ncols && gmt_strtok (record, GMT->current.io.scan_separators, &pos, p)) {	/* Get each field in turn and bail when done */
9078 		if (!(p[0] == '+' || p[0] == '-' || p[0] == '.' || isdigit ((int)p[0]))) break;	/* Numbers must be [+|-][.][<digits>] */
9079 		if (strchr (p, '/')) break;	/* Somehow got to a color triplet? */
9080 		gmt_scanf (GMT, p, gmt_M_type (GMT, GMT_IN, k), &out[k]);	/* Be tolerant of errors */
9081 		k++;
9082 	}
9083 	*ptext = pos;	/* Location of trailing text in record */
9084 	return (k);
9085 }
9086 
9087 /*! . */
gmtlib_ogr_get_type(char * item)9088 int gmtlib_ogr_get_type (char *item) {
9089 	if (!strcmp (item, "double")   || !strcmp (item, "DOUBLE")) return (GMT_DOUBLE);
9090 	if (!strcmp (item, "float")    || !strcmp (item, "FLOAT")) return (GMT_FLOAT);
9091 	if (!strcmp (item, "integer")  || !strcmp (item, "INTEGER")) return (GMT_INT);
9092 	if (!strcmp (item, "char")     || !strcmp (item, "CHAR")) return (GMT_CHAR);
9093 	if (!strcmp (item, "string")   || !strcmp (item, "STRING")) return (GMT_TEXT);
9094 	if (!strcmp (item, "datetime") || !strcmp (item, "DATETIME")) return (GMT_DATETIME);
9095 	if (!strcmp (item, "logical")  || !strcmp (item, "LOGICAL")) return (GMT_UCHAR);
9096 	return (GMT_NOTSET);
9097 }
9098 
9099 /*! . */
gmtlib_free_ogr(struct GMT_CTRL * GMT,struct GMT_OGR ** G,unsigned int mode)9100 void gmtlib_free_ogr (struct GMT_CTRL *GMT, struct GMT_OGR **G, unsigned int mode) {
9101 	/* Free up GMT/OGR structure, if used */
9102 	unsigned int k;
9103 	if (!(*G)) return;	/* Nothing to do */
9104 	/* mode = 0 only frees the aspatial data value array, while mode = 1 frees the entire struct and contents */
9105 	for (k = 0; k < (*G)->n_aspatial; k++) {
9106 		if (mode == 1 && (*G)->name) gmt_M_str_free ((*G)->name[k]);
9107 		if ((*G)->tvalue) gmt_M_str_free ((*G)->tvalue[k]);
9108 	}
9109 	gmt_M_free (GMT, (*G)->tvalue);
9110 	gmt_M_free (GMT, (*G)->dvalue);
9111 	if (mode == 0) return;	/* That's all we do for now */
9112 	/* Here we free up everything */
9113 	gmt_M_free (GMT, (*G)->name);
9114 	gmt_M_free (GMT, (*G)->type);
9115 	gmt_M_str_free ((*G)->region);
9116 	for (k = 0; k < 4; k++) gmt_M_str_free ((*G)->proj[k]);
9117 	gmt_M_free (GMT, (*G));
9118 }
9119 
9120 /*! . */
gmtlib_duplicate_ogr(struct GMT_CTRL * GMT,struct GMT_OGR * G)9121 struct GMT_OGR * gmtlib_duplicate_ogr (struct GMT_CTRL *GMT, struct GMT_OGR *G) {
9122 	/* Duplicate GMT/OGR structure, if used */
9123 	unsigned int k;
9124 	struct GMT_OGR *G_dup = NULL;
9125 	if (!G) return (NULL);	/* Nothing to do */
9126 	G_dup = gmt_M_memory (GMT, NULL, 1, struct GMT_OGR);
9127 	if (G->region) G_dup->region = strdup (G->region);
9128 	for (k = 0; k < 4; k++) if (G->proj[k]) G_dup->proj[k] = strdup (G->proj[k]);
9129 	G_dup->geometry = G->geometry;
9130 	if (G->n_aspatial) {
9131 		G_dup->n_aspatial = G->n_aspatial;
9132 		G_dup->name = gmt_M_memory (GMT, NULL, G->n_aspatial, char *);
9133 		for (k = 0; k < G->n_aspatial; k++) if (G->name[k]) G_dup->name[k] = strdup (G->name[k]);
9134 		G_dup->type = gmt_M_memory (GMT, NULL, G->n_aspatial, enum GMT_enum_type);
9135 		gmt_M_memcpy (G_dup->type, G->type, G->n_aspatial, int);
9136 	}
9137 	return (G_dup);
9138 }
9139 
9140 /*! . */
gmt_get_aspatial_value(struct GMT_CTRL * GMT,int col,struct GMT_DATASEGMENT * S)9141 double gmt_get_aspatial_value (struct GMT_CTRL *GMT, int col, struct GMT_DATASEGMENT *S) {
9142 	/* Return the value associated with the aspatial values given for this column col */
9143 
9144 	uint64_t k;
9145 	int64_t scol = col;
9146 	int id;
9147 	char *V = NULL;
9148 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
9149 	if (S) SH = gmt_get_DS_hidden (S);
9150 	for (k = 0; k < GMT->common.a.n_aspatial; k++) {	/* For each item specified in -a */
9151 		if (scol != GMT->common.a.col[k]) continue;	/* Not the column we want */
9152 		id = gmt_get_ogr_id (GMT->current.io.OGR, GMT->common.a.name[k]);	/* Get the ID */
9153 		V = (SH && SH->ogr) ? SH->ogr->tvalue[id] : GMT->current.io.OGR->tvalue[id];	/* Either from table or from segment (multi) */
9154 		return (gmtio_convert_aspatial_value (GMT, GMT->current.io.OGR->type[id], V));
9155 	}
9156 	GMT_Report (GMT->parent, GMT_MSG_WARNING, "No aspatial value found for column %d [Return NaN]\n", col);
9157 	return (GMT->session.d_NaN);
9158 }
9159 
9160 /*! . */
gmt_load_aspatial_string(struct GMT_CTRL * GMT,struct GMT_OGR * G,uint64_t col,char out[GMT_BUFSIZ])9161 int gmt_load_aspatial_string (struct GMT_CTRL *GMT, struct GMT_OGR *G, uint64_t col, char out[GMT_BUFSIZ]) {
9162 	/* Uses the info in -a and OGR to retrieve the requested aspatial string */
9163 
9164 	uint64_t k;
9165 	int64_t scol = col, id = GMT_NOTSET;
9166 	size_t len;
9167 	if (GMT->current.io.ogr != GMT_OGR_TRUE) return (0);		/* No point checking further since file is not GMT/OGR */
9168 	for (k = 0; k < GMT->common.a.n_aspatial; k++) {	/* For each item specified in -a */
9169 		if (GMT->common.a.col[k] == scol) id = k;			/* ..that matches the given column */
9170 	}
9171 	if (id == GMT_NOTSET) return (0);
9172 	id = gmt_get_ogr_id (G, GMT->common.a.name[id]);
9173 	if (id == GMT_NOTSET) return (0);
9174 	len = strlen (G->tvalue[id]);
9175 	gmt_M_memset (out, GMT_BUFSIZ, char);
9176 	if (G->tvalue[id][0] == '\"' && G->tvalue[id][len-1] == '\"')	/* Skip opening and closing quotes */
9177 		strncpy (out, &G->tvalue[id][1], len-2);
9178 	else
9179 		strcpy (out, G->tvalue[id]);
9180 	return (1);
9181 }
9182 
gmt_polygon_is_hole(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)9183 bool gmt_polygon_is_hole (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
9184 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
9185 	gmt_M_unused (GMT);
9186 	return (SH->pol_mode == GMT_IS_HOLE || (SH->ogr && SH->ogr->pol_mode == GMT_IS_HOLE));
9187 }
9188 
9189 /*! . */
gmtlib_get_dirs(struct GMT_CTRL * GMT,char * path)9190 char ** gmtlib_get_dirs (struct GMT_CTRL *GMT, char *path) {
9191 	/* Return an array of subdirectories found in the given directory, or NULL if path cannot be opened. */
9192 	size_t n = 0, n_alloc = GMT_TINY_CHUNK;
9193 	char **list = NULL;
9194 #ifdef HAVE_DIRENT_H_
9195 	DIR *D = NULL;
9196 	struct dirent *F = NULL;
9197 	size_t d_namlen = 0;
9198 
9199 	if (access (path, F_OK)) return NULL;	/* Quietly skip non-existent directories */
9200 	if ((D = opendir (path)) == NULL) {	/* Unable to open directory listing */
9201 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while opening directory %s\n", path);
9202 		return NULL;
9203 	}
9204 	list = gmt_M_memory (GMT, NULL, n_alloc, char *);
9205 	/* Now read the contents of the dir and add each file to array */
9206 	while ((F = readdir (D)) != NULL) {	/* For each directory entry until end or ok becomes true */
9207 		d_namlen = strlen (F->d_name);
9208 		if (d_namlen == 1U && F->d_name[0] == '.') continue;			/* Skip current dir */
9209 		if (d_namlen == 2U && F->d_name[0] == '.' && F->d_name[1] == '.') continue;	/* Skip parent dir */
9210 #ifdef HAVE_SYS_DIR_H_
9211 		if (F->d_type != DT_DIR) continue;	/* Entry is not a directory; skip it */
9212 #endif
9213 		if (strchr (F->d_name, '.')) continue;	/* Our directories do not have a period in them */
9214 		list[n++] = strdup (F->d_name);	/* Save the directory name */
9215 		if (n == n_alloc) {		/* Allocate more memory for list */
9216 			n_alloc <<= 1;
9217 			list = gmt_M_memory (GMT, list, n_alloc, char *);
9218 		}
9219 	}
9220 	(void)closedir (D);
9221 #elif defined(WIN32)
9222 	char text[PATH_MAX] = {""};
9223 	HANDLE hFind;
9224 	WIN32_FIND_DATA FindFileData;
9225 
9226 	if (access (path, F_OK)) return NULL;	/* Quietly skip non-existent directories */
9227 	snprintf (text, PATH_MAX, "%s/*", path);
9228 	if ((hFind = FindFirstFile (text, &FindFileData)) == INVALID_HANDLE_VALUE) {
9229 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while opening directory %s\n", path);
9230 		return NULL;
9231 	}
9232 	list = gmt_M_memory (GMT, NULL, n_alloc, char *);
9233 	do {
9234 		if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) continue;
9235 		if (strcmp (FindFileData.cFileName, ".") && strcmp (FindFileData.cFileName, "..")) {	/* Don't want the '.' and '..' names */
9236 		if (strchr (FindFileData.cFileName, '.')) continue;	/* Our directories do not have a period in them */
9237 			list[n++] = strdup (FindFileData.cFileName);	/* Save the file name */
9238 			if (n == n_alloc) {			/* Allocate more memory for list */
9239 				n_alloc <<= 1;
9240 				list = gmt_M_memory (GMT, list, n_alloc, char *);
9241 			}
9242 		}
9243 	} while (FindNextFile (hFind, &FindFileData));
9244 	FindClose(hFind);
9245 #else
9246 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your OS does not support directory listings\n");
9247 	return NULL;
9248 #endif /* HAVE_DIRENT_H_ */
9249 
9250 	list = gmt_M_memory (GMT, list, n + 1, char *);	/* The final entry is NULL, indicating end of list */
9251 	list[n] = NULL;	/* Since the realloc will not necessarily have set it to NULL */
9252 	return (list);
9253 }
9254 
9255 /*! . */
gmtlib_get_dir_list(struct GMT_CTRL * GMT,char * path,char * ext)9256 char ** gmtlib_get_dir_list (struct GMT_CTRL *GMT, char *path, char *ext) {
9257 	/* Return an array of filenames found in the given directory, or NULL if path cannot be opened.
9258 	 * If ext is not NULL we only return filenames that end in <ext> */
9259 	size_t n = 0, n_alloc = GMT_TINY_CHUNK;
9260 	char **list = NULL;
9261 #ifdef HAVE_DIRENT_H_
9262 	DIR *D = NULL;
9263 	struct dirent *F = NULL;
9264 	size_t d_namlen = 0, e_len = 0;
9265 
9266 	if (access (path, F_OK)) return NULL;	/* Quietly skip non-existent directories */
9267 	if ((D = opendir (path)) == NULL) {	/* Unable to open directory listing */
9268 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while opening directory %s\n", path);
9269 		return NULL;
9270 	}
9271 	if (ext) e_len = strlen (ext);
9272 	list = gmt_M_memory (GMT, NULL, n_alloc, char *);
9273 	/* Now read the contents of the dir and add each file to array */
9274 	while ((F = readdir (D)) != NULL) {	/* For each directory entry until end or ok becomes true */
9275 		d_namlen = strlen (F->d_name);
9276 		if (d_namlen == 1U && F->d_name[0] == '.') continue;			/* Skip current dir */
9277 		if (d_namlen == 2U && F->d_name[0] == '.' && F->d_name[1] == '.') continue;	/* Skip parent dir */
9278 #ifdef HAVE_SYS_DIR_H_
9279 		if (F->d_type == DT_DIR) continue;	/* Entry is a directory; skip it */
9280 #endif
9281 		if (ext && strncmp (&F->d_name[d_namlen-e_len], ext, e_len)) continue;	/* Does not end in <ext> */
9282 		list[n++] = strdup (F->d_name);	/* Save the file name */
9283 		if (n == n_alloc) {		/* Allocate more memory for list */
9284 			n_alloc <<= 1;
9285 			list = gmt_M_memory (GMT, list, n_alloc, char *);
9286 		}
9287 	}
9288 	(void)closedir (D);
9289 #elif defined(WIN32)
9290 	char text[PATH_MAX] = {""};
9291 	int left;
9292 	HANDLE hFind;
9293 	WIN32_FIND_DATA FindFileData;
9294 
9295 	if (access (path, F_OK)) return NULL;	/* Quietly skip non-existent directories */
9296 	snprintf (text, PATH_MAX, "%s/*", path);
9297 	left = PATH_MAX - (int)strlen (path) - 2;
9298 	left -= ((ext) ? (int)strlen (ext) : 2);
9299 	if (ext)
9300 		strncat (text, ext, left);	/* Look for files with given ending in this dir */
9301 	else
9302 		strncat (text, ".*", left);	/* Look for all files in this dir */
9303 	if ((hFind = FindFirstFile(text, &FindFileData)) == INVALID_HANDLE_VALUE) {
9304 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while opening directory %s\n", path);
9305 		return NULL;
9306 	}
9307 	list = gmt_M_memory (GMT, NULL, n_alloc, char *);
9308 	do {
9309 		if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;	/* Skip sub-directories */
9310 		if (strcmp(FindFileData.cFileName, ".") && strcmp(FindFileData.cFileName, "..")) {	/* Don't want the '.' and '..' names */
9311 			list[n++] = strdup(FindFileData.cFileName);	/* Save the file name */
9312 			if (n == n_alloc) {			/* Allocate more memory for list */
9313 				n_alloc <<= 1;
9314 				list = gmt_M_memory (GMT, list, n_alloc, char *);
9315 			}
9316 		}
9317 	} while (FindNextFile(hFind, &FindFileData));
9318 	FindClose(hFind);
9319 #else
9320 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your OS does not support directory listings\n");
9321 	return NULL;
9322 #endif /* HAVE_DIRENT_H_ */
9323 
9324 	list = gmt_M_memory (GMT, list, n + 1, char *);	/* The final entry is NULL, indicating end of list */
9325 	list[n] = NULL;	/* Since the realloc will not necessarily have set it to NULL */
9326 	return (list);
9327 }
9328 
9329 /*! . */
gmtlib_free_dir_list(struct GMT_CTRL * GMT,char *** addr)9330 void gmtlib_free_dir_list (struct GMT_CTRL *GMT, char ***addr) {
9331 	/* Free allocated array with directory content */
9332 	unsigned int k = 0;
9333 	char **list;
9334 
9335 	if (addr == NULL) return;	/* Sanity check */
9336 	if ((list = *addr) == NULL) return;	/* Sanity check */
9337 	while (list[k]) {
9338 		gmt_M_str_free (list[k]);
9339 		k++;
9340 	}
9341 	gmt_M_free (GMT, list);
9342 }
9343 
9344 /*! . */
gmt_remove_file(struct GMT_CTRL * GMT,const char * file)9345 int gmt_remove_file (struct GMT_CTRL *GMT, const char *file) {
9346 	/* Try to remove a file - give error message if it fails.  Depends on extern int errno */
9347 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Delete %s\n", file);
9348 	if (!access (file, F_OK) && remove (file)) {
9349 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to remove %s! [remove error: %s]\n", file, strerror (errno));
9350 		return errno;
9351 	}
9352 	return GMT_NOERROR;
9353 }
9354 
9355 
9356 /*! . */
gmt_rename_file(struct GMT_CTRL * GMT,const char * oldfile,const char * newfile,unsigned int mode)9357 int gmt_rename_file (struct GMT_CTRL *GMT, const char *oldfile, const char *newfile, unsigned int mode) {
9358 	/* Try to rename a file - give error message if it fails.  Depends on extern int errno.
9359 	 * mode is either GMT_COPY_FILE or GMT_RENAME_FILE */
9360 
9361 	if (mode == GMT_COPY_FILE)
9362 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Copying %s -> %s\n", oldfile, newfile);
9363 	else
9364 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Rename %s -> %s\n", oldfile, newfile);
9365 
9366 	errno = GMT_NOERROR;
9367 	if (mode == GMT_COPY_FILE || rename (oldfile, newfile)) {	/* This may be benign as rename won't move files between different mounted partitions on a drive. Copy/remove instead */
9368 		size_t ni, no, total = 0;
9369 		char *chunk = NULL;
9370 		FILE *fpi = NULL, *fpo = NULL;
9371 		if (mode == GMT_RENAME_FILE) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Failed to rename %s -> %s! [rename error: %s].  Try copy/delete instead.\n", oldfile, newfile, strerror (errno));
9372 		/* Open the two files */
9373 		if ((fpo = fopen (newfile, "wb")) == NULL) {
9374 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to create %s! [fopen error: %s]\n", newfile, strerror (errno));
9375 			return errno;
9376 		}
9377 		if ((fpi = fopen (oldfile, "rb")) == NULL) {
9378 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to open %s! [fopen error: %s]\n", oldfile, strerror (errno));
9379 			fclose (fpo);
9380 			return errno;
9381 		}
9382 		/* Get memory for reading GMT_BUFSIZ characters at the time */
9383 		if ((chunk = calloc (GMT_BUFSIZ, sizeof (char))) == NULL) {
9384 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to allocate memory! [calloc error: %s]\n", strerror (errno));
9385 			fclose (fpi);
9386 			fclose (fpo);
9387 			return errno;
9388 		}
9389 		while ((ni = fread (chunk, sizeof (char), GMT_BUFSIZ, fpi))) {	/* Read until nothing, write each chunk */
9390 			total += ni;
9391 			if ((no = fwrite (chunk, sizeof (char), ni, fpo)) != ni) {
9392 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to write %" PRIuS " bytes to %s! [fwrite error: %s]\n", ni, newfile, strerror (errno));
9393 				fclose (fpi);
9394 				fclose (fpo);
9395 				free (chunk);	/* Free the chunk */
9396 				return errno;
9397 			}
9398 		}
9399 		free (chunk);	/* Free the chunk */
9400 		if (fclose (fpi)) {
9401 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to close %s! [fwrite error: %s]\n", oldfile, strerror (errno));
9402 			fclose (fpo);
9403 			return errno;
9404 		}
9405 		if (fclose (fpo)) {
9406 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to close %s! [fwrite error: %s]\n", newfile, strerror (errno));
9407 			return errno;
9408 		}
9409 		if (total == 0)
9410 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Source file %s was empty (?): \n", oldfile);
9411 
9412 		/* Finally delete the old file */
9413 		if (mode == GMT_RENAME_FILE) errno = gmt_remove_file (GMT, oldfile);
9414 	}
9415 	return errno;
9416 }
9417 
gmt_replace_backslash_in_path(char * dir)9418 void gmt_replace_backslash_in_path (char *dir) {
9419 	size_t k = 0;
9420 	if (dir == NULL) return;	/* No can do */
9421 	while (dir[k]) {
9422 		if (dir[k] == '\\') dir[k] = '/';
9423 		k++;
9424 	}
9425 }
9426 
9427 /*! . */
gmt_set_column_type(struct GMT_CTRL * GMT,unsigned int direction,unsigned int col,enum gmt_col_enum type)9428 void gmt_set_column_type (struct GMT_CTRL *GMT, unsigned int direction, unsigned int col, enum gmt_col_enum type) {
9429 	/* Sets the column type for this input or output column or both (dir == GMT_IO) */
9430 	unsigned int start = (direction == GMT_IO) ? GMT_IN : direction;
9431 	unsigned int stop  = (direction == GMT_IO) ? GMT_OUT : direction;
9432 	for (unsigned int dir = start; dir <= stop; dir++) {
9433 		GMT->current.io.col_type[dir][col] = type;
9434 		GMT->current.io.col_set[dir][col] = 1;	/* Flag as having been set and thus should not be automatically changed */
9435 	}
9436 }
9437 
9438 /*! . */
gmt_get_column_type(struct GMT_CTRL * GMT,unsigned int direction,unsigned int col)9439 enum gmt_col_enum gmt_get_column_type (struct GMT_CTRL *GMT, unsigned int direction, unsigned int col) {
9440 	/* Gets the column type for this input or output column */
9441 	return (GMT->current.io.col_type[direction][col]);	/* Flag as having been set and thus should not be automatically changed */
9442 }
9443 
gmt_mkdir(const char * path)9444 int gmt_mkdir (const char *path)
9445 {	/* Simulates mkdir -p behavior in a function for Unix and Windows */
9446 	/* Adapted from http://stackoverflow.com/a/2336245/119527 */
9447 	const size_t len = strlen (path);
9448 	char _path[PATH_MAX] = {""}, sep;
9449 	char *p = NULL;
9450 
9451 	errno = 0;	/* Global var: No error so far */
9452 
9453 	if (len >= PATH_MAX) {	/* Make sure we don't exceed limits */
9454 		errno = ENAMETOOLONG;
9455 		perror ("gmt_mkdir (too long) error");
9456 		return GMT_NOTSET;
9457 	}
9458 	strcpy (_path, path);	/* Copy string so its mutable */
9459 
9460 	/* Iterate the string */
9461 	p = (_path[1] == ':') ? _path + 3 : _path + 1;  /* Skip any leading X: drive designators */
9462 	while (*p) { /* Create intermediate directories recursively */
9463 		if (*p == '/' || *p == '\\') {	/* Found start of next directory */
9464 			sep = *p;	/* What separator did we use? */
9465 			*p = '\0';	/* Temporarily truncate */
9466 
9467 #ifndef _WIN32
9468 			if (mkdir (_path, S_IRWXU) != 0)
9469 #else
9470 			if (mkdir (_path) != 0)
9471 #endif
9472 			{
9473 				if (errno != EEXIST) {	/* Failed to make or visit intermediate directory */
9474 					perror ("gmt_mkdir (intermediate) error");
9475 					return GMT_NOTSET;
9476 				}
9477 			}
9478 			*p = sep;	/* Reset the separator */
9479 		}
9480 		p++;
9481 	}
9482 
9483 	/* Finally create the last directory name in the path */
9484 #ifndef _WIN32
9485 	if (mkdir (_path, S_IRWXU) != 0)
9486 #else
9487 	if (mkdir (_path) != 0)
9488 #endif
9489 	{
9490 		if (errno != EEXIST) {
9491 			perror ("gmt_mkdir (last dir) error");
9492 			return GMT_NOTSET;
9493 		}
9494 	}
9495 
9496 	return 0;
9497 }
9498 
gmt_quit_bad_record(struct GMTAPI_CTRL * API,struct GMT_RECORD * In)9499 void gmt_quit_bad_record (struct GMTAPI_CTRL *API, struct GMT_RECORD *In) {
9500 	GMT_Report (API, GMT_MSG_ERROR, "No data columns to work with - exiting\n");
9501 	if (In->text) GMT_Report (API, GMT_MSG_ERROR, "Data file only has trailing text. GMT expects numerical columns followed by optional trailing text\n");
9502 	API->error = GMT_DIM_TOO_SMALL;
9503 }
9504