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