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  * Author:	Paul Wessel
19  * Date:	1-JAN-2010
20  * Version:	5
21  */
22 
23 /*!
24  * \file gmt_init.c
25  * \brief gmt_init.c contains code which is used by all GMT programs
26  *
27  * The PUBLIC functions (68) are:
28  *
29  *	gmtlib_explain_options		Prints explanations for the common options\n
30  *	gmt_parse_common_options	Interprets common options, such as -B, -R, --\n
31  *	gmt_getdefaults			Initializes the GMT global parameters\n
32  *	gmt_putdefaults			Dumps the GMT global parameters\n
33  *	gmt_pickdefaults
34  *	gmt_setdefaults
35  *	gmt_hash_init			Initializes a hash\n
36  *	gmt_hash_lookup			Key - id lookup using hashing\n
37  *	gmt_begin			Gets history and init parameters\n
38  *	gmt_end				Cleans up and returns\n
39  *	gmt_putcolor			Encode color argument into textstring\n
40  *	gmt_putrgb			Encode color argument into r/g/b textstring\n
41  *	gmtlib_puthsv			Encode color argument into h-s-v textstring\n
42  *	gmtlib_putcmyk			Encode color argument into c/m/y/k textstring
43  *	gmtlib_putfill
44  *	gmtlib_setparameter		Sets a default value given keyword,value-pair\n
45  *	gmtlib_getparameter
46  *	gmt_GSHHG_syntax
47  *  gmt_GSHHG_resolution_syntax
48  *	gmt_label_syntax
49  *	gmt_cont_syntax
50  *	gmt_inc_syntax
51  *	gmt_fill_syntax
52  *	gmt_pen_syntax
53  *	gmt_rgb_syntax
54  *	gmt_refpoint_syntax
55  *	gmt_mapinset_syntax
56  *	gmt_mapscale_syntax
57  *	gmt_maprose_syntax
58  *	gmt_mappanel_syntax
59  *	gmt_dist_syntax
60  *	gmt_vector_syntax
61  *	gmt_segmentize_syntax
62  *	gmt_img_syntax
63  *	gmt_syntax
64  *	gmt_default_error
65  *	gmt_check_region
66  *	gmt_parse_R_option
67  *	gmtlib_parse_index_range
68  *	gmt_parse_d_option
69  *	gmt_parse_i_option
70  *	gmt_parse_o_option
71  *	gmt_parse_model
72  *	gmt_parse_segmentize
73  *	gmt_check_binary_io
74  *	gmt_parse_g_option
75  *	gmt_get_V
76  *	gmt_convert_units
77  *	gmtlib_unit_lookup
78  *	gmt_get_time_system
79  *	gmt_init_vector_param
80  *	gmt_parse_vector
81  *	gmt_parse_symbol_option
82  *	gmt_init_scales
83  *	gmtlib_get_unit_number
84  *	gmt_check_scalingopt
85  *	gmt_set_measure_unit
86  *	gmt_set_pad
87  *	gmt_check_filearg
88  *	gmt_setmode
89  *	gmt_message
90  *	gmtlib_report_func
91  *	gmtlib_get_num_processors
92  *	gmt_get_ellipsoid		Returns ellipsoid id based on name\n
93  *	gmt_init_time_system_structure  Does what it says\n
94  */
95 
96 #include "gmt_dev.h"
97 #include <stdarg.h>
98 #include "gmt_internals.h"
99 #include "gmt_common_runpath.h"
100 
101 #ifdef GMT_MATLAB
102 #	include <mex.h>
103 #endif
104 
105 /* These are used in gmtinit_init_custom_annot and gmtinit_decode_tinfo only */
106 #define GMT_ITEM_ANNOT		0
107 #define GMT_ITEM_INTVAL		1
108 #define GMT_ITEM_TICK		2
109 #define GMT_ITEM_GRID		3
110 #define GMT_N_AXIS_ITEMS	4
111 
112 #define GMT_USER_MEDIA_OFFSET 1000
113 #define GMT_COMPAT_INFO "Please see " GMT_DOC_URL "/changes.html#new-features-in-gmt-5 for more information.\n"
114 #define GMT_COMPAT_WARN GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Parameter %s is deprecated.\n" GMT_COMPAT_INFO, GMT_keyword[case_val])
115 
116 #define gmt_M_compat_change(new_P) GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Parameter %s is deprecated. Use %s instead.\n" GMT_COMPAT_INFO, GMT_keyword[case_val], new_P)
117 #define gmt_M_compat_translate(new_P) error = (gmt_M_compat_check (GMT, 4) ? gmt_M_compat_change (new_P) + gmtlib_setparameter (GMT, new_P, value, core) : gmtinit_badvalreport (GMT, keyword))
118 #define gmt_M_compat_opt(new_P) if (strchr (list, option)) { GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -%c is deprecated. Use -%c instead.\n" GMT_COMPAT_INFO, option, new_P); option = new_P; }
119 #define gmt_M_def_scale(case_val) GMT->session.u2u[GMT_INCH][gmtlib_unit_lookup(GMT, GMT->current.setting.given_unit[case_val], GMT->current.setting.proj_length_unit)]
120 #define gmt_M_def_unit(case_val) GMT->current.setting.given_unit[case_val]
121 #define gmt_M_more_than_once(GMT,active) (gmt_M_check_condition (GMT, active, "Option -%c given more than once\n", option))
122 
123 /* Leave a record that this keyword is no longer a default one
124    So far, only gmtset calls this function with core = true, but this is a too fragile solution */
125 #define gmt_M_keyword_update(val) if (core) GMT_keyword_updated[val] = true
126 
127 void *global_API;
128 
129 /*--------------------------------------------------------------------*/
130 /* Load private fixed array parameters from include files */
131 /*--------------------------------------------------------------------*/
132 
133 struct GMT_parameter {
134 	const int code;
135 	const char *name;
136 };
137 
138 /* These are the active GMT5+ keywords, containing no backwards-compatible variants.
139  * Also, some grouped keywords such as FONT and FONT_ANNOT are also not listed since they are not in gmt.conf.
140  * If new keywords are added they need to be added here as well as to gmt_keywords.txt, plus
141  * specific entries in both gmtlib_setparameter and gmtlib_getparameter, and gmt.conf.rst */
142 
143 static struct GMT_parameter GMT_keyword_active[]= {
144 	{ 1, "COLOR Parameters"},
145 	{ 0, "COLOR_BACKGROUND"},
146 	{ 0, "COLOR_FOREGROUND"},
147 	{ 0, "COLOR_CPT"},
148 	{ 0, "COLOR_NAN"},
149 	{ 0, "COLOR_MODEL"},
150 	{ 0, "COLOR_HSV_MIN_S"},
151 	{ 0, "COLOR_HSV_MAX_S"},
152 	{ 0, "COLOR_HSV_MIN_V"},
153 	{ 0, "COLOR_HSV_MAX_V"},
154 	{ 0, "COLOR_SET"},
155 	{ 1, "DIR Parameters"},
156 	{ 0, "DIR_CACHE"},
157 	{ 0, "DIR_DATA"},
158 	{ 0, "DIR_DCW"},
159 	{ 0, "DIR_GSHHG"},
160 	{ 1, "FONT Parameters"},
161 	{ 0, "FONT_ANNOT_PRIMARY"},
162 	{ 0, "FONT_ANNOT_SECONDARY"},
163 	{ 0, "FONT_HEADING"},
164 	{ 0, "FONT_LABEL"},
165 	{ 0, "FONT_LOGO"},
166 	{ 0, "FONT_SUBTITLE"},
167 	{ 0, "FONT_TAG"},
168 	{ 0, "FONT_TITLE"},
169 	{ 1, "FORMAT Parameters"},
170 	{ 0, "FORMAT_CLOCK_IN"},
171 	{ 0, "FORMAT_CLOCK_OUT"},
172 	{ 0, "FORMAT_CLOCK_MAP"},
173 	{ 0, "FORMAT_DATE_IN"},
174 	{ 0, "FORMAT_DATE_OUT"},
175 	{ 0, "FORMAT_DATE_MAP"},
176 	{ 0, "FORMAT_GEO_OUT"},
177 	{ 0, "FORMAT_GEO_MAP"},
178 	{ 0, "FORMAT_FLOAT_OUT"},
179 	{ 0, "FORMAT_FLOAT_MAP"},
180 	{ 0, "FORMAT_TIME_PRIMARY_MAP"},
181 	{ 0, "FORMAT_TIME_SECONDARY_MAP"},
182 	{ 0, "FORMAT_TIME_STAMP"},
183 	{ 1, "GMT Miscellaneous Parameters"},
184 	{ 0, "GMT_DATA_SERVER"},
185 	{ 0, "GMT_DATA_SERVER_LIMIT"},
186 	{ 0, "GMT_DATA_UPDATE_INTERVAL"},
187 	{ 0, "GMT_COMPATIBILITY"},
188 	{ 0, "GMT_CUSTOM_LIBS"},
189 	{ 0, "GMT_EXPORT_TYPE"},
190 	{ 0, "GMT_EXTRAPOLATE_VAL"},
191 	{ 0, "GMT_FFT"},
192 	{ 0, "GMT_GRAPHICS_DPU"},
193 	{ 0, "GMT_GRAPHICS_FORMAT"},
194 	{ 0, "GMT_HISTORY"},
195 	{ 0, "GMT_INTERPOLANT"},
196 	{ 0, "GMT_LANGUAGE"},
197 	{ 0, "GMT_MAX_CORES"},
198 	{ 0, "GMT_THEME"},
199 	{ 0, "GMT_TRIANGULATE"},
200 	{ 0, "GMT_VERBOSE"},
201 	{ 1, "I/O Parameters"},
202 	{ 0, "IO_COL_SEPARATOR"},
203 	{ 0, "IO_FIRST_HEADER"},
204 	{ 0, "IO_GRIDFILE_FORMAT"},
205 	{ 0, "IO_GRIDFILE_SHORTHAND"},
206 	{ 0, "IO_HEADER"},
207 	{ 0, "IO_HEADER_MARKER"},
208 	{ 0, "IO_N_HEADER_RECS"},
209 	{ 0, "IO_NAN_RECORDS"},
210 	{ 0, "IO_NC4_CHUNK_SIZE"},
211 	{ 0, "IO_NC4_DEFLATION_LEVEL"},
212 	{ 0, "IO_LONLAT_TOGGLE"},
213 	{ 0, "IO_SEGMENT_BINARY"},
214 	{ 0, "IO_SEGMENT_MARKER"},
215 	{ 1, "MAP Parameters"},
216 	{ 0, "MAP_ANNOT_MIN_ANGLE"},
217 	{ 0, "MAP_ANNOT_MIN_SPACING"},
218 	{ 0, "MAP_ANNOT_OBLIQUE"},
219 	{ 0, "MAP_ANNOT_OFFSET_PRIMARY"},
220 	{ 0, "MAP_ANNOT_OFFSET_SECONDARY"},
221 	{ 0, "MAP_ANNOT_ORTHO"},
222 	{ 0, "MAP_DEFAULT_PEN"},
223 	{ 0, "MAP_DEGREE_SYMBOL"},
224 	{ 0, "MAP_FRAME_AXES"},
225 	{ 0, "MAP_FRAME_PEN"},
226 	{ 0, "MAP_FRAME_PERCENT"},
227 	{ 0, "MAP_FRAME_TYPE"},
228 	{ 0, "MAP_FRAME_WIDTH"},
229 	{ 0, "MAP_GRID_CROSS_SIZE_PRIMARY"},
230 	{ 0, "MAP_GRID_CROSS_SIZE_SECONDARY"},
231 	{ 0, "MAP_GRID_PEN_PRIMARY"},
232 	{ 0, "MAP_GRID_PEN_SECONDARY"},
233 	{ 0, "MAP_HEADING_OFFSET"},
234 	{ 0, "MAP_LABEL_MODE"},
235 	{ 0, "MAP_LABEL_OFFSET"},
236 	{ 0, "MAP_LINE_STEP"},
237 	{ 0, "MAP_LOGO"},
238 	{ 0, "MAP_LOGO_POS"},
239 	{ 0, "MAP_ORIGIN_X"},
240 	{ 0, "MAP_ORIGIN_Y"},
241 	{ 0, "MAP_POLAR_CAP"},
242 	{ 0, "MAP_SCALE_HEIGHT"},
243 	{ 0, "MAP_TICK_LENGTH_PRIMARY"},
244 	{ 0, "MAP_TICK_LENGTH_SECONDARY"},
245 	{ 0, "MAP_TICK_PEN_PRIMARY"},
246 	{ 0, "MAP_TICK_PEN_SECONDARY"},
247 	{ 0, "MAP_TITLE_OFFSET"},
248 	{ 0, "MAP_VECTOR_SHAPE"},
249 	{ 1, "Projection Parameters"},
250 	{ 0, "PROJ_AUX_LATITUDE"},
251 	{ 0, "PROJ_DATUM"},
252 	{ 0, "PROJ_ELLIPSOID"},
253 	{ 0, "PROJ_GEODESIC"},
254 	{ 0, "PROJ_LENGTH_UNIT"},
255 	{ 0, "PROJ_MEAN_RADIUS"},
256 	{ 0, "PROJ_SCALE_FACTOR"},
257 	{ 1, "PostScript Parameters"},
258 	{ 0, "PS_CHAR_ENCODING"},
259 	{ 0, "PS_COLOR_MODEL"},
260 	{ 0, "PS_COMMENTS"},
261 	{ 0, "PS_CONVERT"},
262 	{ 0, "PS_IMAGE_COMPRESS"},
263 	{ 0, "PS_LINE_CAP"},
264 	{ 0, "PS_LINE_JOIN"},
265 	{ 0, "PS_MITER_LIMIT"},
266 	{ 0, "PS_MEDIA"},
267 	{ 0, "PS_PAGE_COLOR"},
268 	{ 0, "PS_PAGE_ORIENTATION"},
269 	{ 0, "PS_SCALE_X"},
270 	{ 0, "PS_SCALE_Y"},
271 	{ 0, "PS_TRANSPARENCY"},
272 	{ 1, "Calendar/Time Parameters"},
273 	{ 0, "TIME_EPOCH"},
274 	{ 0, "TIME_IS_INTERVAL"},
275 	{ 0, "TIME_INTERVAL_FRACTION"},
276 	{ 0, "TIME_LEAP_SECONDS"},
277 	{ 0, "TIME_REPORT"},
278 	{ 0, "TIME_UNIT"},
279 	{ 0, "TIME_WEEK_START"},
280 	{ 0, "TIME_Y2K_OFFSET_YEAR"},
281 	{ GMT_NOTSET, NULL}
282 };
283 
284 #include "gmt_keycases.h"				/* Get all the default case values */
285 static char *GMT_keyword[GMT_N_KEYS] = {		/* Names of all parameters in gmt.conf */
286 #include "gmt_keywords.h"
287 };
288 
289 bool GMT_keyword_updated[GMT_N_KEYS];	/* Initialized to false in gmt_begin. Will be set to 'true' when individual keywords are set via gmtset */
290 
291 static char *GMT_unique_option[GMT_N_UNIQUE] = {	/* The common GMT command-line options [ just the subset that accepts arguments (e.g., -O is not listed) ] */
292 #include "gmt_unique.h"
293 };
294 
295 static char *GMT_media_name[GMT_N_MEDIA] = {		/* Names of all recognized paper formats */
296 #include "gmt_media_name.h"
297 };
298 static struct GMT_MEDIA GMT_media[GMT_N_MEDIA] = {	/* Sizes in points of all paper formats */
299 #include "gmt_media_size.h"
300 };
301 
302 static char *gmt_M_color_name[GMT_N_COLOR_NAMES] = {	/* Names of all the X11 colors */
303 #include "gmt_colornames.h"
304 };
305 
306 static char *GMT_weekdays[7] = {	/* Days of the week in English [Default] */
307 	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
308 };
309 
310 static char *GMT_just_string[12] = {	/* Strings to specify justification */
311 	"", "BL", "BC", "BR", "", "ML", "MC", "MR", "", "TL", "TC", "TR"
312 };
313 
314 /* ISO Font encodings.  Ensure that the order of PSL_ISO_names matches order of includes below */
315 
316 static char *PSL_ISO_name[] = {
317 	"PSL_Standard",
318 	"PSL_Standard+",
319 	"PSL_ISOLatin1",
320 	"PSL_ISOLatin1+",
321 	"PSL_ISO-8859-1",
322 	"PSL_ISO-8859-2",
323 	"PSL_ISO-8859-3",
324 	"PSL_ISO-8859-4",
325 	"PSL_ISO-8859-5",
326 	"PSL_ISO-8859-6",
327 	"PSL_ISO-8859-7",
328 	"PSL_ISO-8859-8",
329 	"PSL_ISO-8859-9",
330 	"PSL_ISO-8859-10",
331 	"PSL_ISO-8859-11",
332 	"PSL_ISO-8859-13",
333 	"PSL_ISO-8859-14",
334 	"PSL_ISO-8859-15",
335 	"PSL_ISO-8859-16",
336 	NULL
337 };
338 
339 static char *PSL_ISO_encoding[] = {
340 #include "PSL_Standard.h"
341 #include "PSL_Standard+.h"
342 #include "PSL_ISOLatin1.h"
343 #include "PSL_ISOLatin1+.h"
344 #include "PSL_ISO-8859-1.h"
345 #include "PSL_ISO-8859-2.h"
346 #include "PSL_ISO-8859-3.h"
347 #include "PSL_ISO-8859-4.h"
348 #include "PSL_ISO-8859-5.h"
349 #include "PSL_ISO-8859-6.h"
350 #include "PSL_ISO-8859-7.h"
351 #include "PSL_ISO-8859-8.h"
352 #include "PSL_ISO-8859-9.h"
353 #include "PSL_ISO-8859-10.h"
354 #include "PSL_ISO-8859-11.h"
355 #include "PSL_ISO-8859-13.h"
356 #include "PSL_ISO-8859-14.h"
357 #include "PSL_ISO-8859-15.h"
358 #include "PSL_ISO-8859-16.h"
359 NULL
360 };
361 
362 /* Listing of "Standard" 35 PostScript fonts found on most PS printers
363  * plus the 4 Japanese fonts we have supported since GMT 3.
364  * The fontheight is the height of A for unit fontsize. */
365 
366 #define GMT_N_STANDARD_FONTS 39
367 static struct GMT_FONTSPEC GMT_standard_fonts[GMT_N_STANDARD_FONTS] = {
368 #include "standard_adobe_fonts.h"
369 };
370 
371 #define DEF_HEADER_MARKERS "#%!;\"\'"	/* Default accepts GMT or MATLAB header records or comments of quoted text */
372 
373 #define N_MAP_ANNOT_OBLIQUE_ITEMS 7
374 
375 static char *map_annot_oblique_item[N_MAP_ANNOT_OBLIQUE_ITEMS] = {
376 	"separate",
377 	"anywhere",
378 	"lon_horizontal",
379 	"lat_horizontal",
380 	"tick_extend",
381 	"tick_normal",
382 	"lat_parallel"
383 };
384 
385 #if defined(USE_COMMON_LONG_OPTIONS)
386 /* List of GMT common keyword/options pairs.  This list is used in gmtinit_translate_to_short_options to convert
387  * the new long-format GMT options (e.g., --timestamp="My plot"+offset=5c/6c) to regular GMT short format
388  * options (e.g., -U"My plot"+o5c/6c) that the common and module parsers expect.
389  *
390  * For testing this there are two define statements that need to be set in ConfigUserAdvanced.cmake:
391  *
392  * -DUSE_COMMON_LONG_OPTIONS will allow us to test the gmt_common_kw array below.
393  * -DUSE_MODULE_LONG_OPTIONS will allow us to test any module_kw settings in the modules
394  *
395  * Without these we are blind to the keyword arrays.  Note that while you can test the
396  * common options without the module options, you cannot do the reverse.
397  *
398  * Note: For the near-global option -I (--increment), see gmt_constants.h for GMT_INCREMENT_KW definition.
399  */
400 
401 static struct GMT_KEYWORD_DICTIONARY gmt_common_kw[] = {
402 #include "gmt_common_longoptions.h"
403 };
404 #endif
405 
406 /* Local variables to gmt_init.c */
407 
408 static struct GMT_HASH keys_hashnode[GMT_N_KEYS];
409 
410 #include "gmt_gsformats.h"
411 
412 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
413 
414  /* Local functions */
415 
416 #if defined (WIN32) /* Use Windows API */
417 #include <Windows.h>
418 
419 /*! . */
gmtlib_file_lock(struct GMT_CTRL * GMT,int fd)420 bool gmtlib_file_lock (struct GMT_CTRL *GMT, int fd) {
421 	OVERLAPPED over = { 0 };
422 	HANDLE hand = (HANDLE)_get_osfhandle(fd);
423 	if (!LockFileEx(hand, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &over)) /* Will block until exclusive lock is acquired */
424 	{
425 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Exclusive lock could not be acquired (%s)\n", dlerror());
426 		return false;
427 	}
428 	return true;
429 }
430 
431 /*! . */
gmtlib_file_unlock(struct GMT_CTRL * GMT,int fd)432 bool gmtlib_file_unlock (struct GMT_CTRL *GMT, int fd) {
433 	HANDLE hand = (HANDLE)_get_osfhandle(fd);
434 	if (!UnlockFile(hand, 0, 0, 0, 1))
435 	{
436 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Failed to release lock (%s)\n", dlerror());
437 		return false;
438 	}
439 	return true;
440 }
441 
442 #elif defined (HAVE_FCNTL_H_) /* Use POSIX fcntl */
443 /*! . */
gmtlib_file_lock(struct GMT_CTRL * GMT,int fd)444 bool gmtlib_file_lock (struct GMT_CTRL *GMT, int fd) {
445 	int status;
446 	struct flock lock;
447 	lock.l_type = F_WRLCK;		/* Lock for exclusive reading/writing */
448 	lock.l_whence = SEEK_SET;	/* These three apply lock to entire file */
449 	lock.l_start = lock.l_len = 0;
450 
451 	if ((status = fcntl (fd, F_SETLKW, &lock))) /* Will block until exclusive lock is acquired */
452 	{
453 		int errsv = status; /* make copy of status */
454 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Exclusive lock could not be acquired (%s)\n", strerror(errsv));
455 		return false;
456 	}
457 	return true;
458 }
459 
460 /*! . */
gmtlib_file_unlock(struct GMT_CTRL * GMT,int fd)461 bool gmtlib_file_unlock (struct GMT_CTRL *GMT, int fd) {
462 	int status;
463 	struct flock lock;
464 	lock.l_type = F_UNLCK;		/* Release lock and close file */
465 	lock.l_whence = SEEK_SET;	/* These three apply lock to entire file */
466 	lock.l_start = lock.l_len = 0;
467 
468 	if ((status = fcntl (fd, F_SETLK, &lock))) /* Release lock */
469 	{
470 		int errsv = status; /* make copy of status */
471 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Failed to release lock (%s)\n", strerror(errsv));
472 		return false;
473 	}
474 	return true;
475 }
476 
477 #else /* Not Windows and fcntl not available */
478 /*! . */
gmtlib_file_lock(struct GMT_CTRL * GMT,int fd)479 bool gmtlib_file_lock (struct GMT_CTRL *GMT, int fd) {
480 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "File locking not supported.\n");
481 	return false;
482 }
483 
484 /*! . */
gmtlib_file_unlock(struct GMT_CTRL * GMT,int fd)485 bool gmtlib_file_unlock (struct GMT_CTRL *GMT, int fd) {
486 	return false;
487 }
488 #endif
489 
490 #if defined(USE_COMMON_LONG_OPTIONS)
gmtinit_handle_escape_text(char * text,char key,int way)491 GMT_LOCAL void gmtinit_handle_escape_text (char *text, char key, int way) {
492 	/* Deal with text that contains modifiers +? that should be seen as plain text
493 	 * because they have a leading escape ("My dumb \+p text").  If way == 1 then
494 	 * we replace \+ with two GMT_ASCII_ES (Escape) bytes and if way == -1 then we
495 	 * remove the first escape and replace the second with a +. Here, key sets
496 	 * what to escape, such as +, but the function is general for any key to escape.
497 	 * Since way == +1 replaces 2 chars by 2 other chars and way == -1 replaces two
498 	 * chars by one char this function can work on both heap and allocated strings. */
499 	size_t k, j;
500 	if (way == +1) {	/* Replace \<key> with two escapes */
501 		for (k = 1; k < strlen (text); k++)
502 				if (text[k] == key && text[k-1] == '\\')
503 						text[k] = text[k-1] = GMT_ASCII_ES;
504 	}
505 	else {	/* Replace two escapes with a single <key> symbol */
506 		char c;
507 		for (k = j = 0; k < strlen (text); k++) {
508 			if (text[k] == GMT_ASCII_ES) {
509 				c = key;	/* Write the special char */
510 				k++;		/* Skip the second escape char */
511 			}
512 			else	/* Just copy out what we found */
513 				c = text[k];
514 			text[j++] = c;	/* Replace char */
515 		}
516 		text[j] = '\0';	/* Chop off remainder of string since no longer used */
517 	}
518 }
519 
520 /*! . */
gmtinit_find_kw(struct GMTAPI_CTRL * API,struct GMT_KEYWORD_DICTIONARY * kw1,struct GMT_KEYWORD_DICTIONARY * kw2,char * arg,int * k)521 GMT_LOCAL struct GMT_KEYWORD_DICTIONARY * gmtinit_find_kw (struct GMTAPI_CTRL *API, struct GMT_KEYWORD_DICTIONARY *kw1, struct GMT_KEYWORD_DICTIONARY *kw2, char *arg, int *k) {
522 	/* Determine if this long-format arg is found in one of the two keyword lists kw1 (common) and kw2 (module).
523 	 * If we find a match we return a pointer to the corresponding keyword list and the index *k for the array entry.
524 	 * If not found then we return NULL and set index to -1 (GMT_NOTSET) */
525 	size_t len, len_given_keyword = strlen(arg);	/* Get the length of given argument */
526 	struct GMT_KEYWORD_DICTIONARY *kw[2] = {kw1, kw2};	/* kw1 is the common options and kw2 is any module options available in long-format version */
527 	gmt_M_unused (API);
528 	for (unsigned int set = 0; set < 2; set++) {	/* Loop over the two keyword structure arrays */
529 		if (kw[set] == NULL) continue;	/* We were not given this set of keywords */
530 		/* Search list of long-format keywords for a match with arg */
531 		for (*k = 0; kw[set][*k].long_option[0]; (*k)++) {
532 			len = MIN (len_given_keyword, strlen (kw[set][*k].long_option));	/* Only compare up to the given # of characters, but less than actual length of long_option */
533 			if (!strncmp (arg, kw[set][*k].long_option, len)) break;	/* Match was found */
534 		}
535 		if (kw[set][*k].short_option) return kw[set];	/* Return if successful, otherwise short_option of last entry is blank */
536 	}
537 	*k = GMT_NOTSET;	/* Nothing found, return GMT_NOTSET and NULL  */
538 	return NULL;
539 }
540 
541 /*! . */
gmtinit_find_argument(struct GMTAPI_CTRL * API,char * longlist,char * shortlist,char * text,char sep,char * argument)542 GMT_LOCAL char gmtinit_find_argument (struct GMTAPI_CTRL *API, char *longlist, char *shortlist, char *text, char sep, char *argument) {
543 	/* Examine the text argument for directives or args and pass it back out via argument.  Here,
544 	 * sep is either ':' or '=' depending on the call argument. */
545 	unsigned int k = 0, pos = 0;
546 	size_t len, lent = strlen(text);
547 	char *c = NULL, m = 0, item[GMT_LEN64] = {""};
548 	gmt_M_unused (API);
549 	if ((c = strchr (text, sep))) c[0] = '\0';	/* Chop off the colon or equal and what follows for now */
550 	while (m == 0 && (gmt_strtok (longlist, ",", &pos, item))) {	/* While there are unprocessed directives to examine */
551 		len = MIN (lent, strlen(item));	/* Only compare up to the given # of characters, but less than length of item */
552 		if (!strncmp (item, text, len))	/* Found this directive in the text */
553 			m = shortlist[k];	/* Assign the corresponding short modifier to m and this stops the while loop */
554 		k += 2;	/* Go to next char in comma-separated list of single characters (2 since we skip the comma) */
555 	}
556 	if (m && c)	/* We found a short-option flag and there is an argument that follows a colon or equal sign */
557 		strcpy (argument, &c[1]);	/* Pass out the directive argument */
558 	else if (m)	/* Found a short-option flag but there is nothing that follows */
559 		argument[0] = '\0';	/* Nothing */
560 	else	/* Not a directive, m is 0 and c is NULL */
561 		strcpy (argument, text);	/* Not a directive, pass out the argument as is */
562 	if (c) c[0] = sep;		/* Restore colon/equal sign we masked out earlier */
563 	return m;	/* Returns 0 if no modifier was found */
564 }
565 
gmtinit_get_section(struct GMTAPI_CTRL * API,char * arg,char separator,int k,int * sx)566 GMT_LOCAL int gmtinit_get_section (struct GMTAPI_CTRL *API, char *arg, char separator, int k, int *sx) {
567 	/* Find the k'th separator occurrence and chop off the rest, return pointer to start of k'th section */
568 	int j = 0, kk = -1, s0 = 0, s = 0, last_s = 0;
569 	gmt_M_unused (API);
570 	while (arg[j] && kk < k) {	/* We need to skip previous sections */
571 		if (arg[j] == separator) {	/* Start of a new section separated by / or comma, probably */
572 			kk++;		/* kk is now the section number, i.e., this will become 0 the first time we get here */
573 			s0 = s;		/* Keep previous start position */
574 			s = j+1;	/* This is position of first character in this section */
575 			last_s = j;	/* Position of previous separator before this section */
576 			if (kk < k) j++;	/* If not at desired section, increment j */
577 		}
578 		else	/* Not there yet, keep going */
579 			j++;
580 	}
581 	if (kk == k) {	/* Found the separator and is now at k'th section */
582 		arg[j] = '\0';	/* Hide the rest of the string */
583 		*sx = j;	/* Return position of separator that was removed */
584 	}
585 	else if (last_s) {	/* Must be the last section since it is missing a trailing separator */
586 		*sx = GMT_NOTSET;	/* Nothing to chop */
587 		s0 = last_s + 1;	/* Start position of last section */
588 	}
589 	return s0;	/* Return start position in arg of current section (i.e., at arg[s0]) */
590 }
591 
592 /*! . */
gmtinit_translate_to_short_options(struct GMTAPI_CTRL * API,struct GMT_KEYWORD_DICTIONARY * this_module_kw,struct GMT_OPTION ** options)593 GMT_LOCAL void gmtinit_translate_to_short_options (struct GMTAPI_CTRL *API, struct GMT_KEYWORD_DICTIONARY *this_module_kw, struct GMT_OPTION **options) {
594 	/* Loop over given options and replace any recognized long-form --parameter[=value] arguments
595 	 * with the corresponding classic short-format version -<code>[value]. Specifically, long-format is defined as
596 	 *
597 	 * --longoption[=[<directive>:]<arg>][+<mod1>[=<arg1>]][+<mod2>[=<arg2>]]...
598 	 *
599 	 * For options that take more than one section of arguments (e.g., -Idx/dy or -icols1,cols2,...)
600 	 * the section
601 	 *
602 	 * [<arg>][+<mod1>[=<arg1>]][+<mod2>[=<arg2>]]
603 	 *
604 	 * may appear more than once after a section separator (e.g., '/' or ',').  The separator is an entry
605 	 * in kw.separator, or it is 0 if the option does not take more than one section.
606 	 */
607 
608 	struct GMT_OPTION *opt = NULL;
609 	struct GMT_KEYWORD_DICTIONARY *kw = NULL;
610 	char new_arg[GMT_LEN256] = {""}, add[GMT_LEN64] = {""}, argument[GMT_LEN64] = {""}, orig[GMT_BUFSIZ] = {""}, copy[GMT_BUFSIZ] = {""};
611 	char *directive = NULL, *modifier = NULL, code = 0, e_code = '=', sep[2] = {'\0', '\0'};
612 	int k, n_sections, section, sect_start = 0, sect_end = 0;
613 	bool modified = false, got_directive = false, got_modifier = false;
614 
615 	if (options == NULL) return;	/* Nothing to process */
616 
617 	#if !defined(USE_MODULE_LONG_OPTIONS)
618 	this_module_kw = NULL;	/* Debugging: Not testing the module long-options */
619 	#endif
620 
621 	for (opt = *options; opt; opt = opt->next) {
622 		if (opt->option != GMT_OPT_PARAMETER) continue;	/* Cannot be a --keyword[=value] pair */
623 		if (isupper (opt->arg[0])) continue;		/* Skip the upper-case GMT Default parameter settings, e.g., --FONT_TITLE=12p */
624 		strcpy (orig, opt->arg);			/* Retain a copy of current option arguments */
625 		strcpy (copy, opt->arg);			/* Retain another copy of current option arguments */
626 		gmtinit_handle_escape_text (copy, '+', +1);	/* Hide any escaped +? sequences */
627 		directive = strchr (copy, '=');		/* Get location of equal sign, if present */
628 		modifier  = strchr (copy, '+');		/* Get location of plus sign, if present */
629 		got_directive = got_modifier = false;	/* Reset these to be false */
630 		/* Check for case where the = is part of a modifier, hence not a value or directive */
631 		if (directive && modifier && ((directive - copy) > (modifier - copy)))
632 			directive = NULL;				/* The = is part of a modifier and not the directive, so ignore it for now */
633 		if (directive) directive[0] = '\0', got_directive = true;	/* Cut off =value for now so opt->arg only has the keyword, but remember a directive was found */
634 		if (modifier) modifier[0] = '\0', got_modifier = true;		/* Cut off +modifier for now so orig only has the keyword, but remember a modifier was found */
635 		if ((kw = gmtinit_find_kw (API, gmt_common_kw, this_module_kw, orig, &k)) == NULL) {	/* Find matching keyword listing */
636 			/* Did not find matching long format keyword; undo damage and move to next option */
637 			if (directive) directive[0] = '=';
638 			if (modifier) modifier[0] = '+';
639 			continue;
640 		}
641 
642 		/* Here we found a matching long-format option name, returned as the kw[k] struct element */
643 		/* Do the long to short option substitution */
644 
645 		e_code = '=';	/* When we remove the '=' we will replace it, but in multi-sections the code may change after the first section */
646 		n_sections = ((kw[k].separator) ? gmt_count_char (API->GMT, orig, kw[k].separator) : 0) + 1;
647 		opt->option = kw[k].short_option;	/* Update the option character first */
648 		sep[0] = kw[k].separator;			/* Need a string with separator to strcat below */
649 		new_arg[0] = '\0';					/* Initialize short option arguments */
650 		modified = true;					/* We have at least modified one option */
651 		/* Special handling for --inrows and --outrows since they both map to q and need -qi and -qo, respectively */
652 		if (!strcmp (kw[k].long_option, "inrows"))
653 			strcat (new_arg, "i");
654 		else if (!strcmp (kw[k].long_option, "outrows"))
655 			strcat (new_arg, "o");
656 
657 		for (section = 0; section < n_sections; section++) {	/* Parse the sections separately but strcat together a single short option */
658 			/* Make sure a few things are correct */
659 			/* Find separator, set to 0, check if modifier is after, if so set to NULL. */
660 			if (n_sections > 1) {	/* Special case since there is only one leading =<value>; other values are given after separators */
661 				got_modifier = false;	/* Start over for each new section since modifiers are section-limited */
662 				sect_start = gmtinit_get_section (API, orig, kw[k].separator, section, &sect_end);	/* Get next section start and truncate */
663 				if (directive)	/* Update what directive is pointing to since no leading keyword for later sections */
664 					directive = (sect_start) ? orig + sect_start - 1 : strchr (orig, '=');	/* directive points to = or char before value */
665 				modifier = strchr (directive, '+');	/* Must also update to see if this section has modifiers... */
666 				if (modifier) modifier[0] = '\0', got_modifier = true;	/* ...and if it does we temporarily chop it off here but remember we found one */
667 			}
668 
669 			if (got_directive) {	/* Process a <directive>[:<arg>] or possibly just the <arg> */
670 				if ((code = gmtinit_find_argument (API, kw[k].long_directives, kw[k].short_directives, &directive[1], ':', argument)))	/* Get the directive, or return 0 if it is an argument instead */
671 					sprintf (add, "%c%s", code, argument);	/* Prepend the directive code */
672 				else	/* Just got an argument; no directive code */
673 					sprintf (add, "%s", argument);
674 				strcat (new_arg, add);	/* Add string to the short-format option argument */
675 				directive[0] = e_code;	/* Put back the = character (at least the first time; later it is a separator) */
676 			}
677 			if (got_modifier) {	/* We have one of more modifiers to process */
678 				unsigned int pos = 0;
679 				char item[GMT_LEN64] = {""};
680 				modifier[0] = '+';	/* Put back the plus sign for the first modifier */
681 				while ((gmt_strtok (modifier, "+", &pos, item))) {	/* While there are unprocessed modifiers */
682 					if ((code = gmtinit_find_argument (API, kw[k].long_modifiers, kw[k].short_modifiers, item, '=', argument)))	/* Get the modifier, or return 0 if unrecognized */
683 						sprintf (add, "+%c%s", code, argument);	/* Append modifier with argument next to it (may be empty) */
684 					else {
685 						GMT_Report (API, GMT_MSG_WARNING, "Long-modifier form %s for option -%c not recognized!\n", &directive[1], opt->option);
686 						add[0] = '\0';
687 					}
688 					strcat (new_arg, add);	/* Add to the short-format option argument */
689 				}
690 			}
691 			if (n_sections > 1) {	/* Need to separate results per section with the separator character */
692 				if (section < (n_sections - 1))	/* Except for last we need to append separator */
693 					strcat (new_arg, sep);	/* Add to the short-format option argument */
694 				if (sect_end > 0) orig[sect_end] = kw[k].separator;	/* Put back separator at end of current section */
695 				e_code = kw[k].separator;	/* Since after first section we no longer have '=' to replace */
696 			}
697 		}
698 		gmt_M_str_free (opt->arg);		/* Free old par=value string argument */
699 		gmtinit_handle_escape_text (new_arg, '+', -1);	/* Restore escaped +? sequences */
700 		opt->arg = strdup (new_arg);	/* Allocate copy of new argument */
701 	}
702 	if (modified && gmt_M_is_verbose (API->GMT, GMT_MSG_INFORMATION)) {	/* Echo the converted options */
703 		char *cmd = GMT_Create_Cmd (API, *options);
704 		GMT_Report (API, GMT_MSG_INFORMATION, "Reformatted options: %s\n", cmd);
705 		GMT_Destroy_Cmd (API, &cmd);	/* Free string */
706 	}
707 }
708 
709 #if 0
710 GMT_LOCAL void gmtinit_translate_to_long_options (struct GMTAPI_CTRL *API, struct GMT_KEYWORD_DICTIONARY *this_module_kw, struct GMT_OPTION **options) {
711 	/* Loop over given options and replace any standard short-form -<code>[value] option with the equivalent
712 	 *  long-form --parameter[=value] arguments. Specifically, long-format is defined as
713 	 *
714 	 * --longoption[=[<directive>:]<arg>][+<mod1>[=<arg1>]][+<mod2>[=<arg2>]]...
715 	 *
716 	 * For options that take more than one section of arguments (e.g., -Idx/dy or -icols1,cols2,...)
717 	 * the section
718 	 *
719 	 * [<arg>][+<mod1>[=<arg1>]][+<mod2>[=<arg2>]]
720 	 *
721 	 * may appear more than once after a section separator (e.g., '/' or ',').  The separator is an entry
722 	 * in kw.separator, or it is 0 if the option does not take more than one section.
723 	 */
724 	gmt_M_unused (API);
725 	gmt_M_unused (this_module_kw);
726 	gmt_M_unused (options);
727 	/* NOT CODED YET - INTENDED FOR TESTING OUR KEYWORD/VALUE TRANSLATIONS */
728 }
729 #endif
730 
731 #endif
732 
gmtinit_check_markers(struct GMT_CTRL * GMT)733 GMT_LOCAL int gmtinit_check_markers (struct GMT_CTRL *GMT) {
734 	int error = GMT_NOERROR;
735 	/* Make sure segment header markers and header markers are not the same */
736 	if (GMT->current.setting.io_head_marker_in[0] == '\0')	/* Nothing, set default before comparison */
737 		strcpy (GMT->current.setting.io_head_marker_in, DEF_HEADER_MARKERS);
738 
739 	if (strchr (GMT->current.setting.io_head_marker_in, GMT->current.setting.io_seg_marker[GMT_IN])) {
740 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Conflict between the settings of IO_HEADER_MARKER and IO_SEGMENT_MARKER for input:\n");
741 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot let %c be both a header record flag and multiple segment header flag for input data\n", GMT->current.setting.io_seg_marker[GMT_IN]);
742 		error++;
743 	}
744 	if (GMT->current.setting.io_seg_marker[GMT_OUT] == GMT->current.setting.io_head_marker_out) {
745 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Conflict between the settings of IO_HEADER_MARKER and IO_SEGMENT_MARKER for input:\n");
746 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot let %c be both a header record flag and multiple segment header flag for output data\n", GMT->current.setting.io_seg_marker[GMT_OUT]);
747 		error++;
748 	}
749 	return (error);
750 }
751 
gmtinit_get_psl_encoding(const char * encoding)752 GMT_LOCAL int gmtinit_get_psl_encoding (const char *encoding) {
753 	/* Return the specified encoding ID */
754 	int k = 0, match = 0;
755 	while (PSL_ISO_name[k] && (match = strcmp (encoding, PSL_ISO_name[k])) != 0) k++;
756 	return (match == 0) ? k : GMT_NOTSET;
757 }
758 
gmtinit_get_uservalue(struct GMT_CTRL * GMT,char * txt,int type,double * value,char * err_msg)759 GMT_LOCAL int gmtinit_get_uservalue (struct GMT_CTRL *GMT, char *txt, int type, double *value, char *err_msg) {
760 	/* Use to get a single data value of given type and exit if error, and return GMT_PARSE_ERROR */
761 	int kind;
762 	if ((kind = gmt_scanf (GMT, txt, type, value)) == GMT_IS_NAN) {
763 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Parsing error %s: %s\n", err_msg, txt);
764 		return GMT_PARSE_ERROR;
765 	}
766 	return 0;
767 }
768 
769 /*! . */
gmtinit_parse_h_option(struct GMT_CTRL * GMT,char * item)770 GMT_LOCAL int gmtinit_parse_h_option (struct GMT_CTRL *GMT, char *item) {
771 	int i, k = 1, error = 0, col = GMT_NOTSET;
772 	unsigned int pos = 0;
773 	char p[GMT_BUFSIZ] = {""}, *c = NULL;
774 
775 	/* Parse the -h option.  Full syntax: -h[i|o][<nrecs>][+c][+d][+r<remark>][+t<title>] */
776 
777 	/* Note: This forces the io to skip the first <nrecs> records, regardless of what they are.
778 	 * In addition, any record starting with # will be considered a comment.
779 	 * For output (-ho) no <nrecs> is allowed since either (a) we output the same number of
780 	 * input records we found or (b) the program writes a specific number of records built from scratch.
781 	 * Use +c to add a header identifying the various columns + [colno].
782 	 * Use +d to have a program delete existing headers in the input [Default appends].
783 	 * Use +r<remark> to add a specified header remark to the output file.
784 	 * Use +t<title> to add a specified header title to the output file.
785 	 */
786 	if (!item || !item[0]) {	/* Just -h: Nothing further to parse; just set defaults */
787 		GMT->current.setting.io_header[GMT_IN] = GMT->current.setting.io_header[GMT_OUT] = true;
788 		return (GMT_NOERROR);
789 	}
790 	if (item[0] == 'i')	{/* Apply to input only */
791 		col = GMT_IN;
792 		strncpy (GMT->common.g.string, item, GMT_LEN64-1);	/* Verbatim copy */
793 	}
794 	else if (item[0] == 'o')	/* Apply to output only */
795 		col = GMT_OUT;
796 	else {			/* Apply to both input and output columns */
797 		k = 0;
798 		strncpy (GMT->common.g.string, item, GMT_LEN64-1);	/* Verbatim copy */
799 	}
800 	if ((c = strchr (item, '+')))	/* Found modifiers */
801 		c[0] = '\0';	/* Truncate modifiers for now */
802 	if (isdigit (item[k])) {	/* Specified how many records for input */
803 		if (col == GMT_OUT) {
804 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Can only set the number of input header records; %s ignored\n", &item[k]);
805 		}
806 		else {
807 			i = atoi (&item[k]);
808 			if (i < 0)
809 				error++;
810 			else
811 				GMT->current.setting.io_n_header_items = i;
812 		}
813 	}
814 
815 	if (col == GMT_IN) {		/* Only input should have header records, set to true unless we gave -h[i]0 */
816 		GMT->current.setting.io_header[GMT_IN] = true;
817 		GMT->current.setting.io_header[GMT_OUT] = false;
818 	}
819 	else if (col == GMT_OUT) {	/* Only output should have header records */
820 		GMT->current.setting.io_header[GMT_OUT] = true;
821 		GMT->current.setting.io_header[GMT_IN] = false;
822 	}
823 	else {	/* Both in and out may have header records */
824 		GMT->current.setting.io_header[GMT_IN] = true;
825 		GMT->current.setting.io_header[GMT_OUT] = true;
826 	}
827 
828 	if (c) {	/* Return to the modifiers modifiers */
829 		c[0] = '+';	/* Put back so strtok can work */
830 		while ((gmt_strtok (c, "+", &pos, p))) {
831 			switch (p[0]) {
832 				case 'd':	/* Delete existing headers */
833 					GMT->common.h.mode = GMT_COMMENT_IS_RESET;
834 					break;
835 				case 'c':	/* Add column names record */
836 					GMT->common.h.add_colnames = true;
837 					break;
838 				case 'm':	/* Add a multi-segment record plus some eventual text */
839 					gmt_M_str_free (GMT->common.h.multi_segment);
840 					GMT->common.h.multi_segment = strdup (&p[1]);
841 					break;
842 				case 'r':	/* Add specific text remark */
843 					gmt_M_str_free (GMT->common.h.remark);
844 					GMT->common.h.remark = strdup (&p[1]);
845 					break;
846 				case 't':	/* Add specific text title */
847 					gmt_M_str_free (GMT->common.h.title);
848 					GMT->common.h.title = strdup (&p[1]);
849 					break;
850 				default:	/* Bad modifier */
851 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized modifier +%c.\n", p[0]);
852 					error++;
853 					break;
854 			}
855 		}
856 		*c = '\0';	/* Truncate the various modifiers to avoid duplicate titles, remarks etc output in command */
857 	}
858 	return (error);
859 }
860 
861 /*! . */
gmtinit_setautopagesize(struct GMT_CTRL * GMT)862 GMT_LOCAL void gmtinit_setautopagesize (struct GMT_CTRL *GMT) {
863 	/* In GMT modern mode we use the largest possible square page (~11x11 meters), portrait mode, and then crop automatically */
864 	GMT->current.setting.ps_page_size[0] = GMT->current.setting.ps_page_size[1] = GMT_PAPER_DIM;	/* Max area in points */
865 	GMT->current.setting.ps_media = -GMT_USER_MEDIA_OFFSET;
866 	GMT->current.setting.ps_orientation = PSL_PORTRAIT;
867 }
868 
869 /*! . */
gmtinit_rectR_to_geoR(struct GMT_CTRL * GMT,char unit,double rect[],double out_wesn[],bool get_R)870 GMT_LOCAL int gmtinit_rectR_to_geoR (struct GMT_CTRL *GMT, char unit, double rect[], double out_wesn[], bool get_R) {
871 	/* If user gives -Re|f|k|M|n<xmin>/<xmax>/<ymin>/<ymax>[/<zmin>/<zmax>][+r] then we must
872 	 * call GMT_mapproject to convert this to geographic degrees.
873 	 * get_R is true when this is done to obtain the -R setting.  */
874 
875 	int proj_class;
876 	uint64_t dim[GMT_DIM_SIZE] = {1, 1, 2, 2};	/* Just a single data table with one segment with two 2-column records */
877 	bool was_R, was_J;
878 	double wesn[4];
879 	char buffer[GMT_LEN256] = {""}, Jstring[GMT_LEN128] = {""}, in_string[GMT_VF_LEN] = {""}, out_string[GMT_VF_LEN] = {""}, origin_flag[4] = {""}, *v = NULL;
880 	struct GMT_DATASET *In = NULL, *Out = NULL;
881 
882 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Call gmtinit_rectR_to_geoR to convert projected -R to geo -R\n");
883 	if (gmt_M_is_dnan (GMT->current.proj.lon0)) {
884 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Central meridian is not known; cannot convert -R...+u<unit>... to geographic corners\n");
885 		return (GMT_MAP_NO_PROJECTION);
886 	}
887 	if (gmt_M_is_dnan (GMT->current.proj.lat0)) {
888 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Projection standard latitude is not known; cannot convert -R...+u<unit>... to geographic corners\n");
889 		return (GMT_MAP_NO_PROJECTION);
890 	}
891 	/* Create dataset to hold the rect coordinates */
892 	if ((In = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) return (GMT_MEMORY_ERROR);
893 
894 	In->table[0]->segment[0]->data[GMT_X][0] = rect[XLO];
895 	In->table[0]->segment[0]->data[GMT_Y][0] = rect[YLO];
896 	In->table[0]->segment[0]->data[GMT_X][1] = rect[XHI];
897 	In->table[0]->segment[0]->data[GMT_Y][1] = rect[YHI];
898 	In->table[0]->segment[0]->n_rows = 2;
899 
900 	/* Set up machinery to call mapproject */
901 
902 	/* Register In as input virtual file and define an output virtual file */
903 	if (GMT_Open_VirtualFile (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_IN|GMT_IS_REFERENCE, In, in_string) == GMT_NOTSET)
904 		return (GMT->parent->error);
905 	if (GMT_Open_VirtualFile (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_OUT|GMT_IS_REFERENCE, NULL, out_string) == GMT_NOTSET)
906 		return (GMT->parent->error);
907 
908 	was_R = GMT->common.R.active[RSET];	was_J = GMT->common.J.active;
909 	GMT->common.R.active[RSET] = GMT->common.J.active = false;	/* To allow new entries */
910 
911 	/* Determine suitable -R setting for this projection */
912 
913 	/* Default w/e/s/n is small patch centered on projection center - this may change below */
914 	wesn[XLO] = GMT->current.proj.lon0 - 1.0;		wesn[XHI] = GMT->current.proj.lon0 + 1.0;
915 	wesn[YLO] = MAX (GMT->current.proj.lat0 -1.0, -90.0);	wesn[YHI] = MIN (GMT->current.proj.lat0 + 1.0, 90.0);
916 
917 	proj_class = GMT->current.proj.projection_GMT / 100;	/* 1-4 for valid projections */
918 	if (GMT->current.proj.projection_GMT == GMT_AZ_EQDIST) proj_class = 4;	/* Make -JE use global region */
919 	switch (proj_class) {
920 		case 1:	/* Cylindrical: pick small equatorial patch centered on central meridian */
921 			if (GMT->current.proj.projection_GMT == GMT_UTM && gmt_UTMzone_to_wesn (GMT, GMT->current.proj.utm_zonex, GMT->current.proj.utm_zoney, GMT->current.proj.utm_hemisphere, wesn)) {
922 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "UTM projection insufficiently specified to auto-determine geographic region\n");
923 				return (GMT_MAP_NO_PROJECTION);
924 			}
925 			if (GMT->current.proj.projection_GMT == GMT_MERCATOR)	/* Special use of Mercator units relative to stated origin */
926 				strcpy (origin_flag, "+m");
927 			break;
928 		case 2: /* Conical: Use default patch */
929 			break;
930 		case 3: /* Azimuthal: Use default patch, or hemisphere for polar projections */
931 			if (doubleAlmostEqualZero (GMT->current.proj.lat0, 90.0)) {
932 				wesn[YLO] = 0.0;	wesn[YHI] = 90.0;
933 				wesn[XLO] = GMT->current.proj.lon0 - 180.0;	wesn[XHI] = GMT->current.proj.lon0 + 180.0;
934 			}
935 			else if (doubleAlmostEqualZero (GMT->current.proj.lat0, -90.0)) {
936 				wesn[YLO] = -90.0;	wesn[YHI] = 0.0;
937 				wesn[XLO] = GMT->current.proj.lon0 - 180.0;	wesn[XHI] = GMT->current.proj.lon0 + 180.0;
938 			}
939 			break;
940 		case 4: /* Global: Give global region */
941 			wesn[XLO] = 0.0;	wesn[XHI] = 360.0;	wesn[YLO] = -90.0;	wesn[YHI] = 90.0;
942 			break;
943 		default:
944 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "No map projection specified to auto-determine geographic region\n");
945 			break;
946 	}
947 	strncpy (Jstring, GMT->common.J.string, GMT_LEN128-1);	/* Make a duplicate in case we must mess around with it */
948 	if (GMT->current.proj.obl_flip) {	/* Rotating by 90 has some challenges, like flipping rect x and y */
949 		In->table[0]->segment[0]->data[GMT_X][0] = rect[YLO];
950 		In->table[0]->segment[0]->data[GMT_Y][0] = rect[XLO];
951 		In->table[0]->segment[0]->data[GMT_X][1] = rect[YHI];
952 		In->table[0]->segment[0]->data[GMT_Y][1] = rect[XHI];
953 		if ((v = strstr (Jstring, "+v"))) {	/* Cannot pass +v in this context since we just need to recover corner coordinates, not rotate yet */
954 			char *d = strstr (Jstring, "+d");	/* Did we also use +d ? */
955 			if (d && d > v) { /* Have both +d and +v, with +v at end, so must move +d up ahead of +v */
956 				size_t len = strlen (d);
957 				memmove (v, d, len);
958 				v[len] = '\0';	/* Truncate the rest */
959 			}
960 			else /* Just need to skip the trailing +v part */
961 				v[0] = '\0';
962 		}
963 	}
964 	snprintf (buffer, GMT_LEN256, "-R%g/%g/%g/%g -J%s -I -F%c -C%s -bi2d -bo2d -<%s ->%s --GMT_HISTORY=readonly",
965 		wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI], Jstring, unit, origin_flag, in_string, out_string);
966 	if (get_R) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Obtaining geographic corner coordinates via mapproject %s\n", buffer);
967 	if (GMT_Call_Module (GMT->parent, "mapproject", GMT_MODULE_CMD, buffer) != GMT_OK)	/* Get the corners in degrees via mapproject */
968 		return (GMT->parent->error);
969 	GMT->common.R.active[RSET] = was_R;	GMT->common.J.active = was_J;
970 	if ((Out = GMT_Read_VirtualFile (GMT->parent, out_string)) == NULL)
971 		return (GMT->parent->error);
972 	/* Close the virtual files */
973 	if (GMT_Close_VirtualFile (GMT->parent, in_string) != GMT_NOERROR)
974 		return (GMT->parent->error);
975 	if (GMT_Close_VirtualFile (GMT->parent, out_string) != GMT_NOERROR)
976 		return (GMT->parent->error);
977 	out_wesn[XLO] = Out->table[0]->segment[0]->data[GMT_X][0];
978 	out_wesn[YLO] = Out->table[0]->segment[0]->data[GMT_Y][0];
979 	out_wesn[XHI] = Out->table[0]->segment[0]->data[GMT_X][1];
980 	out_wesn[YHI] = Out->table[0]->segment[0]->data[GMT_Y][1];
981 	/* Free memory used for projection */
982 	if (GMT_Destroy_Data (GMT->parent, &In) != GMT_OK)
983 		return (GMT->parent->error);
984 	if (GMT_Destroy_Data (GMT->parent, &Out) != GMT_OK)
985 		return (GMT->parent->error);
986 
987 	if (get_R) GMT_Report (GMT->parent, GMT_MSG_INFORMATION,
988 		"Region selection -R%s is replaced by the equivalent geographic region -R%.12g/%.12g/%.12g/%.12g+r\n",
989 		GMT->common.R.string, out_wesn[XLO], out_wesn[YLO], out_wesn[XHI], out_wesn[YHI]);
990 
991 
992 	return (GMT_NOERROR);
993 }
994 
995 /*! . */
gmtinit_parse_X_option(struct GMT_CTRL * GMT,char * text)996 GMT_LOCAL int gmtinit_parse_X_option (struct GMT_CTRL *GMT, char *text) {
997 	/* Syntax: -Xa|r|f|c<off>, -X[-|+]w[/<n>][-|+]<off>, where
998 	 * w is the width of the previous plot command. */
999 	int i = 0;
1000 	if (!text || !text[0]) {	/* Default is -Xr0 */
1001 		GMT->current.ps.origin[GMT_X] = GMT->common.X.mode = 'r';
1002 		GMT->common.X.off = GMT->current.setting.map_origin[GMT_X] = 0.0;
1003 		return (GMT_NOERROR);
1004 	}
1005 	switch (text[0]) {
1006 		case 'r': case 'a': case 'f': case 'c':
1007 			GMT->current.ps.origin[GMT_X] = GMT->common.X.mode = text[0]; i++; break;
1008 		default:
1009 			GMT->current.ps.origin[GMT_X] = GMT->common.X.mode = 'r'; break;
1010 	}
1011 	if (text[i]) {	/* Gave some argument */
1012 		if (strchr (text, 'w')) {	/* Used previous width as part of offset */
1013 			GMT->current.setting.map_origin[GMT_X] = GMT->current.map.last_width;
1014 			if (text[i] == '-') {	/* Wanted the negative width */
1015 				GMT->current.setting.map_origin[GMT_X] *= -1;
1016 				i++;	/* Skip the minus sign */
1017 			}
1018 			else if (text[i] == '+')	/* In case the user explicitly gave a + */
1019 				i++;	/* Skip the plus sign */
1020 			i++;	/* Skip past the w */
1021 			if (text[i] == '/') {	/* Wanted a fraction of the width */
1022 				i++;	/* Skip the slash */
1023 				GMT->current.setting.map_origin[GMT_X] /= atof (&text[i]);
1024 				while (isdigit (text[i]) || text[i] == '.') i++;	/* Wind past the denominator */
1025 			}
1026 			/* Now add the offset the user added, if given */
1027 			if (text[i]) GMT->current.setting.map_origin[GMT_X] += gmt_M_to_inch (GMT, &text[i]);
1028 		}
1029 		else
1030 			GMT->current.setting.map_origin[GMT_X] = gmt_M_to_inch (GMT, &text[i]);
1031 	}
1032 	else	/* Allow use of -Xc or -Xf meaning -Xc0 or -Xf0 */
1033 		GMT->current.setting.map_origin[GMT_X] = 0.0;
1034 	GMT->common.X.off = GMT->current.setting.map_origin[GMT_X];
1035 	return (GMT_NOERROR);
1036 }
1037 
1038 /*! . */
gmtinit_parse_Y_option(struct GMT_CTRL * GMT,char * text)1039 GMT_LOCAL int gmtinit_parse_Y_option (struct GMT_CTRL *GMT, char *text) {
1040 	/* Syntax: -Ya|r|f|c<off>, -Y[-|+]h[/<n>][-|+]<off>, where
1041 	 * h is the height of the previous plot command. */
1042 	int i = 0;
1043 	if (!text || !text[0]) {	/* Default is -Yr0 */
1044 		GMT->current.ps.origin[GMT_Y] = GMT->common.Y.mode = 'r';
1045 		GMT->common.Y.off = GMT->current.setting.map_origin[GMT_Y] = 0.0;
1046 		return (GMT_NOERROR);
1047 	}
1048 	switch (text[0]) {
1049 		case 'r': case 'a': case 'f': case 'c':
1050 			GMT->current.ps.origin[GMT_Y] = GMT->common.Y.mode = text[0]; i++; break;
1051 		default:
1052 			GMT->current.ps.origin[GMT_Y] = GMT->common.Y.mode = 'r'; break;
1053 	}
1054 	if (text[i]) {	/* Gave some argument */
1055 		if (strchr (text, 'h')) {	/* Used previous height as part of offset */
1056 			GMT->current.setting.map_origin[GMT_Y] = GMT->current.map.last_height;
1057 			if (text[i] == '-') {	/* Wanted the negative height */
1058 				GMT->current.setting.map_origin[GMT_Y] *= -1;
1059 				i++;	/* Skip the minus sign */
1060 			}
1061 			else if (text[i] == '+')	/* In case the user explicitly gave a + */
1062 				i++;	/* Skip the plus sign */
1063 			i++;	/* Skip past the h */
1064 			if (text[i] == '/') {	/* Wanted a fraction of the height */
1065 				i++;	/* Skip the slash */
1066 				GMT->current.setting.map_origin[GMT_Y] /= atof (&text[i]);
1067 				while (isdigit (text[i]) || text[i] == '.') i++;	/* Wind past the denominator */
1068 			}
1069 			/* Now add the offset the user added, if given */
1070 			if (text[i]) GMT->current.setting.map_origin[GMT_Y] += gmt_M_to_inch (GMT, &text[i]);
1071 		}
1072 		else
1073 			GMT->current.setting.map_origin[GMT_Y] = gmt_M_to_inch (GMT, &text[i]);
1074 	}
1075 	else	/* Allow use of -Yc or -Yf meaning -Yc0 or -Yf0 */
1076 		GMT->current.setting.map_origin[GMT_Y] = 0.0;
1077 	GMT->common.Y.off = GMT->current.setting.map_origin[GMT_Y];
1078 	return (GMT_NOERROR);
1079 }
1080 
1081 /*! . */
gmtinit_ogr_get_geometry(char * item)1082 GMT_LOCAL int gmtinit_ogr_get_geometry (char *item) {
1083 	if (!strcmp (item, "point")  || !strcmp (item, "POINT")) return (GMT_IS_POINT);
1084 	if (!strcmp (item, "mpoint") || !strcmp (item, "MPOINT")) return (GMT_IS_MULTIPOINT);
1085 	if (!strcmp (item, "line")   || !strcmp (item, "LINE")) return (GMT_IS_LINESTRING);
1086 	if (!strcmp (item, "mline")  || !strcmp (item, "MLINE")) return (GMT_IS_MULTILINESTRING);
1087 	if (!strcmp (item, "poly")   || !strcmp (item, "POLY")) return (GMT_IS_POLYGON);
1088 	if (!strcmp (item, "mpoly")  || !strcmp (item, "MPOLY")) return (GMT_IS_MULTIPOLYGON);
1089 	return (GMT_NOTSET);
1090 }
1091 
1092 
1093 /*! . */
gmtinit_parse_a_option(struct GMT_CTRL * GMT,char * arg)1094 GMT_LOCAL int gmtinit_parse_a_option (struct GMT_CTRL *GMT, char *arg) {
1095 	/* -a<col>=<name>[:<type>][,<col>...][+g|G<geometry>] */
1096 	unsigned int pos = 0;
1097 	int col, a_col = GMT_Z, t;
1098 	char p[GMT_BUFSIZ] = {""}, name[GMT_BUFSIZ] = {""}, A[GMT_LEN64] = {""}, *s = NULL, *c = NULL;
1099 	if (!arg) return (GMT_PARSE_ERROR);	/* -a requires a non-NULL argument */
1100 	//if (!arg[0]) return (GMT_PARSE_ERROR);	/* -a requires an argument */
1101 	strncpy (GMT->common.a.string, arg, GMT_LEN256-1);	/* Verbatim copy */
1102 
1103 	if ((s = strstr (arg, "+g")) || (s = strstr (arg, "+G"))) {	/* Also got +g|G<geometry> */
1104 		if ((t = (int)gmtinit_ogr_get_geometry (s+2)) < 0) {
1105 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -a: No such geometry: %s.\n", s+2);
1106 			return (GMT_PARSE_ERROR);
1107 		}
1108 		GMT->common.a.geometry = t;
1109 		if (s[1] == 'G') GMT->common.a.clip = true;	/* Clip features at Dateline */
1110 		s[0] = '\0';	/* Temporarily truncate off the geometry */
1111 		GMT->common.a.output = true;	/* We are producing, not reading an OGR/GMT file */
1112 		if (GMT->current.setting.io_seg_marker[GMT_OUT] != '>') {
1113 			GMT_Report (GMT->parent, GMT_MSG_WARNING,
1114 				"Option -a: OGR/GMT requires > as output segment marker; your selection of %c will be overruled by >\n",
1115 				GMT->current.setting.io_seg_marker[GMT_OUT]);
1116 			GMT->current.setting.io_seg_marker[GMT_OUT] = '>';
1117 		}
1118 	}
1119 	else if (GMT->current.setting.io_seg_marker[GMT_IN] != '>') {
1120 		GMT_Report (GMT->parent, GMT_MSG_WARNING,
1121 			"Option -a: OGR/GMT requires < as input segment marker; your selection of %c will be overruled by >\n",
1122 			GMT->current.setting.io_seg_marker[GMT_IN]);
1123 		GMT->current.setting.io_seg_marker[GMT_IN] = '>';
1124 	}
1125 	while ((gmt_strtok (arg, ",", &pos, p))) {	/* Another col=name argument */
1126 		if ((c = strchr (p, ':'))) {	/* Also got :<type> */
1127 			if ((t = gmtlib_ogr_get_type (c+1)) < 0) {
1128 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -a: No such type: %s.\n", c+1);
1129 				return (GMT_PARSE_ERROR);
1130 			}
1131 			GMT->common.a.type[GMT->common.a.n_aspatial] = t;
1132 			c[0] = '\0';	/* Truncate off the type */
1133 		}
1134 		else
1135 			GMT->common.a.type[GMT->common.a.n_aspatial] = GMT_DOUBLE;
1136 		if (strchr (p, '=')) {	/* Got col=name */
1137 			if (sscanf (p, "%[^=]=%s", A, name) != 2) return (GMT_PARSE_ERROR);	/* Did not get two items */
1138 		}
1139 		else {	/* Auto-fill col as the next col starting at GMT_Z */
1140 			snprintf (A, GMT_LEN64, "%d", a_col++);
1141 			strcpy (name, p);
1142 		}
1143 		switch (A[0]) {	/* Watch for different multisegment header cases */
1144 			case 'D': col = GMT_IS_D; break;	/* Distance flag */
1145 			case 'G': col = GMT_IS_G; break;	/* Color flag */
1146 			case 'I': col = GMT_IS_I; break;	/* ID flag */
1147 			case 'L': col = GMT_IS_L; break;	/* Label flag */
1148 			case 'T': col = GMT_IS_T; break;	/* Text flag */
1149 			case 'W': col = GMT_IS_W; break;	/* Pen flag */
1150 			case 'Z': col = GMT_IS_Z; break;	/* Value flag */
1151 			default:
1152 				col = atoi (A);
1153 				if (col < GMT_Z)
1154 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -a: Columns 0 and 1 are reserved for lon and lat.\n");
1155 				if (col < GMT_Z || col >= GMT_MAX_COLUMNS) return (GMT_PARSE_ERROR);		/* Col value out of whack */
1156 				break;
1157 		}
1158 		GMT->common.a.col[GMT->common.a.n_aspatial] = col;
1159 		if (col < 0 && col != GMT_IS_Z) GMT->common.a.type[GMT->common.a.n_aspatial] = GMT_TEXT;
1160 		gmt_M_str_free (GMT->common.a.name[GMT->common.a.n_aspatial]);	/* Free any previous names */
1161 		GMT->common.a.name[GMT->common.a.n_aspatial] = strdup (name);
1162 		GMT->common.a.n_aspatial++;
1163 		if (GMT->common.a.n_aspatial == MAX_ASPATIAL) return (GMT_PARSE_ERROR);	/* Too many items */
1164 	}
1165 	if (s) s[0] = '+';	/* Restore the geometry part */
1166 	return (GMT_NOERROR);
1167 }
1168 
1169 /*! . */
gmtinit_parse_b_option(struct GMT_CTRL * GMT,char * text)1170 GMT_LOCAL int gmtinit_parse_b_option (struct GMT_CTRL *GMT, char *text) {
1171 	/* GMT5 Syntax:	-b[i][cvar1/var2/...] or -b[i|o]<n><type>[,<n><type>]...
1172 	 * GMT4 Syntax:	-b[i][o][s|S][d|D][<n>][c[<var1>/<var2>/...]]
1173 	 * -b with no args means both in and out have double precision binary i/o, with #columns determined by module
1174 	 * -bi or -bo means the same for that direction only.
1175 	 * -bif or -bof or any other letter means that type instead of double.
1176 	 * Note to developers: It is allowed NOT to specify anything (e.g., -bi) or not specify how many columns (e.g., -bif).
1177 	 * If those are not set then there are two opportunities in the modules to correct this:
1178 	 *   1) gmtlib_io_banner is called from GMT_Init_IO and if things are not set we default to the default data type [double].
1179 	 *   2) gmt_set_cols sets the actual columns needed for in or out and is also use to set un-initialized data read pointers.
1180 	 */
1181 
1182 	unsigned int i, col = 0, id = GMT_IN, swap_flag;
1183 	int k, ncol = 0;
1184 	bool endian_swab = false, swab = false, error = false, i_or_o = false, set = false, v4_parse = false;
1185 	char *p = NULL, c;
1186 
1187 	if (!text) return (GMT_PARSE_ERROR);	/* -B requires an argument even if it is blank */
1188 	/* First determine if there is an endian modifier supplied */
1189 	if ((p = strchr (text, '+'))) {	/* Yes */
1190 		*p = '\0';	/* Temporarily chop off the modifier */
1191 		switch (p[1]) {
1192 			case 'B':	case 'b':
1193 			case 'L':	case 'l':
1194 				swab = (toupper (p[1]) != GMT_ENDIAN);
1195 				break;	/* Must swap */
1196 			default:
1197 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -b: Bad endian modifier +%c\n", (int)p[1]);
1198 				return (GMT_PARSE_ERROR);
1199 				break;
1200 		}
1201 		if (strchr (text, 'w')) {	/* Cannot do individual swap when endian has been indicated */
1202 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -b: Cannot use both w and endian modifiers\n");
1203 			return (GMT_PARSE_ERROR);
1204 		}
1205 		endian_swab = true;
1206 	}
1207 
1208 	/* Now deal with [i|o] modifier Note: If there is no i|o then id is GMT_IN below */
1209 	if (text[0] == 'i') { id = GMT_IN; i_or_o = true; }
1210 	if (text[0] == 'o') { id = GMT_OUT; i_or_o = true; }
1211 	GMT->common.b.active[id] = true;
1212 	GMT->common.b.type[id] = 'd';	/* Set default to double */
1213 	GMT->common.b.swab[id] = k_swap_none;	/* No byte swapping */
1214 
1215 	/* Because under GMT_COMPAT c means either netCDF or signed char we deal with netCDF up front */
1216 
1217 	k = (i_or_o) ? 1 : 0;
1218 	if (gmt_M_compat_check (GMT, 4)) {	/* GMT4 */
1219 		if (text[k] == 'c' && !text[k+1]) {	/* Ambiguous case "-bic" which MAY mean netCDF */
1220 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Syntax warning: -b[i]c now applies to character tables, not to netCDF\n");
1221 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Syntax warning: If input file is netCDF, just leave out -b[i]c\n");
1222 			GMT->common.b.type[id] = 'c';
1223 			v4_parse = true;	/* Yes, we parsed a GMT4-compatible option */
1224 		}
1225 		else if (text[k] == 'c' && text[k+1] != ',') {	/* netCDF with list of variables */
1226 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Syntax warning: -b[i]c<varlist> is deprecated. Use <file>?<varlist> instead.\n");
1227 			GMT->common.b.nc[id] = true;
1228 			GMT->common.b.active[id] = false;	/* Binary is 'false' if netCDF is to be read */
1229 			strncpy (GMT->common.b.varnames, &text[k+1], GMT_BUFSIZ-1);	/* Copy the list of netCDF variable names */
1230 			v4_parse = true;	/* Yes, we parsed a GMT4-compatible option */
1231 		}
1232 	}
1233 	if (!v4_parse && text[k] && strchr ("cuhHiIfd" GMT_OPT ("sSD"), text[k]) && (!text[k+1] || (text[k+1] == 'w' && !text[k+2] ))) {	/* Just save the type for the entire record */
1234 		GMT->common.b.type[id] = text[k];			/* Set the default column type to the first (and possibly only data type) listed */
1235 		if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: Must switch s,S,D to f, f(with swab), and d (with swab) */
1236 			if (GMT->common.b.type[id] == 's') GMT->common.b.type[id] = 'f';
1237 			if (GMT->common.b.type[id] == 'S') { GMT->common.b.type[id] = 'f'; GMT->common.b.swab[id] = (id == GMT_IN) ? k_swap_in : k_swap_out;	}
1238 			if (GMT->common.b.type[id] == 'D') { GMT->common.b.type[id] = 'd'; GMT->common.b.swab[id] = (id == GMT_IN) ? k_swap_in : k_swap_out;	}
1239 		}
1240 		if (text[k+1] == 'w') GMT->common.b.swab[id] = (id == GMT_IN) ? k_swap_in : k_swap_out;	/* Default swab */
1241 	}
1242 	if (!v4_parse) {	/* Meaning we did not hit netcdf-like options above */
1243 		for (i = k; text[i]; i++) {
1244 			c = text[i];
1245 			switch (c) {
1246 				case 's': case 'S': case 'D':	/* Obsolete GMT 4 syntax with single and double precision w/wo swapping */
1247 					if (gmt_M_compat_check (GMT, 4)) {
1248 						GMT_Report (GMT->parent, GMT_MSG_COMPAT,
1249 							"The -b option with type s, S, or D are deprecated; Use <n>f or <n>d, with w to indicate swab\n");
1250 						if (c == 'S' || c == 'D') swab = true;
1251 						if (c == 'S' || c == 's') c = 'f';
1252 						else if (c == 'D') c = 'd';
1253 						if (ncol == 0) ncol = 1;	/* In order to make -bs work as before */
1254 					}
1255 					else {
1256 						error = true;
1257 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed -b argument [%s]\n", text);
1258 						gmt_syntax (GMT, 'b');
1259 						break;
1260 					}
1261 					/* Intentionally fall through */
1262 				case 'c': case 'u': /* int8_t, uint8_t */
1263 				case 'h': case 'H': /* int16_t, uint16_t */
1264 				case 'i': case 'I': /* int32_t, uint32_t */
1265 				case 'l': case 'L': /* int64_t, uint64_t */
1266 				case 'f': case 'd': /* float, double */
1267 					if (gmt_M_compat_check (GMT, 4) && c == 'd' && ncol == 0) {
1268 						ncol = 1;	/* In order to make -bd work as before */
1269 						GMT_Report (GMT->parent, GMT_MSG_COMPAT, "-b[o]d is deprecated; Use <n>d to indicate how many columns\n");
1270 					}
1271 					if (text[i+1] == 'w')	/* Want to swab the input or output first */
1272 						swab = true;
1273 					set = true;	/* Meaning we have set the data type */
1274 					if (ncol == 0) {	/* Just specifying type, no columns yet */
1275 						GMT->common.b.type[id] = c;	/* Set default to double */
1276 					}
1277 					swap_flag = (swab) ? id + 1 : 0;	/* 0 for no swap, 1 if swap input, 2 if swap output */
1278 					for (k = 0; k < ncol; k++, col++) {	/* Assign io function pointer and data type for each column */
1279 						GMT->current.io.fmt[id][col].io = gmtlib_get_io_ptr (GMT, id, swap_flag, c);
1280 						GMT->current.io.fmt[id][col].type = gmt_get_io_type (GMT, c);
1281 						if (!i_or_o) {	/* Must also set output */
1282 							GMT->current.io.fmt[GMT_OUT][col].io = gmtlib_get_io_ptr (GMT, GMT_OUT, swap_flag, c);
1283 							GMT->current.io.fmt[GMT_OUT][col].type = gmt_get_io_type (GMT, c);
1284 						}
1285 					}
1286 					ncol = 0;	/* Must parse a new number for each item */
1287 					break;
1288 				case 'x':	/* Binary skip before/after column */
1289 					if (col == 0)	/* Must skip BEFORE reading first data column (flag this as a negative skip) */
1290 						GMT->current.io.fmt[id][col].skip = -ncol;	/* Number of bytes to skip */
1291 					else	/* Skip after reading previous column (hence col-1) */
1292 						GMT->current.io.fmt[id][col-1].skip = ncol;	/* Number of bytes to skip */
1293 					if (!i_or_o) GMT->current.io.fmt[GMT_OUT][col-1].skip = GMT->current.io.fmt[id][col-1].skip;
1294 					break;
1295 				case '0':	/* Number of columns */
1296 				case '1': case '2': case '3':
1297 				case '4': case '5': case '6':
1298 				case '7': case '8': case '9':
1299 					ncol = atoi (&text[i]);
1300 					if (ncol < 1) {
1301 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -b: Column count must be > 0\n");
1302 						return (GMT_PARSE_ERROR);
1303 					}
1304 					while (text[++i] && isdigit ((int)text[i])); /* Wind past the digits */
1305 					--i;	/* Then go back to last digit since for loop will do i++ after this */
1306 					break;
1307 				case ',':
1308 					break;	/* Comma between sequences are optional and just ignored */
1309 				case 'w':		/* Turn off the swap on a per-item basis unless it was set via +L|B */
1310 					if (!endian_swab)
1311 						swab = false;
1312 					break;
1313 				default:
1314 					error = true;
1315 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed -b argument [%s]\n", text);
1316 					gmt_syntax (GMT, 'b');
1317 					break;
1318 			}
1319 		}
1320 	}
1321 	if (col == 0)
1322 		col = ncol; /* Maybe we got a column count */
1323 	GMT->common.b.ncol[id] = col;
1324 	if (col && !set) {
1325 		for (col = 0; col < GMT->common.b.ncol[id]; col++) {
1326 			/* Default binary type is double */
1327 			GMT->current.io.fmt[id][col].io   = gmtlib_get_io_ptr (GMT, id, swab, 'd');
1328 			GMT->current.io.fmt[id][col].type = gmt_get_io_type (GMT, 'd');
1329 			if (!i_or_o) {	/* Must also set output */
1330 				GMT->current.io.fmt[GMT_OUT][col].io   = gmtlib_get_io_ptr (GMT, GMT_OUT, swab, 'd');
1331 				GMT->current.io.fmt[GMT_OUT][col].type = gmt_get_io_type (GMT, 'd');
1332 			}
1333 		}
1334 	}
1335 
1336 	if (!i_or_o) {	/* Specified neither i or o so let settings apply to both */
1337 		GMT->common.b.nc[GMT_OUT] = GMT->common.b.nc[GMT_IN];
1338 		GMT->common.b.active[GMT_OUT] = GMT->common.b.active[GMT_IN];
1339 		GMT->common.b.ncol[GMT_OUT] = GMT->common.b.ncol[GMT_IN];
1340 		GMT->common.b.type[GMT_OUT] = GMT->common.b.type[GMT_IN];
1341 		if (GMT->common.b.swab[GMT_IN] == k_swap_in) GMT->common.b.swab[GMT_OUT] = k_swap_out;
1342 		strncpy (GMT->common.b.string, text, GMT_LEN256-1);
1343 	}
1344 	else if (id == GMT_IN)
1345 		strncpy (GMT->common.b.string, text, GMT_LEN256-1);
1346 
1347 
1348 	gmtlib_set_bin_io (GMT);	/* Make sure we point to binary i/o functions after processing -b option */
1349 
1350 	if (p) *p = '+';	/* Restore the + sign we gobbled up earlier */
1351 	return (error);
1352 }
1353 
1354 /*! Routine will decode the -f[i|o]<shortcut>|<cols>[d|f|s|t|T|x|y],... arguments */
gmtinit_parse_f_option(struct GMT_CTRL * GMT,char * arg)1355 GMT_LOCAL int gmtinit_parse_f_option (struct GMT_CTRL *GMT, char *arg) {
1356 
1357 	char copy[GMT_BUFSIZ] = {""}, p[GMT_BUFSIZ] = {""};
1358 	unsigned int dir, k = 1, c, pos = 0, code, *col = NULL;
1359 	int64_t i, start = GMT_NOTSET, stop = GMT_NOTSET, inc;
1360 	enum gmt_enum_units unit = GMT_IS_METER;
1361 	enum gmt_col_enum ctype;
1362 
1363 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -f requires an argument */
1364 
1365 	/* Determine if this is for input, output, or both */
1366 
1367 	if (arg[0] == 'i') {	/* Apply to input columns only */
1368 		dir = GMT_IN;
1369 		col = GMT->current.io.col_type[GMT_IN];
1370 		strncpy (GMT->common.f.string, arg, GMT_LEN64-1);	/* Verbatim copy */
1371 	}
1372 	else if (arg[0] == 'o') {	/* Apply to output columns only */
1373 		dir = GMT_OUT;
1374 		col = GMT->current.io.col_type[GMT_OUT];
1375 	}
1376 	else {			/* Apply to both input and output columns */
1377 		dir = GMT_IO;
1378 		k = 0;
1379 		strncpy (GMT->common.f.string, arg, GMT_LEN64-1);	/* Verbatim copy */
1380 	}
1381 
1382 	strncpy (copy, &arg[k], GMT_BUFSIZ-1);	/* copy should NOT have a leading i|o part */
1383 
1384 	if (copy[0] == 'c') {	/* Got -f[i|o]c which is shorthand for -f[i|o]0:1f (Cartesian) */
1385 		if (dir == GMT_IO) {
1386 			gmt_set_cartesian (GMT, GMT_IN);
1387 			gmt_set_cartesian (GMT, GMT_OUT);
1388 		}
1389 		else {
1390 			col[GMT_X] = GMT_IS_FLOAT;
1391 			col[GMT_Y] = GMT_IS_FLOAT;
1392 		}
1393 		pos = 1;
1394 		start = stop = 1;
1395 	}
1396 	else if (copy[0] == 'g' || copy[0] == 'p') {	/* Got -f[i|o]g which is shorthand for -f[i|o]0x,1y, or -fp[<unit>] (see below for more action) */
1397 		if (dir == GMT_IO) {
1398 			gmt_set_geographic (GMT, GMT_IN);
1399 			gmt_set_geographic (GMT, GMT_OUT);
1400 		}
1401 		else {
1402 			col[GMT_X] = GMT_IS_LON;
1403 			col[GMT_Y] = GMT_IS_LAT;
1404 		}
1405 		pos = 1;
1406 		start = stop = 1;
1407 	}
1408 	if (copy[0] == 'p') {	/* Got -f[i|o]p[<unit>] for projected floating point map coordinates (e.g., UTM meters) */
1409 		if (copy[1] && strchr (GMT_LEN_UNITS2, copy[1])) {	/* Given a unit via -fp<unit>*/
1410 			if ((unit = gmtlib_get_unit_number (GMT, copy[1])) == GMT_IS_NOUNIT) {
1411 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed -f argument [%s] - bad projected unit\n", arg);
1412 				return 1;
1413 			}
1414 			pos++;
1415 		}
1416 		GMT->current.proj.inv_coordinates = true;
1417 		GMT->current.proj.inv_coord_unit = unit;
1418 	}
1419 
1420 	/* Process each group of columns that shall have the same type */
1421 	while ((gmt_strtok (copy, ",", &pos, p))) {	/* While it is not empty, process the next group = <columns><type> */
1422 		if ((inc = gmtlib_parse_index_range (GMT, p, &start, &stop)) == 0) return (GMT_PARSE_ERROR);	/* Get column(s) or bust */
1423 		for (c = 0; p[c] && strchr ("0123456789-:", p[c]); c++);	/* Wind to position after the column or column range */
1424 		switch (p[c]) {	/* p[c] is the potential code T, t, x, y, f, d or s. */
1425 			case 'T':	code = GMT_IS_ABSTIME;	break;		/* Absolute calendar time */
1426 			case 't':	code = GMT_IS_RELTIME;	break;		/* Relative time (units since epoch) */
1427 			case 'x':	code = GMT_IS_LON;		break;		/* Longitude coordinates */
1428 			case 'y':	code = GMT_IS_LAT;		break;		/* Latitude coordinates */
1429 			case 'f':	code = GMT_IS_FLOAT;	break;		/* Plain floating point coordinates */
1430 			case 'd':	code = GMT_IS_DIMENSION;	break;	/* Length dimension (with possible unit) */
1431 			case 's':	code = GMT_IS_STRING;	break;		/* This must be start of trailing text */
1432 			default:	/* No suffix, consider it an error */
1433 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed -f argument [%s]\n", arg);
1434 				return 1;
1435 				break;
1436 		}
1437 
1438 		/* Now set the code for this group of columns */
1439 
1440 		for (i = start; i <= stop; i += inc)
1441 			gmt_set_column_type (GMT, dir, (unsigned int)i, code);
1442 	}
1443 
1444 	/* Check in case anyone used -fT or -ft to set time axes for x, y, or z but forgot to use T in -JX.
1445 	 * If so we update the xyz_projection type */
1446 	for (unsigned int axis = GMT_X; axis <= GMT_Z; axis++) {
1447 		if ((ctype = gmt_get_column_type (GMT, GMT_IN, axis)) == GMT_IS_ABSTIME || ctype == GMT_IS_RELTIME)
1448 			GMT->current.proj.xyz_projection[axis] = GMT_TIME;
1449 	}
1450 
1451 	return (GMT_NOERROR);
1452 }
1453 
1454 /*! . */
gmtinit_compare_cols(const void * point_1,const void * point_2)1455 GMT_LOCAL int gmtinit_compare_cols (const void *point_1, const void *point_2) {
1456 	/* Sorts cols into ascending order  */
1457 	if (((struct GMT_COL_INFO *)point_1)->col < ((struct GMT_COL_INFO *)point_2)->col) return (-1);
1458 	if (((struct GMT_COL_INFO *)point_1)->col > ((struct GMT_COL_INFO *)point_2)->col) return (+1);
1459 	return (0);
1460 }
1461 
1462 /*! parse any --PARAM[=value] arguments */
gmtinit_parse_dash_option(struct GMT_CTRL * GMT,char * text)1463 GMT_LOCAL int gmtinit_parse_dash_option (struct GMT_CTRL *GMT, char *text) {
1464 	int n;
1465 	char *this_c = NULL;
1466 	if (!text)
1467 		return (GMT_NOERROR);
1468 
1469 	if ((this_c = strchr (text, '='))) {
1470 		/* Got --PAR=VALUE */
1471 		this_c[0] = '\0';	/* Temporarily remove the '=' character */
1472 		n = gmtlib_setparameter (GMT, text, &this_c[1], false);
1473 		this_c[0] = '=';	/* Put it back were it was */
1474 	}
1475 	else {
1476 		/* Got --PAR */
1477 		n = gmtlib_setparameter (GMT, text, "true", false);
1478 	}
1479 	if (!strcmp (text, "PS_CHAR_ENCODING")) GMT->current.ps.switch_set = true;
1480 	return (n);
1481 }
1482 
gmtinit_count_x_terms(char * txt,int64_t * xstart,int64_t * xstop)1483 GMT_LOCAL int gmtinit_count_x_terms (char *txt, int64_t *xstart, int64_t *xstop) {
1484 	/* Process things like xxxx, x4, etc and find the number of x items.
1485 	 * We return the start=stop as the number of x's */
1486 
1487 	unsigned int n = 0;
1488 	size_t len = strlen (txt), k = 0;
1489 	while (k < len) {
1490 		if (isdigit (txt[k+1])) {	/* Gave things like x3 */
1491 			n += atoi (&txt[k+1]);
1492 			k++;
1493 			while (txt[k] && isdigit (txt[k])) k++;	/* Wind pass the number */
1494 		}
1495 		else {	/* Just one x */
1496 			n++;
1497 			k++;
1498 		}
1499 	}
1500 	*xstart = *xstop = n;	/* Just a single x combo */
1501 	return 0;
1502 }
1503 
gmtinit_trend_modifiers(struct GMT_CTRL * GMT,char option,char * c,unsigned int dim,struct GMT_MODEL * M)1504 GMT_LOCAL int gmtinit_trend_modifiers (struct GMT_CTRL *GMT, char option, char *c, unsigned int dim, struct GMT_MODEL *M) {
1505 	char p[GMT_BUFSIZ] = {""};
1506 	unsigned int pos = 0;
1507 	int k, sdim = dim;
1508 
1509 	if (dim >= 2) {
1510 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "INTERNAL Failure in gmtinit_trend_modifiers: was passed dim >= 2 (%u)\n", dim);
1511 		return GMT_NOTSET;
1512 	}
1513 
1514 	/* Gave one or more modifiers */
1515 
1516 	while ((gmt_strtok (c, "+", &pos, p))) {
1517 		switch (p[0]) {
1518 			case 'o':	/* Origin of axes */
1519 				if ((k = GMT_Get_Values (GMT->parent, &p[1], M->origin, 2)) < 1) {
1520 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unable to parse the +o arguments (%s)\n", option, &p[1]);
1521 					return GMT_NOTSET;
1522 				}
1523 				else if (k != sdim) {
1524 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Did not provide %u arguments to +o\n", option, dim);
1525 					return GMT_NOTSET;
1526 				}
1527 				for (k = 0; k < sdim; k++) M->got_origin[k] = true;
1528 				break;
1529 			case 'r':
1530 				M->robust = true;
1531 				break;
1532 			case 'l':
1533 				if ((k = GMT_Get_Values (GMT->parent, &p[1], M->period, 2)) < 1) {
1534 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unable to parse the +l argument (%s)\n", option, &p[1]);
1535 					return GMT_NOTSET;
1536 				}
1537 				else if (k != sdim) {
1538 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Did not provide %u arguments to +l\n", option, dim);
1539 					return GMT_NOTSET;
1540 				}
1541 				for (k = 0; k < sdim; k++) M->got_period[k] = true;
1542 				break;
1543 			default:
1544 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unrecognized modifier +%s\n", option, p);
1545 				return GMT_NOTSET;
1546 				break;
1547 		}
1548 	}
1549 	return 0;
1550 }
1551 
gmtinit_old_trendsyntax(struct GMT_CTRL * GMT,char option,char * in_arg)1552 GMT_LOCAL char *gmtinit_old_trendsyntax (struct GMT_CTRL *GMT, char option, char *in_arg) {
1553 	/* Deal with backwards compatibilities: -N[f]<nmodel>[r], where nmodel meant # of terms in polynomial */
1554 	char *arg = strdup (in_arg);
1555 	size_t end = strlen (arg) - 1;
1556 	int order;
1557 	if ((isdigit (arg[0]) || (end > 1 && arg[0] == 'f' && isdigit (arg[2]))) && ((arg[end] == 'r' && arg[end-1] != '+') || isdigit (arg[end])))
1558 	{
1559 		/* Old GMT4-like syntax. If compatibility allows it we rewrite using new syntax so we only have one parser below */
1560 		if (gmt_M_compat_check (GMT, 5)) {	/* Allow old-style syntax */
1561 			char new[GMT_BUFSIZ] = {""}, term[GMT_LEN16] = {""};
1562 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "-%c%s is deprecated; see usage for new syntax\n", option, arg);
1563 			if (arg[0] != 'f') new[0] = 'p';	/* So we start with f or p */
1564 			if (arg[end] == 'r') {
1565 				arg[end] = '\0';	/* Chop off the r */
1566 				order = atoi (arg);
1567 				if (new[0] == 'p') order--;
1568 				snprintf (term, GMT_LEN16, "%d", order);
1569 				strcat (new, term);
1570 				strcat (new, "+r");	/* Add robust flag */
1571 			}
1572 			else {
1573 				order = atoi (arg);
1574 				if (new[0] == 'p') order--;
1575 				snprintf (term, GMT_LEN16, "%d", order);
1576 				strcat (new, term);
1577 			}
1578 			gmt_M_str_free (arg);	/* Wipe old setting */
1579 			arg = strdup (new);	/* Place revised args */
1580 		}
1581 		else {
1582 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Old-style arguments given and chosen compatibility mode does not allow it\n", option);
1583 			gmt_M_str_free (arg);
1584 			return NULL;
1585 		}
1586 	}
1587 	return arg;
1588 }
1589 
gmtinit_compare_terms(const void * term_1v,const void * term_2v)1590 GMT_LOCAL int gmtinit_compare_terms (const void *term_1v, const void *term_2v) {
1591 	const struct GMT_MODEL_TERM *term_1 = term_1v, *term_2 = term_2v;
1592 	/* Ensure polynomial terms are listed before Fourier terms */
1593 	if (term_1->kind  < term_2->kind)  return (-1);
1594 	if (term_1->kind  > term_2->kind)  return (+1);
1595 	if (term_1->order < term_2->order) return (-1);
1596 	if (term_1->order > term_2->order) return (+1);
1597 	return (0);
1598 }
1599 
1600 /*! . */
gmtinit_parse_model1d(struct GMT_CTRL * GMT,char option,char * in_arg,struct GMT_MODEL * M)1601 GMT_LOCAL int gmtinit_parse_model1d (struct GMT_CTRL *GMT, char option, char *in_arg, struct GMT_MODEL *M) {
1602 	/* Parsing for -N arguments in trend1d
1603 	 * Parse -N[p|P|f|F|c|C|s|S|x|X]<list-of-terms>[,...][+l<length>][+o<origin>][+r].
1604 	 * p means polynomial model.
1605 	 * c means cosine series.
1606 	 * s means sine series.
1607 	 * f means both cosine and sine (Fourier series).
1608 	 * <list-of-terms> is either a single order (e.g., 2) or a range (e.g., 0-3)
1609 	 * Give one or more lists separated by commas.
1610 	 * We add the basis x^p, cos(2*pi*p/X), and/or sin(2*pi*p/X) depending on selection.
1611  	 * Indicate robust fit by appending +r.
1612 	 * Set data origin via +o<orig> and fundamental period via +l<length>
1613 	 */
1614 
1615 	unsigned int pos = 0, n_model = 0, k, j, n_P = 0, n_p = 0;
1616 	int64_t order, xstart, xstop, step;
1617 	bool got_pol = false, single = false;
1618 	enum GMT_enum_model kind;
1619 	char p[GMT_BUFSIZ] = {""}, *this_range = NULL, *arg = NULL, *name = "pcs", *c = NULL;
1620 
1621 	if (!in_arg || !in_arg[0]) {
1622 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No arguments given!\n", option);
1623 		return GMT_NOTSET;	/* No arg given */
1624 	}
1625 	/* Deal with backwards compatibilities for GMT4: -N[f]<nmodel>[r] */
1626 	arg = gmtinit_old_trendsyntax (GMT, option, in_arg);	/* Returns a possibly recreated option string */
1627 	if ((c = strchr (arg, '+'))) {	/* Gave one or more modifiers */
1628 		if (gmtinit_trend_modifiers (GMT, option, c, 1U, M)) {
1629 			gmt_M_str_free (arg);
1630 			return GMT_NOTSET;
1631 		}
1632 		c[0] = '\0';	/* Chop off modifiers in arg before processing the model settings */
1633 	}
1634 	while ((gmt_strtok (arg, ",", &pos, p))) {	/* For each item in the series... */
1635 		/* Here, p will hold one instance of [P|p|F|f|C|c|S|s|x]<list-of-terms> */
1636 		if (!strchr ("CFSPcfspx", p[0])) {
1637 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad basis function type (%c)\n", option, p[0]);
1638 			gmt_M_str_free (arg);
1639 			return GMT_NOTSET;
1640 		}
1641 		this_range = &p[1];
1642 		single = false;
1643 		switch (p[0]) {	/* What kind of basis function? */
1644 			case 'p': case 'P': case 'x': kind = GMT_POLYNOMIAL; break;
1645 			case 'c': case 'C': kind = GMT_COSINE; break;
1646 			case 's': case 'S': kind = GMT_SINE; break;
1647 			case 'f': case 'F': kind = GMT_FOURIER; break;
1648 			default: kind = GMT_POLYNOMIAL; break;	/* To please gcc */
1649 
1650 		}
1651 		if (p[0] == 'x')	{	/* Single building block and not all items of given order */
1652 			single = true;
1653 			gmtinit_count_x_terms (p, &xstart, &xstop);
1654 		}
1655 		else if ((step = gmtlib_parse_index_range (GMT, this_range, &xstart, &xstop)) != 1) {
1656 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad basis function order (%s)\n", option, this_range);
1657 			gmt_M_str_free (arg);
1658 			return GMT_NOTSET;
1659 		}
1660 		if (!single && islower (p[0])) xstart = 0;	/* E.g., p3 should become p0-3 */
1661 		if (p[0] == 'p') n_p++;
1662 		if (p[0] == 'P') n_P++;
1663 		/* Here we have range xstart to xstop and component kind */
1664 
1665 		for (order = xstart; order <= xstop; order++) {
1666 			if (!got_pol && order == 0) {	/* When order == 0 for trig function we substitute polynomial of order 0 instead */
1667 				M->term[n_model].kind = GMT_POLYNOMIAL;
1668 				M->term[n_model].order[GMT_X] = (unsigned int)order;
1669 				got_pol = M->intercept = true;
1670 				n_model++;
1671 				M->type |= 1;	/* Polynomial */
1672 				continue;
1673 			}
1674 			/* For each order given in the range, or just this particular order */
1675 			switch (kind) {
1676 				case GMT_POLYNOMIAL:	/* Add one or more polynomial basis */
1677 				case GMT_CHEBYSHEV:		/* Just to keep compiler happy */
1678 					M->term[n_model].kind = GMT_POLYNOMIAL;
1679 					M->term[n_model].order[GMT_X] = (unsigned int)order;
1680 					M->type |= 1;	/* Polynomial */
1681 					got_pol = true;
1682 					if (M->term[n_model].order[GMT_X] == 0) M->intercept = true;
1683 					n_model++;
1684 					break;
1685 				case GMT_FOURIER:	/* Add a Fourier basis (2 parts) */
1686 					if (order == 0) continue;	/* Only to constant via the polynomial parts */
1687 					for (j = 0; j < 2; j++) {	/* Loop over cosine and sine in x */
1688 						M->term[n_model].kind = GMT_COSINE+j;
1689 						M->term[n_model].order[GMT_X] = (unsigned int)order;
1690 						n_model++;
1691 					}
1692 					M->type |= 2;	/* Fourier */
1693 					break;
1694 				case GMT_COSINE:	/* Add a Cosine basis (1 or 2 parts) */
1695 					if (order == 0) continue;	/* Only to constant via the polynomial parts */
1696 					M->term[n_model].kind = GMT_COSINE;
1697 					M->term[n_model].order[GMT_X] = (unsigned int)order;
1698 					M->type |= 2;	/* Fourier */
1699 					n_model++;
1700 					break;
1701 				case GMT_SINE:	/* Add a Sine basis (1 or 2 parts) */
1702 					if (order == 0) continue;	/* Only to constant via the polynomial parts */
1703 					M->term[n_model].kind = GMT_SINE;
1704 					M->term[n_model].order[GMT_X] = (unsigned int)order;
1705 					M->type |= 2;	/* Fourier */
1706 					n_model++;
1707 					break;
1708 			}
1709 			if (n_model == GMT_N_MAX_MODEL) {
1710 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1711 				gmt_M_str_free (arg);
1712 				return GMT_NOTSET;
1713 			}
1714 		}
1715 	}
1716 	gmt_M_str_free (arg);
1717 
1718 	/* Sort so Trig/Fourier terms are last in the list */
1719 
1720 	qsort (M->term, n_model, sizeof (struct GMT_MODEL_TERM), gmtinit_compare_terms);
1721 
1722 	/* Make sure there are no duplicates */
1723 
1724 	for (k = 0; k < n_model; k++) {
1725 		if (M->term[k].kind <= GMT_CHEBYSHEV) M->n_poly++;	/* Set how many poly terms there are in model */
1726 		for (j = k+1; j < n_model; j++) {
1727 			if (M->term[k].kind == M->term[j].kind && M->term[k].order[GMT_X] == M->term[j].order[GMT_X] && M->term[k].order[GMT_Y] == M->term[j].order[GMT_Y] && M->term[k].type == M->term[j].type) {
1728 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Basis %c%u occurs more than once in -%c!\n", name[M->term[k].kind], M->term[k].order[GMT_X], option);
1729 				return GMT_NOTSET;
1730 			}
1731 		}
1732 	}
1733 	if (!single && n_P == 0 && n_p == 1) M->chebyshev = true;	/* Can fit via Chebyshev polynomials */
1734 	M->n_terms = n_model;
1735 
1736 	if (gmt_M_is_verbose (GMT, GMT_MSG_WARNING)) {	/* Report our trend model y(x) if -V */
1737 		char *way[2] = {"last squares", "robust"}, report[GMT_BUFSIZ] = {""}, piece[GMT_LEN64] = {""};
1738 		if (!M->intercept) GMT_Report (GMT->parent, GMT_MSG_WARNING, "No intercept term (p0) given in  -%c\n", option);
1739 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Solving for %u terms using a %s norm.  The model:\n", n_model, way[M->robust]);
1740 		strcpy (report, "y(x) = ");
1741 		for (k =  0; k < n_model; k++) {
1742 			switch (M->term[k].kind) {
1743 				case GMT_POLYNOMIAL:
1744 				case GMT_CHEBYSHEV:
1745 					if (M->term[k].order[GMT_X] == 0)
1746 						snprintf (piece, GMT_LEN64, "%c", 'a');
1747 					else
1748 						snprintf (piece, GMT_LEN64, "%c * x^%d", 'a'+ k, M->term[k].order[GMT_X]);
1749 					break;
1750 				case GMT_COSINE:
1751 					snprintf (piece, GMT_LEN64, "%c * cos (%d*x)", 'a'+k, M->term[k].order[GMT_X]);
1752 					break;
1753 				case GMT_SINE:
1754 					snprintf (piece, GMT_LEN64, "%c * sin (%d*x)", 'a'+k, M->term[k].order[GMT_X]);
1755 					break;
1756 			}
1757 			strcat (report, piece);
1758 			((n_model - k) > 1) ? strcat (report, " + ") : strcat (report, "\n");
1759 		}
1760 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, report);
1761 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "In the above, x = 2*(x - x_mid)/(x_max - x_min) for polynomials and x = 2*pi*(x - origin)/length for Fourier components\n");
1762 		if (M->chebyshev) {
1763 			if (M->type & 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "The complete polynomial will be represented via Chebyshev polynomials\n");
1764 		}
1765 		else {
1766 			if (M->type & 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "The partial polynomial will be represented via powers of x\n");
1767 		}
1768 	}
1769 	/* If we can use Chebyshev polynomials then we switch the kind to indicate this */
1770 	if (M->chebyshev) for (k =  0; k < n_model; k++) if (M->term[k].kind == GMT_POLYNOMIAL) M->term[k].kind = GMT_CHEBYSHEV;
1771 	return (0);
1772 }
1773 
1774 #if 0
1775 GMT_LOCAL int gmtinit_count_xy_terms (char *txt, int64_t *xstart, int64_t *xstop, int64_t *ystart, int64_t *ystop) {
1776 	/* Process things like xxxxyy, x4y2, etc and find the number of x and y items.
1777 	 * We return the start=stop= number of x and ystart=ystop = number of y. */
1778 
1779 	unsigned int n[2] = {0, 0};
1780 	size_t len = strlen (txt), k = 0;
1781 	while (k < len) {
1782 		switch (txt[k]) {
1783 			case 'x':
1784 				if (isdigit (txt[k+1])) {	/* Gave things like x3y */
1785 					n[GMT_X] += atoi (&txt[k+1]);
1786 					k++;
1787 					while (txt[k] && isdigit (txt[k])) k++;	/* Wind pass the number */
1788 				}
1789 				else {	/* Just one x */
1790 					n[GMT_X]++;
1791 					k++;
1792 				}
1793 				break;
1794 			case 'y':
1795 				if (isdigit (txt[k+1])) {	/* Gave things like y3x */
1796 					n[GMT_Y] += atoi (&txt[k+1]);
1797 					k++;
1798 					while (txt[k] && isdigit (txt[k])) k++;	/* Wind pass the number */
1799 				}
1800 				else {	/* Just one y */
1801 					n[GMT_Y]++;
1802 					k++;
1803 				}
1804 				break;
1805 			default:	/* Bad args */
1806 				return GMT_NOTSET;
1807 				break;
1808 		}
1809 	}
1810 	*xstart = *xstop = n[GMT_X];	/* Just a single x combo */
1811 	*ystart = *ystop = n[GMT_Y];	/* Just a particular y combo */
1812 	return 0;
1813 }
1814 
1815 /*! . */
1816 GMT_LOCAL int gmtinit_parse_model2d (struct GMT_CTRL *GMT, char option, char *in_arg, struct GMT_MODEL *M) {
1817 	/* Parse -N[p|P|f|F|c|C|s|S|x|X|y|Y][x|y]<list-of-terms>[,...][+l<lengths>][+o<origins>][+r] for trend2d, grdtrend.
1818 	 * p means polynomial.
1819 	 * c means cosine.  You may optionally add x|y to only add basis for that dimension [both]
1820 	 * s means sine.  Optionally append x|y [both]
1821 	 * f means both cosine and sine (Fourier series).  Optionally append x|y [both]
1822 	 * list-of-terms is either a single order (e.g., 2) or a range (e.g., 0-3)
1823 	 * Give one or more lists separated by commas.
1824 	 * In 2-D, for polynomial the order means all p products of x^m*y^n where m+n == p
1825 	 *   To only have some of these terms you must instead specify which ones you want,
1826 	 *   e.g., xxxy (or x3y) and yyyx (or y3x) for just those two.
1827 	 *   For Fourier we add these 4 terms per order:
1828 	 *   cos(2*pi*p*x/X), sin(2*pi*p*x/X), cos(2*pi*p*y/Y), sin(2*pi*p*y/Y)
1829 	 *   To only add basis in x or y you must apped x|y after the c|s|f.
1830  	 * Indicate robust fit by appending +r
1831 	 */
1832 
1833 	unsigned int pos = 0, n_model = 0, n_parts, k, j;
1834 	int64_t order, xstart, xstop, ystart, ystop, step;
1835 	bool got_intercept, single;
1836 	enum GMT_enum_model kind;
1837 	char p[GMT_BUFSIZ] = {""}, *this_range = NULL, *arg = NULL, *name = "pcs", *c = NULL;
1838 
1839 	if (!in_arg || !in_arg[0]) {
1840 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No arguments given!\n", option);
1841 		return GMT_NOTSET;	/* No arg given */
1842 	}
1843 	/* Deal with backwards compatibilities: -N<nmodel>[r] for 2-D */
1844 	arg = gmtinit_old_trendsyntax (GMT, option, in_arg);
1845 	if ((c = strchr (arg, '+'))) {	/* Gave one or more modifiers */
1846 		if (gmtinit_trend_modifiers (GMT, option, c, 2U, M)) return GMT_NOTSET;
1847 		c[0] = '\0';	/* Chop off modifiers in arg before processing settings */
1848 	}
1849 	while ((gmt_strtok (arg, ",", &pos, p))) {
1850 		/* Here, p will be one instance of [P|p|F|f|C|c|S|s][x|y]<list-of-terms> */
1851 		if (!strchr ("CFSPcfsp", p[0])) {
1852 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad basis function type (%c)\n", option, p[0]);
1853 			return GMT_NOTSET;
1854 		}
1855 		this_range = &p[1];
1856 		n_parts = 1;	/* Normally just one basis function at the time but f implies both c and s */
1857 		single = false;
1858 		switch (p[0]) {	/* What kind of basis function? */
1859 			case 'p': case 'P': kind = GMT_POLYNOMIAL; break;
1860 			case 'c': case 'C': kind = GMT_COSINE; break;
1861 			case 's': case 'S': kind = GMT_SINE; break;
1862 			case 'f': case 'F': kind = GMT_FOURIER; break;
1863 
1864 		}
1865 		if (p[1] == 'x' || p[1] == 'y')	{	/* Single building block and not all items of given order */
1866 			single = true;
1867 			gmtinit_count_xy_terms (&p[1], &xstart, &xstop, &ystart, &ystop);
1868 		}
1869 		else if ((step = gmtlib_parse_index_range (GMT, this_range, &xstart, &xstop)) != 1) {
1870 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad basis function order (%s)\n", option, this_range);
1871 			return GMT_NOTSET;
1872 		}
1873 		if (islower (p[0])) xstart = ystart = 0;
1874 		if (kind != GMT_POLYNOMIAL && !got_intercept) {
1875 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cosine|Sine cannot start with order 0.  Use p0 to add a constant\n", option);
1876 			return GMT_NOTSET;
1877 		}
1878 		/* Here we have range and kind */
1879 
1880 		/* For the Fourier components we need to distinguish between things like cos(x)*sin(y), sin(x)*cos(y), cos(x), etc.  We use these 8 type flags:
1881 		   0 = C- cos (x)
1882 		   1 = -C cos (y)
1883 		   2 = S- sin (x)
1884 		   3 = -S sin (y)
1885 		   4 = CC cos (x)*cos(y)
1886 		   5 = CS cos (x)*sin(y)
1887 		   6 = SC sin (x)*cos(y)
1888 		   7 = SS sin (x)*sin(y)
1889 		 */
1890 		for (order = xstart; order <= xstop; order++) {
1891 			/* For each order given in the range, or just this particular order */
1892 			switch (kind) {
1893 				case GMT_POLYNOMIAL:	/* Add one or more polynomial basis */
1894 					if (!single) {
1895 						ystart = 0;
1896 						ystop = order;
1897 					}
1898 					for (k = ystart; k <= ystop; k++) {
1899 						M->term[n_model].kind = GMT_POLYNOMIAL;
1900 						M->term[n_model].order[GMT_X] = (unsigned int)(order - k);
1901 						M->term[n_model].order[GMT_Y] = (unsigned int)k;
1902 						if (M->term[k].order[GMT_X] == 0) got_intercept = true;
1903 						n_model++;
1904 						if (n_model == GMT_N_MAX_MODEL) {
1905 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1906 							return GMT_NOTSET;
1907 						}
1908 					}
1909 					break;
1910 				case GMT_FOURIER:	/* Add a Fourier basis (2 or 4 parts) */
1911 					if (order == 0) continue;	/* Only to constant via the polynomial parts */
1912 					if (!single) {
1913 						ystart = ystop = order;
1914 					}
1915 					for (j = 0; j < 2; j++) {	/* Loop over cosine and sine in x */
1916 						for (k = 0; k < 2; k++) {	/* Loop over cosine and sine in y, if 2-D */
1917 							M->term[n_model].kind = GMT_FOURIER;
1918 							M->term[n_model].type = 4 + 2*j + k;	/* CC, CS, SC, SS */
1919 							M->term[n_model].order[GMT_X] = (unsigned int)order;
1920 							M->term[n_model].order[GMT_Y] = (unsigned int)ystart;
1921 							n_model++;
1922 							if (n_model == GMT_N_MAX_MODEL) {
1923 								GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1924 								return GMT_NOTSET;
1925 							}
1926 						}
1927 					}
1928 					break;
1929 				case GMT_COSINE:	/* Add a Cosine basis (1 or 2 parts) */
1930 					if (order == 0) continue;	/* Only to constant via the polynomial parts */
1931 					if (!single) {
1932 						ystart = ystop = order;
1933 					}
1934 					/* cosine in x? */
1935 					M->term[n_model].kind = GMT_COSINE;
1936 					M->term[n_model].type = 0;	/* C- */
1937 					M->term[n_model].order[GMT_X] = (unsigned int)order;
1938 					n_model++;
1939 					if (n_model == GMT_N_MAX_MODEL) {
1940 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1941 						return GMT_NOTSET;
1942 					}
1943 					if (ystart) {
1944 						M->term[n_model].kind = GMT_COSINE;
1945 						M->term[n_model].type = 1;	/* -C */
1946 						M->term[n_model].order[GMT_Y] = (unsigned int)ystart;
1947 						n_model++;
1948 						if (n_model == GMT_N_MAX_MODEL) {
1949 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1950 							return GMT_NOTSET;
1951 						}
1952 					}
1953 					break;
1954 				case GMT_SINE:	/* Add a Sine basis (1 or 2 parts) */
1955 					if (!single) {
1956 						ystart = ystop = order;
1957 					}
1958 					/* sine in x? */
1959 					if (order) {
1960 						M->term[n_model].kind = GMT_SINE;
1961 						M->term[n_model].type = 2;	/* S- */
1962 						M->term[n_model].order[GMT_X] = (unsigned int)order;
1963 						n_model++;
1964 						if (n_model == GMT_N_MAX_MODEL) {
1965 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1966 							return GMT_NOTSET;
1967 						}
1968 					}
1969 					if (ystart) {
1970 						M->term[n_model].kind = GMT_SINE;
1971 						M->term[n_model].type = 3;	/* -S */
1972 						M->term[n_model].order[GMT_Y] = (unsigned int)ystart;
1973 						n_model++;
1974 						if (n_model == GMT_N_MAX_MODEL) {
1975 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Exceeding max basis functions (%d) \n", option, GMT_N_MAX_MODEL);
1976 							return GMT_NOTSET;
1977 						}
1978 					}
1979 					break;
1980 			}
1981 		}
1982 	}
1983 	gmt_M_str_free (arg);
1984 	/* Make sure there are no duplicates */
1985 
1986 	for (k = 0; k < n_model; k++) {
1987 		for (j = k+1; j < n_model; j++) {
1988 			if (M->term[k].kind == M->term[j].kind && M->term[k].order[GMT_X] == M->term[j].order[GMT_X] && M->term[k].order[GMT_Y] == M->term[j].order[GMT_Y] && M->term[k].type == M->term[j].type) {
1989 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Basis %cx%uy%u occurs more than once!\n", option, name[M->term[k].kind], M->term[k].order[GMT_X], M->term[k].order[GMT_Y]);
1990 				return GMT_NOTSET;
1991 			}
1992 		}
1993 	}
1994 	if (gmt_M_is_verbose (GMT, GMT_MSG_WARNING)) {	/* Report our findings */
1995 		char *way[2] = {"last squares", "robust"};
1996 		if (!got_intercept) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Warning -%c: No intercept term (p0) given\n", option);
1997 		fprintf (stderr, "Fitting %u terms using a %s norm\n", n_model, way[M->robust]);
1998 		for (k = j = 0; k < n_model; k++, j++) {
1999 			fprintf (stderr, "Model basis %d is of type %c and order %u/%u\n", k, name[M->term[k].kind], M->term[k].order[GMT_X], M->term[k].order[GMT_Y]);
2000 		}
2001 	}
2002 	M->n_terms = n_model;
2003 	return (0);
2004 }
2005 /*! . */
2006 int gmt_parse_model (struct GMT_CTRL *GMT, char option, char *in_arg, unsigned int dim, struct GMT_MODEL *M) {
2007 	if (dim == 1)
2008 		return (gmtinit_parse_model1d (GMT, option, in_arg, M));
2009 	else
2010 		return (gmtinit_parse_model2d (GMT, option, in_arg, M));
2011 }
2012 
2013 #endif
2014 
2015 /* Some local macros to make the test below possible to understand */
2016 #define is_plus(item,k)  (item[k-1] == '+')	/* Previous char is a plus, so maybe start of modifier */
2017 #define is_label(item,k) (item[k] == 'c' && (item[k+1] == '+' || item[k+1] == '\0'))	/* Modifier c followed by another modifier or end of string */
2018 #define is_just(item,k)  (item[k] == 'j' && (strchr ("LCRBMT", item[k+1]) && strchr ("LCRBMT", item[k+2])))	/* Is +j<just> */
2019 #define is_off(item,k)   (item[k] == 'o' && strchr ("-+.0123456789", item[k+1]))	/* Looks like +onumber> */
2020 
2021 /*! Parse the -U option.  Full syntax: -U[<label>][+c][+j<just>][+o]<dx>[/<dy>]]  Old syntax was -U[[<just>]/<dx>/<dy>/][c|<label>] */
gmtinit_parse_U_option(struct GMT_CTRL * GMT,char * item)2022 GMT_LOCAL int gmtinit_parse_U_option (struct GMT_CTRL *GMT, char *item) {
2023 	int just = 1, error = 0;
2024 
2025 	GMT->current.setting.map_logo = true;
2026 	if (!item || !item[0]) return (GMT_NOERROR);	/* Just basic -U with no args */
2027 
2028 	if (gmt_found_modifier (GMT, item, "cjo")) {	/* New syntax */
2029 		unsigned int pos = 0, uerr = 0;
2030 		int k = 1, len = (int)strlen (item);
2031 		char word[GMT_LEN256] = {""}, *c = NULL;
2032 		/* Find the first +c|j|o that looks like it may be a modifier and not random text */
2033 		while (k < len && !(is_plus(item,k) && (is_label(item,k) || is_just(item,k) || is_off(item,k)))) k++;
2034 		if (k == len)	/* MOdifiers were just random text */
2035 			strncpy (GMT->current.ps.map_logo_label, item, GMT_LEN256-1);	/* Got a label */
2036 		else {	/* Appears to have gotten a valid modifier or more */
2037 			c = &item[k-1];	/* Start of the modifier */
2038 			c[0] = '\0';	/* Chop off the + so we can parse the label, if any */
2039 			if (item[0]) strncpy (GMT->current.ps.map_logo_label, item, GMT_LEN256-1);	/* Got a label */
2040 			c[0] = '+';	/* Restore modifiers */
2041 			while (gmt_getmodopt (GMT, 'U', c, "cjo", &pos, word, &uerr) && uerr == 0) {
2042 				switch (word[0]) {
2043 					case 'c':	/* Maybe +c but only if at end of followed by another modifier */
2044 						if (word[1] == '+' || word[1] == '\0')	/* Use command string */
2045 							GMT->current.ps.logo_cmd = true;
2046 						break;
2047 					case 'j':	/* Maybe +j if the next two letters are from LCRBMT */
2048 						if (strchr ("LCRBMT", word[1]) && strchr ("LCRBMT", word[2]))
2049 							just = gmt_just_decode (GMT, &word[1], GMT->current.setting.map_logo_justify);
2050 						break;
2051 					case 'o':	/* Maybe +o if next letter could be part of a number */
2052 						if (strchr ("-+.0123456789", word[1])) {	/* Seems to be a number */
2053 							if ((k = gmt_get_pair (GMT, &word[1], GMT_PAIR_DIM_DUP, GMT->current.setting.map_logo_pos)) < 2) error++;
2054 						}
2055 						break;
2056 					default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
2057 				}
2058 			}
2059 		}
2060 		GMT->current.setting.map_logo_justify = just;
2061 	}
2062 	else {	/* Old syntax or just -U */
2063 		int n = 0, n_slashes;
2064 		char txt_j[GMT_LEN256] = {""}, txt_x[GMT_LEN256] = {""}, txt_y[GMT_LEN256] = {""};
2065 
2066 		n_slashes = gmt_count_char (GMT, item, '/');	/* Count slashes to detect [<just>]/<dx>/<dy>/ presence */
2067 
2068 		if (n_slashes >= 2) {	/* Probably gave -U[<just>]/<dx>/<dy>[/<string>] */
2069 			if (item[0] == '/') { /* No justification given */
2070 				n = sscanf (&item[1], "%[^/]/%[^/]/%[^\n]", txt_x, txt_y, GMT->current.ps.map_logo_label);
2071 				just = 1;	/* Default justification is LL */
2072 			}
2073 			else {
2074 				n = sscanf (item, "%[^/]/%[^/]/%[^/]/%[^\n]", txt_j, txt_x, txt_y, GMT->current.ps.map_logo_label);
2075 				just = gmt_just_decode (GMT, txt_j, GMT->current.setting.map_logo_justify);
2076 			}
2077 			if (just < 0) {
2078 				/* Garbage before first slash: we simply have -U<string> */
2079 				strncpy (GMT->current.ps.map_logo_label, item, GMT_LEN256-1);
2080 			}
2081 			else {
2082 				GMT->current.setting.map_logo_justify = just;
2083 				GMT->current.setting.map_logo_pos[GMT_X] = gmt_M_to_inch (GMT, txt_x);
2084 				GMT->current.setting.map_logo_pos[GMT_Y] = gmt_M_to_inch (GMT, txt_y);
2085 			}
2086 		}
2087 		else
2088 			strncpy (GMT->current.ps.map_logo_label, item, GMT_LEN256-1);
2089 		if (GMT->current.ps.map_logo_label[0] == 'c' && GMT->current.ps.map_logo_label[1] == 0) {	/* Old way of asking for +c */
2090 			GMT->current.ps.logo_cmd = true;
2091 			GMT->current.ps.map_logo_label[0] = '\0';
2092 		}
2093 		if ((item[0] == '/' && n_slashes == 1) || (item[0] == '/' && n_slashes >= 2 && n < 2)) error++;
2094 	}
2095 	return (error);
2096 }
2097 
2098 /*! -x[[-]<ncores>] */
2099 #ifdef GMT_MP_ENABLED
gmtinit_parse_x_option(struct GMT_CTRL * GMT,char * arg)2100 GMT_LOCAL int gmtinit_parse_x_option (struct GMT_CTRL *GMT, char *arg) {
2101 	GMT->common.x.active = true;
2102 	if (!arg) return (GMT_PARSE_ERROR);	/* -x requires a non-NULL argument */
2103 	if (arg[0] == '\0')	/* Use all processors */
2104 		GMT->common.x.n_threads = gmtlib_get_num_processors();
2105 	else
2106 		GMT->common.x.n_threads = atoi (arg);
2107 
2108 	if (GMT->common.x.n_threads == 0)
2109 		GMT->common.x.n_threads = 1;
2110 	else if (GMT->common.x.n_threads < 0)
2111 		GMT->common.x.n_threads = MAX(gmtlib_get_num_processors() - GMT->common.x.n_threads, 1);		/* Max-n but at least one */
2112 	if (GMT->current.setting.max_cores)	/* Limit to max core defaults setting */
2113 		GMT->common.x.n_threads = GMT->current.setting.max_cores;
2114 	return (GMT_NOERROR);
2115 }
2116 #endif
2117 
2118 /*! . */
gmtinit_parse_colon_option(struct GMT_CTRL * GMT,char * item)2119 GMT_LOCAL int gmtinit_parse_colon_option (struct GMT_CTRL *GMT, char *item) {
2120 	int error = 0, way, off = 0;
2121 	bool ok[2] = {false, false};
2122 	static char *mode[4] = {"i", "o", "", ""}, *dir[2] = {"input", "output"};
2123 	char kase = (item) ? item[0] : '\0';
2124 	/* Parse the -: option.  Full syntax: -:[i|o].
2125 	 * We know that if -f was given it has already been parsed due to the parsing order imposed.
2126 	 * Must check that -: does not conflict with -f */
2127 
2128 	switch (kase) {
2129 		case 'i':	/* Toggle on input data only */
2130 			ok[GMT_IN] = true;
2131 			break;
2132 		case 'o':	/* Toggle on output data only */
2133 			ok[GMT_OUT] = true;
2134 			break;
2135 		case '\0':	/* Toggle both input and output data */
2136 			ok[GMT_IN] = ok[GMT_OUT] = true;
2137 			off = 2;
2138 			break;
2139 		default:
2140 			error++;	/* Error */
2141 			break;
2142 	}
2143 	for (way = 0; !error && way < 2; way++) if (ok[way]) {
2144 		if (gmt_M_type (GMT, way, GMT_X) == GMT_IS_UNKNOWN && gmt_M_type (GMT, way, GMT_Y) == GMT_IS_UNKNOWN)	/* Don't know what x/y is yet */
2145 			GMT->current.setting.io_lonlat_toggle[way] = true;
2146 		else if (gmt_M_type (GMT, way, GMT_X) == GMT_IS_FLOAT && gmt_M_type (GMT, way, GMT_Y) == GMT_IS_FLOAT)	/* Cartesian x/y vs y/x cannot be identified */
2147 			GMT->current.setting.io_lonlat_toggle[way] = true;
2148 		else if (gmt_M_is_geographic (GMT, way))	/* Lon/lat becomes lat/lon */
2149 			GMT->current.setting.io_lonlat_toggle[way] = true;
2150 		else if (gmt_M_type (GMT, way, GMT_X) == GMT_IS_LAT && gmt_M_type (GMT, way, GMT_Y) == GMT_IS_LON)	/* Already lat/lon! */
2151 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "-:%s given but %s order already set by -f; -:%s ignored.\n", mode[way+off], dir[way], mode[way+off]);
2152 	}
2153 	if (error) GMT->current.setting.io_lonlat_toggle[GMT_IN] = GMT->current.setting.io_lonlat_toggle[GMT_OUT] = false;	/* Leave in case we had errors */
2154 	return (error);
2155 }
2156 
2157 /*! Compute reverse col-separation before mapping */
gmtinit_neg_col_dist(struct GMT_CTRL * GMT,uint64_t col)2158 GMT_LOCAL double gmtinit_neg_col_dist (struct GMT_CTRL *GMT, uint64_t col) {
2159 	return (GMT->current.io.prev_rec[col] - GMT->current.io.curr_rec[col]);
2160 }
2161 
2162 /*! Compute forward col-separation before mapping */
gmtinit_pos_col_dist(struct GMT_CTRL * GMT,uint64_t col)2163 GMT_LOCAL double gmtinit_pos_col_dist (struct GMT_CTRL *GMT, uint64_t col) {
2164 	return (GMT->current.io.curr_rec[col] - GMT->current.io.prev_rec[col]);
2165 }
2166 
2167 /*! Compute absolute col-separation before mapping */
gmtinit_abs_col_dist(struct GMT_CTRL * GMT,uint64_t col)2168 GMT_LOCAL double gmtinit_abs_col_dist (struct GMT_CTRL *GMT, uint64_t col) {
2169 	return (fabs (GMT->current.io.curr_rec[col] - GMT->current.io.prev_rec[col]));
2170 }
2171 
2172 /*! Compute reverse col-separation after mapping */
gmtinit_neg_col_map_dist(struct GMT_CTRL * GMT,uint64_t col)2173 GMT_LOCAL double gmtinit_neg_col_map_dist (struct GMT_CTRL *GMT, uint64_t col) {
2174 	double X[2][2];
2175 	gmt_geo_to_xy (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], &X[GMT_X][0], &X[GMT_Y][0]);
2176 	gmt_geo_to_xy (GMT, GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y], &X[GMT_X][1], &X[GMT_Y][1]);
2177 	return (X[col][0] - X[col][1]);
2178 }
2179 
2180 /*! Compute forward col-separation after mapping */
gmtinit_pos_col_map_dist(struct GMT_CTRL * GMT,uint64_t col)2181 GMT_LOCAL double gmtinit_pos_col_map_dist (struct GMT_CTRL *GMT, uint64_t col) {
2182 	double X[2][2];
2183 	gmt_geo_to_xy (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], &X[GMT_X][0], &X[GMT_Y][0]);
2184 	gmt_geo_to_xy (GMT, GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y], &X[GMT_X][1], &X[GMT_Y][1]);
2185 	return (X[col][1] - X[col][0]);
2186 }
2187 
2188 /*! Compute forward col-separation after mapping */
gmtinit_abs_col_map_dist(struct GMT_CTRL * GMT,uint64_t col)2189 GMT_LOCAL double gmtinit_abs_col_map_dist (struct GMT_CTRL *GMT, uint64_t col) {
2190 	double X[2][2];
2191 	gmt_geo_to_xy (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], &X[GMT_X][0], &X[GMT_Y][0]);
2192 	gmt_geo_to_xy (GMT, GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y], &X[GMT_X][1], &X[GMT_Y][1]);
2193 	return (fabs (X[col][1] - X[col][0]));
2194 }
2195 
2196 /*! Compute point-separation after mapping */
gmtinit_xy_map_dist(struct GMT_CTRL * GMT,uint64_t col)2197 GMT_LOCAL double gmtinit_xy_map_dist (struct GMT_CTRL *GMT, uint64_t col) {
2198 	gmt_M_unused(col);
2199 	return (gmtlib_cartesian_dist_proj (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]));
2200 }
2201 
2202 /*! . */
gmtinit_xy_deg_dist(struct GMT_CTRL * GMT,uint64_t col)2203 GMT_LOCAL double gmtinit_xy_deg_dist (struct GMT_CTRL *GMT, uint64_t col) {
2204 	gmt_M_unused(col);
2205 	return (gmtlib_great_circle_dist_degree (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]));
2206 }
2207 
2208 /*! . */
gmtinit_xy_true_dist(struct GMT_CTRL * GMT,uint64_t col)2209 GMT_LOCAL double gmtinit_xy_true_dist (struct GMT_CTRL *GMT, uint64_t col) {
2210 	gmt_M_unused(col);
2211 	return (gmt_great_circle_dist_meter (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]));
2212 }
2213 
2214 /*! . */
gmtinit_xy_cart_dist(struct GMT_CTRL * GMT,uint64_t col)2215 GMT_LOCAL double gmtinit_xy_cart_dist (struct GMT_CTRL *GMT, uint64_t col) {
2216 	gmt_M_unused(col);
2217 	return (gmtlib_cartesian_dist (GMT, GMT->current.io.prev_rec[GMT_X], GMT->current.io.prev_rec[GMT_Y], GMT->current.io.curr_rec[GMT_X], GMT->current.io.curr_rec[GMT_Y]));
2218 }
2219 
2220 /*! Parse the -n option for 2-D grid resampling parameters -n[b|c|l|n][+a][+t<BC>][+<threshold>] */
gmtinit_parse_n_option(struct GMT_CTRL * GMT,char * item)2221 int gmtinit_parse_n_option (struct GMT_CTRL *GMT, char *item) {
2222 	unsigned int pos = 0, j, k = 1;
2223 	char p[GMT_LEN256] = {""};
2224 
2225 	strncpy (GMT->common.n.string, item, GMT_LEN64-1);	/* Make copy of -n argument verbatim */
2226 	switch (item[0]) {
2227 		case '+':	/* Means no mode was specified so we get the default */
2228 			GMT->common.n.interpolant = BCR_BICUBIC; k = 0; break;
2229 		case 'n':
2230 			GMT->common.n.interpolant = BCR_NEARNEIGHBOR; break;
2231 		case 'l':
2232 			GMT->common.n.interpolant = BCR_BILINEAR; break;
2233 		case 'b':
2234 			GMT->common.n.interpolant = BCR_BSPLINE; break;
2235 		case 'c':
2236 			GMT->common.n.interpolant = BCR_BICUBIC; break;
2237 		default:
2238 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Use %s to set 2-D grid interpolation mode.\n", GMT_n_OPT);
2239 			return (1);
2240 			break;
2241 	}
2242 
2243 	/* Now look for +modifiers */
2244 
2245 	while ((gmt_strtok (&item[k], "+", &pos, p))) {
2246 		switch (p[0]) {
2247 #ifdef DEBUG
2248 			case 'A':	/* Debug antialias will save the counter to a grid */
2249 				GMT->common.n.save_debug = true;
2250 				break;
2251 #endif
2252 			case 'a':	/* Turn off antialias */
2253 				GMT->common.n.antialias = false;
2254 				break;
2255 			case 'b':	/* Set BCs */
2256 				GMT->common.n.bc_set = true;
2257 				/* coverity[buffer_size_warning] */	/* Do not remove this comment */
2258 				gmt_strncpy (GMT->common.n.BC, &p[1], 4U);
2259 				for (j = 0; j < MIN (4,strlen (GMT->common.n.BC)); j++) {
2260 					switch (GMT->common.n.BC[j]) {
2261 						case 'g': case 'p': case 'x': case 'y': break;
2262 						default:
2263 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -n: +b<BC> requires <BC> to be g or p[x|y], n[x|y]\n");
2264 							break;
2265 					}
2266 				}
2267 				break;
2268 			case 'c':	/* Turn on min/max clipping */
2269 				GMT->common.n.truncate = true;
2270 				break;
2271 			case 't':	/* Set interpolation threshold */
2272 				GMT->common.n.threshold = atof (&p[1]);
2273 				if (GMT->common.n.threshold < 0.0 || GMT->common.n.threshold > 1.0) {
2274 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -n: Interpolation threshold must be in [0,1] range\n");
2275 					return (1);
2276 				}
2277 				break;
2278 			default:	/* Bad modifier */
2279 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Use %s to set 2-D grid interpolation mode.\n", GMT_n_OPT);
2280 				return (1);
2281 				break;
2282 		}
2283 	}
2284 	return (GMT_NOERROR);
2285 }
2286 
2287 /*! . */
gmtinit_parse_p_option(struct GMT_CTRL * GMT,char * item)2288 GMT_LOCAL int gmtinit_parse_p_option (struct GMT_CTRL *GMT, char *item) {
2289 	unsigned int k, l = 0, pos = 0, error = 0;
2290 	double az, el = 0.0, z = 0.0;
2291 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""};
2292 	char p[GMT_LEN256] = {""}, *c = NULL;
2293 
2294 	/* -p[x|y|z]<azim>[/<elev>[/<zlevel>]][+w<lon0>/<lat0>[/<z0>][+v<x0>/<y0>] */
2295 
2296 	if (!GMT->common.J.active) {
2297 		gmt_set_missing_options (GMT, "J");	/* If mode is modern and -J exist in the history, and if an overlay we may add these from history automatically */
2298 		if (!GMT->common.J.active)
2299 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "The -p option works best in consort with -J (and -R or a grid)\n");
2300 	}
2301 	switch (item[0]) {
2302 		case 'x': GMT->current.proj.z_project.view_plane = GMT_X + GMT_ZW; l++; break;
2303 		case 'y': GMT->current.proj.z_project.view_plane = GMT_Y + GMT_ZW; l++;	break;
2304 		case 'z': GMT->current.proj.z_project.view_plane = GMT_Z + GMT_ZW; l++; break;
2305 		default: GMT->current.proj.z_project.view_plane  = GMT_Z + GMT_ZW; break;
2306 	}
2307 
2308 	if ((c = gmt_first_modifier (GMT, item, "vw"))) c[0] = '\0';	/* Chop off modifiers so we can parse the info */
2309 
2310 	if ((k = sscanf (&item[l], "%lf/%lf/%lf", &az, &el, &z)) < 1) {
2311 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -p (%s): Syntax is %s\n", item, GMT_p_OPT);
2312 		return GMT_PARSE_ERROR;
2313 	}
2314 	if (k == 1) { GMT->common.p.do_z_rotation = true; el = 90.0;}
2315 	if (el <= 0.0 || el > 90.0) {
2316 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -p: Elevation must be in 0-90 range\n");
2317 		return GMT_PARSE_ERROR;
2318 	}
2319 	if (c) {	/* Now process any modifiers */
2320 		pos = 0;
2321 		c[0] = '+';	/* Restore that character */
2322 		while (gmt_getmodopt (GMT, 'p', c, "vw", &pos, p, &error) && error == 0) {
2323 			switch (p[0]) {
2324 				case 'v':	/* View point given in projected coordinates */
2325 					if (sscanf (&p[1], "%[^/]/%s", txt_a, txt_b) != 2) {
2326 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -p (%s): Syntax is %s\n", p, GMT_p_OPT);
2327 						return GMT_PARSE_ERROR;
2328 					}
2329 					GMT->current.proj.z_project.view_x = gmt_M_to_inch (GMT, txt_a);
2330 					GMT->current.proj.z_project.view_y = gmt_M_to_inch (GMT, txt_b);
2331 					GMT->current.proj.z_project.view_given = true;
2332 					break;
2333 				case 'w':	/* Specify fixed World point in user's coordinates */
2334 					if (sscanf (&p[1], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c) < 2) {
2335 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -p (%s): Syntax is %s\n", p, GMT_p_OPT);
2336 						return GMT_PARSE_ERROR;
2337 					}
2338 					error += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf (GMT, txt_a, gmt_M_type (GMT, GMT_IN, GMT_X), &GMT->current.proj.z_project.world_x), txt_a);
2339 					error += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_Y), gmt_scanf (GMT, txt_b, gmt_M_type (GMT, GMT_IN, GMT_Y), &GMT->current.proj.z_project.world_y), txt_b);
2340 					if (k == 3) error += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_Z), gmt_scanf (GMT, txt_c, gmt_M_type (GMT, GMT_IN, GMT_Z), &GMT->current.proj.z_project.world_z), txt_c);
2341 					GMT->current.proj.z_project.world_given = true;
2342 					break;
2343 				default:	/* These are caught in gmt_getmodopt so break is just for Coverity */
2344 					break;
2345 			}
2346 		}
2347 		c[0] = '\0';	/* Chop off all modifiers so az/el/z can be determined */
2348 		if (!GMT->common.p.do_z_rotation) GMT->current.proj.z_project.fixed = true;
2349 	}
2350 
2351 	if (k >= 2) {
2352 		GMT->current.proj.z_project.view_azimuth   = az;
2353 		GMT->current.proj.z_project.view_elevation = el;
2354 		if (k == 3) GMT->current.proj.z_level      = z;
2355 	}
2356 	else
2357 		GMT->common.p.z_rotation = az;
2358 
2359 	return (error);
2360 }
2361 
2362 /*! . */
gmt_parse_s_option(struct GMT_CTRL * GMT,char * item)2363 bool gmt_parse_s_option (struct GMT_CTRL *GMT, char *item) {
2364 	unsigned int error = 0, n, pos = 0;
2365 	int64_t i, start = GMT_NOTSET, stop = GMT_NOTSET, inc;
2366 	char p[GMT_BUFSIZ] = {""}, tmp[GMT_MAX_COLUMNS] = {""}, *ca = NULL, *cr = NULL;
2367 	/* Parse the -s option.  Full syntax: -s[<cols>][+a][+r] Old syntax was -s[<cols>][r|a] */
2368 
2369 	gmt_M_memset (GMT->current.io.io_nan_col, GMT_MAX_COLUMNS, int);
2370 	GMT->current.setting.io_nan_mode = 0;
2371 	GMT->current.io.io_nan_col[0] = GMT_Z;	/* The default is to examine the z-column */
2372 	GMT->current.io.io_nan_ncols = 1;		/* Default is that single z column */
2373 	if (!item || !item[0]) {	/* Plain -s */
2374 		GMT->current.setting.io_nan_mode |= GMT_IO_NAN_SKIP;
2375 		return (false);	/* Nothing more to do */
2376 	}
2377 	strncpy (GMT->common.s.string, item, GMT_LEN64-1);	/* Make copy of -s argument verbatim */
2378 	if ((ca = strstr (item, "+a")))
2379 		GMT->current.setting.io_nan_mode |= GMT_IO_NAN_ANY;		/* Set -s+a */
2380 	if ((cr = strstr (item, "+r")))
2381 		GMT->current.setting.io_nan_mode |= GMT_IO_NAN_KEEP;		/* Set -s+r */
2382 	else
2383 		GMT->current.setting.io_nan_mode |= GMT_IO_NAN_SKIP;	/* Plain -s */
2384 	if (ca) {	/* Give +a */
2385 		if (cr && cr < ca)	/* Gave +r+a so chop cr */
2386 			cr[0] = '\0';
2387 		else	/* Either gave +a+r or just +a */
2388 			ca[0] = '\0';
2389 	}
2390 	else if (cr)	/* Gave +r */
2391 		cr[0] = '\0';
2392 	n = (int)strlen (item);
2393 	if (n == 0) {
2394 		if (ca) ca[0] = '+';	/* Restore string */
2395 		if (cr) cr[0] = '+';	/* Restore string */
2396 		return (false);		/* Nothing more to do */
2397 	}
2398 	if (item[n-1] == 'a') GMT->current.setting.io_nan_mode = GMT_IO_NAN_ANY, n--;			/* Old syntax set -sa */
2399 	else if (item[n-1] == 'r') GMT->current.setting.io_nan_mode = GMT_IO_NAN_KEEP, n--;	/* Old syntax set -sr */
2400 	if (n == 0) return (false);		/* No column arguments to process */
2401 	/* Here we have user-supplied column information */
2402 	for (i = 0; i < GMT_MAX_COLUMNS; i++) tmp[i] = GMT_NOTSET;
2403 	while (!error && (gmt_strtok (item, ",", &pos, p))) {	/* While it is not empty, process it */
2404 		if ((inc = gmtlib_parse_index_range (GMT, p, &start, &stop)) == 0) return (true);
2405 
2406 		/* Now set the code for these columns */
2407 		for (i = start; i <= stop; i += inc) tmp[i] = true;
2408 	}
2409 	/* Count and set array of NaN-columns */
2410 	for (i = n = 0; i < GMT_MAX_COLUMNS; i++) if (tmp[i] != GMT_NOTSET) GMT->current.io.io_nan_col[n++] = (unsigned int)i;
2411 	if (error || n == 0) {
2412 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -s: Unable to decode columns from %s\n", item);
2413 		return true;
2414 	}
2415 	GMT->current.io.io_nan_ncols = n;
2416 	if (ca) ca[0] = '+';	/* Restore string */
2417 	if (cr) cr[0] = '+';	/* Restore string */
2418 
2419 	return (false);
2420 }
2421 
gmtinit_var_t_module(struct GMT_CTRL * GMT)2422 bool gmtinit_var_t_module (struct GMT_CTRL *GMT) {
2423 	/* Only modules psxy, psxyz, pstext, psmeca, and pscoupe can do variable transparency */
2424 	if (!strncmp (GMT->init.module_name, "psxyz",   5U)) return true;
2425 	if (!strncmp (GMT->init.module_name, "psxy",    4U)) return true;
2426 	if (!strncmp (GMT->init.module_name, "pstext",  6U)) return true;
2427 	if (!strncmp (GMT->init.module_name, "psmeca",  6U)) return true;
2428 	if (!strncmp (GMT->init.module_name, "psvelo",  6U)) return true;
2429 	if (!strncmp (GMT->init.module_name, "pscoupe", 7U)) return true;
2430 	return false;	/* Anything else */
2431 }
2432 
gmtinit_parse_t_option(struct GMT_CTRL * GMT,char * item)2433 bool gmtinit_parse_t_option (struct GMT_CTRL *GMT, char *item) {
2434 	/* Parse -t[<filltransparency>[/<stroketransparency>]][+f][+s]
2435 	 * Note: The transparency is optional (read from file) only for plot, plot3d, and text */
2436 	unsigned int n_errors = 0, nt = 0;
2437 	char *c = NULL;
2438 
2439 	GMT->common.t.mode = GMT->common.t.n_transparencies = 0;	/* Initialize */
2440 	if (item[0] && (c = gmt_first_modifier (GMT, item, "fs"))) {	/* Got modifiers */
2441 		unsigned int pos = 0;
2442 		char txt[GMT_LEN16] = {""};
2443 		while (gmt_getmodopt (GMT, 't', c, "fs", &pos, txt, &n_errors) && n_errors == 0) {
2444 			switch (txt[0]) {
2445 				case 'f': GMT->common.t.mode |= GMT_SET_FILL_TRANSP;	nt++;	break;	/* Set fill transparency */
2446 				case 's': GMT->common.t.mode |= GMT_SET_PEN_TRANSP;		nt++;	break;	/* Set stroke transparency */
2447 				default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
2448 			}
2449 		}
2450 		c[0] = '\0';	/* Chop off the modifiers */
2451 	}
2452 
2453 	if (item[0]) {
2454 		if (strchr (item, '/')) {	/* Got two transparencies */
2455 			sscanf (item, "%lg/%lg", &GMT->common.t.value[GMT_FILL_TRANSP], &GMT->common.t.value[GMT_PEN_TRANSP]);
2456 			if (GMT->common.t.mode) {
2457 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -t: If both filltrans/stroketrans are given the +f+s modifiers are ignored\n");
2458 				GMT->common.t.mode = 0;
2459 			}
2460 		}
2461 		else if (GMT->common.t.mode == 0)	/* No modifiers specified so set both transparencies */
2462 			GMT->common.t.value[GMT_FILL_TRANSP] = GMT->common.t.value[GMT_PEN_TRANSP] = atof (item);
2463 		else {	/* Must check if modifiers selected one or both of the transparencies */
2464 			if (GMT->common.t.mode & GMT_SET_FILL_TRANSP)
2465 				GMT->common.t.value[GMT_FILL_TRANSP] = atof (item);
2466 			if (GMT->common.t.mode & GMT_SET_PEN_TRANSP)
2467 				GMT->common.t.value[GMT_PEN_TRANSP] = atof (item);
2468 		}
2469 		if (GMT->common.t.value[GMT_FILL_TRANSP] < 0.0 || GMT->common.t.value[GMT_FILL_TRANSP] > 100.0) {
2470 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -t: Fill transparency must be in (0-100]%% range!\n");
2471 			GMT->common.t.value[GMT_FILL_TRANSP] = 0.0;
2472 			n_errors++;
2473 		}
2474 		else if (GMT->common.t.value[GMT_FILL_TRANSP] > 0.0 && GMT->common.t.value[GMT_FILL_TRANSP] <= 1.0) {
2475 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Fill transparency is expected in percentage.  Did you mean %g?\n", GMT->common.t.value[GMT_FILL_TRANSP] * 100.0);
2476 		}
2477 		if (GMT->common.t.value[GMT_PEN_TRANSP] < 0.0 || GMT->common.t.value[GMT_PEN_TRANSP] > 100.0) {
2478 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -t: Stroke transparency must be in (0-100]%% range!\n");
2479 			GMT->common.t.value[GMT_PEN_TRANSP] = 0.0;
2480 			n_errors++;
2481 		}
2482 		else if (GMT->common.t.value[GMT_PEN_TRANSP] > 0.0 && GMT->common.t.value[GMT_PEN_TRANSP] <= 1.0) {
2483 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Stroke transparency is expected in percentage.  Did you mean %g?\n", GMT->common.t.value[GMT_PEN_TRANSP] * 100.0);
2484 		}
2485 		GMT->common.t.active = true;
2486 	}
2487 	else if (gmtinit_var_t_module (GMT)) {	/* Only some modules can do variable transparency */
2488 		GMT->common.t.active = GMT->common.t.variable = true;
2489 		if (nt) GMT->common.t.n_transparencies = nt;	/* If we gave -t+f+s then we need to read two transparencies, else just 1. 0 means we apply a single transp to both settings */
2490 		if (GMT->common.t.mode == 0) GMT->common.t.mode = GMT_SET_FILL_TRANSP;	/* For these modules, plain -t means -t+f */
2491 	}
2492 	else {
2493 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -t was not given any argument (please add transparency in (0-100]0%% range)!\n");
2494 		n_errors++;
2495 	}
2496 	if (c) c[0] = '+';	/* Restore the modifiers */
2497 	if (!GMT->common.t.variable && GMT->common.t.mode == 0) GMT->common.t.mode = GMT_SET_FILL_TRANSP | GMT_SET_PEN_TRANSP;	/* Sets both fill and stroke transparencies unless when we read from files */
2498 	return (n_errors > 0);
2499 }
2500 
2501 /*! Routine will decode the -wy|a|w|d|h|m|s|c<period>[/<phase>][+c<col>] arguments */
gmtinit_parse_w_option(struct GMT_CTRL * GMT,char * arg)2502 GMT_LOCAL int gmtinit_parse_w_option (struct GMT_CTRL *GMT, char *arg) {
2503 
2504 	char *c = NULL;
2505 
2506 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -w requires an argument */
2507 
2508 	if ((c = strstr (arg, "+c"))) {	/* Got a specific column */
2509 		if (c[2]) GMT->current.io.cycle_col = atoi (&c[2]);
2510 		c[0] = '\0';	/* Chop off modifier */
2511 		if (GMT->current.io.cycle_col < 0) {
2512 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -w: Cannot give negative (or missing) column number (%s)\n", arg);
2513 			c[2] = '+';	/* Restore modifier before we return */
2514 			return (GMT_PARSE_ERROR);
2515 		}
2516 	}
2517 	else	/* Default column is the first (x) */
2518 		GMT->current.io.cycle_col = GMT_X;
2519 
2520 	switch (arg[0]) {	/* Look at which valid code we got */
2521 		case 's': GMT->current.io.cycle_operator = GMT_CYCLE_SEC; break;
2522 		case 'm': GMT->current.io.cycle_operator = GMT_CYCLE_MIN; break;
2523 		case 'h': GMT->current.io.cycle_operator = GMT_CYCLE_HOUR; break;
2524 		case 'd': GMT->current.io.cycle_operator = GMT_CYCLE_DAY; break;
2525 		case 'w': GMT->current.io.cycle_operator = GMT_CYCLE_WEEK; break;
2526 		case 'a': GMT->current.io.cycle_operator = GMT_CYCLE_ANNUAL; break;
2527 		case 'y': GMT->current.io.cycle_operator = GMT_CYCLE_YEAR; break;
2528 		case 'c': GMT->current.io.cycle_operator = GMT_CYCLE_CUSTOM;
2529 			if (arg[1] == '\0') {	/* Gave us nuthin' */
2530 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -w: Code c syntax is -wc<period>[/<phase]\n");
2531 				return (GMT_PARSE_ERROR);
2532 			}
2533 			if (strchr (arg, '/')) {	/* Got custom period/phase */
2534 				char PE[GMT_LEN64] = {""}, PH[GMT_LEN64] = {""};
2535 				sscanf (&arg[1], "%[^/]/%s", PE, PH);
2536 				if (gmt_convert_double (GMT, PE, &GMT->current.io.cycle_period))
2537 					return (GMT_PARSE_ERROR);
2538 				if (gmt_convert_double (GMT, PH, &GMT->current.io.cycle_phase))
2539 					return (GMT_PARSE_ERROR);
2540 			}
2541 			else {	/* Just got the custom period, with phase == 0 */
2542 				if (gmt_convert_double (GMT, &arg[1], &GMT->current.io.cycle_period))
2543 					return (GMT_PARSE_ERROR);
2544 			}
2545 			break;
2546 		default:
2547 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -w: Unrecognized periodicity code %c\n", arg[0]);
2548 			return (GMT_PARSE_ERROR);
2549 	}
2550 	if (c) c[2] = '+';	/* Restore modifier */
2551 	/* If input column is not set (yet) then we set it to abstime unless it is the custom period */
2552 	if (GMT->current.io.cycle_operator != GMT_CYCLE_CUSTOM && gmt_get_column_type (GMT, GMT_IN, GMT->current.io.cycle_col) == GMT_IS_UNKNOWN)
2553 		gmt_set_column_type (GMT, GMT_IN, GMT->current.io.cycle_col, GMT_IS_ABSTIME);
2554 	/* Output column is no longer unknown or abstime but float */
2555 	gmt_set_column_type (GMT, GMT_OUT, GMT->current.io.cycle_col, GMT_IS_FLOAT);
2556 
2557 	strncpy (GMT->common.w.string, arg, GMT_LEN64-1);	/* Verbatim copy */
2558 	GMT->common.w.active = true;
2559 
2560 	return (GMT_NOERROR);
2561 }
2562 
2563 /*! Check that special map-related codes are present - if not give warning */
gmtinit_verify_encodings(struct GMT_CTRL * GMT)2564 GMT_LOCAL void gmtinit_verify_encodings (struct GMT_CTRL *GMT) {
2565 
2566 	/* First check for degree symbol */
2567 
2568 	if (GMT->current.setting.ps_encoding.code[gmt_ring] == 32 && GMT->current.setting.ps_encoding.code[gmt_degree] == 32) {	/* Neither /ring or /degree encoded */
2569 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selected character encoding does not have suitable degree symbol - will use space instead\n");
2570 	}
2571 	else if (GMT->current.setting.map_degree_symbol == gmt_ring && GMT->current.setting.ps_encoding.code[gmt_ring] == 32) {		/* want /ring but only /degree is encoded */
2572 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selected character encoding does not have ring symbol - will use degree symbol instead\n");
2573 		GMT->current.setting.map_degree_symbol = gmt_degree;
2574 	}
2575 	else if (GMT->current.setting.map_degree_symbol == gmt_degree && GMT->current.setting.ps_encoding.code[gmt_degree] == 32) {	/* want /degree but only /ring is encoded */
2576 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selected character encoding does not have degree symbol - will use ring symbol instead\n");
2577 		GMT->current.setting.map_degree_symbol = gmt_ring;
2578 	}
2579 
2580 	/* Then single quote for minute symbol... */
2581 
2582 	if (GMT->current.setting.map_degree_symbol < 2 && GMT->current.setting.ps_encoding.code[gmt_squote] == 32) {
2583 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selected character encoding does not have minute symbol (single quote) - will use space instead\n");
2584 	}
2585 
2586 	/* ... and double quote for second symbol */
2587 
2588 	if (GMT->current.setting.map_degree_symbol < 2 && GMT->current.setting.ps_encoding.code[gmt_dquote] == 32) {
2589 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selected character encoding does not have second symbol (double quote) - will use space instead\n");
2590 	}
2591 }
2592 
2593 /*! . */
gmtinit_true_false_or_error(char * value,bool * answer)2594 GMT_LOCAL bool gmtinit_true_false_or_error (char *value, bool *answer) {
2595 	/* Assigns false or true to answer, depending on whether value is false or true.
2596 	 * answer = false, when value is "f", "false" or "0"
2597 	 * answer = true, when value is "t", "true" or "1"
2598 	 * In either case, the function returns false as exit code.
2599 	 * When value is something else, answer is not altered and true is return as error.
2600 	 */
2601 
2602 	if (!strcmp (value, "true") || !strcmp (value, "t") || !strcmp (value, "1")) {	/* true */
2603 		*answer = true;
2604 		return (false);
2605 	}
2606 	if (!strcmp (value, "false") || !strcmp (value, "f") || !strcmp (value, "0")) {	/* false */
2607 		*answer = false;
2608 		return (false);
2609 	}
2610 
2611 	/* Got neither true or false.  Make no assignment and return true for error */
2612 
2613 	return (true);
2614 }
2615 
2616 /*! . */
gmtinit_decode4_wesnz(struct GMT_CTRL * GMT,const char * in,unsigned int side[],unsigned int * draw_box,int part)2617 GMT_LOCAL int gmtinit_decode4_wesnz (struct GMT_CTRL *GMT, const char *in, unsigned int side[], unsigned int *draw_box, int part) {
2618 	/* Scans the WESNZwesnz+ flags at the end of string "in" and sets the side/drawbox parameters
2619 	 * and returns the length of the remaining string.  Assumes any +g<fill> has been removed from in.
2620 	 */
2621 
2622 	int i, k, orig_i;
2623 	unsigned int side_orig[5], orig_draw_box = *draw_box;
2624 	bool go = true;
2625 
2626 	GMT->current.map.frame.set_frame[part]++;
2627 	if (GMT->current.map.frame.set_frame[GMT_PRIMARY] > 1 || GMT->current.map.frame.set_frame[GMT_SECONDARY] > 1) {
2628 		GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -B: <WESNZ-framesettings> given more than once!\n");
2629 		return (1);
2630 	}
2631 	i = (int)strlen (in);
2632 	if (i == 0) return (0);
2633 	i--;	/* Now last char in in[] */
2634 	orig_i = i;
2635 	gmt_M_memcpy (side_orig, side, 5, unsigned int);
2636 
2637 	for (k = 0; go && i >= 0 && strchr ("WESNZwesnz+", in[i]); i--) {
2638 		if (k == 0 && part == 0) {	/* Wipe out default values when the first flag is found */
2639 			for (k = 0; k < 5; k++) side[k] = 0;
2640 			*draw_box = GMT_3D_NONE;
2641 		}
2642 		if (in[i] == 's') {	/* Since s can mean both "draw south axis" and "seconds", check further */
2643 			if (side[S_SIDE]) go = false;	/* If S was set already then s probably means seconds */
2644 			else if (i && in[i-1] == ',') go = true;	/* Special case of ,s to indicate south, e.g. -B30,s */
2645 			else if (i && (in[i-1] == '.' || isdigit ((int)in[i-1]))) go = false;	/* Probably seconds, e.g. -B30s */
2646 			if (!go) { i++; continue; }	/* Break out of loop */
2647 		}
2648 		switch (in[i]) {
2649 			/* Draw AND Annotate */
2650 			case 'W': side[W_SIDE] |= GMT_AXIS_ALL; break;
2651 			case 'E': side[E_SIDE] |= GMT_AXIS_ALL; break;
2652 			case 'S': side[S_SIDE] |= GMT_AXIS_ALL; break;
2653 			case 'N': side[N_SIDE] |= GMT_AXIS_ALL; break;
2654 			case 'Z': side[Z_SIDE] |= GMT_AXIS_ALL; break;
2655 			/* Just Draw and tick */
2656 			case 'w': side[W_SIDE] |= GMT_AXIS_BARB; break;
2657 			case 'e': side[E_SIDE] |= GMT_AXIS_BARB; break;
2658 			case 's': side[S_SIDE] |= GMT_AXIS_BARB; break;
2659 			case 'n': side[N_SIDE] |= GMT_AXIS_BARB; break;
2660 			case 'z': side[Z_SIDE] |= GMT_AXIS_BARB; break;
2661 			/* Draw 3-D box */
2662 			case '+': *draw_box = GMT_3D_BOX; break;
2663 		}
2664 	}
2665 	if (i >= 0 && in[i] == ',') i--;	/* Special case for -BCcustomfile,WESNwesn to avoid the filename being parsed for WESN */
2666 
2667 	if (i == orig_i) {	/* No frame flags.  Restore what we wiped */
2668 		gmt_M_memcpy (side, side_orig, 5, unsigned int);
2669 		*draw_box = orig_draw_box;
2670 	}
2671 	return (i+1);	/* Return remaining string length */
2672 }
2673 
gmtinit_reset_colformats(struct GMT_CTRL * GMT)2674 GMT_LOCAL void gmtinit_reset_colformats (struct GMT_CTRL *GMT) {
2675 	unsigned int i;
2676 	for (i = 0; i < GMT_MAX_COLUMNS; i++) if (GMT->current.io.o_format[i])
2677 		gmt_M_str_free (GMT->current.io.o_format[i]);
2678 }
2679 
2680 /*! . */
gmtinit_parse_format_float_out(struct GMT_CTRL * GMT,char * value)2681 GMT_LOCAL void gmtinit_parse_format_float_out (struct GMT_CTRL *GMT, char *value) {
2682 
2683 	unsigned int pos = 0, col = 0, k;
2684 	char fmt[GMT_LEN64] = {""};
2685 	strncpy (GMT->current.setting.format_float_out_orig, value, GMT_LEN256-1);
2686 	if (strchr (value, ',')) {
2687 		unsigned int start = 0, stop = 0, error = 0;
2688 		char *p = NULL;
2689 		/* Look for multiple comma-separated format statements of type [<cols>:]<format>.
2690 		 * Last format also becomes the default for unspecified columns */
2691 		gmtinit_reset_colformats (GMT);	/* Wipe previous settings */
2692 		while ((gmt_strtok (value, ",", &pos, fmt))) {
2693 			if ((p = strchr (fmt, ':'))) {	/* Must decode which columns */
2694 				if (strchr (fmt, '-'))	/* Range of columns given. e.g., 7-9 */
2695 					sscanf (fmt, "%d-%d", &start, &stop);
2696 				else if (isdigit ((int)fmt[0]))	/* Just a single column, e.g., 3 */
2697 					start = stop = atoi (fmt);
2698 				else				/* Something bad */
2699 					error++;
2700 				p++;	/* Move to format */
2701 				for (k = start; k <= stop; k++)
2702 					GMT->current.io.o_format[k] = strdup (p);
2703 				if (stop > col) col = stop;	/* Retain last column set */
2704 			}
2705 		}
2706 		strncpy (GMT->current.setting.format_float_out, GMT->current.io.o_format[col], GMT_LEN64-1);
2707 	}
2708 	else if (strchr (value, ' ')) {
2709 		/* Look for N space-separated format statements of type <format1> <format2> <format3> ...
2710 		 * and let these apply to the first N output columns.
2711 		 * Last format also becomes the default for unspecified columns. */
2712 		gmtinit_reset_colformats (GMT);	/* Wipe previous settings */
2713 		k = 0;
2714 		while ((gmt_strtok (value, " ", &pos, fmt)))
2715 			GMT->current.io.o_format[k++] = strdup (fmt);
2716 		if (k) strncpy (GMT->current.setting.format_float_out, GMT->current.io.o_format[k-1], GMT_LEN64-1);
2717 	}
2718 	else {	/* No columns, set the default format */
2719 		gmtinit_reset_colformats (GMT);	/* Wipe previous settings */
2720 		strncpy (GMT->current.setting.format_float_out, value, GMT_LEN64-1);
2721 	}
2722 }
2723 
2724 /*! . */
gmtinit_badvalreport(struct GMT_CTRL * GMT,const char * keyword)2725 GMT_LOCAL bool gmtinit_badvalreport (struct GMT_CTRL *GMT, const char *keyword) {
2726 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized keyword %s. You may have been using a deprecated GMT3 or GMT4 keyword.\nChange keyword or use with GMT_COMPATIBILITY=4. " GMT_COMPAT_INFO, keyword);
2727 	return (true);
2728 }
2729 
2730 /*! . */
gmtinit_savedefaults(struct GMT_CTRL * GMT,char * file)2731 GMT_LOCAL int gmtinit_savedefaults (struct GMT_CTRL *GMT, char *file) {
2732 	bool header = false;
2733 	int case_val;
2734 	unsigned int k = 0, current_group = 0;
2735 	FILE *fpo = NULL;
2736 
2737 	if (file[0] == '-' && !file[1])
2738 		fpo = GMT->session.std[GMT_OUT];
2739 	else if ((fpo = fopen (file, "w")) == NULL) {
2740 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not create file %s\n", file);
2741 		return (GMT_NOTSET);
2742 	}
2743 
2744 	fprintf (fpo, "#\n# GMT %d.%d.%d Defaults file\n", GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION);
2745 	while (GMT_keyword_active[k].name != NULL) {
2746 		if (GMT_keyword_active[k].code == 1) {	/* Start of new group */
2747 			current_group = k++;
2748 			header = false;
2749 			continue;
2750 		}
2751 		case_val = gmt_hash_lookup (GMT, GMT_keyword_active[k].name, keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
2752 		if (case_val >= 0 && !GMT_keyword_updated[case_val])
2753 			{k++; continue;}	/* If equal to default, skip it */
2754 		if (!header) {
2755 			fprintf (fpo, "#\n# %s\n#\n", GMT_keyword_active[current_group].name);
2756 			header = true;
2757 		}
2758 		fprintf (fpo, "%-30s = %s\n", GMT_keyword_active[k].name, gmtlib_getparameter (GMT, GMT_keyword_active[k].name));
2759 		k++;
2760 	}
2761 
2762 	if (fpo != GMT->session.std[GMT_OUT]) fclose (fpo);
2763 
2764 	return (0);
2765 }
2766 
2767 /*! . */
gmtinit_append_trans(char * text,double transparency)2768 GMT_LOCAL void gmtinit_append_trans (char *text, double transparency) {
2769 	char trans[GMT_LEN64] = {""};
2770 	if (!gmt_M_is_zero (transparency) && text[0] != '-') {	/* Append nonzero transparency */
2771 		snprintf (trans, GMT_LEN64, "@%ld", lrint (100.0 * transparency));
2772 		strcat (text, trans);
2773 	}
2774 }
2775 
2776 /*! Checks if t fits the format [+|-][xxxx][.][yyyy][e|E[+|-]nn]. */
gmtinit_is_valid_number(char * t)2777 GMT_LOCAL bool gmtinit_is_valid_number (char *t) {
2778 	int i, n;
2779 
2780 
2781 	if (!t) return (true);				/* Cannot be NULL */
2782 	i = n = 0;
2783 	if (t[i] == '+' || t[i] == '-') i++;		/* OK to have leading sign */
2784 	while (isdigit ((int)t[i])) i++, n++;		/* OK to have numbers */
2785 	if (t[i] == '.') {				/* Found a decimal */
2786 		i++;	/* Go to next character */
2787 		while (isdigit ((int)t[i])) i++, n++;	/* OK to have numbers following the decimal */
2788 	}
2789 	/* Here n must be > 0.  Also, we might find exponential notation */
2790 	if (t[i] == 'e' || t[i] == 'E') {
2791 		i++;
2792 		if (t[i] == '+' || t[i] == '-') i++;	/* OK to have leading sign for exponent */
2793 		while (isdigit ((int)t[i])) i++;	/* OK to have numbers for the exponent */
2794 	}
2795 	/* If all is well we should now have run out of characters in t and n > 0 - otherwise it is an error */
2796 	return ((t[i] || n == 0) ? false : true);
2797 }
2798 
2799 /*! . */
gmtinit_hash(struct GMT_CTRL * GMT,const char * v,unsigned int n_hash)2800 GMT_LOCAL int gmtinit_hash (struct GMT_CTRL *GMT, const char *v, unsigned int n_hash) {
2801 	int h;
2802 	gmt_M_unused(GMT);
2803 	assert (v!=NULL); /* We are in trouble if we get a NULL pointer here */
2804 	for (h = 0; *v != '\0'; v++) h = (64 * h + (*v)) % n_hash;
2805 	while (h < 0) h += n_hash;
2806 	return (h);
2807 }
2808 
2809 /*! Read user's gmt.io file and initialize shorthand notation */
gmtinit_setshorthand(struct GMT_CTRL * GMT)2810 GMT_LOCAL int gmtinit_setshorthand (struct GMT_CTRL *GMT) {
2811 	unsigned int id, n = 0;
2812 	size_t n_alloc = 0;
2813 	char file[PATH_MAX] = {""}, line[GMT_BUFSIZ] = {""}, a[GMT_LEN64] = {""}, b[GMT_LEN64] = {""};
2814 	char c[GMT_LEN64] = {""}, d[GMT_LEN64] = {""}, e[GMT_LEN64] = {""};
2815 	FILE *fp = NULL;
2816 
2817 	GMT->session.n_shorthands = 0; /* By default there are no shorthands unless gmt.io is found */
2818 
2819 	if (!gmtlib_getuserpath (GMT, "gmt.io", file)) {
2820 		if (!gmt_getsharepath (GMT, "", "gmt.io", "", file, R_OK)) {	/* try non-hidden file in ~/.gmt */
2821 			if (gmt_M_compat_check (GMT, 4)) {	/* Look for obsolete .gmt_io files */
2822 				if (!gmtlib_getuserpath (GMT, ".gmt_io", file)) {
2823 					if (!gmt_getsharepath (GMT, "", "gmt_io", "", file, R_OK))	/* try non-hidden file in ~/.gmt */
2824 						return GMT_OK;
2825 				}
2826 			}
2827 			else
2828 				return GMT_OK;
2829 		}
2830 	}
2831 	if ((fp = fopen (file, "r")) == NULL)
2832 		return GMT_OK;
2833 
2834 	gmt_set_meminc (GMT, GMT_TINY_CHUNK); /* Only allocate a small amount */
2835 	while (fgets (line, GMT_BUFSIZ, fp)) {
2836 		if (line[0] == '#' || line[0] == '\n')
2837 			continue;
2838 		if (sscanf (line, "%s %s %s %s %s", a, b, c, d, e) != 5) {
2839 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while decoding file %s.  Bad format? [%s]\n", file, line);
2840 			fclose (fp);
2841 			return GMT_DATA_READ_ERROR;
2842 		}
2843 
2844 		if (n == n_alloc)
2845 			GMT->session.shorthand = gmt_M_malloc (GMT, GMT->session.shorthand, n, &n_alloc, struct GMT_SHORTHAND);
2846 
2847 		GMT->session.shorthand[n].suffix = strdup (a);
2848 		if (gmt_grd_format_decoder (GMT, b, &id) != GMT_NOERROR) {
2849 			/* no valid type id */
2850 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unknown shorthand format [%s]\n", file, b);
2851 			fclose (fp);
2852 			return GMT_NOT_A_VALID_TYPE;
2853 		}
2854 		snprintf (line, GMT_BUFSIZ, "%s/%s/%s/%s", b, c, d, e); /* ff/scale/offset/invalid */
2855 		GMT->session.shorthand[n].format = strdup (line);
2856 		++n;
2857 	}
2858 	fclose (fp);
2859 
2860 	n_alloc = GMT->session.n_shorthands = n;
2861 	gmt_reset_meminc (GMT);
2862 	GMT->session.shorthand = gmt_M_malloc (GMT, GMT->session.shorthand, 0, &n_alloc, struct GMT_SHORTHAND);
2863 	return GMT_OK;
2864 }
2865 
2866 /*! . */
gmtinit_freeshorthand(struct GMT_CTRL * GMT)2867 GMT_LOCAL void gmtinit_freeshorthand (struct GMT_CTRL *GMT) {/* Free memory used by shorthand arrays */
2868 	unsigned int i;
2869 
2870 	if (GMT->session.n_shorthands == 0)
2871 		return;
2872 
2873 	for (i = 0; i < GMT->session.n_shorthands; ++i) {
2874 		gmt_M_str_free (GMT->session.shorthand[i].suffix);
2875 		gmt_M_str_free (GMT->session.shorthand[i].format);
2876 	}
2877 	gmt_M_free (GMT, GMT->session.shorthand);
2878 }
2879 
gmt_subplot_status(struct GMTAPI_CTRL * API,int fig)2880 unsigned int gmt_subplot_status (struct GMTAPI_CTRL *API, int fig) {
2881 	/* Return GMT_SUBPLOT_ACTIVE if we are in a subplot situation, and add GMT_PANEL_NOTSET if no panel set yet */
2882 	char file[PATH_MAX] = {""};
2883 	bool answer;
2884 	unsigned int mode = 0;
2885 	/* Now read subplot information file */
2886 	snprintf (file, PATH_MAX, "%s/gmt.subplot.%d", API->gwf_dir, fig);
2887 	answer = (access (file, F_OK) == 0);	/* true if subplot information file is found */
2888 	if (answer) {
2889 		GMT_Report (API, GMT_MSG_INFORMATION, "Subplot information file found\n");
2890 		mode |= GMT_SUBPLOT_ACTIVE;
2891 	}
2892 	snprintf (file, PATH_MAX, "%s/gmt.panel.%d", API->gwf_dir, fig);
2893 	answer = (access (file, F_OK) == 0);	/* true if current panel file is found */
2894 	if (answer)
2895 		GMT_Report (API, GMT_MSG_INFORMATION, "Current panel file found\n");
2896 	else
2897 		mode |= GMT_PANEL_NOTSET;
2898 
2899 	return (mode);
2900 }
2901 
gmtinit_get_inset_dimensions(struct GMTAPI_CTRL * API,int fig,struct GMT_INSET * inset)2902 GMT_LOCAL int gmtinit_get_inset_dimensions (struct GMTAPI_CTRL *API, int fig, struct GMT_INSET *inset) {
2903 	char file[PATH_MAX] = {""}, line[GMT_LEN256] = {""};
2904 	unsigned int k;
2905 	double margin[4] = {0.0, 0.0, 0.0, 0.0};
2906 	FILE *fp = NULL;
2907 
2908 	if (inset) inset->active = false;	/* It is not an inset until we detect that it is */
2909 	snprintf (file, PATH_MAX, "%s/gmt.inset.%d", API->gwf_dir, fig);	/* Inset information file */
2910 
2911 	if (access (file, F_OK)) return (GMT_NOERROR);	/* No inset active */
2912 
2913 	if (inset == NULL) return 1;	/* Just wanted to know if there is an inset active */
2914 
2915 	/* Extract dimensions from the inset information file */
2916 	if ((fp = fopen (file, "r")) == NULL) {	/* Not good */
2917 		GMT_Report (API, GMT_MSG_ERROR, "Cannot read file %s\n", file);
2918 		return (GMT_ERROR_ON_FOPEN);
2919 	}
2920 	/* For now, skip the first 3 comments and get the 4th and 5th record which holds the dim and margin lines */
2921 	for (k = 0; k < 4; k++) gmt_fgets (API->GMT, line, GMT_LEN256, fp);
2922 	if (sscanf (&line[13], "%lf %lf", &inset->w, &inset->h) != 2) {
2923 		GMT_Report (API, GMT_MSG_ERROR, "Cannot parse dimensions %s\n", line);
2924 		fclose (fp);
2925 		return (GMT_DATA_READ_ERROR);
2926 	}
2927 	gmt_fgets (API->GMT, line, GMT_LEN256, fp);
2928 	if (sscanf (&line[11], "%lf %lf %lf %lf", &margin[XLO], &margin[XHI], &margin[YLO], &margin[YHI]) != 4) {
2929 		GMT_Report (API, GMT_MSG_ERROR, "Cannot parse margins %s\n", line);
2930 		fclose (fp);
2931 		return (GMT_DATA_READ_ERROR);
2932 	}
2933 	fclose (fp);
2934 
2935 	snprintf (file, PATH_MAX, "%s/gmt.inset+.%d", API->gwf_dir, fig);	/* Inset continuation file */
2936 	if (access (file, F_OK)) inset->first = true;	/* First time plotting in the inset */
2937 	if ((fp = fopen (file, "w")) == NULL) {	/* Not good */
2938 		GMT_Report (API, GMT_MSG_ERROR, "Cannot create file %s\n", file);
2939 		return (GMT_ERROR_ON_FOPEN);
2940 	}
2941 	fclose (fp);
2942 
2943 	/* Compute dimensions of the plottable part of the inset canvas */
2944 	inset->w -= (margin[XLO] + margin[XHI]);
2945 	inset->h -= (margin[YLO] + margin[YHI]);
2946 
2947 	inset->active = true;	/* It is */
2948 
2949 	GMT_Report (API, GMT_MSG_DEBUG, "Inset plot with canvas dimensions %g by %g\n", inset->w, inset->h);
2950 
2951 	return (GMT_NOERROR);
2952 }
2953 
gmt_hierarchy_tag(struct GMTAPI_CTRL * API,const char * kind,unsigned int direction,char * tag)2954 void gmt_hierarchy_tag (struct GMTAPI_CTRL *API, const char *kind, unsigned int direction, char *tag) {
2955 	/* Under modern mode we maintain separate history and setting files for
2956 	 * figures, subplot, panels, and insets, since they should not share
2957 	 * settings like -R -J between them.
2958 	 * tag should be of size 32 */
2959 	char path[PATH_MAX] = {""}, panel[GMT_LEN32] = {""};
2960 	int fig, subplot, inset;
2961 
2962 	/* Modern mode */
2963 
2964 	gmtlib_get_graphics_item (API, &fig, &subplot, panel, &inset);	/* Determine the hierarchical level */
2965 
2966 	/* Find the appropriate file of this kind for where we are, but may have to go up the hierarchy (if reading) */
2967 
2968 	if (inset) {	/* See if an inset level file exists or should be created */
2969 		sprintf (tag, ".inset");
2970 		if (direction == GMT_OUT) return;	/* We should write it at this level */
2971 		snprintf (path, PATH_MAX, "%s/%s%s", API->gwf_dir, kind, tag);
2972 		if (!access (path, R_OK)) return;	/* Yes, found it */
2973 	}
2974 	if ((subplot & GMT_SUBPLOT_ACTIVE)) {	/* Nothing yet, see if subplot has one */
2975 		if ((subplot & GMT_PANEL_NOTSET) == 0) {	/* Panel-specific item available? */
2976 			sprintf (tag, ".%d.panel.%s", fig, panel);
2977 			if (direction == GMT_OUT) return;	/* We should write it at this level */
2978 			snprintf (path, PATH_MAX, "%s/%s%s", API->gwf_dir, kind, tag);
2979 			if (!access (path, R_OK)) return;	/* Yes, found it */
2980 		}
2981 		/* No, try subplot master item instead */
2982 		sprintf (tag, ".%d.subplot", fig);
2983 		if (direction == GMT_OUT) return;	/* We should write it at this level */
2984 		snprintf (path, PATH_MAX, "%s/%s%s", API->gwf_dir, kind, tag);
2985 		if (!access (path, R_OK)) return;	/* Yes, found it */
2986 	}
2987 	/* Not found the kind file yet, so try it for this specific figure */
2988 	if (fig) {
2989 		sprintf (tag, ".%d", fig);
2990 		if (direction == GMT_OUT) return;	/* We should write it at this level */
2991 		snprintf (path, PATH_MAX, "%s/%s%s", API->gwf_dir, kind, tag);
2992 		if (!access (path, R_OK)) return;	/* Yes, found it */
2993 	}
2994 	/* Fall back is session level */
2995 	tag[0] = '\0';
2996 	snprintf (path, PATH_MAX, "%s/%s%s", API->gwf_dir, kind, tag);
2997 }
2998 
2999 /*! . */
gmtinit_get_history(struct GMT_CTRL * GMT)3000 GMT_LOCAL int gmtinit_get_history (struct GMT_CTRL *GMT) {
3001 	int id;
3002 	size_t len = strlen ("BEGIN GMT " GMT_PACKAGE_VERSION);
3003 	bool done = false, process = false;
3004 	char line[GMT_BUFSIZ] = {""}, hfile[PATH_MAX] = {""}, cwd[PATH_MAX] = {""};
3005 	char option[GMT_LEN64] = {""}, value[GMT_BUFSIZ] = {""};
3006 	FILE *fp = NULL; /* For gmt.history file */
3007 	static struct GMT_HASH unique_hashnode[GMT_N_UNIQUE];
3008 
3009 	if (GMT->parent->no_history)
3010 		return (GMT_NOERROR); /* gmt.history mechanism was disabled by GMT_Create_Session */
3011 
3012 	if (!(GMT->current.setting.history & GMT_HISTORY_READ))
3013 		return (GMT_NOERROR); /* gmt.history mechanism has been disabled */
3014 
3015 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Enter: gmtinit_get_history\n");
3016 
3017 	/* This is called once per GMT Session by GMT_Create_Session via gmt_begin and before any GMT_* module is called.
3018 	 * It loads in the known shorthands found in the gmt.history file
3019 	 */
3020 
3021 	/* If current directory is writable, use it; else use the home directory */
3022 
3023 	if (getcwd (cwd, PATH_MAX) == NULL) {
3024 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to determine current working directory.\n");
3025 	}
3026 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Modern mode: Use the workflow directory and one history per figure */
3027 		char tag[GMT_LEN16] = {""};
3028 		gmt_hierarchy_tag (GMT->parent, GMT_HISTORY_FILE, GMT_IN, tag);
3029 		snprintf (hfile, PATH_MAX, "%s/%s%s", GMT->parent->gwf_dir, GMT_HISTORY_FILE, tag);
3030 	}
3031 	else if (GMT->session.TMPDIR)			/* Isolation mode: Use GMT->session.TMPDIR/gmt.history */
3032 		snprintf (hfile, PATH_MAX, "%s/%s", GMT->session.TMPDIR, GMT_HISTORY_FILE);
3033 	else if (!access (cwd, W_OK))		/* Current directory is writable */
3034 		snprintf (hfile, PATH_MAX, "%s", GMT_HISTORY_FILE);
3035 	else if (GMT->session.HOMEDIR)	/* Try home directory instead */
3036 		snprintf (hfile, PATH_MAX, "%s/%s", GMT->session.HOMEDIR, GMT_HISTORY_FILE);
3037 	else {
3038 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "No writable directory found for gmt history - skipping it.\n");
3039 		return (GMT_NOERROR);
3040 	}
3041 	if ((fp = fopen (hfile, "r+")) == NULL) /* In order to place an exclusive lock, fp must be open for writing */
3042 		return (GMT_NOERROR);	/* OK to be unsuccessful in opening this file */
3043 
3044 	if (gmt_hash_init (GMT, unique_hashnode, GMT_unique_option, GMT_N_UNIQUE, GMT_N_UNIQUE)) {
3045 		fclose (fp);
3046 		return (GMT_NOERROR); /* Cannot do anything without the hash */
3047 	}
3048 
3049 	/* When we get here the file exists */
3050 	gmtlib_file_lock (GMT, fileno(fp));
3051 	/* Format of GMT gmt.history is as follow:
3052 	 * BEGIN GMT <version>		This is the start of parsable section
3053 	 * OPT ARG
3054 	 * where OPT is a 1- or 2-char code, e.g., R, X, JM, JQ, Js.  ARG is the argument.
3055 	 * Exception: if OPT = J then ARG is just the first character of the argument  (e.g., M).
3056 	 * File ends when we find
3057 	 * END				This is the end of parsable section
3058 	 */
3059 
3060 	while (!done && fgets (line, GMT_BUFSIZ, fp)) {
3061 		if (line[0] == '#') continue;	/* Skip comments lines */
3062 		gmt_chop (line);		/* Remove linefeed,CR */
3063 		if (line[0] == '\0') continue;	/* Skip blank lines */
3064 		if (!strncmp (line, "BEGIN GMT " GMT_PACKAGE_VERSION, len))
3065 			process = true;	/* OK to parse gmt.history file compatible with this GMT version */
3066 		else if (!strncmp (line, "END", 3U)) {		/* Logical end of gmt.history file */
3067 			done = true;
3068 			process = false;
3069 		}
3070 		if (!process) continue;		/* Not inside the good stuff yet */
3071 		if (sscanf (line, "%s %[^\n]", option, value) != 2) continue;	/* Quietly skip malformed lines */
3072 		if (!value[0]) continue;	/* No argument found */
3073 		if (option[0] == '@') {	/* PostScript plot information */
3074 			if (option[1] == 'C')	/* Read clip level */
3075 				GMT->current.ps.clip_level = atoi (value);
3076 			else if (option[1] == 'G')	/* Read gridline spacings */
3077 				sscanf (value, "%lg %lg", &GMT->current.plot.gridline_spacing[GMT_X], &GMT->current.plot.gridline_spacing[GMT_Y]);
3078 			else if (option[1] == 'L')	/* Read PS layer */
3079 				GMT->current.ps.layer = atoi (value);
3080 			else if (option[1] == 'S')	/* Read next sequential color IDs */
3081 				sscanf (value, "%d %d", &GMT->current.plot.color_seq_id[0], &GMT->current.plot.color_seq_id[1]);
3082 			continue;
3083 		}
3084 		if ((id = gmt_hash_lookup (GMT, option, unique_hashnode, GMT_N_UNIQUE, GMT_N_UNIQUE)) < 0) continue;	/* Quietly skip malformed lines */
3085 		if (GMT->init.history[id])
3086 			gmt_M_str_free (GMT->init.history[id]);
3087 		GMT->init.history[id] = strdup (value);
3088 	}
3089 
3090 	/* Close the file */
3091 	gmtlib_file_unlock (GMT, fileno(fp));
3092 	fclose (fp);
3093 
3094 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Exit:  gmtinit_get_history\n");
3095 
3096 	return (GMT_NOERROR);
3097 }
3098 
3099 /*! . */
gmtinit_put_history(struct GMT_CTRL * GMT)3100 GMT_LOCAL int gmtinit_put_history (struct GMT_CTRL *GMT) {
3101 	int id;
3102 	bool empty;
3103 	char hfile[PATH_MAX] = {""}, cwd[PATH_MAX] = {""};
3104 	FILE *fp = NULL; /* For gmt.history file */
3105 
3106 	if (GMT->parent->no_history)
3107 		return (GMT_NOERROR); /* gmt.history mechanism was disabled by GMT_Create_Session */
3108 
3109 	if (!(GMT->current.setting.history & GMT_HISTORY_WRITE)) {
3110 		if (GMT->current.setting.run_mode == GMT_MODERN && GMT->current.setting.history == GMT_HISTORY_OFF)
3111 			GMT->current.setting.history = GMT->current.setting.history_orig;
3112 		return (GMT_NOERROR); /* gmt.history mechanism has been disabled */
3113 	}
3114 	/* This is called once per GMT Session by gmt_end via GMT_Destroy_Session.
3115 	 * It writes out the known shorthands to the gmt.history file
3116 	 */
3117 
3118 	/* Do we even need to write? If empty, simply skip */
3119 	for (id = 0, empty = true; id < GMT_N_UNIQUE && empty; id++) {
3120 		if (GMT->init.history[id]) empty = false;	/* Have something to write */
3121 	}
3122 	if (empty) return (GMT_NOERROR);
3123 
3124 	/* If current directory is writable, use it; else use the home directory */
3125 
3126 	if (getcwd (cwd, PATH_MAX) == NULL) {
3127 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to determine current working directory.\n");
3128 	}
3129 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Modern mode: Use the workflow directory */
3130 		char tag[GMT_LEN16] = {""};
3131 		gmt_hierarchy_tag (GMT->parent, GMT_HISTORY_FILE, GMT_OUT, tag);
3132 		snprintf (hfile, PATH_MAX, "%s/%s%s", GMT->parent->gwf_dir, GMT_HISTORY_FILE, tag);
3133 	}
3134 	else if (GMT->session.TMPDIR)			/* Classic isolation mode: Use GMT->session.TMPDIR/gmt.history */
3135 		snprintf (hfile, PATH_MAX, "%s/%s", GMT->session.TMPDIR, GMT_HISTORY_FILE);
3136 	else if (!access (cwd, W_OK))	/* Current directory is writable */
3137 		snprintf (hfile, PATH_MAX, "%s", GMT_HISTORY_FILE);
3138 	else if (GMT->session.HOMEDIR)	/* Try home directory instead */
3139 		snprintf (hfile, PATH_MAX, "%s/%s", GMT->session.HOMEDIR, GMT_HISTORY_FILE);
3140 	else {
3141 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to determine a writable directory - gmt history not updated.\n");
3142 		return (GMT_NOERROR);
3143 	}
3144 	if ((fp = fopen (hfile, "w")) == NULL) return (GMT_NOTSET);	/* Not OK to be unsuccessful in creating this file */
3145 
3146 	/* When we get here the file is open */
3147 	if (!gmtlib_file_lock (GMT, fileno(fp)))
3148 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Directory %s is not locked for exclusive access. Multiple gmt processes running at once could corrupt history file.\n", hfile);
3149 
3150 	fprintf (fp, "# GMT %d Session common arguments shelf\n", GMT_MAJOR_VERSION);
3151 	fprintf (fp, "BEGIN GMT " GMT_PACKAGE_VERSION "\n");
3152 	for (id = 0; id < GMT_N_UNIQUE; id++) {
3153 		if (!GMT->init.history[id]) continue;	/* Not specified */
3154 		fprintf (fp, "%s\t%s\n", GMT_unique_option[id], GMT->init.history[id]);
3155 	}
3156 	if (GMT->current.ps.clip_level) fprintf (fp, "@C\t%d\n", GMT->current.ps.clip_level); /* Write clip level */
3157 	if (GMT->current.plot.gridline_spacing[GMT_X] > 0.0 || GMT->current.plot.gridline_spacing[GMT_Y] > 0.0)	/* Save gridline spacing in history */
3158 		fprintf (fp, "@G\t%g %g\n", GMT->current.plot.gridline_spacing[GMT_X], GMT->current.plot.gridline_spacing[GMT_Y]);
3159 	if (GMT->current.ps.layer) fprintf (fp, "@L\t%d\n", GMT->current.ps.layer); /* Write PS layer, if non-zero */
3160 	if (GMT->current.plot.color_seq_id[0] || GMT->current.plot.color_seq_id[1]) fprintf (fp, "@S\t%d %d\n", GMT->current.plot.color_seq_id[0], GMT->current.plot.color_seq_id[1]); /* Write next sequential color IDs, if non-zero */
3161 	fprintf (fp, "END\n");
3162 
3163 	/* Close the file */
3164 	gmtlib_file_unlock (GMT, fileno(fp));
3165 	fclose (fp);
3166 
3167 	return (GMT_NOERROR);
3168 }
3169 
3170 
3171 /*! . */
gmt_reset_history(struct GMT_CTRL * GMT)3172 void gmt_reset_history (struct GMT_CTRL *GMT) {
3173 	/* We must reset history when doing things like insets, new figure, etc. */
3174 	for (int id = 0; id < GMT_N_UNIQUE; id++) {
3175 		if (GMT->init.history[id]) gmt_M_str_free (GMT->init.history[id]);
3176 	}
3177 }
3178 
3179 /*! . */
gmt_reload_history(struct GMT_CTRL * GMT)3180 void gmt_reload_history (struct GMT_CTRL *GMT) {
3181 	gmt_reset_history (GMT);	/* First remove our memory */
3182 	gmtinit_get_history (GMT);	/* Get the latest history for current scope */
3183 }
3184 
gmt_reload_settings(struct GMT_CTRL * GMT)3185 void gmt_reload_settings (struct GMT_CTRL *GMT) {
3186 	gmt_conf_SI(GMT);				/* Get the original system defaults for SI */
3187 	(void)gmt_getdefaults (GMT, NULL);	/* Overload with any user defaults from initial gmt.conf */
3188 }
3189 
3190 /*! . */
gmtinit_free_plot_array(struct GMT_CTRL * GMT)3191 GMT_LOCAL void gmtinit_free_plot_array (struct GMT_CTRL *GMT) {
3192 	if (GMT->current.plot.n_alloc) {
3193 		gmt_M_free (GMT, GMT->current.plot.x);
3194 		gmt_M_free (GMT, GMT->current.plot.y);
3195 		gmt_M_free (GMT, GMT->current.plot.pen);
3196 	}
3197 	GMT->current.plot.n = GMT->current.plot.n_alloc = 0;
3198 }
3199 
gmtinit_trim_off_any_slash_at_end(char * dir)3200 GMT_LOCAL void gmtinit_trim_off_any_slash_at_end (char *dir) {
3201 	size_t L = strlen (dir);	/* Get length of dir */
3202 	if (L && (dir[L-1] == '/' || dir[L-1] == '\\')) dir[L-1] = '\0', L--;	/* Remove a trailing slash */
3203 	if (L) L--;	/* L is now at last character in dir */
3204 	while (L && dir[L] == ' ') dir[L] = '\0', L--;	/* Remove trailing spaces in directory names */
3205 }
3206 
3207 /*! . */
gmtinit_set_env(struct GMT_CTRL * GMT)3208 GMT_LOCAL int gmtinit_set_env (struct GMT_CTRL *GMT) {
3209 	char *this_c = NULL, path[PATH_MAX+1];
3210 	static char *how[2] = {"detected", "created"};
3211 	unsigned int u = 0, c = 0;
3212 	int err;
3213 	struct GMTAPI_CTRL *API = GMT->parent;
3214 	struct stat S;
3215 
3216 #ifdef SUPPORT_EXEC_IN_BINARY_DIR
3217 	/* If SUPPORT_EXEC_IN_BINARY_DIR is defined we try to set the share dir to
3218 	 * ${GMT_SOURCE_DIR}/share and the user dir to ${GMT_BINARY_DIR}/share in
3219 	 * order to simplify debugging and running in GMT_BINARY_DIR, e.g., when
3220 	 * debugging with Xcode or Visual Studio. This saves us from setting the
3221 	 * env variables GMT_SHAREDIR and GMT_USERDIR and we do not have to install
3222 	 * src/share in its destination dir. */
3223 
3224 	/* Only true, when we are running in a subdir of GMT_BINARY_DIR_SRC_DEBUG: */
3225 	bool running_in_bindir_src = !strncmp (GMT->init.runtime_bindir, GMT_BINARY_DIR_SRC_DEBUG, strlen(GMT_BINARY_DIR_SRC_DEBUG));
3226 #endif
3227 
3228 	/* Determine GMT->session.SHAREDIR (directory containing coast, cpt, etc. subdirectories) */
3229 
3230 	/* Note: gmtinit_set_env cannot use GMT_Report because the verbose level is not yet set */
3231 
3232 	if ((this_c = getenv ("GMT6_SHAREDIR")) != NULL && !access (this_c, F_OK|R_OK))	/* GMT6_SHAREDIR was set to a valid directory */
3233 		GMT->session.SHAREDIR = gmt_strdup_noquote (this_c);
3234 	else if ((this_c = getenv ("GMT5_SHAREDIR")) != NULL && !access (this_c, F_OK|R_OK))	/* GMT5_SHAREDIR was set to a valid directory */
3235 		GMT->session.SHAREDIR = gmt_strdup_noquote (this_c);
3236 	else if ((this_c = getenv ("GMT_SHAREDIR")) != NULL && !access (this_c, F_OK|R_OK)) /* GMT_SHAREDIR was set to a valid directory */
3237 		GMT->session.SHAREDIR = gmt_strdup_noquote (this_c);
3238 #ifdef SUPPORT_EXEC_IN_BINARY_DIR
3239 	else if (running_in_bindir_src)
3240 		/* Use ${GMT_SOURCE_DIR}/share to simplify debugging and running in GMT_BINARY_DIR */
3241 		GMT->session.SHAREDIR = gmt_strdup_noquote (GMT_SHARE_DIR_DEBUG);
3242 #endif
3243 	else if (!access (GMT_SHARE_DIR, F_OK|R_OK))		/* Found in hardcoded GMT_SHARE_DIR pointing to an existent directory */
3244 		GMT->session.SHAREDIR = gmt_strdup_noquote (GMT_SHARE_DIR);
3245 	else {
3246 		/* SHAREDIR still not found, make a smart guess based on runpath: */
3247 		if (gmt_guess_sharedir (path, GMT->init.runtime_bindir))
3248 			GMT->session.SHAREDIR = gmt_strdup_noquote (path);
3249 		else {
3250 			/* Still not found */
3251 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not locate share directory for GMT.\n");
3252 			return GMT_RUNTIME_ERROR;
3253 		}
3254 	}
3255 
3256 	gmt_dos_path_fix (GMT->session.SHAREDIR);
3257 	gmtinit_trim_off_any_slash_at_end (GMT->session.SHAREDIR);
3258 	GMT_Report (API, GMT_MSG_DEBUG, "GMT->session.SHAREDIR = %s\n", GMT->session.SHAREDIR);
3259 
3260 	/* Determine HOMEDIR (user home directory) */
3261 
3262 	if ((this_c = getenv ("HOME")) != NULL)				/* HOME was set */
3263 		GMT->session.HOMEDIR = gmt_strdup_noquote (this_c);
3264 #ifdef WIN32
3265 	else if ((this_c = getenv ("USERPROFILE")) != NULL)	/* USERPROFILE was set */
3266 		GMT->session.HOMEDIR = gmt_strdup_noquote (this_c);
3267 	else if ((this_c = getenv ("HOMEPATH")) != NULL)	/* HOMEPATH was set */
3268 		GMT->session.HOMEDIR = gmt_strdup_noquote (this_c);
3269 #endif
3270 	else {
3271 		/* If HOME not set: use root directory instead (http://gmt.soest.hawaii.edu/issues/710) */
3272 		GMT->session.HOMEDIR = strdup ("/"); /* Note: Windows will use the current drive if drive letter unspecified. */
3273 #ifdef DEBUG
3274 		GMT_Report (API, GMT_MSG_WARNING, "HOME environment not set. Using root directory instead.\n");
3275 #endif
3276 	}
3277 	if (GMT->session.HOMEDIR) {	/* Mostly to keep Coverity happy */
3278 		gmt_dos_path_fix (GMT->session.HOMEDIR);
3279 		gmtinit_trim_off_any_slash_at_end (GMT->session.HOMEDIR);
3280 		GMT_Report (API, GMT_MSG_DEBUG, "GMT->session.HOMEDIR = %s\n", GMT->session.HOMEDIR);
3281 	}
3282 
3283 	/* Determine GMT_USERDIR (directory containing user replacements contents in GMT_SHAREDIR) */
3284 
3285 	if ((this_c = getenv ("GMT_USERDIR")) != NULL && !access (this_c, F_OK|R_OK))		/* GMT_USERDIR was set to a valid directory */
3286 		GMT->session.USERDIR = gmt_strdup_noquote (this_c);
3287 	else if (GMT->session.HOMEDIR) {	/* Use default path for GMT_USERDIR (~/.gmt) */
3288 		snprintf (path, PATH_MAX, "%s/%s", GMT->session.HOMEDIR, ".gmt");
3289 		GMT->session.USERDIR = gmt_strdup_noquote (path);
3290 		u = 1;
3291 	}
3292 	if (GMT->session.USERDIR) {
3293 		gmt_dos_path_fix (GMT->session.USERDIR);
3294 		gmtinit_trim_off_any_slash_at_end (GMT->session.USERDIR);
3295 	}
3296 	if (GMT->session.USERDIR != NULL) {
3297 		err = stat (GMT->session.USERDIR, &S);	/* Stat the userdir path (which may not exist) */
3298 		if (err == ENOENT && gmt_mkdir (GMT->session.USERDIR)) { /* Path does not exist so we create that dir */
3299 			GMT_Report (API, GMT_MSG_WARNING, "Unable to create GMT User directory : %s\n", GMT->session.USERDIR);
3300 			GMT_Report (API, GMT_MSG_WARNING, "Auto-downloading of remote data sets has been disabled.\n");
3301 			GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
3302 			gmt_M_str_free (GMT->session.USERDIR);
3303 		}
3304 	}
3305 	if ((this_c = getenv ("GMT_CACHEDIR")) != NULL && !access (this_c, F_OK|R_OK))		/* GMT_CACHEDIR was set to a valid directory */
3306 		GMT->session.CACHEDIR = gmt_strdup_noquote (this_c);
3307 	else if (GMT->session.USERDIR != NULL) {	/* Use default path for GMT_CACHEDIR as GMT_USERDIR/cache */
3308 		snprintf (path, PATH_MAX, "%s/%s", GMT->session.USERDIR, "cache");
3309 		GMT->session.CACHEDIR = gmt_strdup_noquote (path);
3310 		c = 1;
3311 	}
3312 	if (GMT->session.CACHEDIR) {
3313 		gmt_dos_path_fix (GMT->session.CACHEDIR);
3314 		gmtinit_trim_off_any_slash_at_end (GMT->session.CACHEDIR);
3315 	}
3316 	if (GMT->session.CACHEDIR != NULL) {
3317 		err = stat (GMT->session.CACHEDIR, &S);	/* Stat the cachedir path (which may not exist) */
3318 		if (err == ENOENT && gmt_mkdir (GMT->session.CACHEDIR)) {	/* Path does not exist so we create that dir */
3319 			GMT_Report (API, GMT_MSG_WARNING, "Unable to create GMT User cache directory : %s\n", GMT->session.CACHEDIR);
3320 			GMT_Report (API, GMT_MSG_WARNING, "Auto-downloading of cache data has been disabled.\n");
3321 			GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
3322 			gmt_M_str_free (GMT->session.CACHEDIR);
3323 		}
3324 	}
3325 
3326 	if ((this_c = getenv ("GMT_SESSIONDIR")) != NULL && !access (this_c, F_OK|R_OK))	/* GMT_SESSIONDIR was set to a valid directory */
3327 		API->session_dir = gmt_strdup_noquote (this_c);
3328 	else if (GMT->session.USERDIR != NULL) {	/* Use GMT_USERDIR/sessions as default path for GMT_SESSIONDIR */
3329 		snprintf (path, PATH_MAX, "%s/%s", GMT->session.USERDIR, "sessions");
3330 		API->session_dir = gmt_strdup_noquote (path);
3331 	}
3332 	else {	/* Use the temp dir as the session dir */
3333 		API->session_dir = gmt_strdup_noquote (API->tmp_dir);
3334 		GMT_Report (API, GMT_MSG_WARNING, "No GMT User directory set, GMT session dir selected: %s\n", API->session_dir);
3335 	}
3336 	if (API->session_dir) {
3337 		gmt_dos_path_fix (API->session_dir);
3338 		gmtinit_trim_off_any_slash_at_end (API->session_dir);
3339 	}
3340 	if (API->session_dir != NULL) {
3341 		err = stat (API->session_dir, &S);	/* Stat the session path (which may not exist) */
3342 		if (err == ENOENT && gmt_mkdir (API->session_dir)) { /* Path does not exist so we create that dir */
3343 			GMT_Report (API, GMT_MSG_ERROR, "Unable to create GMT User sessions directory : %s\n", API->session_dir);
3344 			GMT_Report (API, GMT_MSG_ERROR, "Modern mode will fail.\n");
3345 		}
3346 		else if (err == 0) {	/* Path already exists, check why */
3347 			if (!S_ISDIR (S.st_mode))	/* Path already exists, but it is not a directory */
3348 				GMT_Report (API, GMT_MSG_ERROR, "A file named %s already exist and prevents us creating a session directory by that name\n", API->session_dir);
3349 			else if (S_ISDIR (S.st_mode) && (S.st_mode & S_IWUSR) == 0)	/* Directory already exists but is not writeable */
3350 				GMT_Report (API, GMT_MSG_ERROR, "Session directory %s already exist but is not writeable\n", API->session_dir);
3351 		}
3352 	}
3353 	if (GMT->session.USERDIR)  GMT_Report (API, GMT_MSG_DEBUG, "GMT->session.USERDIR = %s [%s]\n",  GMT->session.USERDIR,  how[u]);
3354 	if (GMT->session.CACHEDIR) GMT_Report (API, GMT_MSG_DEBUG, "GMT->session.CACHEDIR = %s [%s]\n", GMT->session.CACHEDIR, how[c]);
3355 
3356 	if (gmt_M_compat_check (GMT, 4)) {
3357 		/* Check if obsolete GMT_CPTDIR was specified */
3358 
3359 		if ((this_c = getenv ("GMT_CPTDIR")) != NULL && !access (this_c, F_OK|R_OK)) {		/* GMT_CPTDIR was set to a valid directory */
3360 			GMT_Report (API, GMT_MSG_WARNING, "Environment variable GMT_CPTDIR was set but is no longer used by GMT.\n");
3361 			GMT_Report (API, GMT_MSG_WARNING, "System-wide color tables are in %s/cpt.\n", GMT->session.SHAREDIR);
3362 			GMT_Report (API, GMT_MSG_WARNING, "Use GMT_USERDIR (%s) instead and place user-defined color tables there.\n", GMT->session.USERDIR);
3363 		}
3364 	}
3365 
3366 	if ((this_c = getenv ("GMT_DATA_SERVER")) != NULL)		/* GMT_DATA_SERVER was set */
3367 		GMT->session.DATASERVER = strdup (this_c);
3368 	else if ((this_c = getenv ("GMT_DATA_URL")) != NULL)		/* GMT_DATA_URL [deprecated in 6.0.0] was set */
3369 		GMT->session.DATASERVER = strdup (this_c);
3370 	else
3371 		GMT->session.DATASERVER = strdup (GMT_DATA_SERVER);	/* SOEST default */
3372 	if (GMT->session.DATASERVER)
3373 		gmtinit_trim_off_any_slash_at_end (GMT->session.DATASERVER);
3374 
3375 	/* Determine GMT_DATADIR (data directories) */
3376 
3377 	if ((this_c = getenv ("GMT_DATADIR")) != NULL) {		/* GMT_DATADIR was set */
3378 		if (strchr (this_c, ',') || strchr (this_c, PATH_SEPARATOR)) {
3379 			/* A list of directories [not checked for validity here] */
3380 			GMT->session.DATADIR = strdup (this_c);
3381 			gmt_dos_path_fix (GMT->session.DATADIR);
3382 		}
3383 		else if (access (this_c, R_OK) == 0) {	/* GMT_DATADIR was set to a single valid directory */
3384 			/* A list of directories or a single directory that is accessible */
3385 			GMT->session.DATADIR = strdup (this_c);
3386 			gmt_dos_path_fix (GMT->session.DATADIR);
3387 		}
3388 #ifdef WIN32
3389 		else if (strchr(this_c, ':')) {		/* May happen to have ':' as a path separator when running a MSYS bash shell*/
3390 			/* A list of directories [not checked for validity here] */
3391 			GMT->session.DATADIR = strdup(this_c);
3392 			gmt_dos_path_fix (GMT->session.DATADIR);
3393 		}
3394 #endif
3395 		else {
3396 			GMT_Report (API, GMT_MSG_WARNING, "Environment variable GMT_DATADIR was set but not pointing to a valid directory - ignored.\n");
3397 
3398 		}
3399 		if (GMT->session.DATADIR) {	/* Fix backslashes and use comma for OS-independent separator */
3400 			gmt_replace_backslash_in_path (GMT->session.DATADIR);
3401 			gmt_strrepc (GMT->session.DATADIR, PATH_SEPARATOR, ',');
3402 		}
3403 	}
3404 
3405 	/* Determine GMT_TMPDIR (for isolation mode). Needs to exist use it. */
3406 
3407 	if ((this_c = getenv ("GMT_TMPDIR")) != NULL) {		/* GMT_TMPDIR was set, check it */
3408 		if (access (this_c, R_OK|W_OK|X_OK)) {
3409 			GMT_Report (API, GMT_MSG_WARNING, "Environment variable GMT_TMPDIR was set to %s, but directory is not accessible.\n", this_c);
3410 			GMT_Report (API, GMT_MSG_WARNING, "GMT_TMPDIR needs to have mode rwx. Isolation mode switched off.\n");
3411 			GMT->session.TMPDIR = NULL;
3412 		}
3413 		else {
3414 			GMT->session.TMPDIR = gmt_strdup_noquote (this_c);
3415 			gmt_dos_path_fix (GMT->session.TMPDIR);
3416 			gmtinit_trim_off_any_slash_at_end (GMT->session.TMPDIR);
3417 		}
3418 	}
3419 	return GMT_OK;
3420 }
3421 
3422 /* Here is the new -B parser with all its sub-functions */
3423 
3424 #ifdef WIN32
3425 /*! . */
gmtinit_handle_dosfile(struct GMT_CTRL * GMT,char * in,int this_mark)3426 GMT_LOCAL void gmtinit_handle_dosfile (struct GMT_CTRL *GMT, char *in, int this_mark) {
3427 	/* Because (1) we use colons to indicate start/stop of text labels and
3428 	 * (2) under Windows, a colon can be part of a path (e.g., C:\dir\file)
3429 	 * we need to temporarily replace <drive>:\ with <drive>;\ so that this
3430 	 * path colon does not interfere with the rest of the parsing.  Once the
3431 	 * colon items have been parsed, we replace the ; back to : */
3432 	int i, len, other = 1 - this_mark;
3433 	char mark[2] = {':', ';'};
3434 	gmt_M_unused(GMT);
3435 
3436 	if (!in)
3437 		return;	/* Nothing to work on */
3438 	if ((len = (int)strlen (in)) < 2)
3439 		return;	/* Nothing to work on */
3440 	len -= 2; /* Since this use of : cannot be at the end anyway and we need to check the next two characters */
3441 	for (i = 1; i < len; ++i) {
3442 		/* Start at position 1 since we need the position before.
3443 		 * Look for "X:/<nocolon>" pattern, with X = A-Z */
3444 		if (in[i] == mark[this_mark] && (in[i-1] >= 'A' && in[i-1] <= 'Z')
3445 				&& (in[i+1] == '/' || in[i+1] == '\\') && (in[i+2] != mark[this_mark]))
3446 			in[i] = mark[other];
3447 	}
3448 }
3449 #endif
3450 
3451 /*! . */
gmtinit_strip_colonitem(struct GMT_CTRL * GMT,int axis,const char * in,const char * pattern,char * item,char * out)3452 GMT_LOCAL int gmtinit_strip_colonitem (struct GMT_CTRL *GMT, int axis, const char *in, const char *pattern, char *item, char *out) {
3453 	/* Removes the searched-for item from in, returns it in item, with the rest in out.
3454 	 * pattern is usually ":." for title, ":," for unit, and ":" for label.
3455 	 * ASSUMPTION: Only pass ":" after first removing titles and units
3456 	 */
3457 
3458 	char *s = NULL, *str = "xyz";
3459 	bool error = false;
3460 
3461 	if ((s = strstr (in, pattern))) {		/* OK, found what we are looking for */
3462 		size_t i, j, k;
3463 		k = (size_t)(s - in);			/* Start index of item */
3464 		strncpy (out, in, k);			/* Copy everything up to the pattern */
3465 		i = k + strlen (pattern);		/* Now go to beginning of item */
3466 		j = 0;
3467 		while (in[i] && in[i] != ':') item[j++] = in[i++];	/* Copy the item... */
3468 		item[j] = '\0';				/* ...and terminate the string */
3469 		if (in[i] != ':') error = true;		/* Missing terminating colon */
3470 		i++;					/* Skip the ending colon */
3471 		while (in[i]) out[k++] = in[i++];	/* Copy rest to out... */
3472 		out[k] = '\0';				/* .. and terminate */
3473 	}
3474 	else	/* No item to update */
3475 		strcpy (out, in);
3476 
3477 	if (error) {	/* Problems with decoding */
3478 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Missing terminating colon in -B string %c-component %s\n", str[axis], in);
3479 		return (1);
3480 	}
3481 	if (strstr (out, pattern) && !strcmp (pattern, ":.")) {	/* Problems with decoding title */
3482 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "More than one title in -B string %c-component %s\n", str[axis], in);
3483 		return (1);
3484 	}
3485 	if (strstr (out, pattern) && !strcmp (pattern, ":,")) {	/* Problems with decoding unit */
3486 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "More than one unit string in -B %c-component %s\n", str[axis], in);
3487 		return (1);
3488 	}
3489 	if (strstr (out, pattern) && !strcmp (pattern, ":=")) {	/* Problems with decoding prefix */
3490 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "More than one prefix string in  -B component %s\n", in);
3491 		return (1);
3492 	}
3493 	if (strstr (out, pattern)) {	/* Problems with decoding label */
3494 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "More than one label string in  -B component %s\n", in);
3495 		return (1);
3496 	}
3497 #ifdef _WIN32
3498 	gmtinit_handle_dosfile (GMT, item, 1);	/* Undo any DOS files like X;/ back to X:/ */
3499 #endif
3500 	return (GMT_NOERROR);
3501 }
3502 
3503 /*! . */
gmtinit_handle_atcolon(struct GMT_CTRL * GMT,char * txt,int old_p)3504 GMT_LOCAL void gmtinit_handle_atcolon (struct GMT_CTRL *GMT, char *txt, int old_p) {
3505 	/* Way = 0: Replaces @:<size>: and @:: with @^<size>^ and @^^ to avoid trouble in -B:label: parsing;
3506 	 * Way = 1: Restores it the way it was. */
3507 	int new_p;
3508 	char *item[2] = {"@:", "@^"}, mark[2] = {':', '^'}, *s = NULL;
3509 	gmt_M_unused(GMT);
3510 
3511 	if (!txt || !txt[0]) return;	/* Nothing to do */
3512 	new_p = 1 - old_p;	/* The opposite of old */
3513 	while ((s = strstr (txt, item[old_p]))) {	/* As long as we keep finding these */
3514 		ptrdiff_t pos = ((size_t)s - (size_t)txt) + 1; /* Skip past the @ character */
3515 		if (txt[pos+1] == mark[old_p]) {			/* Either :: or ^^ */
3516 			txt[pos] = txt[pos+1] = mark[new_p];	/* Replace @:: with @^^ or vice versa */
3517 		}
3518 		else {	/* Found @:<size>: or @^<size>^ */
3519 			txt[pos] = mark[new_p];
3520 			while (txt[pos] && txt[pos] != mark[old_p]) pos++;
3521 			if (txt[pos] == mark[old_p]) txt[pos] = mark[new_p];
3522 		}
3523 	}
3524 }
3525 
3526 /*! Take the -B string (minus the leading -B) and chop into 3 strings for x, y, and z */
gmtinit_split_info_strings(struct GMT_CTRL * GMT,const char * in,char * x_info,char * y_info,char * z_info)3527 GMT_LOCAL int gmtinit_split_info_strings (struct GMT_CTRL *GMT, const char *in, char *x_info, char *y_info, char *z_info) {
3528 
3529 	bool mute = false;
3530 	size_t i, n_slash, s_pos[2];
3531 
3532 	x_info[0] = y_info[0] = z_info[0] = '\0';
3533 
3534 	for (i = n_slash = 0; in[i] && n_slash < 3; i++) {
3535 		if (in[i] == ':') mute = !mute;
3536 		if (in[i] == '/' && !mute) s_pos[n_slash++] = i;	/* Axis-separating slash, not a slash in a label */
3537 	}
3538 
3539 	if (n_slash == 3) {
3540 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while splitting -B string %s\n", in);
3541 		return (1);
3542 	}
3543 
3544 	if (n_slash == 2) {	/* Got x/y/z */
3545 		i = strlen (in);
3546 		strncpy (x_info, in, s_pos[0]);					x_info[s_pos[0]] = '\0';
3547 		strncpy (y_info, &in[s_pos[0]+1], s_pos[1] - s_pos[0] - 1);	y_info[s_pos[1] - s_pos[0] - 1] = '\0';
3548 		strncpy (z_info, &in[s_pos[1]+1], i - s_pos[1] - 1);		z_info[i - s_pos[1] - 1] = '\0';
3549 	}
3550 	else if (n_slash == 1) {	/* Got x/y */
3551 		i = strlen (in);
3552 		strncpy (x_info, in, s_pos[0]);					x_info[s_pos[0]] = '\0';
3553 		strncpy (y_info, &in[s_pos[0]+1], i - s_pos[0] - 1);		y_info[i - s_pos[0] - 1] = '\0';
3554 	}
3555 	else {	/* Got x with implicit copy to y */
3556 		strcpy (x_info, in);
3557 		strcpy (y_info, in);
3558 		GMT->current.map.frame.set_both = true;
3559 	}
3560 	return (GMT_NOERROR);
3561 }
3562 
3563 /*! . */
gmtinit_init_custom_annot(struct GMT_CTRL * GMT,struct GMT_PLOT_AXIS * A,int * n_int)3564 GMT_LOCAL int gmtinit_init_custom_annot (struct GMT_CTRL *GMT, struct GMT_PLOT_AXIS *A, int *n_int) {
3565 	/* Reads a file with one or more records of the form
3566 	 * value	types	[label]
3567 	 * where value is the coordinate of the tickmark, types is a combination
3568 	 * of a|i (annot or interval annot), f (tick), or g (gridline).
3569 	 * The a|i will take a label string (or sentence).
3570 	 * The item argument specifies which type to consider [a|i,f,g].  We return
3571 	 * an array with coordinates and labels, and set interval to true if applicable.
3572 	 */
3573 	int error, k, n_errors = 0;
3574 	bool save_trailing;
3575 	unsigned int save_coltype, save_max_cols_to_read;
3576 	uint64_t row;
3577 	char type[GMT_LEN8] = {""};
3578 	struct GMT_DATASET *D = NULL;
3579 	struct GMT_DATASEGMENT *S = NULL;
3580 
3581 	/* Temporarily change what data type col one is */
3582 	save_coltype = gmt_get_column_type (GMT, GMT_IN, GMT_X);
3583 	save_trailing = GMT->current.io.trailing_text[GMT_IN];
3584 	save_max_cols_to_read = GMT->current.io.max_cols_to_read;
3585 	gmt_set_column_type (GMT, GMT_IN, GMT_X, gmt_M_type (GMT, GMT_IN, A->id));
3586 	gmt_disable_bghio_opts (GMT);	/* Do not want any -b -g -h -i -o to affect the reading this file */
3587 	GMT->current.io.record_type[GMT_IN] = GMT_READ_MIXED;
3588 	GMT->current.io.trailing_text[GMT_IN] = true;
3589 	GMT->current.io.max_cols_to_read = 1;
3590 	if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 1, GMT_COL_FIX)) != GMT_NOERROR) return (1);
3591 	if ((D = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, A->file_custom, NULL)) == NULL) {
3592 		gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
3593 		return (1);
3594 	}
3595 	if (D->n_records == 0) {
3596 		GMT_Destroy_Data (GMT->parent, &D);
3597 		gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
3598 		return (1);
3599 	}
3600 	gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
3601 	GMT->current.io.trailing_text[GMT_IN] = save_trailing;
3602 	GMT->current.io.max_cols_to_read = save_max_cols_to_read;
3603 	gmt_reenable_bghio_opts (GMT);	/* Recover settings provided by user (if -b -g -h -i were used at all) */
3604 
3605 	gmt_M_memset (n_int, GMT_N_AXIS_ITEMS, int);
3606 	S = D->table[0]->segment[0];	/* All we got */
3607 
3608 	for (row = 0; row < S->n_rows; row++) {
3609 		k = sscanf (S->text[row], "%s", type);
3610 		if (k != 1) {
3611 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad format record [%s] at row %d in custom file %s.\n", S->text[row], (int)row, A->file_custom);
3612 			n_errors++;
3613 			continue;
3614 		}
3615 		for (k = 0; type[k]; k++) {
3616 			switch (type[k]) {
3617 				case 'a':	/* Regular annotation */
3618 					n_int[GMT_ITEM_ANNOT]++;
3619 					break;
3620 				case 'i':	/* Interval annotation */
3621 					n_int[GMT_ITEM_INTVAL]++;
3622 					break;
3623 				case 'f':	/* Tick placement */
3624 					n_int[GMT_ITEM_TICK]++;
3625 					break;
3626 				case 'g':	/* Gridline placement */
3627 					n_int[GMT_ITEM_GRID]++;
3628 					break;
3629 				default:
3630 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized type (%c) at row %d in custom file %s.\n", type[k], (int)row, A->file_custom);
3631 					n_errors++;
3632 					break;
3633 			}
3634 		}
3635 	}
3636 	GMT_Destroy_Data (GMT->parent, &D);
3637 
3638 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Processed custom annotations via %s for axis %d.\n", A->file_custom, A->id);
3639 	if (n_int[GMT_ITEM_ANNOT] && n_int[GMT_ITEM_INTVAL]) {
3640 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot mix interval and regular annotations in custom file %s.\n", A->file_custom);
3641 		n_errors++;
3642 	}
3643 	return (n_errors);
3644 }
3645 
gmtlib_set_case_and_kind(struct GMT_CTRL * GMT,char * format,bool * upper_case,unsigned int * flavor)3646 void gmtlib_set_case_and_kind (struct GMT_CTRL *GMT, char *format, bool *upper_case, unsigned int *flavor) {
3647 	gmt_M_unused (GMT);
3648 	/* Examine the format string and determine if we want upper/lower case and what type of abbreviation, if any */
3649 	*upper_case = false;	*flavor = 0;	/* Initialize */
3650 	switch (format[0]) {	/* This parameter controls which version of month/day textstrings we use for plotting */
3651 		case 'F':	/* Full name, upper case */
3652 			*upper_case = true;
3653 			/* Intentionally fall through - to 'f' */
3654 		case 'f':	/* Full name, lower case */
3655 			*flavor = 0;
3656 			break;
3657 		case 'A':	/* Abbreviated name, upper case */
3658 			*upper_case = true;
3659 			/* Intentionally fall through - to 'a' */
3660 		case 'a':	/* Abbreviated name, lower case */
3661 			*flavor = 1;
3662 			break;
3663 		case 'C':	/* 1-char name, upper case */
3664 			*upper_case = true;
3665 			/* Intentionally fall through - to 'c' */
3666 		case 'c':	/* 1-char name, lower case */
3667 			*flavor = 2;
3668 			break;
3669 		default:
3670 			break;
3671 	}
3672 }
3673 
3674 /*! Load the values into the appropriate GMT_PLOT_AXIS_ITEM structure */
gmtinit_set_titem(struct GMT_CTRL * GMT,struct GMT_PLOT_AXIS * A,char * in,char flag,char axis,int custom)3675 GMT_LOCAL int gmtinit_set_titem (struct GMT_CTRL *GMT, struct GMT_PLOT_AXIS *A, char *in, char flag, char axis, int custom) {
3676 
3677 	struct GMT_PLOT_AXIS_ITEM *I = NULL;
3678 	char *format = NULL, *t = NULL, *s = NULL, unit = 0;
3679 	double phase = 0.0, val = 0.0;
3680 
3681 	t = in;
3682 
3683 	/* Here, t must point to a valid number.  If t[0] is not [+,-,.] followed by a digit we have an error */
3684 
3685 	if (strstr (t, "pi"))	/* Treat pi-fractions separately */
3686 		gmt_scanf_float (GMT, in, &val);
3687 	else {
3688 		/* Decode interval, get pointer to next segment */
3689 		if ((val = strtod (t, &s)) < 0.0 && GMT->current.proj.xyz_projection[A->id] != GMT_LOG10) {	/* Interval must be >= 0 */
3690 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Negative interval in -B option (%c-component, %c-info): %s\n", axis, flag, in);
3691 			return (3);
3692 		}
3693 		if (s[0] && (s[0] == '-' || s[0] == '+')) {	/* Phase shift information given */
3694 			t = s;
3695 			phase = strtod (t, &s);
3696 		}
3697 	}
3698 	if (val == 0.0 && t[0] && t == s) {
3699 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad interval in -B option (%c-component, %c-info): %s gave interval = 0\n", axis, flag, in);
3700 		return (3);
3701 	}
3702 
3703 	/* Appended one of the allowed units, or l or p for log10/pow */
3704 	if (s && s[0] && strchr ("YyOoUuKkJjDdHhMmSsCcrRlp", s[0]))
3705 		unit = s[0];
3706 	else if (A->type == GMT_TIME)	/* Default time system unit implied, but use s if custom to avoid resetting of flag below */
3707 		unit = (A->special == GMT_CUSTOM) ? 's' : GMT->current.setting.time_system.unit;
3708 	else
3709 		unit = 0;	/* Not specified */
3710 
3711 	if (!GMT->current.map.frame.primary) flag = (char) toupper ((int)flag);
3712 
3713 	if (A->type == GMT_TIME) {	/* Strict check on time intervals */
3714 		if (gmtlib_verify_time_step (GMT, irint (val), unit))
3715 			return GMT_PARSE_ERROR;
3716 	}
3717 
3718 	switch (unit) {	/* Determine if we have intervals or moments */
3719 		case 'Y':	case 'y':
3720 		case 'O':	case 'o':
3721 		case 'K':	case 'k':
3722 		case 'J':	case 'j':
3723 		case 'D':	case 'd':
3724 		case 'R':	case 'r':
3725 		case 'U':	case 'u':
3726 			if (A->type == GMT_TIME && flag == 'a') flag = 'i';
3727 			if (A->type == GMT_TIME && flag == 'A') flag = 'I';
3728 			break;
3729 		case 'l':	/* Log10 annotation flag */
3730 			A->type = GMT_LOG10;
3731 			unit = 0;
3732 			break;
3733 		case 'p':	/* pow annotation flag */
3734 			A->type = GMT_POW;
3735 			unit = 0;
3736 			break;
3737 		default:
3738 			break;
3739 	}
3740 
3741 	switch (flag) {
3742 		case 'a': case 'i':	/* Upper annotation / major tick annotation */
3743 			I = &A->item[GMT_ANNOT_UPPER];
3744 			break;
3745 		case 'A': case 'I':	/* Lower annotation / major tick annotation */
3746 			I = &A->item[GMT_ANNOT_LOWER];
3747 			break;
3748 		case 'f':	/* Upper minor tick interval */
3749 			I = &A->item[GMT_TICK_UPPER];
3750 			break;
3751 		case 'F':	/* Lower minor tick interval */
3752 			I = &A->item[GMT_TICK_LOWER];
3753 			break;
3754 		case 'g':	/* Upper gridline interval */
3755 			I = &A->item[GMT_GRID_UPPER];
3756 			break;
3757 		case 'G':	/* Lower gridline interval */
3758 			I = &A->item[GMT_GRID_LOWER];
3759 			break;
3760 		default:	/* Bad flag should never get here */
3761 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad flag (%c) passed to gmtinit_set_titem\n", flag);
3762 			return GMT_NOT_A_VALID_TYPE;
3763 			break;
3764 	}
3765 
3766 	if (phase != 0.0) A->phase = phase;	/* phase must apply to entire axis */
3767 	if (I->active) {
3768 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Axis sub-item %c set more than once (typo?)\n", flag);
3769 		return (GMT_NOERROR);
3770 	}
3771 	if (unit == 'c' || unit == 'C') {
3772 		if (gmt_M_compat_check (GMT, 4)) {
3773 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Unit c (arcseconds) is deprecated; use s instead.\n");
3774 			unit = 's';
3775 		}
3776 		else {
3777 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Unit %c not recognized.\n", unit);
3778 			return GMT_NOT_A_VALID_TYPE;
3779 		}
3780 	}
3781 	I->type = flag;
3782 	I->unit = unit;
3783 	I->interval = val;
3784 	I->flavor = 0;
3785 	I->active = true;
3786 	if (!custom && in[0] && val == 0.0) I->active = false;
3787 	I->upper_case = false;
3788 	format = (GMT->current.map.frame.primary) ? GMT->current.setting.format_time[GMT_PRIMARY] : GMT->current.setting.format_time[GMT_SECONDARY];
3789 	gmtlib_set_case_and_kind (GMT, format, &(I->upper_case), &(I->flavor));
3790 	if (axis == 'z')
3791 		GMT->current.map.frame.drawz = true;
3792 	else
3793 		GMT->current.map.frame.draw = true;
3794 
3795 	return (GMT_NOERROR);
3796 }
3797 
3798 /*! Decode the annot/tick segments of the clean -B string pieces */
gmtinit_decode_tinfo(struct GMT_CTRL * GMT,int axis,char flag,char * in,struct GMT_PLOT_AXIS * A)3799 GMT_LOCAL int gmtinit_decode_tinfo (struct GMT_CTRL *GMT, int axis, char flag, char *in, struct GMT_PLOT_AXIS *A) {
3800 	int error = GMT_NOERROR;
3801 	char *str = "xyz";
3802 
3803 	if (!in) return (GMT_NOERROR);	/* NULL pointer passed */
3804 
3805 	if (flag == 'c') {	/* Custom annotation arrangement */
3806 		int k, n_int[GMT_N_AXIS_ITEMS];
3807 		char *list = "aifg";
3808 		if (!(gmt_access (GMT, &in[1], R_OK))) {
3809 			gmt_M_str_free (A->file_custom);
3810 			A->file_custom = strdup (&in[1]);
3811 			A->special = GMT_CUSTOM;
3812 			if (gmtinit_init_custom_annot (GMT, A, n_int)) return (GMT_NOTSET);	/* See what ticks, anots, gridlines etc are requested */
3813 			for (k = 0; k < GMT_N_AXIS_ITEMS; k++) {
3814 				if (n_int[k] == 0) continue;
3815 				flag = list[k];
3816 				if (!GMT->current.map.frame.primary) flag = (char)toupper ((int)flag);
3817 				if ((error = gmtinit_set_titem (GMT, A, "0", flag, str[axis], true)))	/* Store the findings for this segment */
3818 					return (error);
3819 			}
3820 			if (n_int[GMT_ITEM_ANNOT])  A->item[GMT_ANNOT_UPPER].special = true;	/* custom annotations selected */
3821 			if (n_int[GMT_ITEM_INTVAL]) A->item[GMT_ANNOT_UPPER+!GMT->current.map.frame.primary].special = true;	/* custom interval annotations selected */
3822 			if (n_int[GMT_ITEM_TICK])   A->item[GMT_TICK_UPPER+!GMT->current.map.frame.primary].special = true;	/* custom tick annotations selected */
3823 			if (n_int[GMT_ITEM_GRID])   A->item[GMT_GRID_UPPER+!GMT->current.map.frame.primary].special = true;	/* custom gridlines selected */
3824 			if (axis == GMT_Z)
3825 				GMT->current.map.frame.drawz = true;
3826 			else
3827 				GMT->current.map.frame.draw = true;
3828 		}
3829 		else
3830 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot access custom file in -B string %c-component %s\n", str[axis], &in[1]);
3831 	}
3832 	else
3833 		error = gmtinit_set_titem (GMT, A, in, flag, str[axis], false);
3834 
3835 	return (error);
3836 }
3837 
3838 /*! . */
gmtinit_parse4_B_option(struct GMT_CTRL * GMT,char * in)3839 GMT_LOCAL int gmtinit_parse4_B_option (struct GMT_CTRL *GMT, char *in) {
3840 	/* gmtinit_parse4_B_option scans an argument string and extract parameters that
3841 	 * set the interval for tickmarks and annotations on the boundary.
3842 	 * The string must be continuous, i.e. no whitespace must be present
3843 	 * The string may have 1, 2,  or 3 parts, separated by a slash '/'. All
3844 	 * info after the first slash are assigned to the y-axis.  Info after
3845 	 * the second slash are assigned to the z-axis.  If there is no
3846 	 * slash, x-values are copied to y-values.
3847 	 * A substring looks like [t][value][m|s]. The [t] and [m|s] are optional
3848 	 * ([ and ] are NOT part of the string and are just used to clarify)
3849 	 * [t] can be any of [a](annotation int), [f](frame int), or [g](gridline int).
3850 	 * Default is a AND f. The [m], if present indicates value is in minutes.
3851 	 * The [s], if present indicates value is in seconds (s also is used for south...).
3852 	 * Text between : and : are labels for the respective axes. If the first
3853 	 * character of the text is a period, then the rest of the text is used
3854 	 * as the plot title.  If it is a comma, then the rest is used as annotation unit.
3855 	 * For GMT_LINEAR axes: If the first characters in args are one or more of w,e,s,n
3856 	 * only those axes will be drawn. Upper case letters means the chosen axes
3857 	 * also will be annotated. Default is all 4 axes drawn/annotated.
3858 	 * For logscale plots: l will cause log10(x) to be plotted
3859 	 *			p will cause 10 ^ log10(x) to be plotted
3860 	 *	annot/tick/grid interval can here be either:
3861 	 *		1.0	-> Only powers of 10 are annotated
3862 	 *		2.0	-> powers of 10 times (1, 2, 5) are annotated
3863 	 *		3.0	-> powers of 10 times (1,2,3,..9) are annotated
3864 	 *
3865 	 * Up to two -B options may be given on the command line:
3866 	 *	-B[p] the primary specifications
3867 	 *	-Bs   the secondary specifications
3868 	 *
3869 	 *	-Bs must be in addition to -B[p].
3870 	 */
3871 
3872 	char out1[GMT_BUFSIZ] = "", out2[GMT_BUFSIZ] = "", out3[GMT_BUFSIZ] = "", info[3][GMT_BUFSIZ] = {""};
3873 	struct GMT_PLOT_AXIS *A = NULL;
3874 	int i, j, k, ignore, g = 0, o = 0, part = 0, error = 0;
3875 
3876 	if (!in || !in[0]) return (GMT_PARSE_ERROR);	/* -B requires an argument */
3877 
3878 	switch (in[0]) {
3879 		case 's':
3880 			GMT->current.map.frame.primary = false; k = part = 1; break;
3881 		case 'p':
3882 			GMT->current.map.frame.primary = true; k = 1; break;
3883 		default:
3884 			GMT->current.map.frame.primary = true; k = 0; break;
3885 	}
3886 	i = (GMT->current.map.frame.primary) ? 0 : 1;
3887 	strncpy (GMT->common.B.string[i], in, GMT_LEN256-1);	/* Keep a copy of the actual option(s) */
3888 
3889 	/* GMT->current.map.frame.side[] may be set already when parsing gmt.conf flags */
3890 
3891 	if (!GMT->current.map.frame.init) {	/* First time we initialize stuff */
3892 		for (i = 0; i < 3; i++) {
3893 			gmt_M_memset (&GMT->current.map.frame.axis[i], 1, struct GMT_PLOT_AXIS);
3894 			GMT->current.map.frame.axis[i].id = i;
3895 			for (j = 0; j < 6; j++) GMT->current.map.frame.axis[i].item[j].parent = i;
3896 			if (GMT->current.proj.xyz_projection[i] == GMT_TIME) GMT->current.map.frame.axis[i].type = GMT_TIME;
3897 		}
3898 		GMT->current.map.frame.header[0] = GMT->current.map.frame.sub_header[0] = '\0';
3899 		GMT->current.map.frame.init = true;
3900 		GMT->current.map.frame.draw = false;
3901 		GMT->current.map.frame.set_frame[GMT_PRIMARY] = GMT->current.map.frame.set_frame[GMT_SECONDARY] = 0;
3902 	}
3903 
3904 #ifdef _WIN32
3905 	gmtinit_handle_dosfile (GMT, in, 0);	/* Temporarily replace DOS files like X:/ with X;/ to avoid colon trouble */
3906 #endif
3907 
3908 	for (i = (int)strlen(in) - 1, ignore = false; !GMT->current.map.frame.paint[GMT_Z] && !error && i >= 0; i--) {	/** Look for +g<fill */
3909 		if (in[i] == ':') ignore = !ignore;
3910 		if (ignore) continue;	/* Not look inside text items */
3911 		if (in[i] == '+' && in[i+1] == 'o') {	/* Found +o<plon>/<plat> */
3912 			double lon, lat;
3913 			char A[GMT_LEN64] = {""}, B[GMT_LEN64] = {""};
3914 			if (GMT->current.proj.projection_GMT == GMT_OBLIQUE_MERC) {
3915 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Cannot specify oblique gridlines for the oblique Mercator projection\n");
3916 				error++;
3917 			}
3918 			GMT->current.map.frame.obl_grid = true;
3919 			if (sscanf (&in[i+2], "%[^/]/%[^+]", A, B) != 2) {
3920 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Did not find the expected format +o<plon>/<plat>\n");
3921 				error++;
3922 			}
3923 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, A, GMT_IS_LON, &lon), A);
3924 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, B, GMT_IS_LAT, &lat), B);
3925 			if (GMT->current.proj.projection_GMT != GMT_OBLIQUE_MERC) gmtlib_set_oblique_pole_and_origin (GMT, lon, lat, 0.0, 0.0);
3926 			o = i;
3927 			in[o] = '\0';	/* Chop off +o for now */
3928 		}
3929 		if (in[i] == '+' && in[i+1] == 'g') {	/* Found +g<fill> */
3930 			strncpy (out1, &in[i+2], GMT_BUFSIZ-1);	/* Make a copy of the fill argument */
3931 #ifdef _WIN32
3932 			gmtinit_handle_dosfile (GMT, out1, 1);	/* Undo any DOS files like X;/ back to X:/ */
3933 #endif
3934 			if (gmt_getfill (GMT, out1, &GMT->current.map.frame.fill[GMT_Z])) error++;
3935 			if (!error) {
3936 				GMT->current.map.frame.paint[GMT_Z] = true;
3937 				g = i;
3938 				in[g] = '\0';	/* Chop off +g for now */
3939 			}
3940 		}
3941 	}
3942 	/* Note that gmtinit_strip_colonitem calls gmtinit_handle_dosfile so that the item return has been processed for DOS path restoration */
3943 	error += gmtinit_strip_colonitem (GMT, 0, &in[k], ":.", GMT->current.map.frame.header, out1);	/* Extract header string, if any */
3944 	gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.header, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
3945 
3946 	i = gmtinit_decode4_wesnz (GMT, out1, GMT->current.map.frame.side, &GMT->current.map.frame.draw_box, part);		/* Decode WESNZwesnz+ flags, if any */
3947 	out1[i] = '\0';	/* Strip the WESNZwesnz+ flags off */
3948 
3949 	gmtinit_split_info_strings (GMT, out1, info[0], info[1], info[2]);	/* Chop/copy the three axis strings */
3950 
3951 	for (i = 0; i < 3; i++) {	/* Process each axis separately */
3952 
3953 		if (!info[i][0]) continue;	 /* Skip empty format string */
3954 		if (info[i][0] == '0' && !info[i][1]) {	 /* Skip format '0' */
3955 			if (i == GMT_Z)
3956 				GMT->current.map.frame.drawz = true;
3957 			else
3958 				GMT->current.map.frame.draw = true;
3959 			continue;
3960 		}
3961 
3962 		gmtinit_handle_atcolon (GMT, info[i], 0);	/* Temporarily modify text escape @: to @^ to avoid : parsing trouble */
3963 		gmtlib_enforce_rgb_triplets (GMT, info[i], GMT_BUFSIZ);				/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
3964 		error += gmtinit_strip_colonitem (GMT, i, info[i], ":,", GMT->current.map.frame.axis[i].unit, out1);	/* Pull out annotation unit, if any */
3965 		error += gmtinit_strip_colonitem (GMT, i, out1, ":=", GMT->current.map.frame.axis[i].prefix, out2);	/* Pull out annotation prefix, if any */
3966 		error += gmtinit_strip_colonitem (GMT, i, out2, ":", GMT->current.map.frame.axis[i].label, out3);	/* Pull out axis label, if any */
3967 		gmtinit_handle_atcolon (GMT, GMT->current.map.frame.axis[i].label, 1);	/* Restore any @^ to @: */
3968 		gmtinit_handle_atcolon (GMT, GMT->current.map.frame.axis[i].prefix, 1);	/* Restore any @^ to @: */
3969 		gmtinit_handle_atcolon (GMT, GMT->current.map.frame.axis[i].unit, 1);	/* Restore any @^ to @: */
3970 
3971 		if (GMT->current.map.frame.axis[i].prefix[0]) {	/* Deal with space/no space before prefix */
3972 			char workspace[GMT_LEN64] = {""};
3973 			if (GMT->current.map.frame.axis[i].prefix[0] == '-') /* Don't want a space */
3974 				strncpy (workspace, &GMT->current.map.frame.axis[i].prefix[1], GMT_LEN64-1);
3975 			else {	/* Want a space */
3976 				workspace[0] = ' ';	/* The leading space */
3977 				strncpy (&workspace[1], GMT->current.map.frame.axis[i].prefix, GMT_LEN64-1);
3978 			}
3979 			gmt_M_memcpy (GMT->current.map.frame.axis[i].prefix, workspace, GMT_LEN64, char);
3980 		}
3981 		if (GMT->current.map.frame.axis[i].unit[0]) {	/* Deal with space/no space before unit */
3982 			char workspace[GMT_LEN64] = {""};
3983 			if (GMT->current.map.frame.axis[i].unit[0] == '-') /* Don't want a space */
3984 				strncpy (workspace, &GMT->current.map.frame.axis[i].unit[1], GMT_LEN64-1);
3985 			else {	/* Want a space */
3986 				workspace[0] = ' ';	/* The leading space */
3987 				strncpy (&workspace[1], GMT->current.map.frame.axis[i].unit, GMT_LEN64-1);
3988 			}
3989 			gmt_M_memcpy (GMT->current.map.frame.axis[i].unit, workspace, GMT_LEN64, char);
3990 		}
3991 
3992 		if (out3[0] == '\0') continue;	/* No intervals */
3993 		GMT->current.map.frame.set[i] = true;	/* Got here so we are setting x/y/z intervals */
3994 
3995 		/* Parse the annotation/tick info string */
3996 		if (out3[0] == 'c')
3997 			error += gmtinit_decode_tinfo (GMT, i, 'c', out3, &GMT->current.map.frame.axis[i]);
3998 		else {	/* Parse from back for 'a', 'f', 'g' chunks */
3999 			for (k = (int)strlen (out3) - 1; k >= 0; k--) {
4000 				if (out3[k] == 'a' || out3[k] == 'f' || out3[k] == 'g') {
4001 					error += gmtinit_decode_tinfo (GMT, i, out3[k], &out3[k+1], &GMT->current.map.frame.axis[i]);
4002 					out3[k] = '\0';	/* Done with this chunk; replace with terminator */
4003 				}
4004 				else if (k == 0)	/* If no [a|f|g] then it is implicitly 'a' */
4005 					error += gmtinit_decode_tinfo (GMT, i, 'a', out3, &GMT->current.map.frame.axis[i]);
4006 			}
4007 		}
4008 
4009 		/* Make sure we have ticks to match annotation stride */
4010 		A = &GMT->current.map.frame.axis[i];
4011 		if (A->item[GMT_ANNOT_UPPER].active && !A->item[GMT_TICK_UPPER].active)	/* Set frame ticks = annot stride */
4012 			gmt_M_memcpy (&A->item[GMT_TICK_UPPER], &A->item[GMT_ANNOT_UPPER], 1, struct GMT_PLOT_AXIS_ITEM);
4013 		if (A->item[GMT_ANNOT_LOWER].active && !A->item[GMT_TICK_LOWER].active)	/* Set frame ticks = annot stride */
4014 			gmt_M_memcpy (&A->item[GMT_TICK_LOWER], &A->item[GMT_ANNOT_LOWER], 1, struct GMT_PLOT_AXIS_ITEM);
4015 		/* Note that item[].type will say 'a', 'A', 'i' or 'I' in these cases, so we know when minor ticks were not set */
4016 	}
4017 
4018 	/* Check if we asked for linear projections of geographic coordinates and did not specify a unit - if so set degree symbol as unit */
4019 	if (GMT->current.proj.projection_GMT == GMT_LINEAR && GMT->current.setting.map_degree_symbol != gmt_none) {
4020 		for (i = 0; i < 2; i++) {
4021 			if (gmt_M_type (GMT, GMT_IN, i) & GMT_IS_GEO && GMT->current.map.frame.axis[i].unit[0] == 0) {
4022 				GMT->current.map.frame.axis[i].unit[0] = '-';
4023 				GMT->current.map.frame.axis[i].unit[1] = (char)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol];
4024 				GMT->current.map.frame.axis[i].unit[2] = '\0';
4025 			}
4026 		}
4027 	}
4028 	if (g) in[g] = '+';	/* Restore + */
4029 
4030 	return (error);
4031 }
4032 
4033 /* New GMT5 functions for parsing new -B syntax */
4034 
4035 /*! . */
gmt_handle5_plussign(struct GMT_CTRL * GMT,char * in,char * mods,unsigned way)4036 void gmt_handle5_plussign (struct GMT_CTRL *GMT, char *in, char *mods, unsigned way) {
4037 	/* Way = 0: replace any +<letter> with <letter> NOT in <mods> with ASCII 1<letter>
4038 	 *	    We only skip +<letter> the first time it is found (if in <mods>).
4039 	 * Way = 1: Replace ASCII 1 with + */
4040 	gmt_M_unused(GMT);
4041 	if (in == NULL || in[0] == '\0') return;	/* No string to check */
4042 	if (way == 0) {	/* Replace any +<letter> with <letter> NOT in <mods> with ASCII 1<letter> */
4043 		size_t n = (mods) ? strlen (mods) : 0;	/* Since mods may be NULL */
4044 		char *c = in, *p = NULL;
4045 		unsigned int *used = gmt_M_memory (GMT, NULL, n, unsigned int);
4046 		for ( ;; ) { /* Replace super-script escape sequence @+ with @1 */
4047 			c = strstr (c, "@+");
4048 			if (c == NULL) break;
4049 			++c;
4050 			*c = 1;
4051 		}
4052 		c = in;
4053 		for ( ;; ) { /* Now look for +<letter> */
4054 			c = strchr (c, '+');	/* Find next '+' */
4055 			if (c == NULL) break;	/* No more + found */
4056 			p = NULL;
4057 			if (c[1] && (p = strchr (mods, c[1]))) {	/* Any of ours */
4058 				unsigned int k = (p - mods);
4059 				if (used[k]) p = NULL;
4060 				else used[k]++;
4061 			}
4062 			if (!p) /* Not one of the +<mods> cases (or already fixed) so we can replace the + by 1 */
4063 				*c = 1;
4064 			++c;
4065 		}
4066 		gmt_M_free (GMT, used);
4067 	}
4068 	else /* way != 0: Replace single ASCII 1 with + */
4069 		gmt_strrepc (in, 1, '+');
4070 }
4071 
gmtinit_sides2axes(struct GMT_CTRL * GMT)4072 GMT_LOCAL void gmtinit_sides2axes (struct GMT_CTRL *GMT) {
4073 	/* Convert GMT->current.map.frame.side to corresponding MAP_FRAME_AXES string */
4074 	unsigned int k, i = 0;
4075 	char *all = {"WESNZ"}, *tick = {"wesnz"}, *draw = {"lrbtu"};
4076 	for (k = 0; k <= Z_SIDE; k++) {
4077 		if (GMT->current.map.frame.side[k] & GMT_AXIS_ALL)
4078 			GMT->current.setting.map_frame_axes[i++] = all[k];
4079 		else if (GMT->current.map.frame.side[k] & GMT_AXIS_BARB)
4080 			GMT->current.setting.map_frame_axes[i++] = tick[k];
4081 		else if (GMT->current.map.frame.side[k] & GMT_AXIS_DRAW)
4082 			GMT->current.setting.map_frame_axes[i++] = draw[k];
4083 	}
4084 	GMT->current.setting.map_frame_axes[i] = '\0';
4085 }
4086 
4087 /*! Scans the WESNZ[1234]wesnz[1234]lrbtu flags and sets the side/drawbox parameters
4088  * and returns the length of the remaining string.
4089  */
gmtinit_decode5_wesnz(struct GMT_CTRL * GMT,const char * in,bool check)4090 GMT_LOCAL int gmtinit_decode5_wesnz (struct GMT_CTRL *GMT, const char *in, bool check) {
4091 
4092 	unsigned int k, error = 0, f_side[5] = {0, 0, 0, 0, 0}, z_axis[4] = {0, 0, 0, 0};
4093 	bool s_given = false;
4094 
4095 	if (check) {	/* true if coming via -B, false if parsing gmt.conf */
4096 		GMT->current.map.frame.set_frame[GMT_PRIMARY]++, GMT->current.map.frame.set_frame[GMT_SECONDARY]++;
4097 		if (GMT->current.map.frame.set_frame[GMT_PRIMARY] > 1 || GMT->current.map.frame.set_frame[GMT_SECONDARY] > 1) {
4098 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -B: <WESNZ-framesettings> given more than once!\n");
4099 			return (1);
4100 		}
4101 	}
4102 	else {
4103 		GMT->current.map.frame.draw_box = GMT_3D_NONE;
4104 		if (!strcmp (GMT->current.setting.map_frame_axes, "auto")) return GMT_NOERROR;	/* Not ready to parse yet */
4105 	}
4106 	for (k = 0; in[k]; k++) {
4107 		switch (in[k]) {
4108 			/* Draw, Annotate, and Tick */
4109 			case 'W': f_side[W_SIDE] |= GMT_AXIS_ALL; s_given = true; break;
4110 			case 'E': f_side[E_SIDE] |= GMT_AXIS_ALL; s_given = true; break;
4111 			case 'S': f_side[S_SIDE] |= GMT_AXIS_ALL; s_given = true; break;
4112 			case 'N': f_side[N_SIDE] |= GMT_AXIS_ALL; s_given = true; break;
4113 			case 'Z': f_side[Z_SIDE] |= GMT_AXIS_ALL; s_given = true; break;
4114 			/* Just Draw and Tick */
4115 			case 'w': f_side[W_SIDE] |= GMT_AXIS_BARB; s_given = true; break;
4116 			case 'e': f_side[E_SIDE] |= GMT_AXIS_BARB; s_given = true; break;
4117 			case 's': f_side[S_SIDE] |= GMT_AXIS_BARB; s_given = true; break;
4118 			case 'n': f_side[N_SIDE] |= GMT_AXIS_BARB; s_given = true; break;
4119 			case 'z': f_side[Z_SIDE] |= GMT_AXIS_BARB; s_given = true; break;
4120 			/* Just Draw */
4121 			case 'l': f_side[W_SIDE] |= GMT_AXIS_DRAW; s_given = true; break;
4122 			case 'r': f_side[E_SIDE] |= GMT_AXIS_DRAW; s_given = true; break;
4123 			case 'b': f_side[S_SIDE] |= GMT_AXIS_DRAW; s_given = true; break;
4124 			case 't': f_side[N_SIDE] |= GMT_AXIS_DRAW; s_given = true; break;
4125 			case 'u': f_side[Z_SIDE] |= GMT_AXIS_DRAW; s_given = true; break;
4126 			/* Draw 3-D box */
4127 			case '+':
4128 				if (in[k+1] == 'b')	/* Got +b appended to MAP_FRAME_AXES, possibly */
4129 					GMT->current.map.frame.draw_box |= GMT_3D_BOX;
4130 				else if (in[k+1] == 'w')	/* Got +w appended to MAP_FRAME_AXES, possibly */
4131 					GMT->current.map.frame.draw_box |= GMT_3D_WALL;
4132 				else if (in[k+1] == 'n')	/* Got +n appended to MAP_FRAME_AXES, means no frame nor annotations desired */
4133 					GMT->current.map.frame.no_frame = true;
4134 				else if (gmt_M_compat_check (GMT, 4)) {
4135 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Modifier + in MAP_FRAME_AXES is deprecated; use +b instead.\n");
4136 					GMT->current.map.frame.draw_box |= GMT_3D_BOX;
4137 				}
4138 				else {
4139 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Modifier + in MAP_FRAME_AXES not recognized.\n");
4140 					error++;
4141 				}
4142 				break;
4143 			case '1': if (f_side[Z_SIDE]) z_axis[0] = 1; else error++; break;
4144 			case '2': if (f_side[Z_SIDE]) z_axis[1] = 1; else error++; break;
4145 			case '3': if (f_side[Z_SIDE]) z_axis[2] = 1; else error++; break;
4146 			case '4': if (f_side[Z_SIDE]) z_axis[3] = 1; else error++; break;
4147 			default:
4148 				error++;
4149 		}
4150 	}
4151 	if (s_given) {
4152 		gmt_M_memcpy (GMT->current.map.frame.side, f_side, 5, unsigned int);	/* Overwrite the GMT defaults */
4153 		GMT->current.map.frame.no_frame = false;
4154 		GMT->current.map.frame.draw = true;
4155 		if (check && f_side[Z_SIDE]) GMT->current.map.frame.drawz = true;
4156 		if (check) /* Update MAP_FRAME_AXES from sides */
4157 			gmtinit_sides2axes (GMT);
4158 	}
4159 	if (GMT->current.map.frame.no_frame) gmt_M_memset (GMT->current.map.frame.side, 5, unsigned int);	/* Set all to nothing */
4160 	if (z_axis[0] || z_axis[1] || z_axis[2] || z_axis[3]) gmt_M_memcpy (GMT->current.map.frame.z_axis, z_axis, 4, unsigned int);	/* Overwrite the GMT defaults */
4161 	return (error);
4162 }
4163 
gmtlib_B_is_frame(struct GMT_CTRL * GMT,char * in)4164 bool gmtlib_B_is_frame (struct GMT_CTRL *GMT, char *in) {
4165 	gmt_M_unused (GMT);
4166 	if (strstr (in, "+b")) return true;	/* Found a +b so likely frame */
4167 	if (strstr (in, "+g")) return true;	/* Found a +g so likely frame */
4168 	if (strstr (in, "+i")) return true;	/* Found a +i so likely frame */
4169 	if (strstr (in, "+n")) return true;	/* Found a +n so likely frame */
4170 	if (strstr (in, "+o")) return true;	/* Found a +o so likely frame */
4171 	if (strstr (in, "+t")) return true;	/* Found a +t so likely frame */
4172 	if (strstr (in, "+w")) return true;	/* Found a +w so likely frame */
4173 	if (strstr (in, "+x")) return true;	/* Found a +x so likely frame */
4174 	if (strstr (in, "+y")) return true;	/* Found a +y so likely frame */
4175 	if (strstr (in, "+z")) return true;	/* Found a +z so likely frame */
4176 	if (strstr (in, "+a")) return false;	/* Found a +a so likely axis */
4177 	if (strstr (in, "+f")) return false;	/* Found a +f so likely axis */
4178 	if (strstr (in, "+l")) return false;	/* Found a +l so likely axis */
4179 	if (strstr (in, "+L")) return false;	/* Found a +L so likely axis */
4180 	if (strstr (in, "+p")) return false;	/* Found a +p so likely axis */
4181 	if (strstr (in, "+s")) return false;	/* Found a +s so likely axis */
4182 	if (strstr (in, "+S")) return false;	/* Found a +S so likely axis */
4183 	if (strstr (in, "+u")) return false;	/* Found a +u so likely axis */
4184 	if (in[0] != 'z' && strchr ("WESNZwenzlrbtu", in[0])) return true;	/* Found one of the side specifiers so likely frame (check on z since -Bzaf could trick it) */
4185 	if (in[0] == 's' && (in[1] == 0 || strchr ("WESNZwenzlrbtu", in[1]) != NULL)) return true;	/* Found -Bs (just draw south axis) or -Bs<another axis flag> */
4186 	if (in[0] == 'z' && (in[1] == 0 || strchr ("WESNwenlrbtu", in[1]) != NULL)) return true;	/* Found -Bz in frame context, e.g. -Bzwn */
4187 	return false;	/* Cannot be frame */
4188 }
4189 
4190 /*! . */
gmtinit_parse5_B_frame_setting(struct GMT_CTRL * GMT,char * in)4191 GMT_LOCAL int gmtinit_parse5_B_frame_setting (struct GMT_CTRL *GMT, char *in) {
4192 	bool did_g = false, sub = false, title = false;
4193 	unsigned int pos = 0, k, error = 0;
4194 	char p[GMT_BUFSIZ] = {""}, text[GMT_BUFSIZ] = {""}, *mod = NULL;
4195 	double pole[2];
4196 	struct GMT_FILL F;
4197 
4198 	/* Parsing of -B<framesettings>: -B[<axes>][+b][+g<fill>][+n][+o<lon>/<lat>][+s<subtitle>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>] */
4199 
4200 	/* First determine that the given -B<in> string is indeed the framesetting option.  If not return -1 */
4201 
4202 	if (strchr ("pxy", in[0])) return (GMT_NOTSET);	/* -B[p[xy] is definitively not the frame settings (-Bz and -Bs are tricker; see below) */
4203 	if (strchr ("afg", in[0])) return (GMT_NOTSET);	/* -Ba|f|g is definitively not the frame settings; looks like axes settings */
4204 	if (!gmtlib_B_is_frame (GMT, in)) return (GMT_NOTSET);		/* No, nothing matched */
4205 
4206 	/* OK, here we are pretty sure this is a frame -B statement */
4207 
4208 	strncpy (text, in, GMT_BUFSIZ-1);
4209 	gmt_handle5_plussign (GMT, text, "bginostwxyz", 0);	/* Temporarily change double plus-signs to double ASCII 1 to avoid +<modifier> angst */
4210 	GMT->current.map.frame.header[0] = GMT->current.map.frame.sub_header[0] = '\0';
4211 	gmt_M_memset (GMT->current.map.frame.paint, 3U, bool);
4212 	GMT->current.map.frame.draw_box = GMT_3D_NONE;
4213 
4214 	if ((mod = strchr (text, '+'))) {	/* Find start of modifiers, if any */
4215 		while ((gmt_strtok (mod, "+", &pos, p))) {	/* Parse any +<modifier> statements */
4216 			switch (p[0]) {
4217 				case 'b':	/* Draw 3-D box */
4218 					GMT->current.map.frame.draw_box |= (GMT_3D_BOX|GMT_3D_WALL);
4219 					break;
4220 				case 'g':	/* Paint the basemap x-y plane and for 3-D back walls */
4221 					if (p[1] == 0 || gmt_getfill (GMT, &p[1], &F)) {
4222 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +g<fill> argument %s\n", &p[1]);
4223 						error++;
4224 					}
4225 					did_g = true;
4226 					break;
4227 				case 'i':	/* Turn on internal annotation for radial or longitudinal axes when there is no outside place to annotate */
4228 					GMT->current.map.frame.internal_annot = 1;	/* Longitude/angle */
4229 					if (GMT->current.proj.projection == GMT_POLAR)	/* Optional argument is an angle, not longitude, so atof will do */
4230 						GMT->current.map.frame.internal_arg = (p[1]) ? atof (&p[1]) : GMT->common.R.wesn[XLO];
4231 					else if (gmt_M_is_azimuthal (GMT)) {	/* Optional argument is a longitude */
4232 						if (p[1])	/* Giving a specific meridian along which to annotate latitudes or radii */
4233 							error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, &p[1], GMT_IS_LON, &GMT->current.map.frame.internal_arg), &p[1]);
4234 						else	/* Default to west */
4235 							GMT->current.map.frame.internal_arg = GMT->common.R.wesn[XLO];
4236 					}
4237 					else if (gmt_M_is_misc (GMT) && gmt_M_pole_is_point (GMT) && gmt_M_180_range (GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI])) {
4238 						GMT->current.map.frame.internal_annot = 2;	/* Want longitude annotations along a parallel */
4239 						if (p[1])	/* Giving a specific latitude */
4240 							error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, &p[1], GMT_IS_LAT, &GMT->current.map.frame.internal_arg), &p[1]);
4241 						else	/* Default to Equator */
4242 							GMT->current.map.frame.internal_arg = 0.0;
4243 					}
4244 					else {
4245 						GMT_Report (GMT->parent, GMT_MSG_ERROR,
4246 							"Option -B: Cannot specify internal annotation with +i for this projection and region selection\n");
4247 						error++;
4248 					}
4249 					break;
4250 				case 'n':	/* Turn off frame entirely; this is also done in gmtinit_decode5_wesnz */
4251 					GMT->current.map.frame.no_frame = true;
4252 					break;
4253 				case 'o':	/* Specify pole for oblique gridlines */
4254 					if (GMT->current.proj.projection_GMT == GMT_OBLIQUE_MERC) {
4255 						GMT_Report (GMT->parent, GMT_MSG_ERROR,
4256 							"Option -B: Cannot specify oblique gridlines for the oblique Mercator projection\n");
4257 						error++;
4258 					}
4259 					else if (!p[1] || (k = GMT_Get_Values (GMT->parent, &p[1], pole, 2)) != 2) {
4260 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Bad +o[<plon>/<plat>] argument %s\n", &p[1]);
4261 						error++;
4262 					}
4263 					else {	/* Successful parsing of pole */
4264 						GMT->current.map.frame.obl_grid = true;
4265 						gmtlib_set_oblique_pole_and_origin (GMT, pole[GMT_X], pole[GMT_Y], 0.0, 0.0);
4266 					}
4267 					break;
4268 				case 's':
4269 					if (p[1]) {	/* Actual subtitle was appended */
4270 						strncpy (GMT->current.map.frame.sub_header, &p[1], GMT_LEN256-1);
4271 						gmt_handle5_plussign (GMT, GMT->current.map.frame.sub_header, NULL, 1);	/* Recover any non-modifier plus signs */
4272 						gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.sub_header, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4273 						sub = true;
4274 					}
4275 					break;
4276 				case 't':
4277 					if (p[1]) {	/* Actual title was appended */
4278 						strncpy (GMT->current.map.frame.header, &p[1], GMT_LEN256-1);
4279 						gmt_handle5_plussign (GMT, GMT->current.map.frame.header, NULL, 1);	/* Recover any non-modifier plus signs */
4280 						gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.header, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4281 						title = true;
4282 					}
4283 					break;
4284 				case 'w':	/* Set back-wall outline and optionally the pen to use */
4285 					GMT->current.map.frame.draw_box |= GMT_3D_WALL;
4286 					GMT->current.map.frame.draw_wall = true;	/* We will draw the outline of the walls */
4287 					if (p[1] && gmt_getpen (GMT, &p[1], &GMT->current.map.frame.pen)) {
4288 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +w<pen> argument %s\n", &p[1]);
4289 						error++;
4290 					}
4291 					else if (p[1] == 0)	/* Use grid pen as default */
4292 						gmt_M_memcpy (&GMT->current.map.frame.pen, &GMT->current.setting.map_grid_pen[GMT_PRIMARY], 1, struct GMT_PEN);
4293 					break;
4294 				case 'x':	/* Paint the basemap yz-plane for 3-D plots */
4295 					if (p[1] == 0 || gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_Y])) {
4296 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +x<fill> argument %s (or possibly missing)\n", &p[1]);
4297 						error++;
4298 					}
4299 					GMT->current.map.frame.paint[GMT_Y] = true;
4300 					GMT->current.map.frame.draw_box |= GMT_3D_WALL;
4301 					break;
4302 				case 'y':	/* Paint the basemap xz-plane for 3-D plots */
4303 					if (p[1] == 0 || gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_X])) {
4304 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +y<fill> argument %s (or possibly missing)\n", &p[1]);
4305 						error++;
4306 					}
4307 					GMT->current.map.frame.paint[GMT_X] = true;
4308 					GMT->current.map.frame.draw_box |= GMT_3D_WALL;
4309 					break;
4310 				case 'z':	/* Paint the basemap xy-plane for 3-D plots */
4311 					if (p[1] && gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_Z])) {
4312 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +z<fill> argument %s\n", &p[1]);
4313 						error++;
4314 					}
4315 					GMT->current.map.frame.paint[GMT_Z] = true;
4316 					GMT->current.map.frame.draw_box |= GMT_3D_WALL;
4317 					break;
4318 				default:
4319 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Unrecognized frame modifier %s\n", p);
4320 					error++;
4321 					break;
4322 			}
4323 		}
4324 		*mod = '\0';	/* Separate the modifiers from the frame selectors */
4325 	}
4326 
4327 	if (sub && !title) {	/* Cannot request subtitle and not set a title */
4328 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot request subtitle (+s) if no title (+t) is set\n");
4329 		error++;
4330 	}
4331 	if (!GMT->current.map.frame.paint[GMT_X] && did_g) {	/* Let +g set all sides not already set */
4332 		gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_X], &F, 1U, struct GMT_FILL);
4333 		GMT->current.map.frame.paint[GMT_X] = true;
4334 	}
4335 	if (!GMT->current.map.frame.paint[GMT_Y] && did_g) {	/* Let +g set all sides not already set */
4336 		gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Y], &F, 1U, struct GMT_FILL);
4337 		GMT->current.map.frame.paint[GMT_Y] = true;
4338 	}
4339 	if (!GMT->current.map.frame.paint[GMT_Z] && did_g) {	/* Let +g set all sides not already set */
4340 		gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Z], &F, 1U, struct GMT_FILL);
4341 		GMT->current.map.frame.paint[GMT_Z] = true;
4342 	}
4343 
4344 	/* Now parse the frame choices, if any */
4345 	error += gmtinit_decode5_wesnz (GMT, text, true);
4346 
4347 	return (error);
4348 }
4349 
gmt_init_B(struct GMT_CTRL * GMT)4350 void gmt_init_B (struct GMT_CTRL *GMT) {
4351 	unsigned int no, k;
4352 	for (no = 0; no < 3; no++) {
4353 		gmt_M_memset (&GMT->current.map.frame.axis[no], 1, struct GMT_PLOT_AXIS);
4354 		GMT->current.map.frame.axis[no].id = no;
4355 		for (k = 0; k < 6; k++) GMT->current.map.frame.axis[no].item[k].parent = no;
4356 		if (GMT->current.proj.xyz_projection[no] == GMT_TIME) GMT->current.map.frame.axis[no].type = GMT_TIME;
4357 	}
4358 	gmt_M_memset (GMT->current.map.frame.paint, 3U, bool);
4359 	GMT->common.B.string[0][0] = GMT->common.B.string[1][0] = '\0';
4360 	GMT->current.map.frame.init = true;
4361 	GMT->current.map.frame.draw = false;
4362 	GMT->current.map.frame.set_frame[GMT_PRIMARY] = GMT->current.map.frame.set_frame[GMT_SECONDARY] = 0;
4363 }
4364 
4365 /*! . */
gmtinit_parse5_B_option(struct GMT_CTRL * GMT,char * in)4366 GMT_LOCAL int gmtinit_parse5_B_option (struct GMT_CTRL *GMT, char *in) {
4367 	/* GMT5 clean version based on new syntax:
4368 	 * Frame settings:
4369 	 *	-B[WESNwesnz|Z[1234]][+b][+g<fill>][+i][+o<lon/lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>]
4370 	 *    		+b draws 3-D box.
4371 	 *    		+g<fill> as plot interior fill and backwalls (if 3-D) [none].
4372 	 *			+i Annotate along parallel or meridian <val> [0] when no such axes can be plotted.
4373 	 *    		+t<title> as plot title [none].
4374 	 *    		+w[<pen>] draws the outline of the xz and yz planes [no outline].
4375 	 *    		of one or more of w,e,s,n,z then only those axes will be drawn.
4376 	 *    		+x, +y, +z paints the back walls for yz, xz, xy [with +g fill if not specified].
4377 	 *		Upper case letters means the chosen axes also will be annotated.
4378 	 *		Default is determined by MAP_FRAME_AXES setting [WESN].
4379 	 * Axis settings:
4380 	 * 	-B[p|s][x|y|z]<info>
4381 	 *   where <info> is of the format
4382 	 * 	<intervals>[+a<angle>|n|p][+e[l|u]][+L|l<label>][+S|s<altlabel>][+p<prefix>][+u<unit>]
4383 	 *   and each <intervals> is a concatenation of one or more [t][value]
4384 	 *    		+a<angle> sets a fixed annotation angle with respect to axis (Cartesian only), n or p for normal or parallel
4385 	 *    		+e[l|u] tells GMT to not plot an annotation at the lower and|or upper end of an axis [both are plotted]
4386 	 *    		+l<label> as labels for the respective axes [none]. Use +L for only horizontal labels
4387 	 *    		+s<altlabel> as alternate label for the far axis [same as <label>]. Use +S for only horizontal labels
4388 	 *    		+u<unit> as as annotation suffix unit [none].
4389 	 *    		+p<prefix> as as annotation prefix unit [none].
4390 	 *
4391 	 * The [t] and [<unit] are optional ([ and ] are NOT part of the string and are
4392 	 * just used to clarify). [t] can be any of [a](annotation int), [f](frame int),
4393 	 * or [g](gridline int).  Default is a AND f.
4394 	 * At the top level, these modifiers are recognized once [repeats are ignored]:
4395 	 * For each axes, these modifies are recognized:
4396 	 * For logscale plots: l will cause log10(x) to be plotted
4397 	 *			p will cause 10 ^ log10(x) to be plotted
4398 	 *	annot/tick/grid interval can here be either:
4399 	 *		1.0	-> Only powers of 10 are annotated
4400 	 *		2.0	-> powers of 10 times (1, 2, 5) are annotated
4401 	 *		3.0	-> powers of 10 times (1,2,3,..9) are annotated
4402 	 *
4403 	 *	-Bs must be in addition to -B[p].
4404 	 */
4405 
4406 	char string[GMT_BUFSIZ] = {""}, orig_string[GMT_BUFSIZ] = {""}, text[GMT_BUFSIZ] = {""}, *mod = NULL, *the_axes = "xyz";
4407 	struct GMT_PLOT_AXIS *A = NULL;
4408 	unsigned int no;
4409 	int k, error = 0;
4410 	bool side[3] = {false, false, false}, implicit = false;
4411 
4412 	if (!in || !in[0]) return (GMT_PARSE_ERROR);	/* -B requires an argument */
4413 
4414 	if (!GMT->current.map.frame.init)	/* First time we initialize stuff */
4415 		gmt_init_B (GMT);
4416 
4417 	if ((error = gmtinit_parse5_B_frame_setting (GMT, in)) >= 0) return (error);	/* Parsed the -B frame settings separately */
4418 	error = 0;	/* Reset since otherwise it is -1 */
4419 
4420 	if (gmt_found_modifier (GMT, in, GMT_FRAME_MODIFIERS)) {
4421 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Found frame setting modifiers (+b|g|i|n|o|t|w|x|y|z) mixed with axes settings!\n");
4422 		return (GMT_PARSE_ERROR);
4423 	}
4424 
4425 	/* Below here are the axis settings only -B[p|s][x|y|z] */
4426 	switch (in[0]) {
4427 		case 's': GMT->current.map.frame.primary = false; k = 1; break;
4428 		case 'p': GMT->current.map.frame.primary = true;  k = 1; break;
4429 		default:  GMT->current.map.frame.primary = true;  k = 0; break;
4430 	}
4431 	no = (GMT->current.map.frame.primary) ? 0 : 1;
4432 	if (GMT->common.B.string[no][0]) {	/* Append this option */
4433 		char group_sep[2] = {" "};
4434 		group_sep[0] = GMT_ASCII_GS;
4435 		strcat (GMT->common.B.string[no], group_sep);
4436 		strncat (GMT->common.B.string[no], in, GMT_LEN256-1);
4437 	}
4438 	else
4439 		strncpy (GMT->common.B.string[no], in, GMT_LEN256-1);	/* Keep a copy of the actual option(s) */
4440 
4441 	/* Set which axes this option applies to */
4442 	while (in[k] && strchr (the_axes, in[k])) {	/* As long as there are leading x,y,z axes specifiers */
4443 		switch (in[k]) {	/* We specified a named axis */
4444 			case 'x': side[GMT_X] = true; break;
4445 			case 'y': side[GMT_Y] = true; break;
4446 			case 'z': side[GMT_Z] = true; break;
4447 		}
4448 		k++;
4449 	}
4450 	if (!(side[GMT_X] || side[GMT_Y] || side[GMT_Z])) GMT->current.map.frame.set_both = side[GMT_X] = side[GMT_Y] = implicit = true;	/* If no axis were named we default to both x and y */
4451 	GMT->current.map.frame.axis[GMT_Z].angle = 0.0;	/* Default is plotting normal to axis for Z, i.e., will look horizontal on the plot */
4452 	GMT->current.map.frame.axis[GMT_Z].use_angle = true;
4453 	strncpy (text, &in[k], GMT_BUFSIZ-1);	/* Make a copy of the input, starting after the leading -B[p|s][xyz] indicators */
4454 	gmt_handle5_plussign (GMT, text, GMT_AXIS_MODIFIERS, 0);	/* Temporarily change any +<letter> except +L|l, +f, +p, +S|s, +u to ASCII 1 to avoid interference with +modifiers */
4455 	k = 0;					/* Start at beginning of text and look for first occurrence of +L|l, +e, +f, +p, +S|s or +u */
4456 	while (text[k] && !(text[k] == '+' && strchr (GMT_AXIS_MODIFIERS, text[k+1]))) k++;
4457 	gmt_M_memset (orig_string, GMT_BUFSIZ, char);
4458 	strncpy (orig_string, text, k);		/* orig_string now has the interval information */
4459 	gmt_handle5_plussign (GMT, orig_string, NULL, 1);	/* Recover any non-modifier plus signs */
4460 	if (text[k]) mod = &text[k];		/* mod points to the start of the modifier information in text*/
4461 	for (no = 0; no < 3; no++) {		/* Process each axis separately */
4462 		if (!side[no]) continue;	/* Except we did not specify this axis */
4463 		if (no == GMT_Z) GMT->current.map.frame.drawz = true;
4464 		if (!text[0]) continue;	 	/* Skip any empty format string */
4465 		if (text[0] == '0' && !text[1]) {	 /* Understand format '0' to mean "no annotation, ticks, or gridlines" */
4466 			GMT->current.map.frame.draw = true;	/* But we do wish to draw the frame */
4467 			if (GMT->common.J.zactive) GMT->current.map.frame.drawz = true;	/* Also brings z-axis into contention */
4468 			GMT->current.setting.map_frame_type = GMT_IS_PLAIN;	/* Since checkerboard without intervals look stupid */
4469 			GMT->current.map.frame.set[no] = true;	/* Since we want this axis drawn */
4470 			continue;
4471 		}
4472 
4473 		if (mod) {	/* Process the given axis modifiers */
4474 			unsigned int pos = 0;
4475 			char p[GMT_BUFSIZ];
4476 			while ((gmt_strtok (mod, "+", &pos, p))) {	/* Parse any +<modifier> statements */
4477 				switch (p[0]) {
4478 					case 'a':	/* Set annotation angle */
4479 						if (gmt_M_is_geographic (GMT, GMT_IN)) {
4480 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Cannot use +a for geographic basemaps\n");
4481 							error++;
4482 							continue;
4483 						}
4484 						if (no == GMT_X) {	/* Variable angles for the x-axis */
4485 							if (p[1] == 'n')	/* +an is short for +a90 or normal to x-axis */
4486 								GMT->current.map.frame.axis[no].angle = 90.0;
4487 							else if (p[1] == 'p')	/* +ap is short for +a0 or parallel to x-axis */
4488 								GMT->current.map.frame.axis[no].angle = 0.0;
4489 							else	/* Assume a variable angle */
4490 								GMT->current.map.frame.axis[no].angle = atof (&p[1]);
4491 						}
4492 						else {	/* Variable angles for the y/z-axis */
4493 							GMT->current.map.frame.axis[no].use_angle = true;
4494 							if (p[1] == 'n')	/* +an is code for normal to y/z-axis;*/
4495 								GMT->current.map.frame.axis[no].angle = 0.0;
4496 							else if (p[1] == 'p')	/* +ap is code for normal to y/z-axis; this triggers ortho=false later */
4497 								GMT->current.map.frame.axis[no].angle = 90.0;
4498 							else	/* Assume a variable angle */
4499 								GMT->current.map.frame.axis[no].angle = atof (&p[1]);
4500 						}
4501 						if (GMT->current.map.frame.axis[no].angle < -90.0 || GMT->current.map.frame.axis[no].angle > 90.0) {
4502 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: +a<angle> must be in the -90 to +90 range\n");
4503 							error++;
4504 						}
4505 						else
4506 							GMT->current.map.frame.axis[no].use_angle = true;
4507 						break;
4508 					case 'f':	/* Select fancy annotations with trailing W|E|S|N */
4509 						if (gmt_M_x_is_lon(GMT,GMT_IN) || gmt_M_y_is_lat(GMT,GMT_IN))
4510 							GMT->current.plot.calclock.geo.wesn = 1;
4511 						else
4512 							GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -B: Cannot use +f for Cartesian axes - modifier ignored\n");
4513 						break;
4514 					case 'e':	/* Turn off end annotations if within distance of axis en */
4515 							switch (p[1]) {	/* See if lower or upper was specified, or default to both */
4516 								case 'l': GMT->current.map.frame.axis[no].skip[0] = true; break;
4517 								case 'u': GMT->current.map.frame.axis[no].skip[1] = true; break;
4518 								case '\0': GMT->current.map.frame.axis[no].skip[0] = GMT->current.map.frame.axis[no].skip[1] = true; break;
4519 								default:
4520 									GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -B: Unrecognized argument to modifier +e (%s). Syntax is +e[l|u].\n", &p[1]);
4521 									error++;
4522 									break;
4523 						}
4524 						break;
4525 					case 'L':	/* Force horizontal axis label */
4526 						GMT->current.map.frame.axis[no].label_mode = 1;
4527 						/* Intentionally fall through - to 'l' */
4528 					case 'l':	/* Axis label */
4529 						if (p[1] == 0) {
4530 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: No axis label given after +l|L\n");
4531 							error++;
4532 						}
4533 						else {
4534 							strncpy (GMT->current.map.frame.axis[no].label, &p[1], GMT_LEN256-1);
4535 							gmt_handle5_plussign (GMT, GMT->current.map.frame.axis[no].label, NULL, 1);	/* Recover any non-modifier plus signs */
4536 							gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.axis[no].label, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4537 						}
4538 						break;
4539 					case 'p':	/* Annotation prefix */
4540 						if (p[1] == 0) {
4541 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: No annotation prefix given after +p\n");
4542 							error++;
4543 						}
4544 						else {
4545 							strncpy (GMT->current.map.frame.axis[no].prefix, &p[1], GMT_LEN64-1);
4546 							gmt_handle5_plussign (GMT, GMT->current.map.frame.axis[no].prefix, NULL, 1);	/* Recover any non-modifier plus signs */
4547 							gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.axis[no].prefix, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4548 						}
4549 						break;
4550 					case 'S':	/* Force horizontal secondary axis label */
4551 						GMT->current.map.frame.axis[no].label_mode = 1;
4552 						/* Intentionally fall through */
4553 					case 's':	/* Axis secondary label (optional) */
4554 						if (p[1] == 0) {
4555 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: No secondary axis label given after +s|S\n");
4556 							error++;
4557 						}
4558 						else {
4559 							strncpy (GMT->current.map.frame.axis[no].secondary_label, &p[1], GMT_LEN256-1);
4560 							gmt_handle5_plussign (GMT, GMT->current.map.frame.axis[no].secondary_label, NULL, 1);	/* Recover any non-modifier plus signs */
4561 							gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.axis[no].secondary_label, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4562 						}
4563 						break;
4564 					case 'u':	/* Annotation unit */
4565 						if (p[1] == 0) {
4566 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: No annotation suffix given after +u\n");
4567 							error++;
4568 						}
4569 						else {
4570 							strncpy (GMT->current.map.frame.axis[no].unit, &p[1], GMT_LEN64-1);
4571 							gmt_handle5_plussign (GMT, GMT->current.map.frame.axis[no].unit, NULL, 1);	/* Recover any non-modifier plus signs */
4572 							gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.axis[no].unit, GMT_LEN256);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
4573 						}
4574 						break;
4575 					default:
4576 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Unrecognized axis modifier %s\n", p);
4577 						error++;
4578 						break;
4579 				}
4580 			}
4581 		}
4582 
4583 		/* Now parse the annotation/tick info string */
4584 
4585 		if (orig_string[0] == '\0') continue;	/* Got nothing */
4586 		GMT->current.map.frame.set[no] = true;	/* Got here so we are setting intervals */
4587 		if (strstr (orig_string, "pi")) GMT->current.map.frame.axis[no].substitute_pi = true;	/* Use pi in formatting labels */
4588 
4589 		gmt_M_memset (string, GMT_BUFSIZ, char);
4590 		strcpy (string, orig_string);	/* Make a copy of string as it gets messed with below */
4591 		if (string[0] == 'c')		/* Special custom annotation information given via file */
4592 			error += gmtinit_decode_tinfo (GMT, no, 'c', string, &GMT->current.map.frame.axis[no]);
4593 		else {				/* Parse from back of string for 'a', 'f', 'g' chunks */
4594 			for (k = (int)strlen (string) - 1; k >= 0; k--) {
4595 				if (string[k] == 'a' || string[k] == 'f' || string[k] == 'g') {
4596 					error += gmtinit_decode_tinfo (GMT, no, string[k], &string[k+1], &GMT->current.map.frame.axis[no]);
4597 					string[k] = '\0';	/* Done with this chunk; replace with terminator */
4598 				}
4599 				else if (k == 0)		/* If no [a|f|g] given then it is implicitly 'a' */
4600 					error += gmtinit_decode_tinfo (GMT, no, 'a', string, &GMT->current.map.frame.axis[no]);
4601 			}
4602 		}
4603 
4604 		/* Make sure we have ticks to match specified annotation stride */
4605 		A = &GMT->current.map.frame.axis[no];
4606 		if (A->item[GMT_ANNOT_UPPER].active && !A->item[GMT_TICK_UPPER].active)	/* Set frame ticks = annot stride */
4607 			gmt_M_memcpy (&A->item[GMT_TICK_UPPER], &A->item[GMT_ANNOT_UPPER], 1, struct GMT_PLOT_AXIS_ITEM);
4608 		if (A->item[GMT_ANNOT_LOWER].active && !A->item[GMT_TICK_LOWER].active)	/* Set frame ticks = annot stride */
4609 			gmt_M_memcpy (&A->item[GMT_TICK_LOWER], &A->item[GMT_ANNOT_LOWER], 1, struct GMT_PLOT_AXIS_ITEM);
4610 		/* Note that item[].type will say 'a', 'A', 'i' or 'I' in these cases, so we know when minor ticks were not set */
4611 	}
4612 
4613 	/* Check if we asked for linear projections of geographic coordinates and did not specify a unit (suffix) - if so set degree symbol as unit */
4614 	if (GMT->current.proj.projection_GMT == GMT_LINEAR && GMT->current.setting.map_degree_symbol != gmt_none) {
4615 		for (no = 0; no < 2; no++) {
4616 			if (gmt_M_type (GMT, GMT_IN, no) & GMT_IS_GEO && GMT->current.map.frame.axis[no].unit[0] == 0) {
4617 				GMT->current.map.frame.axis[no].unit[0] = (char)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol];
4618 				GMT->current.map.frame.axis[no].unit[1] = '\0';
4619 			}
4620 		}
4621 	}
4622 
4623 	return (error);
4624 }
4625 
4626 /*! . */
gmtlib_parse_B_option(struct GMT_CTRL * GMT,char * in)4627 int gmtlib_parse_B_option (struct GMT_CTRL *GMT, char *in) {
4628 	int error = 0;
4629 	if (GMT->common.B.mode == 0) {
4630 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Internal error: Calling gmtlib_parse_B_option before gmt_check_b_options somehow\n");
4631 		error = 1;
4632 	}
4633 	else if (GMT->common.B.mode == -1) {
4634 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Mixing of GMT 4 and 5 level syntax is not possible\n");
4635 		error = 1;
4636 	}
4637 	else if (GMT->common.B.mode == 4) {	/* Got GMT 4 syntax */
4638 		if (gmt_M_compat_check (GMT, 4))
4639 			error = gmtinit_parse4_B_option (GMT, in);
4640 		else {
4641 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Cannot use GMT 4 syntax except in GMT classic with compatibility mode\n");
4642 			error = 1;
4643 		}
4644 	}
4645 	else	/* Clean >= GMT5 syntax */
4646 		error = gmtinit_parse5_B_option (GMT, in);
4647 
4648 	return (error);
4649 }
4650 
4651 /*! . */
gmtinit_project_type(char * args,int * pos,bool * width_given)4652 GMT_LOCAL int gmtinit_project_type (char *args, int *pos, bool *width_given) {
4653 	/* Parse the start of the -J option to determine the projection type.
4654 	 * If the first character of args is uppercase, width_given is set to 1.
4655 	 * Pos returns the position of the first character of the parameters
4656 	 * following the projections type.
4657 	 * The return value is the projection type ID (see gmt_project.h), or
4658 	 * GMT_NO_PROJ when unsuccessful.
4659 	 */
4660 
4661 	unsigned char t;
4662 
4663 	/* Check for upper case */
4664 
4665 	*width_given = (args[0] >= 'A' && args[0] <= 'Z');
4666 
4667 	/* Compared the first part of the -J arguments against a number of Proj4
4668 	   projection names (followed by a slash) or the 1- or 2-letter abbreviation
4669 	   used prior to GMT 4.2.2. Case is ignored */
4670 
4671 	if ((*pos = (int)gmt_strlcmp ("aea/"      , args))) return (GMT_ALBERS);
4672 	if ((*pos = (int)gmt_strlcmp ("aeqd/"     , args))) return (GMT_AZ_EQDIST);
4673 	if ((*pos = (int)gmt_strlcmp ("cyl_stere/", args))) return (GMT_CYL_STEREO);
4674 	if ((*pos = (int)gmt_strlcmp ("cass/"     , args))) return (GMT_CASSINI);
4675 	if ((*pos = (int)gmt_strlcmp ("cea/"      , args))) return (GMT_CYL_EQ);
4676 	if ((*pos = (int)gmt_strlcmp ("eck4/"     , args))) return (GMT_ECKERT4);
4677 	if ((*pos = (int)gmt_strlcmp ("eck6/"     , args))) return (GMT_ECKERT6);
4678 	if ((*pos = (int)gmt_strlcmp ("eqc/"      , args))) return (GMT_CYL_EQDIST);
4679 	if ((*pos = (int)gmt_strlcmp ("eqdc/"     , args))) return (GMT_ECONIC);
4680 	if ((*pos = (int)gmt_strlcmp ("gnom/"     , args))) return (GMT_GNOMONIC);
4681 	if ((*pos = (int)gmt_strlcmp ("hammer/"   , args))) return (GMT_HAMMER);
4682 	if ((*pos = (int)gmt_strlcmp ("laea/"     , args))) return (GMT_LAMB_AZ_EQ);
4683 	if ((*pos = (int)gmt_strlcmp ("lcc/"      , args))) return (GMT_LAMBERT);
4684 	if ((*pos = (int)gmt_strlcmp ("merc/"     , args))) return (GMT_MERCATOR);
4685 	if ((*pos = (int)gmt_strlcmp ("mill/"     , args))) return (GMT_MILLER);
4686 	if ((*pos = (int)gmt_strlcmp ("moll/"     , args))) return (GMT_MOLLWEIDE);
4687 	if ((*pos = (int)gmt_strlcmp ("nsper/"    , args))) return (GMT_GENPER);
4688 	if ((*pos = (int)gmt_strlcmp ("omerc/"    , args))) return (GMT_OBLIQUE_MERC);
4689 	if ((*pos = (int)gmt_strlcmp ("omercp/"   , args))) return (GMT_OBLIQUE_MERC_POLE);
4690 	if ((*pos = (int)gmt_strlcmp ("ortho/"    , args))) return (GMT_ORTHO);
4691 	if ((*pos = (int)gmt_strlcmp ("polar/"    , args))) return (GMT_POLAR);
4692 	if ((*pos = (int)gmt_strlcmp ("poly/"     , args))) return (GMT_POLYCONIC);
4693 	if ((*pos = (int)gmt_strlcmp ("robin/"    , args))) return (GMT_ROBINSON);
4694 	if ((*pos = (int)gmt_strlcmp ("sinu/"     , args))) return (GMT_SINUSOIDAL);
4695 	if ((*pos = (int)gmt_strlcmp ("stere/"    , args))) return (GMT_STEREO);
4696 	if ((*pos = (int)gmt_strlcmp ("tmerc/"    , args))) return (GMT_TM);
4697 	if ((*pos = (int)gmt_strlcmp ("utm/"      , args))) return (GMT_UTM);
4698 	if ((*pos = (int)gmt_strlcmp ("vandg/"    , args))) return (GMT_VANGRINTEN);
4699 	if ((*pos = (int)gmt_strlcmp ("wintri/"   , args))) return (GMT_WINKEL);
4700 	if ((*pos = (int)gmt_strlcmp ("xy/"       , args))) return (GMT_LINEAR);
4701 	if ((*pos = (int)gmt_strlcmp ("z/"        , args))) return (GMT_ZAXIS);
4702 
4703 	/* These older codes (up to GMT 4.2.1) took 2 characters */
4704 
4705 	if ((*pos = (int)gmt_strlcmp ("kf", args))) return (GMT_ECKERT4);
4706 	if ((*pos = (int)gmt_strlcmp ("ks", args))) return (GMT_ECKERT6);
4707 	if ((*pos = (int)gmt_strlcmp ("oa", args))) return (GMT_OBLIQUE_MERC);
4708 	if ((*pos = (int)gmt_strlcmp ("ob", args))) return (GMT_OBLIQUE_MERC);
4709 	if ((*pos = (int)gmt_strlcmp ("oc", args))) return (GMT_OBLIQUE_MERC_POLE);
4710 
4711 	/* Finally, check only the first letter (used until GMT 4.2.1) */
4712 
4713 	*pos = 1;
4714 	t = (unsigned char) tolower((unsigned char) args[0]);
4715 	if (t == 'a') return (GMT_LAMB_AZ_EQ);
4716 	if (t == 'b') return (GMT_ALBERS);
4717 	if (t == 'c') return (GMT_CASSINI);
4718 	if (t == 'd') return (GMT_ECONIC);
4719 	if (t == 'e') return (GMT_AZ_EQDIST);
4720 	if (t == 'f') return (GMT_GNOMONIC);
4721 	if (t == 'g') return (GMT_GENPER);
4722 	if (t == 'h') return (GMT_HAMMER);
4723 	if (t == 'i') return (GMT_SINUSOIDAL);
4724 	if (t == 'j') return (GMT_MILLER);
4725 	if (t == 'k') return (GMT_ECKERT6);
4726 	if (t == 'l') return (GMT_LAMBERT);
4727 	if (t == 'm') return (GMT_MERCATOR);
4728 	if (t == 'n') return (GMT_ROBINSON);
4729 	if (t == 'o') return (GMT_OBLIQUE_MERC);
4730 	if (t == 'p') return (GMT_POLAR);
4731 	if (t == 'q') return (GMT_CYL_EQDIST);
4732 	if (t == 'r') return (GMT_WINKEL);
4733 	if (t == 's') return (GMT_STEREO);
4734 	if (t == 't') return (GMT_TM);
4735 	if (t == 'u') return (GMT_UTM);
4736 	if (t == 'v') return (GMT_VANGRINTEN);
4737 	if (t == 'w') return (GMT_MOLLWEIDE);
4738 	if (t == 'x') return (GMT_LINEAR);
4739 	if (t == 'y') return (GMT_CYL_EQ);
4740 	if (t == 'z') return (GMT_ZAXIS);
4741 
4742 	/* Did not find any match. Report error */
4743 
4744 	*pos = 0;
4745 	return (GMT_NO_PROJ);
4746 }
4747 
4748 /*! . */
gmtinit_scale_or_width(struct GMT_CTRL * GMT,char * scale_or_width,double * value,bool xy_plane)4749 GMT_LOCAL int gmtinit_scale_or_width (struct GMT_CTRL *GMT, char *scale_or_width, double *value, bool xy_plane) {
4750 	/* Scans character that may contain a scale (1:xxxx or units per degree) or a width.
4751 	   Return 1 upon error. Here we want to make an exception for users giving a scale
4752 	   of 1 in grdproject and mapproject as it is most likely meant to be 1:1. */
4753 	int n, answer;
4754 	if (isalpha (scale_or_width[0])) return (1);	/* Neither scale nor width can start with a letter - probably junk input */
4755 	answer = strncmp (scale_or_width, "1:", 2U);	/* 0 if scale given as 1:xxxx */
4756 	GMT->current.proj.units_pr_degree = (answer != 0);
4757 	if (GMT->current.proj.units_pr_degree) {	/* Check if we got "1" and this is grd|map-project */
4758 		size_t k = strlen (scale_or_width);
4759 		if (k == 1 && scale_or_width[0] == '1' && gmt_M_is_grdmapproject (GMT)) {	/* OK, pretend we got 1:1 */
4760 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your scale of 1 in -J was interpreted to mean 1:1 since no plotting is involved.\n");
4761 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "If a scale of 1 was intended, please append a unit from %s.\n", GMT_DIM_UNITS_DISPLAY);
4762 			gmtinit_scale_or_width (GMT, strcat(scale_or_width,":1"), value, xy_plane);
4763 			return (GMT_NOERROR);
4764 		}
4765 
4766 		*value = gmt_M_to_inch (GMT, scale_or_width);
4767 	}
4768 	else {
4769 		n = sscanf (scale_or_width, "1:%lf", value);
4770 		if (n != 1 || *value < 0.0) return (1);
4771 		*value = 1.0 / (*value * GMT->current.proj.unit);
4772 		if (GMT->current.proj.gave_map_width) {
4773 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot specify map width with 1:xxxx format in -J option\n");
4774 			return (1);
4775 		}
4776 	}
4777 	if (gmt_M_is_zero (*value)) {
4778 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your scale or width (%s) resulted in a zero value.\n", scale_or_width);
4779 		return (1);
4780 	}
4781 	else if (xy_plane && gmt_M_is_geographic (GMT, GMT_IN) && (*value) < 0.0) {
4782 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Geographic scale (%s) cannot be negative.\n", scale_or_width);
4783 		return (1);
4784 	}
4785 	GMT->current.proj.pars[15] = *value;	/* Store the scale here so we always know where to find it */
4786 	return (GMT_NOERROR);
4787 }
4788 
4789 /*! . */
gmtinit_autoscale(char * arg)4790 GMT_LOCAL int gmtinit_autoscale (char *arg) {
4791 	/* Check for 0[d], -0[d], +0[d] */
4792 	if (!strcmp (arg, "-0") || !strcmp (arg, "-0d")) return -1;	/* Want same as other axis but negative direction */
4793 	if (!strcmp (arg,  "0") || !strcmp (arg,  "0d") || !strcmp (arg, "+0") || !strcmp (arg, "+0d")) return +1;	/* Same as other axis and same direction */
4794 	return 0;	/* No */
4795 }
4796 
4797 #ifdef DEBUG
4798 #define GMT_GENPER_MODIFIERS "adtvwz"
4799 #else
4800 #define GMT_GENPER_MODIFIERS "atvwz"
4801 #endif
4802 
gmtinit_parse_genper_modern(struct GMT_CTRL * GMT,char * args,bool got_xxx_scale,bool width_given)4803 GMT_LOCAL int gmtinit_parse_genper_modern (struct GMT_CTRL *GMT, char *args, bool got_xxx_scale, bool width_given) {
4804 	/* Modern form for genper: -Jg|g<lon>/<lat>/scale|width>[+a<azimuth>][+t<tilt>][+v<vwidth>/<vheight>][+w<twist>][+z<altitude>[r|R]|g]
4805 	 * For developers there is also [+d<level>] if -DDEBUG was used */
4806 	int m, n, error = 0;
4807 	char txt_arr[4][GMT_LEN256], *c = NULL, *d = NULL;
4808 
4809 	/* Initialize settings */
4810 	GMT->current.proj.g_debug = 0;
4811 	GMT->current.proj.g_box = GMT->current.proj.g_outside = GMT->current.proj.g_longlat_set = GMT->current.proj.g_radius = \
4812 		GMT->current.proj.g_geosync = GMT->current.proj.g_earth_radius = false;
4813 	GMT->current.proj.g_sphere = true; /* force spherical as default */
4814 	gmt_M_memset (GMT->current.proj.pars, 10, double);
4815 
4816 	d = strstr (args, "+d");	/* This is width modifier already processed  earlier - we must hide to avoid problems with other modifiers */
4817 	if (d) d[0] = 'X';
4818 
4819 	if ((c = gmt_first_modifier (GMT, args, GMT_GENPER_MODIFIERS))) {	/* Process modifiers */
4820 		unsigned int pos = 0, n_errors = 0, nlen;
4821 		char txt[GMT_LEN256] = {""}, W[GMT_LEN256] = {""}, H[GMT_LEN256] = {""};
4822 		while (gmt_getmodopt (GMT, 'J', c, GMT_GENPER_MODIFIERS, &pos, txt, &n_errors) && n_errors == 0) {
4823 			nlen = strlen(txt);
4824 			switch (txt[0]) {
4825 				case 'a':	/* View azimuth */
4826 					if (strchr ("lL", txt[nlen-1])) {	/* Undocumented trailing l|L to set this variable */
4827 						GMT->current.proj.g_longlat_set = true;
4828 						txt[nlen-1] = 0;
4829 					}
4830 					GMT->current.proj.pars[5] = atof (&txt[1]);
4831 					break;
4832 				case 'd': /* Debug settings+d[d|D|X][s|e] (requires -DDEBUG during build) */
4833 					switch (txt[1]) {
4834 						case 'd': GMT->current.proj.g_debug = 1; break;
4835 						case 'D': GMT->current.proj.g_debug = 2; break;
4836 						case 'X': GMT->current.proj.g_debug = 3; break;
4837 						case 'e': GMT->current.proj.g_sphere = false; break;
4838 						case 's': GMT->current.proj.g_sphere = true; break;
4839 						default:	break;
4840 					}
4841 					if (txt[2]) GMT->current.proj.g_sphere = (txt[2] == 's') ? true : false;
4842 					break;
4843 				case 't':	/* View tilt */
4844 					if (strchr ("lL", txt[nlen-1])) {	/* Undocumented trailing l|L to set this variable */
4845 						GMT->current.proj.g_longlat_set = true;
4846 						txt[nlen-1] = 0;
4847 					}
4848 					n_errors += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &txt[1], GMT_IS_GEO, &GMT->current.proj.pars[6]), &txt[1]);
4849 					break;
4850 				case 'v': /* View window */
4851 					if (strchr (txt, '/'))	/* Got width and height in separate strings */
4852 						sscanf (&txt[1], "%[^/]/%s", W, H);
4853 					else
4854 						strcpy (W, &txt[1]), strcpy (H, &txt[1]);	/* Duplicate */
4855 					n_errors += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, W, GMT_IS_GEO, &GMT->current.proj.pars[8]), W);
4856 					n_errors += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, H, GMT_IS_GEO, &GMT->current.proj.pars[9]), H);
4857 					break;
4858 				case 'w':	/* View twist */
4859 					n_errors += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &txt[1], GMT_IS_GEO, &GMT->current.proj.pars[7]), &txt[1]);
4860 					break;
4861 				case 'z':	/* View altitude */
4862 					switch (txt[nlen-1]) {	/* Check flag */
4863 						case 'g':	/* Geosynchronous orbit altitude */
4864 							GMT->current.proj.g_geosync = true;	break;
4865 						case 'r':/* Altitude is actually radius from center of Earth */
4866 							GMT->current.proj.g_radius = true;	txt[nlen-1] = 0;	break;
4867 						case 'R':	/* Altitude is number of Earth radii, not km */
4868 							GMT->current.proj.g_earth_radius = true;	txt[nlen-1] = 0;break;
4869 						default:	break; /* Probably just an altitude with numbers */
4870 					}
4871 					if (!GMT->current.proj.g_geosync) n_errors += gmt_verify_expectations (GMT, GMT_IS_FLOAT, gmt_scanf (GMT, &txt[1], GMT_IS_FLOAT, &GMT->current.proj.pars[4]), &txt[1]);
4872 					break;
4873 				default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
4874 			}
4875 		}
4876 		c[0] = '\0';	/* Chop off the modifiers */
4877 		error += n_errors;
4878 	}
4879 
4880 	if (d) d[0] = '\0';	/* Chop off the optional +d modifier for map width */
4881 
4882 	/* Here we are only dealing with <lon>/<lat>/<scale-or-width> */
4883 
4884 	n = sscanf (args, "%[^/]/%[^/]/%[^/]/%s", &(txt_arr[0][0]), &(txt_arr[1][0]), &(txt_arr[2][0]), &(txt_arr[3][0]));
4885 	if (n < 3) {
4886 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Option -Jg|G: Arguments <lon>/<lat>/<scale-or-width> are required\n");
4887 		error++;
4888 	}
4889 
4890 	/* Check to see if scale is specified in 1:xxxx */
4891 	GMT->current.proj.units_pr_degree = (strchr (txt_arr[n-1], ':')) ? false : true;
4892 	gmt_set_geographic (GMT, GMT_IN);
4893 
4894 	if (got_xxx_scale) {
4895 		/* Scale entered as 1:mmmmm */
4896 		m = sscanf (&(txt_arr[n-1][0]),"1:%lf", &GMT->current.proj.pars[2]);
4897 		if (GMT->current.proj.pars[2] != 0.0) {
4898 			GMT->current.proj.pars[2] = 1.0 / (GMT->current.proj.pars[2] * GMT->current.proj.unit);
4899 		}
4900 		error += (m == 0) ? 1 : 0;
4901 		if (error) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: scale entered but couldn't read\n");
4902 	}
4903 	else if (width_given) {
4904 		GMT->current.proj.pars[2] = gmt_M_to_inch (GMT, &(txt_arr[n-1][0]));
4905 	}
4906 	else {
4907 		double olat;
4908 		GMT->current.proj.pars[2] = gmt_M_to_inch (GMT, &(txt_arr[n-2][0]));
4909 		if (gmtinit_get_uservalue (GMT, &(txt_arr[n-1][0]), gmt_M_type (GMT, GMT_IN, GMT_Y), &olat, "oblique latitude")) return true;
4910 		if (olat <= -90.0 || olat >= 90.0) {
4911 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
4912 			error++;
4913 		}
4914 		else
4915 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, &(txt_arr[n-1][0]), GMT_IS_LAT, &GMT->current.proj.pars[3]), &(txt_arr[n-1][0]));
4916 		if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "genper: error in reading last lat value\n");
4917 	}
4918 	error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, &(txt_arr[0][0]), GMT_IS_LON, &GMT->current.proj.pars[0]), &(txt_arr[0][0]));
4919 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error is reading longitude '%s'\n", &(txt_arr[0][0]));
4920 	error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, &(txt_arr[1][0]), GMT_IS_LAT, &GMT->current.proj.pars[1]), &(txt_arr[1][0]));
4921 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading latitude '%s'\n", &(txt_arr[1][0]));
4922 
4923 	error += (GMT->current.proj.pars[2] <= 0.0 || (got_xxx_scale && width_given));
4924 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "final error %d\n", error);
4925 	GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
4926 	if (d) d[0] = '+';	/* Restore the optional +d modifier for map width */
4927 	if (c) c[0] = '+';	/* Restore the optional perspective  modifiers */
4928 	return (error);
4929 }
4930 
gmtinit_parse_genper_deprecated(struct GMT_CTRL * GMT,char * args,bool got_xxx_scale,bool width_given)4931 GMT_LOCAL int gmtinit_parse_genper_deprecated (struct GMT_CTRL *GMT, char *args, bool got_xxx_scale, bool width_given) {
4932 	/* This is the deprecated parser for general perspective projection */
4933 	int i, j, m, n, n_slashes, nlen, error = 0;
4934 	char txt_arr[11][GMT_LEN256];
4935 
4936 	GMT->current.proj.g_debug = 0;
4937 	GMT->current.proj.g_box = GMT->current.proj.g_outside = GMT->current.proj.g_longlat_set = GMT->current.proj.g_radius = \
4938 		GMT->current.proj.g_geosync = GMT->current.proj.g_earth_radius = false;
4939 	GMT->current.proj.g_sphere = true; /* force spherical as default */
4940 
4941 	n_slashes = gmt_count_char (GMT, args, '/');	/* Count slashes to distinguish args */
4942 
4943 	i = 0;
4944 	for (j = 0 ; j < 2 ; j++) {
4945 		if (args[j] == 'd') {         /* standard genper debugging */
4946 			GMT->current.proj.g_debug = 1;
4947 			i++;
4948 		} else if (args[j] == 'D') {  /* extensive genper debugging */
4949 			GMT->current.proj.g_debug = 2;
4950 			i++;
4951 		} else if (args[j] == 'X') {  /* extreme genper debugging */
4952 			GMT->current.proj.g_debug = 3;
4953 			i++;
4954 		} else if (args[j] == 's') {
4955 			GMT->current.proj.g_sphere = true;
4956 			i++;
4957 		} else if (args[j] == 'e') {
4958 			GMT->current.proj.g_sphere = false;
4959 			i++;
4960 		}
4961 	}
4962 
4963 	GMT->current.proj.pars[4] = GMT->current.proj.pars[5] = GMT->current.proj.pars[6] = GMT->current.proj.pars[7] = GMT->current.proj.pars[8] = GMT->current.proj.pars[9] = 0.0;
4964 
4965 	if (GMT->current.proj.g_debug > 1) {
4966 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: arg '%s' n_slashes %d k %d\n", args, n_slashes, j);
4967 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper:  initial error %d  j = %d  width_given %d\n", error, j, width_given);
4968 	}
4969 
4970 	n = sscanf(args+i, "%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%[^/]/%s",
4971 		&(txt_arr[0][0]), &(txt_arr[1][0]), &(txt_arr[2][0]), &(txt_arr[3][0]),
4972 		&(txt_arr[4][0]), &(txt_arr[5][0]), &(txt_arr[6][0]), &(txt_arr[7][0]),
4973 		&(txt_arr[8][0]), &(txt_arr[9][0]), &(txt_arr[10][0]));
4974 
4975 	if (GMT->current.proj.g_debug > 1) {
4976 		for (i = 0 ; i < n ; i ++) {
4977 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: txt_arr[%d] '%s'\n", i, &(txt_arr[i][0]));
4978 		}
4979 		fflush (NULL);
4980 	}
4981 
4982 	if (got_xxx_scale) {
4983 		/* Scale entered as 1:mmmmm */
4984 		m = sscanf(&(txt_arr[n-1][0]),"1:%lf", &GMT->current.proj.pars[2]);
4985 		if (GMT->current.proj.pars[2] != 0.0) {
4986 			GMT->current.proj.pars[2] = 1.0 / (GMT->current.proj.pars[2] * GMT->current.proj.unit);
4987 		}
4988 		error += (m == 0) ? 1 : 0;
4989 		if (error) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: scale entered but couldn't read\n");
4990 	}
4991 	else  if (width_given) {
4992 		GMT->current.proj.pars[2] = gmt_M_to_inch (GMT, &(txt_arr[n-1][0]));
4993 	}
4994 	else {
4995 		double c;
4996 		GMT->current.proj.pars[2] = gmt_M_to_inch (GMT, &(txt_arr[n-2][0]));
4997 		/*            GMT->current.proj.pars[3] = GMT_ddmmss_to_degree(txt_i); */
4998 		if (gmtinit_get_uservalue (GMT, &(txt_arr[n-1][0]), gmt_M_type (GMT, GMT_IN, GMT_Y), &c, "oblique latitude")) return true;
4999 		if (c <= -90.0 || c >= 90.0) {
5000 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
5001 			error++;
5002 		}
5003 		else
5004 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, &(txt_arr[n-1][0]), GMT_IS_LAT, &GMT->current.proj.pars[3]), &(txt_arr[n-1][0]));
5005 		if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "genper: error in reading last lat value\n");
5006 	}
5007 	error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, &(txt_arr[0][0]), GMT_IS_LON, &GMT->current.proj.pars[0]), &(txt_arr[0][0]));
5008 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error is reading longitude '%s'\n", &(txt_arr[0][0]));
5009 	error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, &(txt_arr[1][0]), GMT_IS_LAT, &GMT->current.proj.pars[1]), &(txt_arr[1][0]));
5010 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading latitude '%s'\n", &(txt_arr[1][0]));
5011 
5012 	/* g_alt    GMT->current.proj.pars[4] = atof(txt_c); */
5013 	nlen = (int)strlen(&(txt_arr[2][0]));
5014 	if (txt_arr[2][nlen-1] == 'r') {
5015 		GMT->current.proj.g_radius = true;
5016 		txt_arr[2][nlen-1] = 0;
5017 	}
5018 	error += gmt_verify_expectations (GMT, GMT_IS_FLOAT, gmt_scanf (GMT, &(txt_arr[2][0]), GMT_IS_FLOAT, &GMT->current.proj.pars[4]), &(txt_arr[2][0]));
5019 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading altitude '%s'\n", &(txt_arr[2][0]));
5020 	if (!GMT->current.proj.g_radius && GMT->current.proj.pars[4] < -10.0)	/* Obsolete way to say altitude is distance from Earth center */
5021 		GMT->current.proj.g_radius = true;
5022 	else if (!GMT->current.proj.g_radius && GMT->current.proj.pars[4] < 10.0)	/* Gave Earth radii as unit instead of altitude */
5023 		GMT->current.proj.g_earth_radius = true;
5024 
5025 	/* g_az    GMT->current.proj.pars[5] = atof(txt_d); */
5026 	nlen = (int)strlen(&(txt_arr[3][0]));
5027 	if (txt_arr[3][nlen-1] == 'l' || txt_arr[3][nlen-1] == 'L') {
5028 		GMT->current.proj.g_longlat_set = true;
5029 		txt_arr[3][nlen-1] = 0;
5030 	}
5031 	error += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &(txt_arr[3][0]), GMT_IS_GEO, &GMT->current.proj.pars[5]), &(txt_arr[3][0]));
5032 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading azimuth '%s'\n", &(txt_arr[3][0]));
5033 
5034 	/* g_tilt    GMT->current.proj.pars[6] = atof(txt_e); */
5035 	nlen = (int)strlen(&(txt_arr[4][0]));
5036 	if (txt_arr[4][nlen-1] == 'l' || txt_arr[4][nlen-1] == 'L') {
5037 		GMT->current.proj.g_longlat_set = true;
5038 		txt_arr[4][nlen-1] = 0;
5039 	}
5040 	error += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &(txt_arr[4][0]), GMT_IS_GEO, &GMT->current.proj.pars[6]), &(txt_arr[4][0]));
5041 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading tilt '%s'\n", &(txt_arr[4][0]));
5042 
5043 	if (n > 6) {
5044 		/*g_twist   GMT->current.proj.pars[7] = atof(txt_f); */
5045 		error += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &(txt_arr[5][0]), GMT_IS_GEO, &GMT->current.proj.pars[7]), &(txt_arr[5][0]));
5046 		if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading twist '%s'\n", &(txt_arr[5][0]));
5047 
5048 		/*g_width   GMT->current.proj.pars[8] = atof(txt_f); */
5049 		if (n > 7) {
5050 			error += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &(txt_arr[6][0]), GMT_IS_GEO, &GMT->current.proj.pars[8]), &(txt_arr[6][0]));
5051 			if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error reading width '%s'\n", &(txt_arr[6][0]));
5052 
5053 			if (n > 8) {
5054 				/* g_height  GMT->current.proj.pars[9] = atof(txt_g); */
5055 				error += gmt_verify_expectations (GMT, GMT_IS_GEO, gmt_scanf (GMT, &(txt_arr[7][0]), GMT_IS_GEO, &GMT->current.proj.pars[9]), &(txt_arr[7][0]));
5056 				if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "error height '%s'\n", &(txt_arr[7][0]));
5057 			}
5058 		}
5059 	}
5060 	error += (GMT->current.proj.pars[2] <= 0.0 || (got_xxx_scale && width_given));
5061 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "final error %d\n", error);
5062 	GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5063 	return (error);
5064 }
5065 
5066 /*! . */
gmtinit_parse_J_option(struct GMT_CTRL * GMT,char * args_in)5067 GMT_LOCAL bool gmtinit_parse_J_option (struct GMT_CTRL *GMT, char *args_in) {
5068 	/* gmtinit_parse_J_option scans the arguments given and extracts the parameters needed
5069 	 * for the specified map projection. These parameters are passed through the
5070 	 * GMT->current.proj structure.  The function returns true if an error is encountered.
5071 	 */
5072 
5073 	int i, j, k = 0, n, slash, l_pos[3], p_pos[3], t_pos[3], d_pos[3], id, project;
5074 	int n_slashes = 0, last_pos = 0, error = 0;
5075 	unsigned int mod_flag = 0;
5076 	bool width_given = false;
5077 	double c, az, GMT_units[3] = {0.01, 0.0254, 1.0};      /* No of meters in a cm, inch, m */
5078 	char mod, args_cp[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""};
5079 	char txt_d[GMT_LEN256] = {""}, txt_e[GMT_LEN256] = {""}, last_char = 0, *dd = NULL, *d = NULL, *args;
5080 	char args_buf[GMT_LEN128] = {""};
5081 
5082 	if (args_in == NULL) {
5083 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -J: No argument for parsing\n");
5084 		return (true);
5085 	}
5086 	strncpy (args_buf, args_in, GMT_LEN128);	/* Must duplicate since args_in may be a static string */
5087 	args = args_buf;	/* Start of arguments */
5088 	gmt_M_memset (l_pos, 3, int);	gmt_M_memset (p_pos, 3, int);
5089 	gmt_M_memset (t_pos, 3, int);	gmt_M_memset (d_pos, 3, int);
5090 	if (!GMT->common.J.active)	/* Down want to clobber this during -Jz/Z after the horizontal part has been set */
5091 		GMT->current.proj.lon0 = GMT->current.proj.lat0 = GMT->session.d_NaN;	/* Projection center, to be set via -J */
5092 
5093 	project = gmtinit_project_type (args, &i, &width_given);
5094 	if (project == GMT_NO_PROJ) return (true);	/* No valid projection specified */
5095 
5096 	if (project == GMT_ZAXIS)
5097 		strncpy (GMT->common.J.zstring, args, GMT_LEN128-1);	/* Verbatim copy of -Jz|Z */
5098 	else
5099 		strncpy (GMT->common.J.string, args, GMT_LEN128-1);	/* Verbatim copy or map -J */
5100 
5101 	args += i;		/* Skip to first argument */
5102 	if (width_given) {	/* If given size (not scale) then we may have modifiers */
5103 		mod_flag = 1;	/* Default is that size meant width */
5104 		if ((dd = strstr (args, "+d"))) {	/* Specify type of size argument via modifier +d */
5105 			switch (dd[2]) {
5106 				case 'l':	mod_flag = 4; break;	/* Want this to be the MIN (lower) dimension of map */
5107 				case 'u':	mod_flag = 3; break;	/* Want this to be the MAX (upper) dimension of map */
5108 				case 'h':	mod_flag = 2; break;	/* Want map HEIGHT instead of width */
5109 				case 'w':	mod_flag = 1; break;	/* Want map width [Default] */
5110 				default:
5111 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -J: Invalid modifier %s\n", d);
5112 					return (true);
5113 					break;
5114 			}
5115 			if (project == GMT_OBLIQUE_MERC && strstr (args, "+v"))	/* To guard against -JOa120W/25N/150/6i+dh+v and losing the +v */
5116 				GMT->current.proj.obl_flip = true;
5117 			dd[0] = '\0';	/* Chop off this modifier */
5118 		}
5119 		else {	/* Backwards compatibility for trailing +,-,h */
5120 			last_pos = (int)strlen (args) - 1;	/* Position of last character in this string */
5121 			last_char = args[last_pos];
5122 			if (last_pos > 0) {	/* Examine trailing character for modifying behavior */
5123 				switch (last_char) {	/* Check for what kind of width is given (only used if upper case is given below */
5124 					case 'h':	mod_flag = 2; break;	/* Want map HEIGHT instead */
5125 					case '+':	mod_flag = 3; break;	/* Want this to be the MAX dimension of map */
5126 					case '-':	mod_flag = 4; break;	/* Want this to be the MIN dimension of map */
5127 				}
5128 				if (mod_flag > 1) args[last_pos] = 0;	/* Temporarily chop off modifier */
5129 			}
5130 		}
5131 	}
5132 
5133 	n_slashes = gmt_count_char (GMT, args, '/');	/* Count slashes to distinguish args */
5134 
5135 	/* Differentiate between general perspective and orthographic projection based on number of slashes */
5136 	if (project == GMT_GENPER || project == GMT_ORTHO) {
5137 		if (n_slashes >= 5 || gmt_found_modifier (GMT, GMT->common.J.string, GMT_GENPER_MODIFIERS))
5138 			project = GMT_GENPER;
5139 		else
5140 			project = GMT_ORTHO;
5141 	}
5142 
5143 	if (project != GMT_ZAXIS) {
5144 		/* Check to see if scale is specified in 1:xxxx */
5145 		for (j = (int)strlen (args), k = -1; j > 0 && k < 0 && args[j] != '/'; j--)
5146 			if (args[j] == ':') k = j + 1;
5147 		GMT->current.proj.units_pr_degree = (k == -1) ? true : false;
5148 		gmt_set_cartesian (GMT, GMT_OUT);	/* This may be overridden by mapproject -I */
5149 		if (project != GMT_LINEAR)
5150 			gmt_set_geographic (GMT, GMT_IN);
5151 		GMT->current.proj.gave_map_width = mod_flag;
5152 	}
5153 
5154 	GMT->current.proj.unit = GMT_units[GMT_INCH];	/* No of meters in an inch */
5155 	n = 0;	/* Initialize with no fields found */
5156 
5157 	switch (project) {
5158 		case GMT_LINEAR:	/* Linear x/y scaling */
5159 			gmt_set_cartesian (GMT, GMT_IN);	/* This will be overridden below if -Jx or -Jp, for instance */
5160 			GMT->current.proj.compute_scale[GMT_X] = GMT->current.proj.compute_scale[GMT_Y] = width_given;
5161 
5162 			/* Default is not involving geographical coordinates */
5163 			gmt_set_column_type (GMT, GMT_IO, GMT_X, GMT_IS_UNKNOWN);
5164 			gmt_set_column_type (GMT, GMT_IO, GMT_Y, GMT_IS_UNKNOWN);
5165 
5166 			error += (n_slashes > 1) ? 1 : 0;
5167 
5168 			/* Find occurrences of /, l, p, t, or d */
5169 			for (j = 0, slash = 0; args[j] && slash == 0; j++)
5170 				if (args[j] == '/') slash = j;
5171 			for (j = id = 0; args[j]; j++) {
5172 				if (args[j] == '/') id = 1;
5173 				if (strchr ("Ll"  , (int)args[j])) l_pos[id] = j;
5174 				if (strchr ("Pp"  , (int)args[j])) p_pos[id] = j;
5175 				if (strchr ("Tt"  , (int)args[j])) t_pos[id] = j;
5176 				if (strchr ("DdGg", (int)args[j])) d_pos[id] = j;
5177 			}
5178 
5179 			/* Distinguish between p for points and p<power> for scaling */
5180 
5181 			n = (int)strlen (args);
5182 			for (j = 0; j < 2; j++) {
5183 				if (!p_pos[j]) continue;
5184 				i = p_pos[j] + 1;
5185 				if (i == n || strchr ("/LlTtDdGg", (int)args[i]))	/* This p is for points since no power is following */
5186 					p_pos[j] = 0;
5187 				else if (strchr("Pp", (int)args[i]))	/* The 2nd p is the p for power */
5188 					p_pos[j]++;
5189 			}
5190 
5191 			/* Get x-arguments */
5192 
5193 			strncpy (args_cp, args, GMT_BUFSIZ-1);	/* Since gmt_M_to_inch modifies the string */
5194 			if (slash) args_cp[slash] = 0;	/* Chop off y part */
5195 			if (gmtinit_autoscale (args_cp) == +1)
5196 				GMT->current.proj.autoscl[GMT_X] = 1;	/* Want same scale as for y; compute width from x-range */
5197 			else if (gmtinit_autoscale (args_cp) == -1)
5198 				GMT->current.proj.autoscl[GMT_X] = -1;	/* Want same scale as for y but reverse direction; compute width from x-range */
5199 			k = (!strncmp (args_cp, "1:", 2U)) ? 1 : -1;	/* Special check for linear proj with 1:xxx scale */
5200 			if (k > 0) {	/* For 1:xxxxx  we cannot have /LlTtDdGg modifiers */
5201 				if (l_pos[GMT_X] || p_pos[GMT_X] || t_pos[GMT_X] || d_pos[GMT_X]) error++;
5202 			}
5203 
5204 			if ((i = MAX (l_pos[GMT_X], p_pos[GMT_X])) > 0)
5205 				args_cp[i] = 0;	/* Chop off log or power part */
5206 			else if (t_pos[GMT_X] > 0)
5207 				args_cp[t_pos[GMT_X]] = 0;	/* Chop off time part */
5208 			else if (d_pos[GMT_X] > 0)	/* Chop of trailing 'd' */
5209 				args_cp[d_pos[GMT_X]] = 0;
5210 			if (k > 0)	/* Scale entered as 1:mmmmm - this implies -R is in meters */
5211 				GMT->current.proj.pars[0] = GMT->session.u2u[GMT_M][GMT_INCH] / atof (&args_cp[2]);
5212 			else
5213 				GMT->current.proj.pars[0] = gmt_M_to_inch (GMT, args_cp);	/* x-scale */
5214 			GMT->current.proj.xyz_projection[GMT_X] = GMT_LINEAR;
5215 			if (l_pos[GMT_X] > 0)
5216 				GMT->current.proj.xyz_projection[GMT_X] = GMT_LOG10;
5217 			else if (p_pos[GMT_X] > 0) {
5218 				GMT->current.proj.xyz_projection[GMT_X] = GMT_POW;
5219 				GMT->current.proj.pars[2] = atof (&args[p_pos[GMT_X]+1]);	/* pow to raise x */
5220 			}
5221 			else if (t_pos[GMT_X] > 0) {	/* Add option to append time_systems or epoch/unit later */
5222 				GMT->current.proj.xyz_projection[GMT_X] = GMT_TIME;
5223 				gmt_set_column_type (GMT, GMT_IN, GMT_X, (args[t_pos[GMT_X]] == 'T') ?  GMT_IS_ABSTIME : GMT_IS_RELTIME);
5224 			}
5225 
5226 			if (d_pos[GMT_X] > 0) gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_LON);
5227 
5228 			if (slash) {	/* Separate y-scaling desired */
5229 				strncpy (args_cp, &args[slash+1], GMT_BUFSIZ-1);	/* Since gmt_M_to_inch modifies the string */
5230 				if (gmtinit_autoscale (args_cp) == +1)
5231 					GMT->current.proj.autoscl[GMT_Y] = 1;	/* Want same scale as for x; compute height from y-range */
5232 				else if (gmtinit_autoscale (args_cp) == -1)
5233 					GMT->current.proj.autoscl[GMT_Y] = -1;	/* Want same scale as for x but reverse direction; compute height from y-range */
5234 				k = (!strncmp (args_cp, "1:", 2U)) ? 1 : -1;	/* Special check for linear proj with separate 1:xxx scale for y-axis */
5235 				if (k > 0) {	/* For 1:xxxxx  we cannot have /LlTtDdGg modifiers */
5236 					if (l_pos[GMT_Y] || p_pos[GMT_Y] || t_pos[GMT_Y] || d_pos[GMT_Y]) error++;
5237 				}
5238 				if ((i = MAX (l_pos[GMT_Y], p_pos[GMT_Y])) > 0)
5239 					args_cp[i-slash-1] = 0;	/* Chop off log or power part */
5240 				else if (t_pos[GMT_Y] > 0)
5241 					args_cp[t_pos[GMT_Y]-slash-1] = 0;	/* Chop off log or power part */
5242 				else if (d_pos[GMT_Y] > 0)
5243 					args_cp[d_pos[GMT_Y]-slash-1] = 0;	/* Chop of trailing 'd' part */
5244 				if (k > 0)	/* Scale entered as 1:mmmmm - this implies -R is in meters */
5245 					GMT->current.proj.pars[1] = GMT->session.u2u[GMT_M][GMT_INCH] / atof (&args_cp[2]);
5246 				else
5247 					GMT->current.proj.pars[1] = gmt_M_to_inch (GMT, args_cp);	/* y-scale */
5248 
5249 				GMT->current.proj.xyz_projection[GMT_Y] = GMT_LINEAR;
5250 				if (l_pos[GMT_Y] > 0)
5251 					GMT->current.proj.xyz_projection[GMT_Y] = GMT_LOG10;
5252 				else if (p_pos[GMT_Y] > 0) {
5253 					GMT->current.proj.xyz_projection[GMT_Y] = GMT_POW;
5254 					GMT->current.proj.pars[3] = atof (&args[p_pos[GMT_Y]+1]);	/* pow to raise y */
5255 				}
5256 				else if (t_pos[GMT_Y] > 0) {	/* Add option to append time_systems or epoch/unit later */
5257 					GMT->current.proj.xyz_projection[GMT_Y] = GMT_TIME;
5258 					gmt_set_column_type (GMT, GMT_IN, GMT_Y, (args[t_pos[GMT_Y]] == 'T') ?  GMT_IS_ABSTIME : GMT_IS_RELTIME);
5259 				}
5260 				if (d_pos[GMT_Y] > 0) gmt_set_column_type (GMT, GMT_IN, GMT_Y, GMT_IS_LAT);
5261 			}
5262 			else {	/* Just copy x parameters */
5263 				GMT->current.proj.xyz_projection[GMT_Y] = GMT->current.proj.xyz_projection[GMT_X];
5264 				GMT->current.proj.pars[1] = GMT->current.proj.pars[0];
5265 				GMT->current.proj.pars[3] = GMT->current.proj.pars[2];
5266 				/* Assume -JX<width>d means a linear geographic plot so x = lon and y = lat */
5267 				if (gmt_M_type (GMT, GMT_IN, GMT_X) & GMT_IS_LON) gmt_set_column_type (GMT, GMT_IN, GMT_Y, GMT_IS_LAT);
5268 			}
5269 			/* Not both sizes can be zero, but if one is, we will adjust to the scale of the other */
5270 			if (GMT->current.proj.pars[GMT_X] == 0.0 && GMT->current.proj.pars[GMT_Y] == 0.0) error++;
5271 			break;
5272 
5273 		case GMT_ZAXIS:	/* 3D plot */
5274 
5275 			GMT->current.proj.compute_scale[GMT_Z] = width_given;
5276 			error += (n_slashes > 0) ? 1 : 0;
5277 			gmt_set_column_type (GMT, GMT_IN, GMT_Z, GMT_IS_UNKNOWN);
5278 
5279 			/* Find occurrences of l, p, or t */
5280 			for (j = 0; args[j]; j++) {
5281 				if (strchr ("Ll", (int)args[j])) l_pos[GMT_Z] = j;
5282 				if (strchr ("Pp", (int)args[j])) p_pos[GMT_Z] = j;
5283 				if (strchr ("Tt", (int)args[j])) t_pos[GMT_Z] = j;
5284 			}
5285 
5286 			/* Distinguish between p for points and p<power> for scaling */
5287 
5288 			n = (int)strlen (args);
5289 			if (p_pos[GMT_Z]) {
5290 				i = p_pos[GMT_Z] + 1;
5291 				if (i == n || strchr ("LlTtDdGg", (int)args[i]))	/* This p is for points since no power is following */
5292 					p_pos[GMT_Z] = 0;
5293 				else if (strchr ("Pp", (int)args[i]))	/* The 2nd p is the p for power */
5294 					p_pos[GMT_Z]++;
5295 			}
5296 
5297 			/* Get arguments */
5298 
5299 			strncpy (args_cp, args, GMT_BUFSIZ-1);	/* Since gmt_M_to_inch modifies the string */
5300 			if ((i = MAX (l_pos[GMT_Z], p_pos[GMT_Z])) > 0)
5301 				args_cp[i] = 0;
5302 			else if (t_pos[GMT_Z] > 0)
5303 				args_cp[t_pos[GMT_Z]] = 0;
5304 			error += gmtinit_scale_or_width (GMT, args_cp, &GMT->current.proj.z_pars[0], false);	/* z-scale */
5305 
5306 			GMT->current.proj.xyz_projection[GMT_Z] = GMT_LINEAR;
5307 			if (l_pos[GMT_Z] > 0)
5308 				GMT->current.proj.xyz_projection[GMT_Z] = GMT_LOG10;
5309 			else if (p_pos[GMT_Z] > 0) {
5310 				GMT->current.proj.xyz_projection[GMT_Z] = GMT_POW;
5311 				GMT->current.proj.z_pars[1] = atof (&args[p_pos[GMT_Z]+1]);	/* pow to raise z */
5312 			}
5313 			else if (t_pos[GMT_Z] > 0) {
5314 				GMT->current.proj.xyz_projection[GMT_Z] = GMT_TIME;
5315 				gmt_set_column_type (GMT, GMT_IN, GMT_Z, (args[t_pos[GMT_Z]] == 'T') ?  GMT_IS_ABSTIME : GMT_IS_RELTIME);
5316 			}
5317 			if (GMT->current.proj.z_pars[0] == 0.0) error++;
5318 			GMT->current.proj.JZ_set = true;
5319 			break;
5320 
5321 		case GMT_POLAR:		/* Polar (theta,r) */
5322 			gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_LON);
5323 			gmt_set_column_type (GMT, GMT_IN, GMT_Y, GMT_IS_FLOAT);
5324 			GMT->current.proj.got_azimuths = GMT->current.proj.got_elevations = false;
5325 			GMT->current.proj.z_down = GMT_ZDOWN_R;
5326 			if ((d = gmt_first_modifier (GMT, args, "afkrtz"))) {	/* Process all modifiers */
5327 				unsigned int pos = 0, uerr = 0;
5328 				char word[GMT_LEN32] = {""};
5329 				while (gmt_getmodopt (GMT, 'J', d, "afkrtz", &pos, word, &uerr) && uerr == 0) {
5330 					switch (word[0]) {
5331 						case 'a':	/* Using azimuths instead of directions */
5332 							GMT->current.proj.got_azimuths = true;
5333 							break;
5334 						case 'f':	/* Flip radial direction to be positive inward */
5335 							GMT->current.proj.flip = true;
5336 							switch (word[1]) {	/* Check argument */
5337 								case 'e': GMT->current.proj.flip_radius = 90.0; GMT->current.proj.got_elevations = true; break;	/* Gave optional +fe for reverse (angular elevations, presumably) */
5338 								case 'p': GMT->current.proj.flip_radius = 0.0;	break; /* Determine planetary radius from current ellipsoid setting in gmt_mapsetup */
5339 								case '\0': GMT->current.proj.flip_radius = -1.0; break;	/* Just flip, set fli_radius be set to north in map_setup [Default +f] */
5340 								default:
5341 									if (gmt_not_numeric (GMT, &word[1])) {
5342 										GMT_Report (GMT->parent, GMT_MSG_ERROR, "Polar projection: +%s not a valid argument\n", word);
5343 										error++;
5344 									}
5345 									else	/* Gave flip with optional flip radius */
5346 										GMT->current.proj.flip_radius = atof (&word[1]);
5347 									break;
5348 							}
5349 							break;
5350 						case 'k':	/* Gave optional +k to indicate angles are longitude (x) or latitude (y) [Just great circle angles] */
5351 							switch (word[1]) {
5352 								case 'x':	GMT->current.proj.angle_kind = GMT_IS_LON;	break;
5353 								case 'y':	GMT->current.proj.angle_kind = GMT_IS_LAT;	break;
5354 								default:
5355 									GMT_Report (GMT->parent, GMT_MSG_ERROR, "Polar projection: +%s not a valid argument\n", word);
5356 									error++;
5357 									break;
5358 							}
5359 							break;
5360 						case 'r':	/* Gave optional +r for a nonzero radial offset [0] */
5361 							GMT->current.proj.radial_offset = gmt_M_to_inch (GMT, &word[1]);
5362 							break;
5363 						case 't':	/* Gave optional zero-base angle, i.e., a rotation [0] */
5364 							GMT->current.proj.pars[1] = atof (&word[1]);
5365 							break;
5366 						case 'z':	/* Gave optional +z[p|radius] for annotating depths rather than radius */
5367 							switch (word[1]) {	/* Check argument */
5368 								case 'p':  /* Annotate planetary radius minus r */
5369 									GMT->current.proj.z_down = GMT_ZDOWN_ZP;
5370 									break;
5371 								case '\0':	/* Annotate north - r */
5372 									GMT->current.proj.z_down = GMT_ZDOWN_Z;
5373 									break;
5374 								default:	/* Gave specific radius for annotation radius - r */
5375 									GMT->current.proj.z_down = GMT_ZDOWN_ZR;
5376 									if (gmt_not_numeric (GMT, &word[1])) {
5377 										GMT_Report (GMT->parent, GMT_MSG_ERROR, "Polar projection: +%s not a valid argument\n", word);
5378 										error++;
5379 									}
5380 									else	/* Specific annotation radius */
5381 										GMT->current.proj.z_radius = atof (&word[1]);
5382 									break;
5383 							}
5384 							break;
5385 						default:	/* These are caught in gmt_getmodopt so break is just for Coverity */
5386 							break;
5387 					}
5388 				}
5389 				d[0] = '\0';	/* Chop off all modifiers so other arguments can be parsed */
5390 				if (uerr) return (GMT_PARSE_ERROR);
5391 				GMT->current.proj.pars[0] = gmt_M_to_inch (GMT, args);
5392 				if (d) d[0] = '+';	/* Restore modifiers */
5393 			}
5394 			else {	/* No modifiers, so look for deprecated syntax such as -Jp|p[a|A], slash-separated scale|width and angular offset, and trailing r or z codes */
5395 				if (args[0] == 'a' || args[0] == 'A') {	/* Using azimuths instead of directions */
5396 					GMT->current.proj.got_azimuths = true;
5397 					i = 1;
5398 				}
5399 				else
5400 					i = 0;
5401 				j = (int)strlen (args) - 1;	/* Last character check for deprecated r or z */
5402 				if (j >= 0 && args[j] == 'r')	/* Gave trailing r for reverse (elevations, presumably) */
5403 					GMT->current.proj.got_elevations = true;
5404 				else if (j >= 0 && args[j] == 'z')	/* Gave trailing z for annotating depths rather than radius */
5405 					GMT->current.proj.z_down = GMT_ZDOWN_Z;
5406 				if (n_slashes == 1) {	/* Gave optional zero-base angle [0] Deprecated syntax, use +r<rotation> instead */
5407 					n = sscanf (&args[i], "%[^/]/%lf", txt_a, &GMT->current.proj.pars[1]);
5408 					if (n == 2) GMT->current.proj.pars[0] = gmt_M_to_inch (GMT, txt_a);
5409 					error += (GMT->current.proj.pars[0] <= 0.0 || n != 2) ? 1 : 0;
5410 				}
5411 				else if (n_slashes == 0) {	/* Modern syntax, only give scale|width */
5412 					GMT->current.proj.pars[0] = gmt_M_to_inch (GMT, &args[i]);
5413 					n = (args) ? 1 : 0;
5414 					error += (GMT->current.proj.pars[0] <= 0.0 || n != 1) ? 1 : 0;
5415 				}
5416 				else
5417 					error++;
5418 			}
5419 			if (GMT->current.proj.got_azimuths && GMT->current.proj.angle_kind) {
5420 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Polar projection: Cannot select both +a and +k\n");
5421 				error++;
5422 			}
5423 			if (GMT->current.proj.got_azimuths) GMT->current.proj.pars[1] = -GMT->current.proj.pars[1];	/* Because azimuths go clockwise */
5424 			break;
5425 
5426 		/* Map projections */
5427 
5428 		case GMT_ECKERT4:	/* Eckert IV */
5429 		case GMT_ECKERT6:	/* Eckert VI */
5430 		case GMT_HAMMER:	/* Hammer-Aitoff Equal-Area */
5431 		case GMT_MILLER:	/* Miller cylindrical */
5432 		case GMT_MOLLWEIDE:	/* Mollweide Equal-Area */
5433 		case GMT_ROBINSON:	/* Robinson Projection */
5434 		case GMT_SINUSOIDAL:	/* Sinusoidal Equal-Area */
5435 		case GMT_VANGRINTEN:	/* Van der Grinten */
5436 		case GMT_WINKEL:	/* Winkel Tripel Modified azimuthal */
5437 			GMT->current.proj.pars[0] = GMT->session.d_NaN;	/* Will be replaced by central meridian either below or in GMT_map_init_... */
5438 			if (n_slashes == 0)
5439 				n = sscanf (args, "%s", txt_b);
5440 			else if (n_slashes == 1) {
5441 				n = sscanf (args, "%[^/]/%s", txt_a, txt_b);
5442 				error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5443 				GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = 0.0;
5444 			}
5445 			error += gmtinit_scale_or_width (GMT, txt_b, &GMT->current.proj.pars[1], true);
5446 			error += !(n == n_slashes + 1);
5447 			break;
5448 
5449 		case GMT_CYL_EQ:	/* Cylindrical Equal Area */
5450 		case GMT_CYL_EQDIST:	/* Equidistant Cylindrical */
5451 		case GMT_CYL_STEREO:	/* Cylindrical Stereographic */
5452 		case GMT_CASSINI:	/* Cassini */
5453 		case GMT_MERCATOR:	/* Mercator */
5454 		case GMT_TM:		/* Transverse Mercator */
5455 		case GMT_POLYCONIC:	/* Polyconic */
5456 			GMT->current.proj.pars[0] = GMT->session.d_NaN;
5457 			GMT->current.proj.pars[1] = 0.0;
5458 			txt_a[0] = txt_b[0] = 0;
5459 			if (n_slashes == 0)
5460 				n = sscanf (args, "%s", txt_c);
5461 			else if (n_slashes == 1)
5462 				n = sscanf (args, "%[^/]/%s", txt_a, txt_c);
5463 			else if (n_slashes == 2)
5464 				n = sscanf (args, "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
5465 			if (txt_a[0]) {
5466 				error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5467 				GMT->current.proj.lon0 = GMT->current.proj.pars[0];
5468 			}
5469 			if (txt_b[0]) {
5470 				error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5471 				GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5472 			}
5473 			error += gmtinit_scale_or_width (GMT, txt_c, &GMT->current.proj.pars[2], true);
5474 			error += ((project == GMT_CYL_EQ || project == GMT_MERCATOR || project == GMT_POLYCONIC)
5475 				&& fabs (GMT->current.proj.pars[1]) >= 90.0);
5476 			error += !(n == n_slashes + 1);
5477 			break;
5478 
5479 		case GMT_ALBERS:	/* Albers Equal-area Conic */
5480 		case GMT_ECONIC:	/* Equidistant Conic */
5481 		case GMT_LAMBERT:	/* Lambert Conformal Conic */
5482 			n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, txt_e);
5483 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5484 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5485 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_c, GMT_IS_LAT, &GMT->current.proj.pars[2]), txt_c);
5486 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_d, GMT_IS_LAT, &GMT->current.proj.pars[3]), txt_d);
5487 			error += gmtinit_scale_or_width (GMT, txt_e, &GMT->current.proj.pars[4], true);
5488 			error += !(n_slashes == 4 && n == 5);
5489 			GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5490 			break;
5491 
5492 		case GMT_ORTHO:
5493 			/* Reset any genper related settings */
5494 			GMT->current.proj.g_debug = 0;
5495 			GMT->current.proj.g_box = GMT->current.proj.g_outside = GMT->current.proj.g_longlat_set =
5496 				GMT->current.proj.g_radius = GMT->current.proj.g_geosync = GMT->current.proj.g_earth_radius = false;
5497 			GMT->current.proj.g_sphere = true; /* force spherical as default */
5498 			GMT->current.proj.pars[5] = GMT->current.proj.pars[6] = GMT->current.proj.pars[7] = 0.0;
5499 			/* Intentionally fall through */
5500 		case GMT_AZ_EQDIST:	/* Azimuthal equal-distant */
5501 		case GMT_LAMB_AZ_EQ:	/* Lambert Azimuthal Equal-Area */
5502 		case GMT_GNOMONIC:	/* Gnomonic */
5503 			/* -Ja|A or e|e or g|G <lon0>/<lat0>[/<horizon>]/<scale>|<width> */
5504 
5505 			if (project == GMT_AZ_EQDIST)	/* Initialize default horizons */
5506 				strcpy (txt_c, "180");
5507 			else if (project == GMT_GNOMONIC)
5508 				strcpy (txt_c, "60");
5509 			else
5510 				strcpy (txt_c, "90");
5511 			if (k >= 0) {	/* Scale entered as 1:mmmmm */
5512 				if (n_slashes == 2)	/* Got lon0/lat0/1:mmmm */
5513 					n = sscanf (args, "%[^/]/%[^/]/1:%lf", txt_a, txt_b, &GMT->current.proj.pars[3]);
5514 				else if (n_slashes == 3)	/* Got lon0/lat0/lath/1:mmmm */
5515 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/1:%lf", txt_a, txt_b, txt_c, &GMT->current.proj.pars[3]);
5516 				if (GMT->current.proj.pars[3] != 0.0) GMT->current.proj.pars[3] = 1.0 / (GMT->current.proj.pars[3] * GMT->current.proj.unit);
5517 			}
5518 			else if (width_given) {
5519 				if (n_slashes == 2)	/* Got lon0/lat0/width */
5520 					n = sscanf (args, "%[^/]/%[^/]/%s", txt_a, txt_b, txt_d);
5521 				else if (n_slashes == 3)	/* Got lon0/lat0/lath/width */
5522 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d);
5523 				GMT->current.proj.pars[3] = gmt_M_to_inch (GMT, txt_d);
5524 			}
5525 			else {	/* Scale entered as radius/lat */
5526 				if (n_slashes == 3)	/* Got lon0/lat0/radius/lat */
5527 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_d, txt_e);
5528 				else if (n_slashes == 4)	/* Got lon0/lat0/lat_h/radius/lat */
5529 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, txt_e);
5530 				if (n == n_slashes + 1) {
5531 					GMT->current.proj.pars[3] = gmt_M_to_inch (GMT, txt_d);
5532 					if (gmtinit_get_uservalue (GMT, txt_e, gmt_M_type (GMT, GMT_IN, GMT_Y), &c, "oblique latitude")) return true;
5533 					if (c <= -90.0 || c >= 90.0) {
5534 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
5535 						error++;
5536 					}
5537 					else
5538 						error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_e, GMT_IS_LAT, &GMT->current.proj.pars[4]), txt_e);
5539 				}
5540 			}
5541 			error += (n != n_slashes + 1);
5542 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5543 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5544 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_c, GMT_IS_LON, &GMT->current.proj.pars[2]), txt_c);  /* As co-latitude it may be 180 so cannot parse as latitude */
5545 			error += (GMT->current.proj.pars[2] <= 0.0 || GMT->current.proj.pars[2] > 180.0 || GMT->current.proj.pars[3] <= 0.0 || (k >= 0 && width_given));
5546 			error += (project == GMT_GNOMONIC && GMT->current.proj.pars[2] >= 90.0);
5547 			//error += (project == GMT_ORTHO && GMT->current.proj.pars[2] >= 180.0);
5548 			error += (project == GMT_ORTHO && GMT->current.proj.pars[2] > 90.0);
5549 			GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5550 			break;
5551 
5552 		case GMT_STEREO:	/* Stereographic */
5553 			strcpy (txt_c, "90");	/* Initialize default horizon */
5554 			if (k >= 0) {	/* Scale entered as 1:mmmmm */
5555 				if (n_slashes == 2)
5556 					n = sscanf (args, "%[^/]/%[^/]/1:%lf", txt_a, txt_b, &GMT->current.proj.pars[3]);
5557 				else if (n_slashes == 3) {	/* with true scale at specified latitude */
5558 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/1:%lf", txt_a, txt_b, txt_e, &GMT->current.proj.pars[3]);
5559 					if (gmtinit_get_uservalue (GMT, txt_e, gmt_M_type (GMT, GMT_IN, GMT_Y), &c, "oblique latitude")) return true;
5560 					if (c < -90.0 || c > 90.0) {
5561 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
5562 						error++;
5563 					}
5564 					else
5565 						error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_e, GMT_IS_LAT, &GMT->current.proj.pars[4]), txt_e);
5566 					GMT->current.proj.pars[5] = 1.0;	/* flag for true scale case */
5567 				}
5568 				else if (n_slashes == 4) {
5569 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/1:%lf", txt_a, txt_b, txt_c, txt_e, &GMT->current.proj.pars[3]);
5570 					if (gmtinit_get_uservalue (GMT, txt_e, gmt_M_type (GMT, GMT_IN, GMT_Y), &c, "oblique latitude")) return true;
5571 					if (c < -90.0 || c > 90.0) {
5572 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
5573 						error++;
5574 					}
5575 					else
5576 						error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_e, GMT_IS_LAT, &GMT->current.proj.pars[4]), txt_e);
5577 					GMT->current.proj.pars[5] = 1.0;	/* flag for true scale case */
5578 				}
5579 				if (GMT->current.proj.pars[3] != 0.0) GMT->current.proj.pars[3] = 1.0 / (GMT->current.proj.pars[3] * GMT->current.proj.unit);
5580 			}
5581 			else if (width_given) {
5582 				if (n_slashes == 2)
5583 					n = sscanf (args, "%[^/]/%[^/]/%s", txt_a, txt_b, txt_d);
5584 				else if (n_slashes == 3)
5585 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d);
5586 				GMT->current.proj.pars[3] = gmt_M_to_inch (GMT, txt_d);
5587 			}
5588 			else {	/* Scale entered as radius/lat */
5589 				if (n_slashes == 3)
5590 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_d, txt_e);
5591 				else if (n_slashes == 4)
5592 					n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, txt_e);
5593 				if (n == n_slashes + 1) {
5594 					GMT->current.proj.pars[3] = gmt_M_to_inch (GMT, txt_d);
5595 					if (gmtinit_get_uservalue (GMT, txt_e, gmt_M_type (GMT, GMT_IN, GMT_Y), &c, "oblique latitude")) return true;
5596 					if (c <= -90.0 || c >= 90.0) {
5597 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Oblique latitude must be in -90 to +90 range\n");
5598 						error++;
5599 					}
5600 					else
5601 						error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_e, GMT_IS_LAT, &GMT->current.proj.pars[4]), txt_e);
5602 				}
5603 			}
5604 			error += (n != n_slashes + 1);
5605 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5606 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5607 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_c, GMT_IS_LON, &GMT->current.proj.pars[2]), txt_c);
5608 			error += (GMT->current.proj.pars[2] <= 0.0 || GMT->current.proj.pars[2] >= 180.0 || GMT->current.proj.pars[3] <= 0.0 || (k >= 0 && width_given));
5609 			GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5610 			break;
5611 
5612 		case GMT_GENPER:	/* General perspective */
5613 
5614 			if (n_slashes <= 3)	/* Simple orthographic or modernized general perspective */
5615 				error += gmtinit_parse_genper_modern (GMT, &GMT->common.J.string[1], k >= 0, width_given);
5616 			else	/* The old deprecated long syntax for general perspective */
5617 				error += gmtinit_parse_genper_deprecated (GMT, args, k >= 0, width_given);
5618 			break;
5619 
5620 		case GMT_OBLIQUE_MERC:		/* Oblique mercator, specifying origin and azimuth or second point */
5621 			if ((d = strstr (args, "+v"))) {
5622 				GMT->current.proj.obl_flip = true;
5623 				d[0] = '\0';	/* Chop off modifier */
5624 			}
5625 			GMT->current.proj.N_hemi = (strchr ("AB", GMT->common.J.string[1]) == NULL) ? true : false;	/* Upper case -JoA, -JoB allows S pole views */
5626 			if (n_slashes == 3) {
5627 				double distance = 10.0;	/* Default spherical length for gmt_translate_point */
5628 				n = sscanf (args, "%[^/]/%[^/]/%lf/%s", txt_a, txt_b, &az, txt_e);
5629 				error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5630 				error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5631 				if ((90.0 - fabs (GMT->current.proj.pars[1])) < distance)
5632 					distance = 90.0 - fabs (GMT->current.proj.pars[1]) - GMT_CONV4_LIMIT;
5633 				/* compute point <distance> degrees from origin along azimuth */
5634 				gmt_translate_point (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], az, distance, &GMT->current.proj.pars[2], &GMT->current.proj.pars[3], NULL);
5635 			}
5636 			else if (n_slashes == 4) {
5637 				n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, txt_e);
5638 				error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5639 				error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5640 				error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_c, GMT_IS_LON, &GMT->current.proj.pars[2]), txt_c);
5641 				error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_d, GMT_IS_LAT, &GMT->current.proj.pars[3]), txt_d);
5642 			}
5643 			error += gmtinit_scale_or_width (GMT, txt_e, &GMT->current.proj.pars[4], true);
5644 			GMT->current.proj.pars[6] = 0.0;
5645 			error += !(n == n_slashes + 1);
5646 			GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5647 			if (d) d[0] = '+';	/* Restore modifier */
5648 			break;
5649 
5650 		case GMT_OBLIQUE_MERC_POLE:	/* Oblique mercator, specifying origin and pole */
5651 			GMT->current.proj.N_hemi = (GMT->common.J.string[1] != 'C') ? true : false;	/* Upper case -JoC allows S pole views */
5652 			n = sscanf (args, "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d, txt_e);
5653 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &GMT->current.proj.pars[0]), txt_a);
5654 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_b, GMT_IS_LAT, &GMT->current.proj.pars[1]), txt_b);
5655 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_c, GMT_IS_LON, &GMT->current.proj.pars[2]), txt_c);
5656 			error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_d, GMT_IS_LAT, &GMT->current.proj.pars[3]), txt_d);
5657 			if (GMT->current.proj.pars[3] < 0.0) {	/* Flip from S hemisphere to N */
5658 				GMT->current.proj.o_spole = true;
5659 				GMT->current.proj.pars[3] = -GMT->current.proj.pars[3];
5660 				GMT->current.proj.pars[2] += 180.0;
5661 				if (GMT->current.proj.pars[2] >= 360.0) GMT->current.proj.pars[2] -= 360.0;
5662 			}
5663 			error += gmtinit_scale_or_width (GMT, txt_e, &GMT->current.proj.pars[4], true);
5664 			GMT->current.proj.pars[6] = 1.0;
5665 			error += !(n_slashes == 4 && n == 5);
5666 			project = GMT_OBLIQUE_MERC;
5667 			GMT->current.proj.lon0 = GMT->current.proj.pars[0];	GMT->current.proj.lat0 = GMT->current.proj.pars[1];
5668 			break;
5669 
5670 		case GMT_UTM:	/* Universal Transverse Mercator */
5671 			if (!strchr (args, '/')) {	/* No UTM zone given, must obtain from -R */
5672 				GMT->current.proj.pars[0] = -1.0;	/* Flag we need zone to be set later */
5673 				error += gmtinit_scale_or_width (GMT, args, &GMT->current.proj.pars[1], true);
5674 			}
5675 			else {
5676 				n = sscanf (args, "%[^/]/%s", txt_a, txt_b);
5677 				GMT->current.proj.pars[0] = atof (txt_a);
5678 				switch (args[0]) {
5679 					case '-':	/* Enforce Southern hemisphere convention for y */
5680 						GMT->current.proj.utm_hemisphere = -1;
5681 						break;
5682 					case '+':	/* Enforce Northern hemisphere convention for y */
5683 						GMT->current.proj.utm_hemisphere = +1;
5684 						break;
5685 					default:	/* Decide in gmt_map_setup based on -R */
5686 						GMT->current.proj.utm_hemisphere = 0;
5687 						break;
5688 				}
5689 				mod = (char)toupper ((int)txt_a[strlen(txt_a)-1]);	/* Check if UTM zone has a valid latitude modifier */
5690 				error = 0;
5691 				if (mod >= 'A' && mod <= 'Z') {	/* Got fully qualified UTM zone, e.g., 33N */
5692 					GMT->current.proj.utm_zoney = mod;
5693 					GMT->current.proj.utm_hemisphere = -1;
5694 					if (mod >= 'N') GMT->current.proj.utm_hemisphere = +1;
5695 					if (mod == 'I' || mod == 'O') error++;	/* No such zones */
5696 				}
5697 				GMT->current.proj.pars[0] = fabs (GMT->current.proj.pars[0]);
5698 				GMT->current.proj.lat0 = 0.0;
5699 				k = irint (GMT->current.proj.pars[0]);
5700 				GMT->current.proj.lon0 = -180.0 + k * 6.0 - 3.0;
5701 
5702 				error += (k < 1 || k > 60);	/* Zones must be 1-60 */
5703 				GMT->current.proj.utm_zonex = k;
5704 				error += gmtinit_scale_or_width (GMT, txt_b, &GMT->current.proj.pars[1], true);
5705 				error += !(n_slashes == 1 && n == 2);
5706 			}
5707 			break;
5708 
5709 		default:
5710 			error++;
5711 			project = GMT_NO_PROJ;
5712 			break;
5713 	}
5714 
5715 	if (project != GMT_ZAXIS) {
5716 		GMT->current.proj.projection = project;
5717 		GMT->current.proj.projection_GMT = project;		/* Make a copy to use when using the Proj4 lib */
5718 	}
5719 	if (dd) dd[0] = '+';	/* Restore modifier +dl|h|w|h */
5720 	else if (mod_flag > 1) args[last_pos] = last_char;	/* Restore modifier +,-,h,w */
5721 
5722 	return (error > 0);
5723 }
5724 
5725 /*! Converts c, i, and p into GMT_CM (0),GMT_INCH (1), or GMT_PT (3) */
gmt_get_dim_unit(struct GMT_CTRL * GMT,char c)5726 int gmt_get_dim_unit (struct GMT_CTRL *GMT, char c) {
5727 	int i;
5728 	switch ((int)c) {
5729 		case 'c':	/* cm */
5730 			i = GMT_CM;
5731 			break;
5732 		case 'i':	/* inch */
5733 			i = GMT_INCH;
5734 			break;
5735 		case 'm':	/* meter */
5736 			if (gmt_M_compat_check (GMT, 4)) {
5737 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Specifying a plot distance unit in meters is deprecated; use c, i, or p.\n");
5738 				i = GMT_M;
5739 			}
5740 			else	/* error */
5741 				i = GMT_NOTSET;
5742 			break;
5743 		case 'p':	/* point */
5744 			i = GMT_PT;
5745 			break;
5746 		default:	/* error */
5747 			i = GMT_NOTSET;
5748 			break;
5749 	}
5750 	return (i);
5751 }
5752 
5753 /*! . */
gmtinit_parse_front(struct GMT_CTRL * GMT,char * text,struct GMT_SYMBOL * S)5754 GMT_LOCAL int gmtinit_parse_front (struct GMT_CTRL *GMT, char *text, struct GMT_SYMBOL *S) {
5755 	/* Parser for -Sf[+|-]<tickgap>[/<ticklen>][+l|+r][+<type>][+o<offset>][+p<pen>]
5756 	 * <tickgap> is required and is either a distance in some unit (append c|i|p)
5757 	 * or it starts with - and gives the number of desired ticks instead.
5758 	 * <ticklen> defaults to 15% of <tickgap> but is required if the number
5759 	 * of ticks are specified. If + is prepended to <tickgap> then we use that
5760 	 * gap distance as given [Default distributes n gaps evenly along length] */
5761 
5762 	unsigned int pos = 0, k, k0 = 1, error = 0;
5763 	int mods, n;
5764 	char p[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
5765 
5766 	/* text[0] is the leading 'f' for front */
5767 	if (text[1] == '+' && !strchr ("bcfilrsStopv", text[2])) S->f.f_exact = true, k0 = 2;	/* Special leading + to be skipped when looking for modifiers */
5768 	for (k = k0; text[k] && text[k] != '+'; k++);	/* Either find the first plus or run out or chars */
5769 	strncpy (p, text, k); p[k] = 0;
5770 	mods = (text[k] == '+');
5771 	if (mods) text[k] = 0;		/* Temporarily chop off the modifiers */
5772 	n = sscanf (&text[1], "%[^/]/%s", txt_a, txt_b);
5773 	if (mods) text[k] = '+';	/* Restore the modifiers */
5774 	if (txt_a[0] == '-' && n == 1) {
5775 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sf: Must specify <ticklen> when specifying the number of ticks\n");
5776 		error++;
5777 	}
5778 	S->f.f_gap = (txt_a[0] == '-') ? atof (txt_a) : gmt_M_to_inch (GMT, txt_a);
5779 	S->f.f_len = (n == 1) ? 0.15 * S->f.f_gap : gmt_M_to_inch (GMT, txt_b);
5780 
5781 	S->f.f_symbol = GMT_FRONT_FAULT;	/* Default is the fault symbol */
5782 	S->f.f_angle = 20.0;			/* Default slip arrow angle */
5783 	S->f.f_sense = GMT_FRONT_CENTERED;	/* Default is centered symbols unless +l or +r is found */
5784 	S->f.f_pen = 0;				/* Draw outline with pen set via -W, i.e., same as frontline */
5785 	while ((gmt_strtok (&text[k], "+", &pos, p))) {	/* Parse any +<modifier> statements */
5786 		switch (p[0]) {
5787 			case 'b':	S->f.f_symbol = GMT_FRONT_BOX;		break;	/* [half-]square front */
5788 			case 'c':	S->f.f_symbol = GMT_FRONT_CIRCLE;	break;	/* [half-]circle front */
5789 			case 'f':	S->f.f_symbol = GMT_FRONT_FAULT;	break;	/* Fault front */
5790 			case 'i':	S->f.invisible = true;				break;	/* Do not draw line */
5791 			case 'l':	S->f.f_sense  = GMT_FRONT_LEFT;		break;	/* Symbols to the left */
5792 			case 'r':	S->f.f_sense  = GMT_FRONT_RIGHT;	break;	/* Symbols to the right */
5793 			case 's':	S->f.f_symbol = GMT_FRONT_SLIP;				/* Strike-slip front */
5794 						if (p[1]) S->f.f_angle = atof (&p[1]);		/* Set slip arrow angle */
5795 						break;
5796 			case 'S':	S->f.f_symbol = GMT_FRONT_SLIPC;		/* Strike-slip front */
5797 					S->f.f_angle = (p[1]) ? atof (&p[1]) : 40.0;	/* Set curved slip arrow angle [40] */
5798 					break;
5799 			case 't':	S->f.f_symbol = GMT_FRONT_TRIANGLE;	break;	/* Triangle front */
5800 			case 'v':	S->f.f_symbol = GMT_FRONT_ITRIANGLE;	break;	/* Inverted triangle front */
5801 			case 'o':	S->f.f_off = gmt_M_to_inch (GMT, &p[1]);	break;	/* Symbol offset along line */
5802 			case 'p':	if (p[1]) {	/* Get alternate pen for front-symbol outline [-W] */
5803 						if (gmt_getpen (GMT, &p[1], &S->f.pen)) {
5804 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +p<pen> argument %s\n", &p[1]);
5805 							error++;
5806 						}
5807 						else
5808 							S->f.f_pen = +1;
5809 					}
5810 					else	/* Turn off outline */
5811 						S->f.f_pen = -1;
5812 					break;
5813 			default:
5814 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sf: Bad modifier +%c\n", p[0]);
5815 				error++;	break;
5816 		}
5817 	}
5818 
5819 	return (error);
5820 }
5821 
5822 /*! Parse the arguments given to -Sl.  The allowed syntax is:
5823  * -Sl<size>+t<text>[+f<font<][+j<justify>] */
gmtinit_parse_text(struct GMT_CTRL * GMT,char * text,struct GMT_SYMBOL * S)5824 GMT_LOCAL int gmtinit_parse_text (struct GMT_CTRL *GMT, char *text, struct GMT_SYMBOL *S) {
5825 
5826 	unsigned int pos = 0, k, j, slash, error = 0;
5827 	if ((!strstr (text, "+t") && strchr (text, '/')) || strchr (text, '%')) {	/* GMT4 syntax */
5828 		char *c = NULL;
5829 		if (gmt_M_compat_check (GMT, 4)) {
5830 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -Sl: Sl<size>/<string>[%<font>] is deprecated syntax\n");
5831 			if ((c = strchr (text, '%'))) {	/* Gave font name or number, too */
5832 				*c = 0;	/* Chop off the %font info */
5833 				c++;		/* Go to next character */
5834 				if (gmt_getfont (GMT, c, &S->font)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "-Sl contains bad font (set to %s)\n", gmt_putfont (GMT, &S->font));
5835 			}
5836 			/* Look for a slash that separates size and string: */
5837 			for (j = 1, slash = 0; text[j] && !slash; j++) if (text[j] == '/') slash = j;
5838 			/* Set j to the first char in the string: */
5839 			j = slash + 1;
5840 			/* Copy string characters */
5841 			k = 0;
5842 			while (text[j] && text[j] != ' ' && k < (GMT_LEN256-1)) S->string[k++] = text[j++];
5843 			S->string[k] = '\0';
5844 			if (!k) {
5845 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sl: No string given\n");
5846 				error++;
5847 			}
5848 		}
5849 		else {	/* Not accept it unless under compatibility mode 4 */
5850 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sl: Usage is -Sl[<size>]+t<string>[+f<font>][+j<justify]\n");
5851 			error++;
5852 		}
5853 	}
5854 	else {	/* GMT5 syntax */
5855 		char p[GMT_BUFSIZ];
5856 		for (k = 0; text[k] && text[k] != '+'; k++);	/* Either find the first plus or run out or chars; should at least find +t */
5857 		if (!text[k]) {
5858 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sl: No string information given\n");
5859 			return (1);
5860 		}
5861 		while ((gmt_strtok (&text[k], "+", &pos, p))) {	/* Parse any +<modifier> statements */
5862 			switch (p[0]) {
5863 				case 'f':	/* Change font */
5864 					if (gmt_getfont (GMT, &p[1], &S->font))
5865 						GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -Sl contains bad +<font> modifier (set to %s)\n", gmt_putfont (GMT, &S->font));
5866 					break;
5867 				case 'j':	S->justify = gmt_just_decode (GMT, &p[1], PSL_NO_DEF);	break;	/* text justification */
5868 				case 't':	strncpy (S->string, &p[1], GMT_LEN256-1);	break;	/* Get the symbol text */
5869 				default:
5870 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sl: Bad modifier +%c\n", p[0]);
5871 					error++;
5872 					break;
5873 			}
5874 		}
5875 	}
5876 
5877 	return (error);
5878 }
5879 
5880 /*! Loads the m_per_unit array with the scaling factors that converts various units to meters.
5881  * Also sets all the names for the units.
5882  * See gmt_project.h for enums that can be used as array indices) */
5883 
gmtinit_init_unit_conversion(struct GMT_CTRL * GMT)5884 GMT_LOCAL void gmtinit_init_unit_conversion (struct GMT_CTRL *GMT) {
5885 
5886 	GMT->current.proj.m_per_unit[GMT_IS_METER]		= 1.0;				/* m in m */
5887 	GMT->current.proj.m_per_unit[GMT_IS_KM]			= METERS_IN_A_KM;		/* m in km */
5888 	GMT->current.proj.m_per_unit[GMT_IS_MILE]		= METERS_IN_A_MILE;		/* m in miles */
5889 	GMT->current.proj.m_per_unit[GMT_IS_NAUTICAL_MILE]	= METERS_IN_A_NAUTICAL_MILE;	/* m in nautical mile */
5890 	GMT->current.proj.m_per_unit[GMT_IS_INCH]		= 0.0254;			/* m in inch */
5891 	GMT->current.proj.m_per_unit[GMT_IS_CM]			= 0.01;				/* m in cm */
5892 	GMT->current.proj.m_per_unit[GMT_IS_PT]			= 0.0254 / 72.0;		/* m in point */
5893 	GMT->current.proj.m_per_unit[GMT_IS_FOOT]		= METERS_IN_A_FOOT;		/* m in foot */
5894 	GMT->current.proj.m_per_unit[GMT_IS_SURVEY_FOOT]	= METERS_IN_A_SURVEY_FOOT;	/* m in US Survey foot */
5895 
5896 	strcpy (GMT->current.proj.unit_name[GMT_IS_METER],		"m");
5897 	strcpy (GMT->current.proj.unit_name[GMT_IS_KM],		 	"km");
5898 	strcpy (GMT->current.proj.unit_name[GMT_IS_MILE],		"mile");
5899 	strcpy (GMT->current.proj.unit_name[GMT_IS_NAUTICAL_MILE], 	"nautical mile");
5900 	strcpy (GMT->current.proj.unit_name[GMT_IS_INCH],		"inch");
5901 	strcpy (GMT->current.proj.unit_name[GMT_IS_CM],		 	"cm");
5902 	strcpy (GMT->current.proj.unit_name[GMT_IS_PT],		 	"point");
5903 	strcpy (GMT->current.proj.unit_name[GMT_IS_FOOT],		"foot");
5904 	strcpy (GMT->current.proj.unit_name[GMT_IS_SURVEY_FOOT],	"survey foot");
5905 }
5906 
5907 /*! . */
gmtinit_scanf_epoch(struct GMT_CTRL * GMT,char * s,int64_t * rata_die,double * t0)5908 GMT_LOCAL int gmtinit_scanf_epoch (struct GMT_CTRL *GMT, char *s, int64_t *rata_die, double *t0) {
5909 
5910 	/* Read a string which must be in one of these forms:
5911 		[-]yyyy-mm-dd[T| [hh:mm:ss.sss]]
5912 		[-]yyyy-Www-d[T| [hh:mm:ss.sss]]
5913 	   Hence, data and clock can be separated by 'T' or ' ' (space), and the clock string is optional.
5914 	   In fact, seconds can be decimal or integer, or missing. Minutes and hour are optional too.
5915 	   Examples: 2000-01-01, 2000-01-01T, 2000-01-01 00:00, 2000-01-01T00, 2000-01-01T00:00:00.000
5916 	*/
5917 
5918 	double ss = 0.0;
5919 	int i, yy, mo, dd, hh = 0, mm = 0;
5920 	int64_t rd;
5921 	char tt[GMT_LEN8];
5922 
5923 	i = 0;
5924 	while (s[i] && s[i] == ' ') i++;
5925 	if (!(s[i])) return (GMT_NOTSET);
5926 	if (strchr (&s[i], 'W') ) {	/* ISO calendar string, date with or without clock */
5927 		if (sscanf (&s[i], "%5d-W%2d-%1d%[^0-9:-]%2d:%2d:%lf", &yy, &mo, &dd, tt, &hh, &mm, &ss) < 3) return (GMT_NOTSET);
5928 		if (gmtlib_iso_ywd_is_bad (yy, mo, dd) ) return (GMT_NOTSET);
5929 		rd = gmtlib_rd_from_iywd (GMT, yy, mo, dd);
5930 	}
5931 	else {				/* Gregorian calendar string, date with or without clock */
5932 		if (sscanf (&s[i], "%5d-%2d-%2d%[^0-9:-]%2d:%2d:%lf", &yy, &mo, &dd, tt, &hh, &mm, &ss) < 3) return (GMT_NOTSET);
5933 		if (gmtlib_g_ymd_is_bad (yy, mo, dd) ) return (GMT_NOTSET);
5934 		rd = gmt_rd_from_gymd (GMT, yy, mo, dd);
5935 	}
5936 	if (gmt_M_hms_is_bad (hh, mm, ss)) return (GMT_NOTSET);
5937 
5938 	*rata_die = rd;								/* Rata day number of epoch */
5939 	*t0 =  (GMT_HR2SEC_F * hh + GMT_MIN2SEC_F * mm + ss) * GMT_SEC2DAY;	/* Fractional day (0<= t0 < 1) since rata_die of epoch */
5940 	return (GMT_NOERROR);
5941 }
5942 
5943 /*! Scan a PostScript encoding string and look for degree, ring and other special encodings.
5944  * Use Brute Force and Ignorance.
5945  * Scanning to find the codes for 7 symbols we plot but whose code depends on character set
5946  * (ring, degree, colon, squote, dquote, minus, hyphen).
5947  */
5948 
gmtinit_load_encoding(struct GMT_CTRL * GMT)5949 GMT_LOCAL int gmtinit_load_encoding (struct GMT_CTRL *GMT) {
5950 	char symbol[GMT_LEN256] = {""};
5951 	unsigned int code = 0, pos = 0;
5952 	int k;
5953 	struct GMT_ENCODING *enc = &GMT->current.setting.ps_encoding;
5954 
5955 	snprintf (symbol, GMT_LEN256, "PSL_%s", enc->name);	/* Prepend the PSL_ prefix */
5956 	if ((k = gmtinit_get_psl_encoding (symbol)) == GMT_NOTSET) {
5957 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot find the %s encoding\n", symbol);
5958 		return GMT_RUNTIME_ERROR;
5959 	}
5960 
5961 	while ((gmt_strtok (PSL_ISO_encoding[k], " /\t\n", &pos, symbol))) {	/* Here, symbol is reused */
5962 		if (strcmp (symbol, "[") == 0)	/* We have found the start of the encoding array. */ {
5963 			code = 0;
5964 			continue;
5965 		}
5966 		if (strcmp (symbol, "degree") == 0)
5967 			enc->code[gmt_degree] = code;
5968 		else if (strcmp (symbol, "ring") == 0)
5969 			enc->code[gmt_ring] = code;
5970 		else if (strcmp (symbol, "quotedbl") == 0)
5971 			enc->code[gmt_dquote] = code;
5972 		else if (strcmp (symbol, "quotesingle") == 0)
5973 			enc->code[gmt_squote] = code;
5974 		else if (strcmp (symbol, "colon") == 0)
5975 			enc->code[gmt_colon] = code;
5976 		else if (strcmp (symbol, "minus") == 0)
5977 			enc->code[gmt_minus] = code;
5978 		else if (strcmp (symbol, "hyphen") == 0)
5979 			enc->code[gmt_hyphen] = code;
5980 		code++;
5981 	}
5982 
5983 	return (GMT_NOERROR);
5984 }
5985 
gmtinit_def_us_locale(struct GMT_CTRL * GMT)5986 GMT_LOCAL void gmtinit_def_us_locale (struct GMT_CTRL *GMT) {
5987 
5988 	/* GMT Time language file for US (english) mode [US] */
5989 
5990 	/* Month record */
5991 	strcpy (GMT->current.language.month_name[0][0], "January"); strcpy (GMT->current.language.month_name[1][0], "Jan");
5992 	strcpy (GMT->current.language.month_name[2][0], "J");       strcpy (GMT->current.language.month_name[3][0], "JAN");
5993 	strcpy (GMT->current.language.month_name[0][1], "February");strcpy (GMT->current.language.month_name[1][1], "Feb");
5994 	strcpy (GMT->current.language.month_name[2][1], "F");       strcpy (GMT->current.language.month_name[3][1], "FEB");
5995 	strcpy (GMT->current.language.month_name[0][2], "March");   strcpy (GMT->current.language.month_name[1][2], "Mar");
5996 	strcpy (GMT->current.language.month_name[2][2], "M");       strcpy (GMT->current.language.month_name[3][2], "MAR");
5997 	strcpy (GMT->current.language.month_name[0][3], "April");   strcpy (GMT->current.language.month_name[1][3], "Apr");
5998 	strcpy (GMT->current.language.month_name[2][3], "A");       strcpy (GMT->current.language.month_name[3][3], "APR");
5999 	strcpy (GMT->current.language.month_name[0][4], "May");     strcpy (GMT->current.language.month_name[1][4], "May");
6000 	strcpy (GMT->current.language.month_name[2][4], "M");       strcpy (GMT->current.language.month_name[3][4], "MAY");
6001 	strcpy (GMT->current.language.month_name[0][5], "June");    strcpy (GMT->current.language.month_name[1][5], "Jun");
6002 	strcpy (GMT->current.language.month_name[2][5], "J");       strcpy (GMT->current.language.month_name[3][5], "JUN");
6003 	strcpy (GMT->current.language.month_name[0][6], "July");    strcpy (GMT->current.language.month_name[1][6], "Jul");
6004 	strcpy (GMT->current.language.month_name[2][6], "J");       strcpy (GMT->current.language.month_name[3][6], "JUL");
6005 	strcpy (GMT->current.language.month_name[0][7], "August");  strcpy (GMT->current.language.month_name[1][7], "Aug");
6006 	strcpy (GMT->current.language.month_name[2][7], "A");       strcpy (GMT->current.language.month_name[3][7], "AUG");
6007 	strcpy (GMT->current.language.month_name[0][8], "September");strcpy(GMT->current.language.month_name[1][8], "Sep");
6008 	strcpy (GMT->current.language.month_name[2][8], "S");       strcpy (GMT->current.language.month_name[3][8], "SEP");
6009 	strcpy (GMT->current.language.month_name[0][9], "October"); strcpy (GMT->current.language.month_name[1][9], "Oct");
6010 	strcpy (GMT->current.language.month_name[2][9], "O");       strcpy (GMT->current.language.month_name[3][9], "OCT");
6011 	strcpy (GMT->current.language.month_name[0][10],"November");strcpy (GMT->current.language.month_name[1][10],"Nov");
6012 	strcpy (GMT->current.language.month_name[2][10],"N");       strcpy (GMT->current.language.month_name[3][10],"NOV");
6013 	strcpy (GMT->current.language.month_name[0][11],"December");strcpy (GMT->current.language.month_name[1][11],"Dec");
6014 	strcpy (GMT->current.language.month_name[2][11],"D");       strcpy (GMT->current.language.month_name[3][11],"DEC");
6015 
6016 	/* Week name record */
6017 	strcpy (GMT->current.language.week_name[0], "Week");        strcpy (GMT->current.language.week_name[1], "Wk");
6018 	strcpy (GMT->current.language.week_name[2], "W");
6019 
6020 	/* Weekday record */
6021 	strcpy (GMT->current.language.day_name[0][0], "Sunday");   strcpy (GMT->current.language.day_name[1][0], "Sun");
6022 	strcpy (GMT->current.language.day_name[2][0], "S");
6023 	strcpy (GMT->current.language.day_name[0][1], "Monday");   strcpy (GMT->current.language.day_name[1][1], "Mon");
6024 	strcpy (GMT->current.language.day_name[2][1], "M");
6025 	strcpy (GMT->current.language.day_name[0][2], "Tuesday");  strcpy (GMT->current.language.day_name[1][2], "Tue");
6026 	strcpy (GMT->current.language.day_name[2][2], "T");
6027 	strcpy (GMT->current.language.day_name[0][3], "Wednesday");strcpy (GMT->current.language.day_name[1][3], "Wed");
6028 	strcpy (GMT->current.language.day_name[2][3], "W");
6029 	strcpy (GMT->current.language.day_name[0][4], "Thursday"); strcpy (GMT->current.language.day_name[1][4], "Thu");
6030 	strcpy (GMT->current.language.day_name[2][4], "T");
6031 	strcpy (GMT->current.language.day_name[0][5], "Friday");   strcpy (GMT->current.language.day_name[1][5], "Fri");
6032 	strcpy (GMT->current.language.day_name[2][5], "F");
6033 	strcpy (GMT->current.language.day_name[0][6], "Saturday"); strcpy (GMT->current.language.day_name[1][6], "Sat");
6034 	strcpy (GMT->current.language.day_name[2][6], "S");
6035 
6036 	/* Compass name record */
6037 	strcpy (GMT->current.language.cardinal_name[0][0], "West"); strcpy (GMT->current.language.cardinal_name[1][0], "W");
6038 	strcpy (GMT->current.language.cardinal_name[2][0], "W");
6039 	strcpy (GMT->current.language.cardinal_name[0][1], "East"); strcpy (GMT->current.language.cardinal_name[1][1], "E");
6040 	strcpy (GMT->current.language.cardinal_name[2][1], "E");
6041 	strcpy (GMT->current.language.cardinal_name[0][2], "South"); strcpy (GMT->current.language.cardinal_name[1][2], "S");
6042 	strcpy (GMT->current.language.cardinal_name[2][2], "S");
6043 	strcpy (GMT->current.language.cardinal_name[0][3], "North"); strcpy (GMT->current.language.cardinal_name[1][3], "N");
6044 	strcpy (GMT->current.language.cardinal_name[2][3], "N");
6045 }
6046 
6047 /*! . */
gmt_get_V(char arg)6048 int gmt_get_V (char arg) {
6049 	/* Parse the verbosity selection.  Using -Vqewticd [GMT_VERBOSE_CODES], but backwards compatible with -Vqntcvld */
6050 	int mode = GMT_MSG_QUIET;
6051 	switch (arg) {
6052 		case 'q': case '0': mode = GMT_MSG_QUIET;	break;	/* -Vq */
6053 		case 'e': case 'n': mode = GMT_MSG_ERROR;	break;	/* -Ve [was -Vn] */
6054 		case 'w': case 'v': case '2':mode = GMT_MSG_WARNING;	break;	/* -Vw [was -Vv] */
6055 		case 't': mode = GMT_MSG_TICTOC;  break;	/* -Vt */
6056 		case 'i': case 'l': case '3':  case '\0': mode = GMT_MSG_INFORMATION;	break;	/* -V[i] [was -Vl] */
6057 		case 'c': case '1': mode = GMT_MSG_COMPAT;	break;	/* -Vc */
6058 		case 'd': case '4': mode = GMT_MSG_DEBUG;	break;	/* -Vd */
6059 		default: mode = GMT_NOTSET;
6060 	}
6061 	return mode;
6062 }
6063 
6064 /*! . */
gmtinit_parse_V_option(struct GMT_CTRL * GMT,char arg)6065 GMT_LOCAL int gmtinit_parse_V_option (struct GMT_CTRL *GMT, char arg) {
6066 	int mode = gmt_get_V (arg);
6067 	if (mode < 0) return true;	/* Error in parsing */
6068 	GMT->current.setting.verbose = (unsigned int)mode;
6069 	return 0;
6070 }
6071 
6072 /*! . */
gmtinit_key_lookup(char * name,char ** list,unsigned int n)6073 GMT_LOCAL unsigned int gmtinit_key_lookup (char *name, char **list, unsigned int n) {
6074 	unsigned int i;
6075 
6076 	for (i = 0; i < n && strcmp (name, list[i]); i++);
6077 	return (i);
6078 }
6079 
gmt_set_length_unit(struct GMT_CTRL * GMT,char unit)6080 int gmt_set_length_unit (struct GMT_CTRL *GMT, char unit) {
6081 	/* UPdate the current setting for length unit index */
6082 	switch (unit) {
6083 		case 'c': GMT->current.setting.proj_length_unit = GMT_CM;   break;
6084 		case 'i': GMT->current.setting.proj_length_unit = GMT_INCH; break;
6085 		case 'p': GMT->current.setting.proj_length_unit = GMT_PT;   break;
6086 		default:
6087 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized projected length unit given (%c)!\n", unit);
6088 			return GMT_NOTSET;
6089 	}
6090 	return (GMT_NOERROR);
6091 }
6092 
6093 /*! . */
gmtinit_get_language(struct GMT_CTRL * GMT)6094 GMT_LOCAL int gmtinit_get_language (struct GMT_CTRL *GMT) {
6095 	FILE *fp = NULL;
6096 	char file[PATH_MAX] = {""}, line[GMT_BUFSIZ] = {""}, full[16] = {""}, abbrev[16] = {""}, c[16] = {""}, dwu;
6097 	char *months[12];
6098 
6099 	int i, nm = 0, nw = 0, nu = 0, nc = 0;
6100 
6101 	if (!strcmp(GMT->current.setting.language, "us")) {
6102 		gmtinit_def_us_locale (GMT);
6103 		return 0;
6104 	}
6105 
6106 	gmt_M_memset (months, 12, char *);
6107 
6108 	snprintf (line, GMT_BUFSIZ, "gmt_%s", GMT->current.setting.language);
6109 	gmt_getsharepath (GMT, "localization", line, ".locale", file, R_OK);
6110 	if ((fp = fopen (file, "r")) == NULL) {
6111 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Could not load language %s - revert to us (English)!\n",
6112 			GMT->current.setting.language);
6113 		gmt_getsharepath (GMT, "localization", "gmt_us", ".locale", file, R_OK);
6114 		if ((fp = fopen (file, "r")) == NULL) {
6115 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find %s!\n", file);
6116 			return GMT_ERROR_ON_FOPEN;
6117 		}
6118 		strcpy (GMT->current.setting.language, "us");
6119 	}
6120 
6121 	while (fgets (line, GMT_BUFSIZ, fp)) {
6122 		if (line[0] == '#' || line[0] == '\n') continue;
6123 		sscanf (line, "%c %d %s %s %s", &dwu, &i, full, abbrev, c);
6124 		if (i <= 0) {
6125 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Index in %s is zero or negative!\n", line);
6126 			fclose (fp);
6127 			return GMT_NOT_A_VALID_PARAMETER;
6128 		}
6129 		if (dwu == 'M') {	/* Month record */
6130 			if (i > 12) {
6131 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Month index in %s exceeds 12!\n", line);
6132 				fclose (fp);
6133 				return GMT_NOT_A_VALID_PARAMETER;
6134 			}
6135 			strncpy (GMT->current.language.month_name[0][i-1], full, GMT_LEN16-1);
6136 			strncpy (GMT->current.language.month_name[1][i-1], abbrev, GMT_LEN16-1);
6137 			strncpy (GMT->current.language.month_name[2][i-1], c, GMT_LEN16-1);
6138 			gmt_str_toupper(abbrev);
6139 			strncpy (GMT->current.language.month_name[3][i-1], abbrev, GMT_LEN16-1);
6140 			nm += i;
6141 		}
6142 		else if (dwu == 'W') {	/* Weekday record */
6143 			if (i > 7) {
6144 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Weekday index in %s exceeds 7!\n", line);
6145 				fclose (fp);
6146 				return GMT_NOT_A_VALID_PARAMETER;
6147 			}
6148 			strncpy (GMT->current.language.day_name[0][i-1], full, GMT_LEN16-1);
6149 			strncpy (GMT->current.language.day_name[1][i-1], abbrev, GMT_LEN16-1);
6150 			strncpy (GMT->current.language.day_name[2][i-1], c, GMT_LEN16-1);
6151 			nw += i;
6152 		}
6153 		else if (dwu == 'U') {			/* Week name record */
6154 			strncpy (GMT->current.language.week_name[0], full, GMT_LEN16-1);
6155 			strncpy (GMT->current.language.week_name[1], abbrev, GMT_LEN16-1);
6156 			strncpy (GMT->current.language.week_name[2], c, GMT_LEN16-1);
6157 			nu += i;
6158 		}
6159 		else {	/* Compass name record */
6160 			if (i > 4) {
6161 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cardinal name index in %s exceeds 4!\n", line);
6162 				fclose (fp);
6163 				return GMT_NOT_A_VALID_PARAMETER;
6164 			}
6165 			strncpy (GMT->current.language.cardinal_name[0][i-1], full, GMT_LEN16-1);
6166 			strncpy (GMT->current.language.cardinal_name[1][i-1], abbrev, GMT_LEN16-1);
6167 			strncpy (GMT->current.language.cardinal_name[2][i-1], c, GMT_LEN16-1);
6168 			nc += i;
6169 		}
6170 	}
6171 	fclose (fp);
6172 	if (!(nm == 78 && nw == 28 && nu == 1 && nc == 10)) {	/* Sums of 1-12, 1-7, 1, and 1-4, respectively */
6173 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Mismatch between expected and actual contents in %s!\n", file);
6174 		return GMT_NOT_A_VALID_PARAMETER;
6175 	}
6176 	return (GMT_NOERROR);
6177 }
6178 
6179 /*! . */
gmtinit_conf_classic_US(struct GMT_CTRL * GMT)6180 GMT_LOCAL void gmtinit_conf_classic_US (struct GMT_CTRL *GMT) {
6181 	int i, case_val;
6182 	/* Update the settings to US where they differ from standard SI settings:
6183 	 *     Setting			SI			US
6184 	 * --------------------------------------------
6185 	 * PROJ_LENGTH_UNIT		cm	 		inch
6186 	 * PS_CHAR_ENCODING		ISOLatin1+	Standard+
6187 	 * PS_MEDIA				a4			letter
6188 	 * TIME_WEEK_START		Monday		Sunday
6189 	 */
6190 
6191 	/* PROJ_LENGTH_UNIT */
6192 	case_val = gmt_hash_lookup (GMT, "PROJ_LENGTH_UNIT", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
6193 	if (case_val >= 0) GMT_keyword_updated[case_val] = true;
6194 	GMT->current.setting.proj_length_unit = GMT_INCH;
6195 	/* PS_CHAR_ENCODING */
6196 	case_val = gmt_hash_lookup (GMT, "PS_CHAR_ENCODING", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
6197 	if (case_val >= 0) GMT_keyword_updated[case_val] = true;
6198 	strcpy (GMT->current.setting.ps_encoding.name, "Standard+");
6199 	gmtinit_load_encoding (GMT);
6200 	/* PS_MEDIA */
6201 	i = gmtinit_key_lookup ("letter", GMT_media_name, GMT_N_MEDIA);
6202 	GMT->current.setting.ps_def_page_size[0] = GMT_media[i].width;
6203 	GMT->current.setting.ps_def_page_size[1] = GMT_media[i].height;
6204 	if (GMT->current.setting.run_mode == GMT_MODERN)
6205 		gmtinit_setautopagesize (GMT);
6206 	else {
6207 		case_val = gmt_hash_lookup (GMT, "PS_MEDIA", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
6208 		if (case_val >= 0) GMT_keyword_updated[case_val] = true;
6209 		/* Use the specified standard format */
6210 		GMT->current.setting.ps_media = i;
6211 		GMT->current.setting.ps_page_size[0] = GMT_media[i].width;
6212 		GMT->current.setting.ps_page_size[1] = GMT_media[i].height;
6213 	}
6214 	/* TIME_WEEK_START */
6215 	case_val = gmt_hash_lookup (GMT, "TIME_WEEK_START", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
6216 	if (case_val >= 0) GMT_keyword_updated[case_val] = true;
6217 	GMT->current.setting.time_week_start = gmtinit_key_lookup ("Sunday", GMT_weekdays, 7);
6218 }
6219 
6220 /*! . */
gmtinit_conf_classic(struct GMT_CTRL * GMT)6221 GMT_LOCAL void gmtinit_conf_classic (struct GMT_CTRL *GMT) {
6222 	int i, error = 0;
6223 	char path[PATH_MAX] = {""};
6224 	double const pt = 1.0/72.0;	/* points to inch */
6225 	/* Initialize all the settings to standard SI settings */
6226 
6227 		/* FORMAT group */
6228 
6229 	/* FORMAT_CLOCK_IN */
6230 	strcpy(GMT->current.setting.format_clock_in, "hh:mm:ss");
6231 	gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_in, &GMT->current.io.clock_input, 0);
6232 	/* FORMAT_DATE_IN */
6233 	strcpy (GMT->current.setting.format_date_in, "yyyy-mm-dd");
6234 	gmtlib_date_C_format (GMT, GMT->current.setting.format_date_in, &GMT->current.io.date_input, 0);
6235 	/* FORMAT_CLOCK_OUT */
6236 	strcpy (GMT->current.setting.format_clock_out, "hh:mm:ss");
6237 	gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_out, &GMT->current.io.clock_output, 1);
6238 	/* FORMAT_DATE_OUT */
6239 	strcpy (GMT->current.setting.format_date_out, "yyyy-mm-dd");
6240 	gmtlib_date_C_format (GMT, GMT->current.setting.format_date_out, &GMT->current.io.date_output, 1);
6241 	/* FORMAT_GEO_OUT */
6242 	strcpy (GMT->current.setting.format_geo_out, "D");
6243 	gmtlib_geo_C_format (GMT);	/* Can fail if FORMAT_FLOAT_OUT not yet set, but is repeated at the end of gmt_begin */
6244 	/* FORMAT_CLOCK_MAP */
6245 	strcpy (GMT->current.setting.format_clock_map, "hh:mm:ss");
6246 	gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_map, &GMT->current.plot.calclock.clock, 2);
6247 	/* FORMAT_DATE_MAP */
6248 	strcpy (GMT->current.setting.format_date_map, "yyyy-mm-dd");
6249 	gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
6250 	/* FORMAT_GEO_MAP */
6251 	strcpy (GMT->current.setting.format_geo_map, "ddd:mm:ss");
6252 	gmtlib_plot_C_format (GMT);	/* Update format statements */
6253 	/* FORMAT_TIME_PRIMARY_MAP */
6254 	strcpy (GMT->current.setting.format_time[GMT_PRIMARY], "full");
6255 	/* FORMAT_TIME_SECONDARY_MAP */
6256 	strcpy (GMT->current.setting.format_time[GMT_SECONDARY], "full");
6257 	/* FORMAT_FLOAT_OUT */
6258 	strcpy (GMT->current.setting.format_float_out, "%.12g");
6259 	strcpy (GMT->current.setting.format_float_out_orig, "%.12g");
6260 	/* FORMAT_FLOAT_MAP */
6261 	strcpy (GMT->current.setting.format_float_map, "%.12g");
6262 	/* FORMAT_TIME_STAMP */
6263 	strcpy (GMT->current.setting.format_time_stamp, "%Y %b %d %H:%M:%S");
6264 
6265 		/* FONT group */
6266 
6267 	/* FONT_ANNOT_PRIMARY */
6268 	error += gmt_getfont (GMT, "12p,Helvetica,black", &GMT->current.setting.font_annot[GMT_PRIMARY]);
6269 	GMT->current.setting.given_unit[GMTCASE_FONT_ANNOT_PRIMARY] = 'p';
6270 	/* FONT_ANNOT_SECONDARY */
6271 	error += gmt_getfont (GMT, "14p,Helvetica,black", &GMT->current.setting.font_annot[GMT_SECONDARY]);
6272 	GMT->current.setting.given_unit[GMTCASE_FONT_ANNOT_SECONDARY] = 'p';
6273 	/* FONT_HEADING */
6274 	error += gmt_getfont (GMT, "32p,Helvetica,black", &GMT->current.setting.font_heading);
6275 	GMT->current.setting.given_unit[GMTCASE_FONT_HEADING] = 'p';
6276 	/* FONT_SUBTITLE */
6277 	error += gmt_getfont (GMT, "18p,Helvetica,black", &GMT->current.setting.font_subtitle);
6278 	GMT->current.setting.given_unit[GMTCASE_FONT_SUBTITLE] = 'p';
6279 	/* FONT_TITLE */
6280 	error += gmt_getfont (GMT, "24p,Helvetica,black", &GMT->current.setting.font_title);
6281 	GMT->current.setting.given_unit[GMTCASE_FONT_TITLE] = 'p';
6282 	/* FONT_LABEL */
6283 	error += gmt_getfont (GMT, "16p,Helvetica,black", &GMT->current.setting.font_label);
6284 	GMT->current.setting.given_unit[GMTCASE_FONT_LABEL] = 'p';
6285 	/* FONT_TAG */
6286 	error += gmt_getfont (GMT, "20p,Helvetica,black", &GMT->current.setting.font_tag);
6287 	GMT->current.setting.given_unit[GMTCASE_FONT_TAG] = 'p';
6288 	/* FONT_LOGO */
6289 	error += gmt_getfont (GMT, "8p,Helvetica,black", &GMT->current.setting.font_logo);
6290 	GMT->current.setting.given_unit[GMTCASE_FONT_LOGO] = 'p';
6291 
6292 		/* MAP group */
6293 
6294 	/* MAP_ANNOT_OFFSET_PRIMARY, MAP_ANNOT_OFFSET_SECONDARY */
6295 	GMT->current.setting.map_annot_offset[GMT_PRIMARY] = GMT->current.setting.map_annot_offset[GMT_SECONDARY] = 5 * pt; /* 5p */
6296 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_OFFSET_PRIMARY] = 'p';
6297 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_OFFSET_SECONDARY] = 'p';
6298 	/* MAP_ANNOT_OBLIQUE */
6299 	GMT->current.setting.map_annot_oblique = GMT_OBL_ANNOT_LON_HORIZONTAL | GMT_OBL_ANNOT_LAT_HORIZONTAL | GMT_OBL_ANNOT_EXTEND_TICKS;
6300 	/* MAP_ANNOT_MIN_ANGLE */
6301 	GMT->current.setting.map_annot_min_angle = 20;
6302 	/* MAP_ANNOT_MIN_SPACING */
6303 	GMT->current.setting.map_annot_min_spacing = 0; /* p */
6304 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_MIN_SPACING] = 'p';
6305 	strncpy (GMT->current.setting.map_annot_min_spacing_txt, "0p", GMT_LEN16);
6306 	/* MAP_ANNOT_ORTHO */
6307 	strcpy (GMT->current.setting.map_annot_ortho, "we");
6308 	/* MAP_DEGREE_SYMBOL (degree) */
6309 	GMT->current.setting.map_degree_symbol = gmt_degree;
6310 	/* MAP_FRAME_AXES */
6311 	strcpy (GMT->current.setting.map_frame_axes, "WESNZ");
6312 	error += gmtinit_decode5_wesnz (GMT, "WESNZ", false);
6313 	/* MAP_DEFAULT_PEN */
6314 	error += gmt_getpen (GMT, "default,black", &GMT->current.setting.map_default_pen);
6315 	/* MAP_FRAME_PEN */
6316 	error += gmt_getpen (GMT, "thicker,black", &GMT->current.setting.map_frame_pen);
6317 	/* MAP_FRAME_PERCENT */
6318 	GMT->current.setting.map_frame_percent = 100.0;
6319 	/* MAP_FRAME_TYPE (fancy) */
6320 	GMT->current.setting.map_frame_type = GMT_IS_FANCY;
6321 	GMT->current.setting.map_graph_extension_unit = GMT_GRAPH_EXTENSION_UNIT;	/* Defaults for graph */
6322 	GMT->current.setting.map_graph_extension = GMT_GRAPH_EXTENSION;
6323 	/* MAP_FRAME_WIDTH */
6324 	GMT->current.setting.map_frame_width = 5 * pt; /* 5p */
6325 	GMT->current.setting.given_unit[GMTCASE_MAP_FRAME_WIDTH] = 'p';
6326 	/* MAP_GRID_CROSS_SIZE_PRIMARY, MAP_GRID_CROSS_SIZE_SECONDARY */
6327 	GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] = GMT->current.setting.map_grid_cross_size[GMT_SECONDARY] = 0; /* 0p */
6328 	GMT->current.setting.map_grid_cross_type[GMT_PRIMARY] = GMT->current.setting.map_grid_cross_type[GMT_SECONDARY] = GMT_CROSS_NORMAL; /* regular cross type */
6329 	GMT->current.setting.given_unit[GMTCASE_MAP_GRID_CROSS_SIZE_PRIMARY] = 'p';
6330 	GMT->current.setting.given_unit[GMTCASE_MAP_GRID_CROSS_SIZE_SECONDARY] = 'p';
6331 	/* MAP_GRID_PEN_PRIMARY */
6332 	error += gmt_getpen (GMT, "default,black", &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
6333 	/* MAP_GRID_PEN_SECONDARY */
6334 	error += gmt_getpen (GMT, "thinner,black", &GMT->current.setting.map_grid_pen[GMT_SECONDARY]);
6335 	/* MAP_HEADING_OFFSET */
6336 	GMT->current.setting.map_heading_offset = 18 * pt;	/* 18p */
6337 	GMT->current.setting.given_unit[GMTCASE_MAP_HEADING_OFFSET] = 'p';
6338 	/* MAP_LABEL_OFFSET */
6339 	GMT->current.setting.map_label_offset[GMT_X] = GMT->current.setting.map_label_offset[GMT_Y] = 8 * pt;	/* 8p */
6340 	GMT->current.setting.given_unit[GMTCASE_MAP_LABEL_OFFSET] = 'p';
6341 	/* MAP_LINE_STEP */
6342 	GMT->current.setting.map_line_step = 0.75 * pt;	/* 0.75p */
6343 	GMT->current.setting.given_unit[GMTCASE_MAP_LINE_STEP] = 'p';
6344 	/* MAP_LOGO */
6345 	GMT->current.setting.map_logo = false;
6346 	/* MAP_LOGO_POS */
6347 	GMT->current.setting.map_logo_justify = PSL_BL;	/* BL */
6348 	GMT->current.setting.map_logo_pos[GMT_X] = GMT->current.setting.map_logo_pos[GMT_Y] = -54 * pt;	/* -54p */
6349 	GMT->current.setting.given_unit[GMTCASE_MAP_LOGO_POS] = 'p';
6350 	/* MAP_ORIGIN_X, MAP_ORIGIN_Y */
6351 	GMT->current.setting.map_origin[GMT_X] = GMT->current.setting.map_origin[GMT_Y] = 72 * pt;	/* 72p = 1i */
6352 	GMT->current.setting.given_unit[GMTCASE_MAP_ORIGIN_X] = 'p';
6353 	GMT->current.setting.given_unit[GMTCASE_MAP_ORIGIN_Y] = 'p';
6354 	/* MAP_POLAR_CAP */
6355 	GMT->current.setting.map_polar_cap[0] = 85;
6356 	GMT->current.setting.map_polar_cap[1] = 90;
6357 	/* MAP_SCALE_HEIGHT */
6358 	GMT->current.setting.map_scale_height = 5 * pt;	/* 5p */
6359 	GMT->current.setting.given_unit[GMTCASE_MAP_SCALE_HEIGHT] = 'p';
6360 	/* MAP_TICK_LENGTH_PRIMARY */
6361 	GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = 5 * pt;	/* 5p */
6362 	GMT->current.setting.map_tick_length[GMT_TICK_UPPER] = 2.5 * pt;	/* 2.5p */
6363 	GMT->current.setting.given_unit[GMTCASE_MAP_TICK_LENGTH_PRIMARY] = 'p';
6364 	/* MAP_TICK_LENGTH_SECONDARY */
6365 	GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = 15 * pt;	/* 15p */
6366 	GMT->current.setting.map_tick_length[GMT_TICK_LOWER] = 3.75 * pt;	/* 3.75p */
6367 	GMT->current.setting.given_unit[GMTCASE_MAP_TICK_LENGTH_SECONDARY] = 'p';
6368 	/* MAP_TICK_PEN_PRIMARY */
6369 	error += gmt_getpen (GMT, "thinner,black", &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
6370 	/* MAP_TICK_PEN_SECONDARY */
6371 	error += gmt_getpen (GMT, "thinner,black", &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
6372 	/* MAP_TITLE_OFFSET */
6373 	GMT->current.setting.map_title_offset = 14 * pt;	/* 14p */
6374 	GMT->current.setting.given_unit[GMTCASE_MAP_TITLE_OFFSET] = 'p';
6375 	/* MAP_VECTOR_SHAPE */
6376 	GMT->current.setting.map_vector_shape = 0;
6377 
6378 		/* COLOR group */
6379 
6380 	/* COLOR_BACKGROUND */
6381 	error += gmt_getrgb (GMT, "black", GMT->current.setting.color_patch[GMT_BGD]);
6382 	/* COLOR_FOREGROUND */
6383 	error += gmt_getrgb (GMT, "white", GMT->current.setting.color_patch[GMT_FGD]);
6384 	/* COLOR_CPT */
6385 	strncpy (GMT->current.setting.cpt, GMT_DEFAULT_CPT_NAME, GMT_LEN64-1);
6386 	/* COLOR_MODEL */
6387 	GMT->current.setting.color_model = GMT_RGB;
6388 	/* COLOR_NAN */
6389 	error += gmt_getrgb (GMT, "128", GMT->current.setting.color_patch[GMT_NAN]);
6390 	/* COLOR_HSV_MIN_S */
6391 	GMT->current.setting.color_hsv_min_s = 1;
6392 	/* COLOR_HSV_MAX_S */
6393 	GMT->current.setting.color_hsv_max_s = 0.1;
6394 	/* COLOR_HSV_MIN_V */
6395 	GMT->current.setting.color_hsv_min_v = 0.3;
6396 	/* COLOR_HSV_MAX_V */
6397 	GMT->current.setting.color_hsv_max_v = 1;
6398 	/* COLOR_SET */
6399 	strncpy (GMT->current.setting.color_set, GMT_DEFAULT_COLOR_SET, GMT_LEN256-1);
6400 
6401 		/* PS group */
6402 
6403 	/* PS_CHAR_ENCODING */
6404 	strcpy (GMT->current.setting.ps_encoding.name, "ISOLatin1+");
6405 	gmtinit_load_encoding (GMT);
6406 	/* PS_COLOR_MODEL */
6407 	GMT->current.setting.ps_color_mode = PSL_RGB;
6408 	/* PS_IMAGE_COMPRESS */
6409 	if (GMT->PSL) {	/* Only when using PSL in this session */
6410 #ifdef HAVE_ZLIB
6411 		GMT->PSL->internal.compress = PSL_DEFLATE;
6412 		GMT->PSL->internal.deflate_level = 5;
6413 #else
6414 		/* Silently fall back to LZW compression when ZLIB not available */
6415 		GMT->PSL->internal.compress = PSL_LZW;
6416 #endif
6417 		/* PS_LINE_CAP */
6418 		GMT->PSL->internal.line_cap = PSL_BUTT_CAP;
6419 		/* PS_LINE_JOIN */
6420 		GMT->PSL->internal.line_join = PSL_MITER_JOIN;
6421 		/* PS_MITER_LIMIT */
6422 		GMT->PSL->internal.miter_limit = 35;
6423 	}
6424 	/* PS_PAGE_COLOR */
6425 	error += gmt_getrgb (GMT, "white", GMT->current.setting.ps_page_rgb);
6426 	/* PS_PAGE_ORIENTATION */
6427 	/* PS_MEDIA */
6428 	/* Set default media size */
6429 	i = gmtinit_key_lookup ("a4", GMT_media_name, GMT_N_MEDIA);
6430 	/* Use the specified standard format */
6431 	GMT->current.setting.ps_def_page_size[0] = GMT_media[i].width;
6432 	GMT->current.setting.ps_def_page_size[1] = GMT_media[i].height;
6433 	if (GMT->current.setting.run_mode == GMT_MODERN) {
6434 		gmtinit_setautopagesize (GMT);
6435 	}
6436 	else {
6437 		GMT->current.setting.ps_orientation = PSL_LANDSCAPE;
6438 		/* Use the specified standard format */
6439 		GMT->current.setting.ps_media = i;
6440 		GMT->current.setting.ps_page_size[0] = GMT_media[i].width;
6441 		GMT->current.setting.ps_page_size[1] = GMT_media[i].height;
6442 	}
6443 	/* PS_SCALE_X */
6444 	GMT->current.setting.ps_magnify[GMT_X] = 1;
6445 	/* PS_SCALE_Y */
6446 	GMT->current.setting.ps_magnify[GMT_Y] = 1;
6447 	/* PS_TRANSPARENCY */
6448 	strcpy (GMT->current.setting.ps_transpmode, "Normal");
6449 	/* PS_CONVERT */
6450 	strcpy (GMT->current.setting.ps_convert, GMT_SESSION_CONVERT);
6451 	/* PS_COMMENTS */
6452 	if (GMT->PSL) GMT->PSL->internal.comments = 0;	/* Only when using PSL in this session */
6453 
6454 		/* IO group */
6455 
6456 	/* IO_COL_SEPARATOR */
6457 	strcpy (GMT->current.setting.io_col_separator, "\t");
6458 	/* IO_FIRST_HEADER */
6459 	GMT->current.setting.io_first_header = 0;
6460 	/* IO_GRIDFILE_FORMAT */
6461 	strcpy (GMT->current.setting.io_gridfile_format, "nf");
6462 	/* IO_GRIDFILE_SHORTHAND */
6463 	GMT->current.setting.io_gridfile_shorthand = false;
6464 	/* IO_HEADER */
6465 	GMT->current.setting.io_header[GMT_IN] = GMT->current.setting.io_header[GMT_OUT] = false;
6466 	/* IO_HEADER_MARKER */
6467 	strcpy (GMT->current.setting.io_head_marker_in, DEF_HEADER_MARKERS);	/* Accept GMT or MATLAB header records or comments or quoted text */
6468 	GMT->current.setting.io_head_marker_out = '#';
6469 	/* IO_N_HEADER_RECS */
6470 	GMT->current.setting.io_n_header_items = 0;
6471 	/* IO_NAN_RECORDS (pass) */
6472 	GMT->current.setting.io_nan_records = true;
6473 	/* IO_NC4_CHUNK_SIZE (auto) */
6474 	GMT->current.setting.io_nc4_chunksize[0] = k_netcdf_io_chunked_auto;
6475 	/* IO_NC4_DEFLATION_LEVEL */
6476 	GMT->current.setting.io_nc4_deflation_level = 3;
6477 	/* IO_LONLAT_TOGGLE */
6478 	GMT->current.setting.io_lonlat_toggle[GMT_IN] = false;
6479 	/* We got false/f/0 or true/t/1. Set outgoing setting to the same as the ingoing. */
6480 	GMT->current.setting.io_lonlat_toggle[GMT_OUT] = GMT->current.setting.io_lonlat_toggle[GMT_IN];
6481 	/* IO_SEGMENT_BINARY */
6482 	GMT->current.setting.n_bin_header_cols = 2;
6483 	/* IO_SEGMENT_MARKER */
6484 	GMT->current.setting.io_seg_marker[GMT_OUT] = GMT->current.setting.io_seg_marker[GMT_IN] = '>';
6485 
6486 		/* PROJ group */
6487 
6488 	/* PROJ_AUX_LATITUDE (authalic) */
6489 	GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2A;
6490 	/* PROJ_ELLIPSOID */
6491 	GMT->current.setting.proj_ellipsoid = gmt_get_ellipsoid (GMT, "WGS-84");
6492 	gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid */
6493 	/* PROJ_DATUM (Not implemented yet) */
6494 	/* PROJ_GEODESIC */
6495 	GMT->current.setting.proj_geodesic = GMT_GEODESIC_VINCENTY;
6496 	gmtlib_init_geodesic (GMT);	/* Set function pointer depending on the geodesic selected */
6497 	/* PROJ_LENGTH_UNIT */
6498 	GMT->current.setting.proj_length_unit = GMT_CM;
6499 	/* PROJ_MEAN_RADIUS */
6500 	GMT->current.setting.proj_mean_radius = GMT_RADIUS_AUTHALIC;
6501 	gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid */
6502 	/* PROJ_SCALE_FACTOR (default) */
6503 	GMT->current.setting.proj_scale_factor = -1.0;
6504 
6505 		/* GMT group */
6506 
6507 	/* GMT_COMPATIBILITY */
6508 	GMT->current.setting.compatibility = (GMT->current.setting.run_mode == GMT_CLASSIC) ? 4 : 6;
6509 	/* GMTCASE_GMT_AUTO_DOWNLOAD [Deprecated] */
6510 	GMT->current.setting.auto_download = GMT_YES_DOWNLOAD;
6511 	/* GMTCASE_GMT_DATA_SERVER_LIMIT */
6512 	GMT->current.setting.url_size_limit = 0;
6513 	/* GMTCASE_GMT_DATA_UPDATE_INTERVAL */
6514 	GMT->current.setting.refresh_time = 1;
6515 	/* GMT_CUSTOM_LIBS (default to none) */
6516 	/* GMT_EXPORT_TYPE */
6517 	GMT->current.setting.export_type = GMT_DOUBLE;
6518 	/* GMT_EXTRAPOLATE_VAL (NaN) */
6519 	GMT->current.setting.extrapolate_val[0] = GMT_EXTRAPOLATE_NONE;
6520 	/* GMT_FFT */
6521 	GMT->current.setting.fft = k_fft_auto;
6522 	/* GMT_GRAPHICS_DPU */
6523 	GMT->current.setting.graphics_dpu = GMT_IMAGE_DPU_VALUE;
6524 	GMT->current.setting.graphics_dpu_unit = GMT_IMAGE_DPU_UNIT;
6525 	/* GMT_GRAPHICS_FORMAT */
6526 	GMT->current.setting.graphics_format = GMT_SESSION_FORMAT;
6527 	/* GMT_HISTORY */
6528 	GMT->current.setting.history = (GMT_HISTORY_READ | GMT_HISTORY_WRITE);
6529 	/* GMT_INTERPOLANT */
6530 	GMT->current.setting.interpolant = GMT_SPLINE_AKIMA;
6531 	/* GMT_LANGUAGE */
6532 	strcpy (GMT->current.setting.language, "us");
6533 	/* GMT_MAX_CORES */
6534 	GMT->current.setting.max_cores = 0;
6535 	/* GMT_THEME */
6536 	strcpy (GMT->current.setting.theme, "classic");
6537 	/* GMT_TRIANGULATE */
6538 #ifdef TRIANGLE_D
6539 	GMT->current.setting.triangulate = GMT_TRIANGLE_SHEWCHUK;
6540 #else
6541 	GMT->current.setting.triangulate = GMT_TRIANGLE_WATSON;
6542 #endif
6543 	/* GMT_VERBOSE (warnings) */
6544 	error += gmtinit_parse_V_option (GMT, 'w');
6545 
6546 		/* DIR group */
6547 
6548 	/* DIR_DATA (Empty) */
6549 	/* DIR_USER (Empty) */
6550 	/* DIR_CACHE (Empty) */
6551 	/* DIR_DCW */
6552 	if (GMT->session.DCWDIR)
6553 		gmt_M_str_free (GMT->session.DCWDIR);
6554 	if (strstr (DCW_INSTALL_PATH, "PATH-NOTFOUND")) {	/* Assign download path instead */
6555 		sprintf (path, "%s/geography/dcw", GMT->session.USERDIR);
6556 		GMT->session.DCWDIR = strdup (path);
6557 	}
6558 	else
6559 		GMT->session.DCWDIR = strdup (DCW_INSTALL_PATH);
6560 	/* DIR_GSHHG */
6561 	if (GMT->session.GSHHGDIR)
6562 		gmt_M_str_free (GMT->session.GSHHGDIR);
6563 	if (strstr (GSHHG_INSTALL_PATH, "PATH-NOTFOUND")) {	/* Assign download path instead */
6564 		sprintf (path, "%s/geography/gshhg", GMT->session.USERDIR);
6565 		GMT->session.GSHHGDIR = strdup (path);
6566 	}
6567 	else
6568 		GMT->session.GSHHGDIR = strdup (GSHHG_INSTALL_PATH);
6569 
6570 		/* TIME group */
6571 
6572 	/* TIME_EPOCH */
6573 	strcpy (GMT->current.setting.time_system.epoch, "1970-01-01T00:00:00");
6574 	(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
6575 	/* TIME_IS_INTERVAL */
6576 	GMT->current.setting.time_is_interval = false;
6577 	/* TIME_INTERVAL_FRACTION */
6578 	GMT->current.setting.time_interval_fraction = 0.5;
6579 	gmtinit_get_language (GMT);	/* Load in names and abbreviations in chosen language */
6580 	/* TIME_REPORT */
6581 	GMT->current.setting.timer_mode = GMT_NO_TIMER;
6582 	/* TIME_UNIT */
6583 	GMT->current.setting.time_system.unit = 's';
6584 	(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
6585 	/* TIME_WEEK_START */
6586 	GMT->current.setting.time_week_start = gmtinit_key_lookup ("Monday", GMT_weekdays, 7);
6587 	/* TIME_Y2K_OFFSET_YEAR */
6588 	GMT->current.setting.time_Y2K_offset_year = 1950;
6589 	GMT->current.time.Y2K_fix.y2_cutoff = GMT->current.setting.time_Y2K_offset_year % 100;
6590 	GMT->current.time.Y2K_fix.y100 = GMT->current.setting.time_Y2K_offset_year - GMT->current.time.Y2K_fix.y2_cutoff;
6591 	GMT->current.time.Y2K_fix.y200 = GMT->current.time.Y2K_fix.y100 + 100;
6592 
6593 	if (error)
6594 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized value during gmtdefaults initialization.\n");
6595 
6596 	if (!strncmp (GMT_DEF_UNITS, "US", 2U))
6597 		gmtinit_conf_classic_US (GMT);	/* Override with US settings */
6598 
6599 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Means we switch to classic in a modern mode setssion */
6600 		GMT_keyword_updated[GMTCASE_FONT_ANNOT_PRIMARY] = true;
6601 		GMT_keyword_updated[GMTCASE_FONT_ANNOT_SECONDARY] = true;
6602 		GMT_keyword_updated[GMTCASE_FONT_LABEL] = true;
6603 		GMT_keyword_updated[GMTCASE_FONT_HEADING] = true;
6604 		GMT_keyword_updated[GMTCASE_FONT_TAG] = true;
6605 		GMT_keyword_updated[GMTCASE_FONT_TITLE] = true;
6606 		GMT_keyword_updated[GMTCASE_FONT_SUBTITLE] = true;
6607 		GMT_keyword_updated[GMTCASE_FONT_LOGO] = true;
6608 		GMT_keyword_updated[GMTCASE_FORMAT_GEO_MAP] = true;
6609 		GMT_keyword_updated[GMTCASE_MAP_ANNOT_OFFSET_PRIMARY] = true;
6610 		GMT_keyword_updated[GMTCASE_MAP_ANNOT_OFFSET_SECONDARY] = true;
6611 		GMT_keyword_updated[GMTCASE_MAP_FRAME_AXES] = true;
6612 		GMT_keyword_updated[GMTCASE_MAP_LABEL_OFFSET] = true;
6613 		GMT_keyword_updated[GMTCASE_MAP_TITLE_OFFSET] = true;
6614 		GMT_keyword_updated[GMTCASE_MAP_HEADING_OFFSET] = true;
6615 		GMT_keyword_updated[GMTCASE_MAP_TICK_LENGTH_PRIMARY] = true;
6616 		GMT_keyword_updated[GMTCASE_MAP_TICK_LENGTH_SECONDARY] = true;
6617 		GMT_keyword_updated[GMTCASE_MAP_FRAME_WIDTH] = true;
6618 		GMT_keyword_updated[GMTCASE_MAP_FRAME_PEN] = true;
6619 		GMT_keyword_updated[GMTCASE_MAP_TICK_PEN_PRIMARY] = true;
6620 		GMT_keyword_updated[GMTCASE_MAP_TICK_PEN_SECONDARY] = true;
6621 		GMT_keyword_updated[GMTCASE_MAP_GRID_PEN_PRIMARY] = true;
6622 		GMT_keyword_updated[GMTCASE_MAP_GRID_PEN_SECONDARY] = true;
6623 		GMT_keyword_updated[GMTCASE_MAP_VECTOR_SHAPE] = true;
6624 	}
6625 }
6626 
6627 /*! . */
gmtinit_conf_modern_override(struct GMT_CTRL * GMT)6628 GMT_LOCAL void gmtinit_conf_modern_override (struct GMT_CTRL *GMT) {
6629 	int error = 0;
6630 #if NO_THEMES
6631 	return;		/* Ignore all the modern theme stuff */
6632 #endif
6633 
6634 	/* These settings override the classic defaults settings and make the modern settings.
6635 	 * In addition to some changes in font names, the key thing is lack of dimension as those
6636 	 * will be set based on map size.  The user can override any of those with a specific
6637 	 * dimension (font size, length, etc.) with gmt set or --PAR=value. Below, all modern
6638 	 * font sizes are set to auto [->NaN] and all dimensions are set to NaN.  If these remain
6639 	 * NaN after reading gmt.conf then they are auto-scaled in gmt_set_undefined_defaults. */
6640 
6641 	/* FONT group */
6642 
6643 	/* FONT_ANNOT_PRIMARY */
6644 	error += gmt_getfont (GMT, "auto,Helvetica,black", &GMT->current.setting.font_annot[GMT_PRIMARY]);
6645 	GMT->current.setting.given_unit[GMTCASE_FONT_ANNOT_PRIMARY] = 'p';
6646 	/* FONT_ANNOT_SECONDARY */
6647 	error += gmt_getfont (GMT, "auto,Helvetica,black", &GMT->current.setting.font_annot[GMT_SECONDARY]);
6648 	GMT->current.setting.given_unit[GMTCASE_FONT_ANNOT_SECONDARY] = 'p';
6649 	/* FONT_HEADING */
6650 	error += gmt_getfont (GMT, "auto,Helvetica-Bold,black", &GMT->current.setting.font_heading);
6651 	GMT->current.setting.given_unit[GMTCASE_FONT_HEADING] = 'p';
6652 	/* FONT_TITLE */
6653 	error += gmt_getfont (GMT, "auto,Helvetica-Bold,black", &GMT->current.setting.font_title);
6654 	GMT->current.setting.given_unit[GMTCASE_FONT_TITLE] = 'p';
6655 	/* FONT_SUBTITLE */
6656 	error += gmt_getfont (GMT, "auto,Helvetica-Bold,black", &GMT->current.setting.font_subtitle);
6657 	GMT->current.setting.given_unit[GMTCASE_FONT_SUBTITLE] = 'p';
6658 	/* FONT_LABEL */
6659 	error += gmt_getfont (GMT, "auto,Helvetica,black", &GMT->current.setting.font_label);
6660 	GMT->current.setting.given_unit[GMTCASE_FONT_LABEL] = 'p';
6661 	/* FONT_TAG */
6662 	error += gmt_getfont (GMT, "auto,Helvetica,black", &GMT->current.setting.font_tag);
6663 	GMT->current.setting.given_unit[GMTCASE_FONT_TAG] = 'p';
6664 	/* FONT_LOGO */
6665 	error += gmt_getfont (GMT, "auto,Helvetica,black", &GMT->current.setting.font_logo);
6666 	GMT->current.setting.given_unit[GMTCASE_FONT_LOGO] = 'p';
6667 
6668 	/* FORMAT_GEO_MAP */
6669 	strcpy (GMT->current.setting.format_geo_map, "ddd:mm:ssF");
6670 	gmtlib_plot_C_format (GMT);	/* Update format statements */
6671 
6672 	/* GMT_THEME */
6673 	strcpy (GMT->current.setting.theme, "modern");
6674 
6675 	/* MAP group */
6676 
6677 	/* MAP_ANNOT_MIN_SPACING */
6678 	GMT->current.setting.map_annot_min_spacing = GMT->session.d_NaN; /* 28p */
6679 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_MIN_SPACING] = 'p';
6680 	/* MAP_ANNOT_OFFSET_PRIMARY, MAP_ANNOT_OFFSET_SECONDARY */
6681 	GMT->current.setting.map_annot_offset[GMT_PRIMARY] = GMT->current.setting.map_annot_offset[GMT_SECONDARY] = GMT->session.d_NaN; /* 3p */
6682 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_OFFSET_PRIMARY] = 'p';
6683 	GMT->current.setting.given_unit[GMTCASE_MAP_ANNOT_OFFSET_SECONDARY] = 'p';
6684 	/* MAP_FRAME_AXES */
6685 	strcpy (GMT->current.setting.map_frame_axes, "auto");
6686 	/* MAP_FRAME_TYPE (plain) */
6687 	GMT->current.setting.map_frame_type = GMT_IS_FANCY;
6688 	/* MAP_FRAME_WIDTH */
6689 	GMT->current.setting.map_frame_width = GMT->session.d_NaN; /* 3p */
6690 	GMT->current.setting.given_unit[GMTCASE_MAP_FRAME_WIDTH] = 'p';
6691 	/* MAP_HEADING_OFFSET */
6692 	GMT->current.setting.map_heading_offset = GMT->session.d_NaN;	/* 16p */
6693 	GMT->current.setting.given_unit[GMTCASE_MAP_HEADING_OFFSET] = 'p';
6694 	/* MAP_LABEL_OFFSET */
6695 	GMT->current.setting.map_label_offset[GMT_X] = GMT->current.setting.map_label_offset[GMT_Y] = GMT->session.d_NaN;	/* 6p */
6696 	GMT->current.setting.given_unit[GMTCASE_MAP_LABEL_OFFSET] = 'p';
6697 	/* MAP_POLAR_CAP */
6698 	GMT->current.setting.map_polar_cap[0] = GMT->session.d_NaN;	/* 85 */
6699 	/* MAP_TICK_LENGTH_PRIMARY */
6700 	GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = GMT->session.d_NaN;	/* 4p */
6701 	GMT->current.setting.map_tick_length[GMT_TICK_UPPER] = GMT->session.d_NaN;	/* 2p */
6702 	GMT->current.setting.given_unit[GMTCASE_MAP_TICK_LENGTH_PRIMARY] = 'p';
6703 	/* MAP_TICK_LENGTH_SECONDARY */
6704 	GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = GMT->session.d_NaN;	/* 12p */
6705 	GMT->current.setting.map_tick_length[GMT_TICK_LOWER] = GMT->session.d_NaN;	/* 3p */
6706 	GMT->current.setting.given_unit[GMTCASE_MAP_TICK_LENGTH_SECONDARY] = 'p';
6707 	/* MAP_TITLE_OFFSET */
6708 	GMT->current.setting.map_title_offset = GMT->session.d_NaN;	/* 12p */
6709 	GMT->current.setting.given_unit[GMTCASE_MAP_TITLE_OFFSET] = 'p';
6710 	/* MAP_VECTOR_SHAPE */
6711 	GMT->current.setting.map_vector_shape = GMT->session.d_NaN;	/* 0.5 */
6712 
6713 
6714 	/* MAP_FRAME_PEN */
6715 	GMT->current.setting.map_frame_pen.width = GMT->session.d_NaN; /* 1.5p (thicker) */
6716 	/* MAP_TICK_PEN_PRIMARY */
6717 	GMT->current.setting.map_tick_pen[GMT_PRIMARY].width = GMT->session.d_NaN;	/* 0.5p (thinner) */
6718 	/* MAP_TICK_PEN_SECONDARY */
6719 	GMT->current.setting.map_tick_pen[GMT_SECONDARY].width = GMT->session.d_NaN;	/* 0.5p (thinner) */
6720 	/* MAP_GRID_PEN_PRIMARY */
6721 	GMT->current.setting.map_grid_pen[GMT_PRIMARY].width = GMT->session.d_NaN;	/* 0.25p (default) */
6722 	/* MAP_GRID_PEN_SECONDARY */
6723 	GMT->current.setting.map_grid_pen[GMT_SECONDARY].width = GMT->session.d_NaN;	/* 0.5p (thinner) */
6724 
6725 	if (error)
6726 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized value during gmtdefaults modern initialization.\n");}
6727 
6728 /*! . */
gmtinit_conf_modern_US(struct GMT_CTRL * GMT)6729 GMT_LOCAL void gmtinit_conf_modern_US (struct GMT_CTRL *GMT) {
6730 	gmtinit_conf_classic_US (GMT);	/* Override with US settings */
6731 	gmtinit_conf_modern_override (GMT);
6732 }
6733 
6734 /*! . */
gmtinit_conf_modern(struct GMT_CTRL * GMT)6735 GMT_LOCAL void gmtinit_conf_modern (struct GMT_CTRL *GMT) {
6736 	/* REPLACE WITH gmtinit_conf_modern when ready */
6737 	gmtinit_conf_classic (GMT);
6738 	gmtinit_conf_modern_override (GMT);
6739 }
6740 
6741 /*! . */
gmt_conf_US(struct GMT_CTRL * GMT)6742 void gmt_conf_US (struct GMT_CTRL *GMT) {
6743 	if (GMT->current.setting.run_mode == GMT_MODERN)
6744 		gmtinit_conf_modern_US (GMT);
6745 	else
6746 		gmtinit_conf_classic_US (GMT);
6747 }
6748 
6749 /*! . */
gmt_conf_SI(struct GMT_CTRL * GMT)6750 void gmt_conf_SI (struct GMT_CTRL *GMT) {
6751 	if (GMT->current.setting.run_mode == GMT_MODERN)
6752 		gmtinit_conf_modern (GMT);
6753 	else
6754 		gmtinit_conf_classic (GMT);
6755 	GMT->current.setting.map_annot_oblique_set = false;
6756 }
6757 
6758 /*! . */
gmtinit_init_fonts(struct GMT_CTRL * GMT)6759 GMT_LOCAL int gmtinit_init_fonts (struct GMT_CTRL *GMT) {
6760 	unsigned int i = 0, n_GMT_fonts;
6761 	size_t n_alloc = 0;
6762 	char fullname[GMT_BUFSIZ] = {""};
6763 
6764 	/* Loads all available fonts for this installation */
6765 
6766 	/* First the standard 35 + 4 PostScript fonts from Adobe */
6767 
6768 	gmt_set_meminc (GMT, GMT_SMALL_CHUNK);	/* Only allocate a small amount [64] */
6769 	GMT->session.font = gmt_M_malloc (GMT, GMT->session.font, 0, &n_alloc, struct GMT_FONTSPEC);
6770 
6771 	/* First the standard 35 + 4 PostScript fonts from Adobe */
6772 	gmt_M_memcpy (GMT->session.font, GMT_standard_fonts, GMT_N_STANDARD_FONTS, struct GMT_FONTSPEC);
6773 	GMT->session.n_fonts = n_GMT_fonts = i = GMT_N_STANDARD_FONTS;
6774 
6775 	/* Then any custom fonts:
6776 	   To add additional fonts, create a file called PSL_custom_fonts.txt
6777 	   in GMT/share/postscriptlight and add your extra font information there.
6778 	   The fontheight below is the height of A for unit fontsize.
6779 	   Encoded = 0 if we may re-encode this font as needed. */
6780 
6781 	if (gmt_getsharepath (GMT, "postscriptlight", "PSL_custom_fonts", ".txt", fullname, R_OK)) {	/* Decode Custom font file */
6782 		FILE *in = NULL;
6783 		char buf[GMT_BUFSIZ] = {""};
6784 		if ((in = fopen (fullname, "r")) == NULL) {
6785 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot initialize PostScript fonts because we cannot open %s\n", fullname);
6786 			return GMT_ERROR_ON_FOPEN;
6787 		}
6788 
6789 		while (fgets (buf, GMT_BUFSIZ, in)) {
6790 			if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') continue;
6791 			if (i == n_alloc) GMT->session.font = gmt_M_malloc (GMT, GMT->session.font, i, &n_alloc, struct GMT_FONTSPEC);
6792 			if (sscanf (buf, "%s %lf %*d", fullname, &GMT->session.font[i].height) != 2) {
6793 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Trouble decoding custom font info [%s].  Skipping this font.\n", buf);
6794 				continue;
6795 			}
6796 			if (strlen (fullname) >= PSL_FONTNAME_LEN) {
6797 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Font %s exceeds %d characters and will be truncated\n", fullname, PSL_FONTNAME_LEN);
6798 				fullname[PSL_FONTNAME_LEN-1] = '\0';
6799 			}
6800 			strncpy (GMT->session.font[i].name, fullname, PSL_FONTNAME_LEN);
6801 			i++;
6802 		}
6803 		fclose (in);
6804 		GMT->session.n_fonts = i;
6805 	}
6806 	n_alloc = i;
6807 	GMT->session.font = gmt_M_malloc (GMT, GMT->session.font, 0, &n_alloc, struct GMT_FONTSPEC);
6808 	gmt_reset_meminc (GMT);
6809 	return (GMT_NOERROR);
6810 }
6811 
6812 /*! Gets the rata die of today */
gmtinit_set_today(struct GMT_CTRL * GMT)6813 GMT_LOCAL void gmtinit_set_today (struct GMT_CTRL *GMT) {
6814 	time_t right_now = time (NULL);			/* Unix time right now */
6815 	struct tm *moment = gmtime (&right_now);	/* Convert time to a TM structure */
6816 	/* Calculate rata die from yy, mm, and dd */
6817 	/* tm_mon is 0-11, so add 1 for 1-12 range, tm_year is years since 1900, so add 1900, but tm_mday is 1-31 so use as is */
6818 	GMT->current.time.today_rata_die = gmt_rd_from_gymd (GMT, 1900 + moment->tm_year, moment->tm_mon + 1, moment->tm_mday);
6819 }
6820 
6821 /*! . */
gmtinit_free_user_media(struct GMT_CTRL * GMT)6822 GMT_LOCAL void gmtinit_free_user_media (struct GMT_CTRL *GMT) {
6823 	/* Free any user-specified media formats */
6824 	unsigned int i;
6825 
6826 	if (GMT->session.n_user_media == 0) return;	/* Nothing to free */
6827 
6828 	for (i = 0; i < GMT->session.n_user_media; i++)
6829 		gmt_M_str_free (GMT->session.user_media_name[i]);
6830 	gmt_M_free (GMT, GMT->session.user_media_name);
6831 	gmt_M_free (GMT, GMT->session.user_media);
6832 	GMT->session.n_user_media = 0;
6833 }
6834 
6835 /*! . */
gmtinit_load_user_media(struct GMT_CTRL * GMT)6836 GMT_LOCAL unsigned int gmtinit_load_user_media (struct GMT_CTRL *GMT) {
6837 	/* Load any user-specified media formats */
6838 	size_t n_alloc = 0;
6839 	unsigned int n = 0;
6840 	double w, h;
6841 	char line[GMT_BUFSIZ] = {""}, file[PATH_MAX] = {""}, media[GMT_LEN64] = {""};
6842 	FILE *fp = NULL;
6843 
6844 	gmt_getsharepath (GMT, "postscriptlight", "gmt_custom_media", ".conf", file, R_OK);
6845 	if ((fp = fopen (file, "r")) == NULL) return (0);	/* Not a critical file so no error if we cannot read it */
6846 
6847 	gmtinit_free_user_media (GMT);	/* Free any previously allocated user-specified media formats */
6848 	gmt_set_meminc (GMT, GMT_TINY_CHUNK);	/* Only allocate a small amount */
6849 	while (fgets (line, GMT_BUFSIZ, fp)) {
6850 		if (line[0] == '#' || line[0] == '\n') continue;	/* Skip comments and blank lines */
6851 
6852 		if (sscanf (line, "%s %lg %lg", media, &w, &h) != 3) {
6853 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while decoding file %s.  Bad format? [%s]\n", file, line);
6854 			fclose (fp);
6855 			return GMT_PARSE_ERROR;
6856 		}
6857 
6858 		gmt_str_tolower (media);	/* Convert string to lower case */
6859 
6860 		if (n == n_alloc) {
6861 			size_t k = n_alloc;	/* So we don't update n_alloc in the first gmt_M_malloc call */
6862 			GMT->session.user_media = gmt_M_malloc (GMT, GMT->session.user_media, n, &k, struct GMT_MEDIA);
6863 			GMT->session.user_media_name = gmt_M_malloc (GMT, GMT->session.user_media_name, n, &n_alloc, char *);
6864 		}
6865 		GMT->session.user_media_name[n] = strdup (media);
6866 		GMT->session.user_media[n].width  = w;
6867 		GMT->session.user_media[n].height = h;
6868 		n++;
6869 	}
6870 	fclose (fp);
6871 
6872 	n_alloc = n;
6873 	GMT->session.user_media = gmt_M_malloc (GMT, GMT->session.user_media, 0, &n_alloc, struct GMT_MEDIA);
6874 	GMT->session.user_media_name = gmt_M_malloc (GMT, GMT->session.user_media_name, 0, &n_alloc, char *);
6875 	gmt_reset_meminc (GMT);
6876 
6877 	GMT->session.n_user_media = n;
6878 
6879 	return (n);
6880 }
6881 
6882 /*! . */
gmtinit_free_dirnames(struct GMT_CTRL * GMT)6883 GMT_LOCAL void gmtinit_free_dirnames (struct GMT_CTRL *GMT) {
6884 	/* Free any directory settings */
6885 
6886 	gmt_M_str_free (GMT->session.SHAREDIR);
6887 	gmt_M_str_free (GMT->session.HOMEDIR);
6888 	gmt_M_str_free (GMT->session.DATADIR);
6889 	gmt_M_str_free (GMT->session.DCWDIR);
6890 	gmt_M_str_free (GMT->session.GSHHGDIR);
6891 	gmt_M_str_free (GMT->session.USERDIR);
6892 	gmt_M_str_free (GMT->session.CACHEDIR);
6893 	gmt_M_str_free (GMT->session.TMPDIR);
6894 	gmt_M_str_free (GMT->session.CUSTOM_LIBS);
6895 	gmt_M_str_free (GMT->session.DATASERVER);
6896 }
6897 
6898 
6899 /*! . */
gmtinit_free_GMT_ctrl(struct GMT_CTRL * GMT)6900 GMT_LOCAL void gmtinit_free_GMT_ctrl (struct GMT_CTRL *GMT) {	/* Deallocate control structure */
6901 	if (!GMT) return;	/* Never was allocated */
6902 	gmt_M_str_free (GMT);
6903 }
6904 
6905 /*! . */
gmtinit_new_GMT_ctrl(struct GMTAPI_CTRL * API,const char * session,unsigned int pad)6906 GMT_LOCAL struct GMT_CTRL *gmtinit_new_GMT_ctrl (struct GMTAPI_CTRL *API, const char *session, unsigned int pad) {	/* Allocate and initialize a new common control structure */
6907 	int i;
6908 	char path[PATH_MAX+1];
6909 	char *unit_name[4] = {"cm", "inch", "m", "point"};
6910 	double u2u[4][4] = {	/* Local initialization of unit conversion factors */
6911 		{   1.00,    1.0/2.54,    0.01,         72.0/2.54 },
6912 		{   2.54,    1.0,         0.0254,       72.0 },
6913 		{ 100.00,    100.0/2.54,  1.0,          72.0/0.0254 },
6914 		{ 2.54/72.0, 1.0/72.0,    0.0254/72.0,  1.0 }
6915 	};
6916 	struct GMT_PROJ4 GMT_proj4[GMT_N_PROJ4] = {
6917 		{ "aea"      , GMT_ALBERS },
6918 		{ "aeqd"     , GMT_AZ_EQDIST },
6919 		{ "cyl_stere", GMT_CYL_STEREO },
6920 		{ "cass"     , GMT_CASSINI },
6921 		{ "cea"      , GMT_CYL_EQ },
6922 		{ "eck4"     , GMT_ECKERT4 },
6923 		{ "eck6"     , GMT_ECKERT6 },
6924 		{ "eqc"      , GMT_CYL_EQDIST },
6925 		{ "eqdc"     , GMT_ECONIC },
6926 		{ "gnom"     , GMT_GNOMONIC },
6927 		{ "hammer"   , GMT_HAMMER },
6928 		{ "laea"     , GMT_LAMB_AZ_EQ },
6929 		{ "lcc"      , GMT_LAMBERT },
6930 		{ "merc"     , GMT_MERCATOR },
6931 		{ "mill"     , GMT_MILLER },
6932 		{ "moll"     , GMT_MOLLWEIDE },
6933 		{ "nsper"    , GMT_GENPER },
6934 		{ "omerc"    , GMT_OBLIQUE_MERC },
6935 		{ "omercp"   , GMT_OBLIQUE_MERC_POLE },
6936 		{ "ortho"    , GMT_ORTHO },
6937 		{ "polar"    , GMT_POLAR },
6938 		{ "poly"     , GMT_POLYCONIC },
6939 		{ "robin"    , GMT_ROBINSON },
6940 		{ "sinu"     , GMT_SINUSOIDAL },
6941 		{ "stere"    , GMT_STEREO },
6942 		{ "tmerc"    , GMT_TM },
6943 		{ "utm"      , GMT_UTM },
6944 		{ "vandg"    , GMT_VANGRINTEN },
6945 		{ "wintri"   , GMT_WINKEL },
6946 		{ "xy"       , GMT_LINEAR },
6947 		{ "z"        , GMT_ZAXIS }
6948 	};
6949 	struct GMT_CTRL *GMT = NULL;
6950 	struct ELLIPSOID ref_ellipsoid[GMT_N_ELLIPSOIDS] = {   /* This constant is created by CMake - do not edit */
6951 	#include "gmt_ellipsoids.h"                            /* This include file is created by CMake - do not edit */
6952 	};
6953 	struct DATUM datum[GMT_N_DATUMS] = {    /* This constant is created by CMake - do not edit */
6954 	#include "gmt_datums.h"                 /* This include file is created by CMake - do not edit */
6955 	};
6956 	gmt_M_unused(session);
6957 
6958 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmtinit_new_GMT_ctrl\n");
6959 	/* Alloc using calloc since gmt_M_memory may use resources not yet initialized */
6960 	GMT = calloc (1U, sizeof (struct GMT_CTRL));
6961 	gmt_M_memcpy (GMT->current.setting.ref_ellipsoid, ref_ellipsoid, 1, ref_ellipsoid);
6962 	gmt_M_memcpy (GMT->current.setting.proj_datum, datum, 1, datum);
6963 
6964 	/* Assign the daddy */
6965 	GMT->parent = API;
6966 
6967 	/* Assign the three std* pointers */
6968 
6969 	GMT->session.std[GMT_IN]  = stdin;
6970 	GMT->session.std[GMT_OUT] = stdout;
6971 	GMT->session.std[GMT_ERR] = stderr;
6972 
6973 	/* Set default verbosity level */
6974 	GMT->current.setting.verbose = GMT_MSG_WARNING;
6975 
6976 #ifdef MEMDEBUG
6977 	gmt_memtrack_init (GMT);	/* Helps us determine memory leaks */
6978 	GMT->session.min_meminc = GMT_MIN_MEMINC;
6979 	GMT->session.max_meminc = GMT_MAX_MEMINC;
6980 #endif
6981 
6982 	/* We don't know the module or library names yet */
6983 	GMT->init.module_name = GMT->init.module_lib = NULL;
6984 
6985 	/* Set runtime bindir */
6986 	GMT_runtime_bindir (path, session);
6987 	GMT->init.runtime_bindir = strdup (path);
6988 
6989 	/* Set runtime libdir */
6990 #if defined(__CYGWIN__)
6991 	/* Since no dladdr under Cygwin we must assume lib dir parallels bin dir */
6992 	if (strlen (path) > 4 && !strncmp (&path[strlen(path)-4], "/bin", 4U))
6993 		strncpy (&path[strlen(path)-3], "lib", 3U);
6994 #else
6995 	gmt_runtime_libdir (path);
6996 #endif
6997 	GMT->init.runtime_libdir = strdup (path);
6998 
6999 	if (gmtinit_set_env (GMT)) {	/* Get GMT_SHAREDIR and other environment path parameters */
7000 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
7001 		return NULL;
7002 	}
7003 
7004 	gmtinit_init_fonts (GMT);	/* Load in available font names */
7005 
7006 	/* Initialize values whose defaults are not necessarily 0/false/NULL */
7007 
7008 	/* MAP settings */
7009 
7010 	gmt_init_distaz (GMT, 'X', 0, GMT_MAP_DIST);	/* Default distance calculations are in user units */
7011 
7012 	GMT->current.map.n_lon_nodes = 360;
7013 	GMT->current.map.n_lat_nodes = 180;
7014 	GMT->current.map.frame.check_side = false;
7015 	GMT->current.map.frame.horizontal = 0;
7016 	GMT->current.map.dlon = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
7017 	GMT->current.map.dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / GMT->current.map.n_lat_nodes;
7018 
7019 	/* PLOT settings */
7020 
7021 	GMT->current.plot.mode_3D = 3;	/* Draw both fore and/or back 3-D box lines [1 + 2] */
7022 
7023 	/* PROJ settings */
7024 
7025 	GMT->current.proj.projection = GMT_NO_PROJ;
7026 	/* We need some defaults here for the cases where we do not actually set these with gmt_map_setup */
7027 	GMT->current.proj.fwd_x = GMT->current.proj.fwd_y = GMT->current.proj.fwd_z = &gmtlib_translin;
7028 	GMT->current.proj.inv_x = GMT->current.proj.inv_y = GMT->current.proj.inv_z = &gmtlib_itranslin;
7029 	/* z_level will be updated in GMT_init_three_D, but if it doesn't, it does not matter,
7030 	 * because by default, z_scale = 0.0 */
7031 	GMT->current.proj.z_level = DBL_MAX;
7032 	GMT->current.proj.xyz_pos[GMT_X] = GMT->current.proj.xyz_pos[GMT_Y] = GMT->current.proj.xyz_pos[GMT_Z] = true;
7033 	GMT->current.proj.z_project.view_azimuth = 180.0;
7034 	GMT->current.proj.z_project.view_elevation = 90.0;
7035 	GMT->current.proj.z_project.plane = GMT_NOTSET;	/* Initialize no perspective projection */
7036 	GMT->current.proj.z_project.level = 0.0;
7037 	for (i = 0; i < 4; i++) GMT->current.proj.edge[i] = true;
7038 	gmtlib_grdio_init (GMT);
7039 	gmt_set_pad (GMT, pad); /* Sets default number of rows/cols for boundary padding in this session */
7040 	GMT->current.proj.f_horizon = 90.0;
7041 	GMT->current.proj.proj4 = gmt_M_memory (GMT, NULL, GMT_N_PROJ4, struct GMT_PROJ4);
7042 	for (i = 0; i < GMT_N_PROJ4; i++) {	/* Load up proj4 structure once and for all */
7043 		GMT->current.proj.proj4[i].name = strdup (GMT_proj4[i].name);
7044 		GMT->current.proj.proj4[i].id = GMT_proj4[i].id;
7045 	}
7046 	/* TIME_SYSTEM settings */
7047 	strcpy (GMT->current.setting.time_system.epoch, "2000-01-01T12:00:00");
7048 	GMT->current.setting.time_system.unit = 'd';
7049 
7050 	/* INIT settings */
7051 
7052 	gmt_M_memcpy (GMT->session.u2u, u2u, 1, u2u);
7053 	for (i = 0; i < 4; i++) strncpy (GMT->session.unit_name[i], unit_name[i], 7U);
7054 	gmt_M_make_fnan (GMT->session.f_NaN);
7055 	gmt_M_make_dnan (GMT->session.d_NaN);
7056 	for (i = 0; i < 3; i++) GMT->session.no_rgb[i] = -1.0;
7057 
7058 #ifdef _OPENMP
7059 	/* Set the default number of threads = number of available cores, but only under OpenMP */
7060 	GMT->common.x.n_threads = gmtlib_get_num_processors();
7061 #endif
7062 
7063 	GMT_Report (API, GMT_MSG_DEBUG, "Exit:  gmtinit_new_GMT_ctrl\n");
7064 	return (GMT);
7065 }
7066 
7067 /*----------------------------------------------------------|
7068  * Public functions that are part of the GMT Devel library  |
7069  *----------------------------------------------------------|
7070  */
7071 
7072 /*!
7073 	\brief Print to stderr a short explanation for each of the options listed by the variable <options>
7074 	\param GMT ...
7075 	\param options ...
7076 
7077  * The function print to stderr a short explanation for each of the options listed by
7078  * the variable <options>. Only the common parameter options are covered.
7079  * Note: The cases below do not directly correspond to the common option letters,
7080  * although in some cases they do (e.g., case 'B' explains -B). For instance, to
7081  * display the help for the -r (registration setting for grid) option we use case F.
7082  * Part of this is historic and part is multiple flavor of output for same option.
7083  * However, gmtlib_explain_options is not called directly but via GMT_Option which
7084  * do accept a list of comma-separated options and there are the normal GMT common
7085  * option letters, sometimes with modifiers, and it translate between those and the
7086  * crazy cases below.\n
7087  * Remaining cases for additional options: A,H,L,M,N,T,W,e,m,q,u,v,w
7088  */
7089 
gmtinit_explain_R_geo(struct GMT_CTRL * GMT)7090 GMT_LOCAL void gmtinit_explain_R_geo (struct GMT_CTRL *GMT) {
7091 	struct GMTAPI_CTRL *API = GMT->parent;
7092 	GMT_Usage (API, 1, "\n%s", GMT_Rgeo_OPT);
7093 	GMT_Usage (API, -2, "Specify the min/max coordinates of your data region in user units. "
7094 		"Use dd:mm[:ss] for regions given in arc degrees, minutes [and seconds]. "
7095 		"Use -R<xmin>/<xmax>/<ymin>/<ymax>[+u<unit>] for regions given in projected coordinates, "
7096 		"with <unit> selected from %s. [e] "
7097 		"Use [yyyy[-mm[-dd]]]T[hh[:mm[:ss[.xxx]]]] format for time axes. "
7098 		"Append +r if -R specifies the coordinates of the lower left and "
7099 		"upper right corners of a rectangular area.", GMT_LEN_UNITS2_DISPLAY);
7100 	if (GMT->current.setting.run_mode == GMT_MODERN) {
7101 		GMT_Usage (API, -2, "Use -Re and -Ra to set exact or approximate regions based on your input data (if applicable). "
7102 		"Use -R<gridfile> to use its limits (and increments if applicable). "
7103 		"Use -Rg and -Rd as shorthand for -R0/360/-90/90 and -R-180/180/-90/90. "
7104 		"Derive region from closed polygons from the Digital Chart of the World (DCW): "
7105 		"Append a comma-separated list of ISO 3166 codes for countries to set region, i.e., "
7106 		"<code1>,<code2>,... etc., using the 2-character ISO country codes (see pscoast -E+l for list). "
7107 		"To select a state of a country (if available), append .state, e.g, US.TX for Texas. "
7108 		"To select a whole continent, give =AF|AN|AS|EU|OC|NA|SA as <code>. "
7109 		"Use +r to modify the region from polygon(s): Append <inc>, <xinc>/<yinc>, or <winc>/<einc>/<sinc>/<ninc> "
7110 		"to round region to these multiples; use +R to extend region by those increments instead, "
7111 		"or use +e which is like +r but makes sure the region extends at least by %g x <inc>.", GMT_REGION_INCFACTOR);
7112 	}
7113 }
7114 
gmtlib_explain_options(struct GMT_CTRL * GMT,char * options)7115 void gmtlib_explain_options (struct GMT_CTRL *GMT, char *options) {
7116 
7117 	char u, *GMT_choice[2] = {"OFF", "ON"}, *V_code = GMT_VERBOSE_CODES;
7118 #ifdef GMT_MP_ENABLED
7119 	int cores = 0;
7120 #endif
7121 	double s;
7122 	unsigned int k;
7123 	size_t s_length;
7124 	struct GMTAPI_CTRL *API = GMT->parent;
7125 	if (!options) return;
7126 	if (GMT->common.synopsis.extended) return;	/* Only want to list module-specific options, i.e gave + instead of - */
7127 	u = GMT->session.unit_name[GMT->current.setting.proj_length_unit][0];
7128 	s = GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];	/* Convert from internal inch to users unit */
7129 
7130 	s_length = strlen(options);
7131 	for (k = 0; k < s_length; k++) {
7132 
7133 		switch (options[k]) {
7134 
7135 		case 'B':	/* Tickmark option */
7136 
7137 			GMT_Usage (API, 1, "\n-B Specify both (1) basemap frame settings and (2) axes parameters. ");
7138 			GMT_Usage (API, 2, "Frame settings are modified via an optional single invocation of "
7139 				 "-B[<axes>][+b][+g<fill>][+i[<val>]][+n][+o<lon>/<lat>][+s<subtitle>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>]");
7140 			GMT_Usage (API, 2, "Axes parameters are specified via one or more invocations of "
7141 				"-B[p|s][x|y|z]<info>\n");
7142 			GMT_Usage (API, -2, "Frame settings control which axes to plot, frame fill, title (and subtitle), and type of gridlines. "
7143 				"<axes> is a combination of W,E,S,N,Z and plots those axes only [Default is WESNZ (all)]. "
7144 				"Use lower case w,e,s,n,z just to draw and tick (but not annotate) those axes, "
7145 				"and use l,r,b,t,u just to draw (but not annotate and tick) those axes. "
7146 				"For 3-D plots the Z|z[<corners>] controls the vertical axis.  The <corners> specifies "
7147 				"at which corner(s) to erect the z-axis via a combination of 1,2,3,4; 1 means lower left corner, "
7148 				"2 is lower right, etc., in a counter-clockwise order [Default automatically selects one axis]. Optional modifiers:");
7149 			GMT_Usage (API, 3, "+b Erect a 3-D frame box to outline the 3-D domain [no frame box].");
7150 			GMT_Usage (API, 3, "+g Paint the inside of the map region with <fill> before further plotting [no fill].");
7151 			GMT_Usage (API, 3, "+i Annotate along parallel or meridian <val> [0] when no such axes can be plotted.");
7152 			GMT_Usage (API, 3, "+n No frame and annotations whatsoever [Default is controlled by WESNZ/wesnz].");
7153 			GMT_Usage (API, 3, "+o Draw oblique gridlines about <plon>/<plat> [regular gridlines]. "
7154 				"Note: The +o modifier is ignored unless gridlines are specified via the axes parameters (below).");
7155 			GMT_Usage (API, 3, "+s Place a <subtitle> over the map frame [no subtitle]. Requires +t as well.");
7156 			GMT_Usage (API, 3, "+t Place a <title> over the map frame [no title]. "
7157 				"Note: Both <title> and <subtitle> can be set across multiple lines by using \"@^\" or \"<break>\" to mark breaks. "
7158 				"A single-line <title> and <subtitle> may contain LaTeX code enclosed by @[ .... @[ (or alternatively <math> ... </math>). "
7159 				"Using LaTeX expressions requires you to have a functioning latex and dvips installation, including required fonts.");
7160 			GMT_Usage (API, 3, "+w Draw the outline of the xz and yz planes [no outlines].");
7161 			GMT_Usage (API, 3, "+x|y|z[<fill>] paint the yz, xz, xy planes [no fill]. The +g<fill> sets all three planes. ");
7162 			GMT_Usage (API, -2, "Axes settings control the annotation, tick, and grid intervals and labels. "
7163 				"The full axes specification is");
7164 			GMT_Usage (API, 3, "-B[p|s][x|y|z]<intervals>[+a<angle>|n|p][+f][+l|L<label>][+p<prefix>][+s|S<secondary_label>][+u<unit>");
7165 			GMT_Usage (API, -2, "Alternatively, you may break this syntax into two separate -B options:");
7166 			GMT_Usage (API, 3, "-B[p|s][x|y|z][+a<angle>|n|p][+e[l|u]][+f][+l|L<label>][+p<prefix>][+s|S<secondary_label>][+u<unit>]");
7167 			GMT_Usage (API, 3, "-B[p|s][x|y|z]<intervals>");
7168 			GMT_Usage (API, -2, "There are two levels of annotations: Primary and secondary (most situations only require primary). "
7169 				"The -B[p] sets (p)rimary (more frequent) annotations while -Bs sets (s)econdary (less frequent) annotations. "
7170 				"The [x|y|z] selects which axes the settings apply to.  If none are given we default to xy. "
7171 				"To specify different settings for different axes you must repeat the -B axes option for "
7172 				"each dimension, i.e., provide separate -B[p|s]x, -B[p|s]y, and -B[p|s]z settings. "
7173 				"The <intervals> setting controls the annotation spacing and is a textstring made up of one or "
7174 				"more substrings of the form [a|f|g][<stride>[+-<phase>]], where the (optional) a "
7175 				"indicates annotation and major tick interval, f minor tick interval, and g grid interval. "
7176 				"Here, <stride> is the spacing between ticks or annotations, the (optional, and with required sign)"
7177 				"<phase> specifies phase-shifted annotations/ticks by that amount, and the (optional) "
7178 				"<unit> specifies the <stride> unit [Default is the unit implied in -R]. There can be "
7179 				"no spaces between the substrings; just append items to make one very long string. "
7180 				"For custom annotations or intervals, let <intervals> be c<intfile>; see documentation for details. "
7181 				"The optional <unit> modifies the <stride> value accordingly. For geographic maps you may use:");
7182 			GMT_Usage (API, 3, "d: arc degree [Default].");
7183 			GMT_Usage (API, 3, "m: arc minute.");
7184 			GMT_Usage (API, 3, "s: arc second.");
7185 			GMT_Usage (API, -2, "For time axes, several units are recognized:");
7186 			GMT_Usage (API, 3, "Y: year - plot using all 4 digits.");
7187 			GMT_Usage (API, 3, "y: year - plot only last 2 digits.");
7188 			GMT_Usage (API, 3, "O: month - format annotation according to FORMAT_DATE_MAP.");
7189 			GMT_Usage (API, 3, "o: month - plot as 2-digit integer (1-12).");
7190 			GMT_Usage (API, 3, "U: ISO week - format annotation according to FORMAT_DATE_MAP.");
7191 			GMT_Usage (API, 3, "u: ISO week - plot as 2-digit integer (1-53).");
7192 			GMT_Usage (API, 3, "r: Gregorian week - 7-day stride from chosen start of week (%s).",
7193 			             GMT_weekdays[GMT->current.setting.time_week_start]);
7194 			GMT_Usage (API, 3, "K: ISO weekday - plot name of weekdays in selected language [%s].", GMT->current.setting.language);
7195 			GMT_Usage (API, 3, "k: weekday - plot number of the day in the week (see TIME_WEEK_START).");
7196 			GMT_Usage (API, 3, "D: day - format annotation according to FORMAT_DATE_MAP, which also determines whether "
7197 				"we should plot day of month (1-31) or day of year (1-366).");
7198 			GMT_Usage (API, 3, "d: day - plot as 2- (day of month) or 3- (day of year) integer.");
7199 			GMT_Usage (API, 3, "R: Same as d but annotates from start of Gregorian week.");
7200 			GMT_Usage (API, 3, "H: hour - format annotation according to FORMAT_CLOCK_MAP.");
7201 			GMT_Usage (API, 3, "h: hour - plot as 2-digit integer (0-23).");
7202 			GMT_Usage (API, 3, "M: minute - format annotation according to FORMAT_CLOCK_MAP.");
7203 			GMT_Usage (API, 3, "m: minute - plot as 2-digit integer (0-59).");
7204 			GMT_Usage (API, 3, "S: second - format annotation according to FORMAT_CLOCK_MAP.");
7205 			GMT_Usage (API, 3, "s: second - plot as 2-digit integer (0-59; 60-61 if leap seconds are enabled).");
7206 			GMT_Usage (API, -2, "Cartesian intervals take no units. "
7207 				"When <stride> is omitted, a reasonable value will be determined automatically, e.g., -Bafg. "
7208 				"Log10 axis: Append l to annotate log10 (value) or p for 10^(log10(value)) [Default annotates value]. "
7209 				"Power axis: Append p to annotate value at equidistant pow increments [Default is nonlinear]. Optional modifiers: ");
7210 			GMT_Usage (API, 3, "+a Append <angle> for slanted or use +an|p for orthogonal|parallel annotations [+ap].");
7211 			GMT_Usage (API, 3, "+e Skip annotation that land exactly at one (append l or u) or both ends of the axis [no skipping].");
7212 			GMT_Usage (API, 3, "+f Let geographic axes place \"fancy\" annotations with W|E|S|N suffices.");
7213 			GMT_Usage (API, 3, "+l Place <label> for the axis.  Use +L to enforce horizontal labels for y-axes. "
7214 				"For another axis label on the opposite axis, use +s|S as well. "
7215 				"Use quotes if any of the <label>, <prefix> or <unit> have spaces. "
7216 				"For Cartesian axes you can have different labels on the left vs right or bottom vs top "
7217 				"by separating the two labels with ||, e.g., +l\"Left label||Right label\". "
7218 				"A <label> may contain LaTeX code enclosed by @[ .... @[  (or alternatively <math> ... </math>). "
7219 				"Using LaTeX expressions requires you to have a functioning latex and dvips installation, including required fonts. "
7220 				"Geographic map annotations will automatically have degree, minute, seconds units.");
7221 			GMT_Usage (API, 3, "+p Prepend <prefix> to each annotation (e.g., $ 10, $ 20 ...).");
7222 			GMT_Usage (API, 3, "+u Append <unit> to each annotation (e.g., 5 km, 10 km ...).");
7223 			GMT_Usage (API, -2, "See basemap documentation for more details and examples of all settings.");
7224 			break;
7225 
7226 		case 'b':	/* Condensed tickmark option */
7227 
7228 			GMT_Usage (API, 1, "\n-B Specify both (1) basemap frame settings and (2) axes parameters.");
7229 			GMT_Usage (API, 2, "Frame settings are modified via an optional single invocation of "
7230 				"-B[<axes>][+b][+g<fill>][+i[<val>]][+n][+o<lon>/<lat>][+s<subtitle>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>]");
7231 			GMT_Usage (API, 2, "Axes parameters are specified via one or more invocations of "
7232 				"-B[p|s][x|y|z]<intervals>[+a<angle>|n|p][+e[l|u]][+f][+l|L<label>][+p<prefix>][+s|S<secondary_label>][+u<unit>");
7233 			GMT_Usage (API, -2, "<intervals> is composed of concatenated [<type>]<stride>[l|p] sub-strings. "
7234 				"See basemap documentation for more details and examples of all settings.");
7235 			break;
7236 
7237 		case 'J':	/* Map projection option */
7238 
7239 			GMT_Usage (API, 1, "\n-J Select the map proJection. The projection type is identified by a 1- or "
7240 				"2-character ID (e.g. 'm' or 'kf') or by an abbreviation followed by a slash "
7241 				"(e.g. 'cyl_stere/'). When using a lower-case ID <scale> can be given either "
7242 				"as 1:<xxxx> or in %s/degree along the standard parallel. Alternatively, when "
7243 				"the projection ID is Capitalized, <scale>|<width> denotes the width of the "
7244 				"plot in %s. Optional modifiers:",
7245 					GMT->session.unit_name[GMT->current.setting.proj_length_unit],
7246 					GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7247 			GMT_Usage (API, 3, "+dh Specifying map height.");
7248 			GMT_Usage (API, 3, "+du Specifying maximum map dimension.");
7249 			GMT_Usage (API, 3, "+dl Specifying minimum map dimension.");
7250 			GMT_Usage (API, 3, "+dw Specifying map width [Default].");
7251 			GMT_Usage (API, -2, "When the central meridian (lon0) is optional and omitted, the center of the "
7252 				"longitude range set by -R is used. The default standard parallel is the equator. "
7253 				"Azimuthal projections set -Rg unless polar aspect or -R<...>+r is given. Available projections:");
7254 
7255 			GMT_Usage (API, 2, "-Ja|A<lon0>/<lat0>[/<horizon>]/<scale>|<width> (Lambert Azimuthal Equal Area). "
7256 				"<lon0>/<lat0> is the center of the projection, and "
7257 				"<horizon> is max distance from center of the projection (<= 180, default 90). "
7258 				"The <scale> can also be given as <radius>/<lat>, where <radius> is the distance "
7259 				"in %s to the oblique parallel <lat>.", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7260 
7261 			GMT_Usage (API, 2, "-Jb|B<lon0>/<lat0>/<lat1>/<lat2>/<scale>|<width> (Albers Equal-Area Conic). "
7262 				"Give origin, two standard parallels, and true <scale>.");
7263 
7264 			GMT_Usage (API, 2, "-Jc|C<lon0>/<lat0>/<scale>|<width> (Cassini). Give central point and <scale>.");
7265 
7266 			GMT_Usage (API, 2, "-Jcyl_stere|Cyl_stere/[<lon0>/[<lat0>/]]<scale>|<width> (Cylindrical Stereographic). "
7267 				"Optionally give central meridian and standard parallel and <scale>. Common parallels: "
7268 				"<lat0> = 66.159467 (Miller's modified Gall), 55 (Kamenetskiy's First), "
7269 				"45 (Gall Stereographic), 30 (Bolshoi Sovietskii Atlas Mira), and 0 (Braun) [Default].");
7270 
7271 			GMT_Usage (API, 2, "-Jd|D<lon0>/<lat0>/<lat1>/<lat2>/<scale>|<width> (Equidistant Conic). "
7272 				"Give origin, two standard parallels, and true <scale>.");
7273 
7274 			GMT_Usage (API, 2, "-Je|E<lon0>/<lat0>[/<horizon>]/<scale>|<width> (Azimuthal Equidistant). "
7275 				"<lon0>/<lat0> is the center of the projection, and "
7276 				"<horizon> is max distance from center of the projection (<= 180, default 180). "
7277 				"The <scale> can also be given as <radius>/<lat>, where <radius> is the distance "
7278 				"in %s to the oblique parallel <lat0>.", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7279 
7280 			GMT_Usage (API, 2, "-Jf|F<lon0>/<lat0>[/<horizon>]/<scale>|<width> (Gnomonic). "
7281 				"<lon0>/<lat0> is the center of the projection, and "
7282 				"<horizon> is max distance from center of the projection (< 90, default 60). "
7283 				"The <scale> can also be given as <radius>/<lat>, where <radius> is distance "
7284 				"in %s to the oblique parallel <lat0>.", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7285 
7286 			GMT_Usage (API, 2, "-Jg|G<lon0>/<lat0>/<scale>|<width> (Orthographic). "
7287 				"<lon0>/<lat0> is the center of the projection. "
7288 				"<scale> can also be given as <radius>/<lat>, where <radius> is distance"
7289 				"in %s to the oblique parallel <lat>.", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7290 
7291 			GMT_Usage (API, 2, "-Jg|G<lon0>/<lat0>/<scale>|<width>[+a<azimuth>][+t<tilt>][+v<vwidth>/<vheight>][+w<twist>][+z<altitude>[r|R]|g] (General Perspective). "
7292 				"<lon0>/<lat0> is the center of the projection. "
7293 				"The <scale> can also be given as <radius>/<lat>, where <radius> is distance "
7294 				"in %s to the oblique parallel <lat0>. "
7295 				"Several optional modifiers control the perspective:", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7296 			GMT_Usage (API, 3, "+a Append <azimuth> east of North of view [0].");
7297 			GMT_Usage (API, 3, "+t Append the upward <tilt> of the plane of projection; "
7298 				"if <tilt> < 0 then viewpoint is centered on the horizon [0].");
7299 			GMT_Usage (API, 3, "+v Append restricted view: <vwidth> is width of the viewpoint in degree, and "
7300 				"<vheight> is the height of the viewpoint in degrees [unrestricted].");
7301 			GMT_Usage (API, 3, "+w Append the CW <twist> of the viewpoint in degree [0].");
7302 			GMT_Usage (API, 3, "+z Append <altitude> (in km) of viewpoint above local sea level [infinity]. "
7303 				"Alternatively, append r to give radius from center of Earth to viewpoint (in km); "
7304 				"use R instead if radius is given in Earth radii, or "
7305 				"set <altitude> = g to use the altitude of the geosynchronous orbit.");
7306 
7307 			GMT_Usage (API, 2, "-Jh|H[<lon0>/]<scale>|<width> (Hammer-Aitoff). Give optional central meridian and <scale>.");
7308 
7309 			GMT_Usage (API, 2, "-Ji|I[<lon0>/]<scale>|<width> (Sinusoidal). Give optional central meridian and <scale>.");
7310 
7311 			GMT_Usage (API, 2, "-Jj|J[<lon0>/]<scale>|<width> (Miller). Give optional central meridian and <scale>.");
7312 
7313 			GMT_Usage (API, 2, "-Jkf|Kf[<lon0>/]<scale>|<width> (Eckert IV). Give optional central meridian and <scale>).");
7314 
7315 			GMT_Usage (API, 2, "-Jk|K[s][<lon0>/]<scale>|<width> (Eckert VI). Give optional central meridian and <scale>.");
7316 
7317 			GMT_Usage (API, 2, "-Jl|L<lon0>/<lat0>/<lat1>/<lat2>/<scale>|<width> (Lambert Conformal Conic). "
7318 				"Give origin, 2 standard parallels, and true scale.");
7319 
7320 			GMT_Usage (API, 2, "-Jm|M[<lon0>/[<lat0>/]]<scale>|<width> (Mercator). "
7321 				"Give optional central meridian and true scale parallel, and <scale>.");
7322 
7323 			GMT_Usage (API, 2, "-Jn|N[<lon0>/]<scale>|<width> (Robinson projection). Give optional central meridian and <scale>.");
7324 
7325 			GMT_Usage (API, 2, "-Jo|O<parameters>[+v] (Oblique Mercator).  Specify one of three definitions:");
7326 			GMT_Usage (API, 3, "-Jo|O[a|A]<lon0>/<lat0>/<azimuth>/<scale>|<width>. "
7327 				"Give origin, azimuth of oblique equator, and scale at oblique equator");
7328 			GMT_Usage (API, 3, "-Jo|O[b|B]<lon0>/<lat0>/<lon1>/<lat1>/<scale>|<width>. "
7329 				"Give origin, second point on oblique equator, and scale at oblique equator.");
7330 			GMT_Usage (API, 3, "-Jo|Oc|C<lon0>/<lat0>/<lonp>/<latp>/<scale>|<width>."
7331 				"Give origin, pole of projection, and scale at oblique equator.");
7332 			GMT_Usage (API, -3, "Specify region in oblique degrees OR use -R<...>+r. "
7333 				"Uppercase A|B|C removes enforcement of a northern hemisphere pole. "
7334 				"Append +v to make the oblique Equator the y-axis [x-axis].");
7335 
7336 			GMT_Usage (API, 2, "-Jp|P<scale>|<width>[+a][+f[e|p|<radius>]][+r<offset>][+t<origin>][+z[p|<radius>]] (Polar (theta,radius)). "
7337 				"Linear scaling for polar coordinates.  Give <scale> in %s/units. Optional modifiers:",
7338 			             GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7339 			GMT_Usage (API, 3, "+a Use azimuths (CW from North) instead of directions (CCW from East) [Default].");
7340 			GMT_Usage (API, 3, "+f Flip radial direction so south is on the outside and north is at the center. "
7341 				"Append e to indicate data are elevations in degrees (s/n must be in 0-90 range). "
7342 				"Append p to set r = current planetary radius to be the center. "
7343 				"Append <radius> to indicate the radius at the center.");
7344 			GMT_Usage (API, 3, "+r Offset the radial values [0].");
7345 			GMT_Usage (API, 3, "+t Set <origin> value for angles or azimuths [0].");
7346 			GMT_Usage (API, 3, "+z Annotate depths rather than radius [Default]. Alternatively, if you provided depths "
7347 				"then append p (planetary radius) or <radius> to annotate r = radius - z instead.");
7348 
7349 			GMT_Usage (API, 2, "-Jpoly|Poly/[<lon0>/[<lat0>/]]<scale>|<width> ((American) Polyconic). "
7350 				"Give optional central meridian and reference parallel [Default is equator], and <scale>.");
7351 
7352 			GMT_Usage (API, 2, "-Jq|Q[<lon0>/[<lat0>/]]<scale>|<width> (Equidistant Cylindrical). "
7353 				"Give optional central meridian and standard parallel, and <scale>. Common parallels: "
7354 				"<lat0> = 61.7 (Min. linear distortion), 50.5 (R. Miller equirectangular), "
7355 				"45 (Gall isographic), 43.5 (Min. continental distortion), 42 (Grafarend & Niermann), "
7356 				"37.5 (Min. overall distortion), and 0 (Plate Carree [Default]).");
7357 
7358 			GMT_Usage (API, 2, "-Jr|R[<lon0>/]<scale>|<width> (Winkel Tripel). Give optional central meridian and <scale>.");
7359 
7360 			GMT_Usage (API, 2, "-Js|S<lon0>/<lat0>[/<horizon>]/<scale>|<width> (Stereographic). "
7361 				"<lon0>/<lat0> is the center or the projection, "
7362 				"<horizon> is max distance from center of the projection (< 180, default 90), and "
7363 				"<scale> is either <1:xxxx> (true at pole) or <slat>/<1:xxxx> (true at <slat>) "
7364 				"or <radius>/<lat> (distance in %s to the (oblique) parallel <lat0>.",
7365 					GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7366 
7367 			GMT_Usage (API, 2, "-Jt|T<lon0>/[<lat0>/]<scale>|<width> (Transverse Mercator). Give central meridian and scale. "
7368 				"Optionally, also give the central parallel [Default is equator].");
7369 
7370 			GMT_Usage (API, 2, "-Ju|U<zone>/<scale>|<width> (UTM). "
7371 				"Give zone (A,B,Y,Z, or 1-60 (negative for S hemisphere) or append code C-X) and <scale>. "
7372 				"Or, give -Ju|U<scale>|<width> to have the UTM zone determined from the region.");
7373 
7374 			GMT_Usage (API, 2, "-Jv|V[<lon0>/]<scale>|<width> (van der Grinten). Give optional central meridian  and <scale>.");
7375 
7376 			GMT_Usage (API, 2, "-Jw|W[<lon0>/]<scale>|<width> (Mollweide). Give optional central meridian and <scale>.");
7377 
7378 			GMT_Usage (API, 2, "-Jy|Y[<lon0>/[<lat0>/]]<scale>|<width> (Cylindrical Equal-area). "
7379 				"Give optional central meridian and standard parallel, and <scale>. Common parallels:  "
7380 				"<lat0> = 50 (Balthasart), 45 (Gall), 37.5 (Hobo-Dyer), 37.4 (Trystan Edwards), "
7381 				"37.0666 (Caster), 30 (Behrmann), and 0 (Lambert [Default])");
7382 
7383 			GMT_Usage (API, 2, "-Jx|X<x-scale|<width>[/<y-scale|height>] (Linear, log, power scaling). "
7384 				"<scale> in %s/units (or 1:xxxx). Optionally, append to <x-scale> and/or <y-scale> one of "
7385 				"d for Geographic coordinate (in degrees), "
7386 				"l for Log10 projection, "
7387 				"p<power> for x^power projection, "
7388 				"t for calendar time projection using relative time coordinates, "
7389 				"or T for Calendar time projection using absolute time coordinates. "
7390 				"Use / to specify separate x/y scaling (e.g., -Jx0.5c/0.3c).  If 1:xxxxx is used it implies -R is in meters. "
7391 				"If -JX is used then give axes lengths rather than scales.",
7392 					GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7393 			break;
7394 
7395 		case 'j':	/* Condensed version of J */
7396 
7397 			GMT_Usage (API, 1, "\n-J Select map proJection (<scale> in %s/degree, <width> in %s). Modifiers:",
7398 				GMT->session.unit_name[GMT->current.setting.proj_length_unit],
7399 				GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7400 			GMT_Usage (API, 3, "+dh Specifying map height.");
7401 			GMT_Usage (API, 3, "+du Specifying maximum map dimension.");
7402 			GMT_Usage (API, 3, "+dl Specifying minimum map dimension.");
7403 			GMT_Usage (API, 3, "+dw Specifying map width [Default].");
7404 			GMT_Usage (API, -2, "Azimuthal projections set -Rg unless polar aspect or -R<...>+r is set.");
7405 
7406 			GMT_Usage (API, 2, "-Ja|A<lon0>/<lat0>[/<hor>]/<scl> (or <radius>/<lat>)|<width> (Lambert Azimuthal Equal-area)");
7407 
7408 			GMT_Usage (API, 2, "-Jb|B<lon0>/<lat0>/<lat1>/<lat2>/<scl>|<width> (Albers Conic Equal-area)");
7409 
7410 			GMT_Usage (API, 2, "-Jcyl_stere|Cyl_stere/[<lon0>/[<lat0>/]]<lat1>/<lat2>/<scl>|<width> (Cylindrical Stereographic)");
7411 
7412 			GMT_Usage (API, 2, "-Jc|C<lon0>/<lat0><scl>|<width> (Cassini)");
7413 
7414 			GMT_Usage (API, 2, "-Jd|D<lon0>/<lat0>/<lat1>/<lat2>/<scl>|<width> (Equidistant Conic)");
7415 
7416 			GMT_Usage (API, 2, "-Je|E<lon0>/<lat0>[/<horizon>]/<scl> (or <radius>/<lat>)|<width> (Azimuthal Equidistant)");
7417 
7418 			GMT_Usage (API, 2, "-Jf|F<lon0>/<lat0>[/<horizon>]/<scl> (or <radius>/<lat>)|<width> (Gnomonic)");
7419 
7420 			GMT_Usage (API, 2, "-Jg|G<lon0>/<lat0>/<scl> (or <radius>/<lat>)|<width> (Orthographic)");
7421 
7422 			GMT_Usage (API, 2, "-Jg|G<lon0>/<lat0>/<scl>|<width>[+a<azimuth>][+t<tilt>][+v<vwidth>/<vheight>][+w<twist>][+z<altitude>[r|R]|g] (General Perspective)");
7423 
7424 			GMT_Usage (API, 2, "-Jh|H[<lon0>/]<scl>|<width> (Hammer-Aitoff)");
7425 
7426 			GMT_Usage (API, 2, "-Ji|I[<lon0>/]<scl>|<width> (Sinusoidal)");
7427 
7428 			GMT_Usage (API, 2, "-Jj|J[<lon0>/]<scl>|<width> (Miller)");
7429 
7430 			GMT_Usage (API, 2, "-Jkf|Kf[<lon0>/]<scl>|<width> (Eckert IV)");
7431 
7432 			GMT_Usage (API, 2, "-Jks|Ks[<lon0>/]<scl>|<width> (Eckert VI)");
7433 
7434 			GMT_Usage (API, 2, "-Jl|L<lon0>/<lat0>/<lat1>/<lat2>/<scl>|<width> (Lambert Conformal Conic)");
7435 
7436 			GMT_Usage (API, 2, "-Jm|M[<lon0>/[<lat0>/]]<scl>|<width> (Mercator)");
7437 
7438 			GMT_Usage (API, 2, "-Jn|N[<lon0>/]<scl>|<width> (Robinson projection)");
7439 
7440 			GMT_Usage (API, 2, "-Jo|O (Oblique Mercator).  Specify one of three definitions:");
7441 			GMT_Usage (API, 3, "-Jo|O[a|A]<lon0>/<lat0>/<azimuth>/<scl>|<width>[+v]");
7442 			GMT_Usage (API, 3, "-Jo|O[b|B]<lon0>/<lat0>/<lon1>/<lat1>/<scl>|<width>[+v]");
7443 			GMT_Usage (API, 3, "-Jo|Oc|C<lon0>/<lat0>/<lonp>/<latp>/<scl>|<width>[+v]");
7444 
7445 			GMT_Usage (API, 2, "-Jpoly|Poly/[<lon0>/[<lat0>/]]<scl>|<width> ((American) Polyconic)");
7446 
7447 			GMT_Usage (API, 2, "-Jq|Q[<lon0>/[<lat0>/]]<scl>|<width> (Equidistant Cylindrical)");
7448 
7449 			GMT_Usage (API, 2, "-Jr|R[<lon0>/]<scl>|<width> (Winkel Tripel)");
7450 
7451 			GMT_Usage (API, 2, "-Js|S<lon0>/<lat0>/[<horizon>/]<scl> (or <slat>/<scl> or <radius>/<lat>)|<width> (Stereographic)");
7452 
7453 			GMT_Usage (API, 2, "-Jt|T<lon0>/[<lat0>/]<scl>|<width> (Transverse Mercator)");
7454 
7455 			GMT_Usage (API, 2, "-Ju|U[<zone>/]<scl>|<width> (UTM)");
7456 
7457 			GMT_Usage (API, 2, "-Jv|V<lon0>/<scl>|<width> (van der Grinten)");
7458 
7459 			GMT_Usage (API, 2, "-Jw|W<lon0>/<scl>|<width> (Mollweide)");
7460 
7461 			GMT_Usage (API, 2, "-Jy|Y[<lon0>/[<lat0>/]]<scl>|<width> (Cylindrical Equal-area)");
7462 
7463 			GMT_Usage (API, 2, "-Jp|P<scl>|<width>[+a][+f[e|p|<radius>]][+k<kind>][+r<offset>][+t<origin][+z[p|<radius>]] (Polar [azimuth] (theta,radius)).");
7464 
7465 			GMT_Usage (API, 2, "-Jx|X<x-scl>|<width>[d|l|p<power>|t|T][/<y-scl>|<height>[d|l|p<power>|t|T]] (Linear, log, and power projections)");
7466 			GMT_Usage (API, 2, "(See basemap for more details on projection syntax)");
7467 			break;
7468 
7469 		case 'I':	/* Near-common option for grid increments */
7470 			gmt_inc_syntax (GMT, 'I', false);
7471 			break;
7472 
7473 		case 'K':	/* Append-more-PostScript-later */
7474 			if (GMT->current.setting.run_mode == GMT_CLASSIC && !GMT->current.setting.use_modern_name)	/* -K don't exist in modern mode */
7475 				GMT_Usage (API, 1, "\n-K Allow for more plot code to be appended later.");
7476 			break;
7477 
7478 		case 'O':	/* Overlay plot */
7479 
7480 			if (GMT->current.setting.run_mode == GMT_CLASSIC && !GMT->current.setting.use_modern_name)	/* -O don't exist in modern mode */
7481 				GMT_Usage (API, 1, "\n-O Set Overlay plot mode, i.e., append to an existing plot.");
7482 			break;
7483 
7484 		case 'P':	/* Portrait or landscape */
7485 
7486 			if (GMT->current.setting.run_mode == GMT_CLASSIC && !GMT->current.setting.use_modern_name)	/* -P don't exist in modern mode */
7487 				GMT_Usage (API, 1, "\n-P Set Portrait page orientation [%s].", GMT_choice[GMT->current.setting.ps_orientation]);
7488 			break;
7489 
7490 		case 'S':	/* CarteSian Region option */
7491 
7492 			GMT_Usage (API, 1, "\n%s", GMT_Rx_OPT);
7493 			GMT_Usage (API, -2, "Specify the coordinates of data region in user units. "
7494 				"Use [yyyy[-mm[-dd]]]T[hh[:mm[:ss[.xxx]]]] format for time coordinates. "
7495 				"Or, give a <gridfile> to use its region (and increments, registration if applicable).");
7496 			break;
7497 
7498 		case 'G':	/* Geographic Region option */
7499 
7500 			gmtinit_explain_R_geo (GMT);
7501 			break;
7502 
7503 		case 'R':	/* Generic [Default] Region option */
7504 
7505 			gmtinit_explain_R_geo (GMT);
7506 			GMT_Usage (API, -2, "Alternatively, use -R<code><x0>/<y0>/<n_columns>/<n_rows> for origin and grid dimensions, where "
7507 				"<code> is a 2-char combo from [T|M|B][L|C|R] (top/middle/bottom/left/center/right) "
7508 				"and grid spacing must be specified via -I<dx>[/<dy>] (also see -r).");
7509 			break;
7510 
7511 		case 'z':	/* Region addition for 3-D */
7512 
7513 			GMT_Usage (API, -2, "Append /zmin/zmax coordinates for the vertical domain limits.");
7514 			break;
7515 
7516 		case 'r':	/* Region option for 3-D */
7517 
7518 			GMT_Usage (API, 1, "\n-R Specify the xyz min/max coordinates of the plot window in user units. "
7519 				"Use dd:mm[:ss] for regions given in degrees, minutes [and seconds]. "
7520 				"Append +r if first 4 arguments to -R specify the longitudes/latitudes"
7521 				"of the lower left and upper right corners of a rectangular area. "
7522 				"Or, give a gridfile to use its limits (and increments if applicable).");
7523 			break;
7524 
7525 		case 'U':	/* Plot time mark and [optionally] command line */
7526 
7527 			GMT_Usage (API, 1, "\n%s", GMT_U_OPT);
7528 			GMT_Usage (API, -2, "Plot GMT Unix System Time stamp and optionally append a <label>. Optional modifiers:");
7529 			GMT_Usage (API, 3, "+c Use the command line as the label [%s].", GMT_choice[GMT->current.setting.map_logo]);
7530 			GMT_Usage (API, 3, "+j Set frame justification point [BL].");
7531 			GMT_Usage (API, 3, "+o Offset stamp by <dx>[/<dy>] [-54p/-54p].");
7532 			break;
7533 
7534 		case 'V':	/* Verbose */
7535 
7536 			GMT_Usage (API, 1, "\n%s", GMT_V_OPT);
7537 			GMT_Usage (API, -2, "Change the verbosity level (currently %c). "
7538 				"Choose among 7 levels; each level adds more detailed messages:", V_code[GMT->current.setting.verbose]);
7539 			GMT_Usage (API, 3, "q: Quiet, not even fatal error messages.");
7540 			GMT_Usage (API, 3, "e: Error messages only.");
7541 			GMT_Usage (API, 3, "w: Warnings [Default].");
7542 			GMT_Usage (API, 3, "t: Timings (time-intensive operations only).");
7543 			GMT_Usage (API, 3, "i: Informational messages (or just -V).");
7544 			GMT_Usage (API, 3, "c: Compatibility warnings.");
7545 			GMT_Usage (API, 3, "d: Debugging messages.");
7546 			break;
7547 
7548 		case 'X':
7549 		case 'Y':	/* Reset plot origin option */
7550 
7551 			GMT_Usage (API, 1, "\n%s %s", GMT_X_OPT, GMT_Y_OPT);
7552 			GMT_Usage (API, -2, "Shift origin of plot to (<xshift>, <yshift>). "
7553 				"Prepend r for shift relative to current point [Default], prepend a for temporary "
7554 				"adjustment of origin, prepend f to position relative to lower left corner of page, "
7555 				"prepend c for offset of center of plot to center of page. "
7556 				"For overlays (-O), the default setting is [r0], otherwise [f%g%c].", GMT->current.setting.map_origin[GMT_Y] * s, u);
7557 			break;
7558 
7559 		case 'x':	/* Just linear -Jx|X allowed for this program */
7560 			GMT_Usage (API, 1, "\n-Jx|X<scl>|<height>[d|l|p<power>|t|T][/<scl>|<height>[d|l|p<power>|t|T]]");
7561 			GMT_Usage (API, -2, "Scaling for linear projection.  Scale in %s/units (or width in %s). "
7562 				"Use / to specify separate x/y scaling. If -JX is used then give axes lengths in %s rather than scales.",
7563 				GMT->session.unit_name[GMT->current.setting.proj_length_unit], GMT->session.unit_name[GMT->current.setting.proj_length_unit],
7564 				GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7565 			break;
7566 
7567 #ifdef GMT_MP_ENABLED
7568 		case 'y':	/* Number of threads (reassigned from -x in GMT_Option) */
7569 			cores = gmtlib_get_num_processors();
7570 			GMT_Usage (API, 1, "\n%s.", GMT_x_OPT);
7571 			GMT_Usage (API, -2, "Limit the number of cores used in multi-threaded algorithms [Default uses all %d cores]. "
7572 				"If <n> is negative then we select (%d - <n>) cores (or at least 1).", cores, cores);
7573 			break;
7574 #endif
7575 		case 'Z':	/* Vertical scaling for 3-D plots */
7576 
7577 			GMT_Usage (API, 1, "\n-Jz|Z<z-scl>|<height>[d|l|p<power>|t|T]");
7578 			GMT_Usage (API, -2, "Scaling for z-component of 3-D projections.  Same syntax as -JX|x for Linear, log, and power projections.");
7579 			break;
7580 
7581 		case 'a':	/* -a option for aspatial field substitution into data columns */
7582 
7583 			GMT_Usage (API, 1, "\n%s", GMT_a_OPT);
7584 			GMT_Usage (API, -2, "Aspatial data selection.  Give one or more comma-separated [<col>=]<name> associations "
7585 				"[Default selects all aspatial fields].");
7586 			break;
7587 
7588 		case 'C':	/* -b binary option with input only */
7589 
7590 			GMT_Usage (API, 1, "\n%s", GMT_bi_OPT);
7591 			GMT_Usage (API, -2, "Select native binary table input; <record> is comma-separated  groups of [<ncols>][<type>][w]; "
7592 				"<ncols> is number of consecutive columns of given <type> from c|u|h|H|i|I|l|L|f|d [d]. "
7593 				"A group may be byte-swapped by appending w. Available modifiers: ");
7594 			GMT_Usage (API, 3, "+b Read table assuming big-endian byte-order.");
7595 			GMT_Usage (API, 3, "+l Read table assuming little-endian byte-order.");
7596 			break;
7597 
7598 		case '0':	/* -bi/-bo addendum when input format is unknown */
7599 
7600 			/* Nothing anymore, but leave case since may still be requested by a module */
7601 			break;
7602 
7603 		case '1':	/* -bi/-bo addendum when input format is unknown */
7604 		case '2':
7605 		case '3':
7606 		case '4':
7607 		case '5':
7608 		case '6':
7609 		case '7':
7610 
7611 			GMT_Usage (API, -2, "Note: If <ncols> is not given we default to %c.", options[k]);
7612 			break;
7613 
7614 		case 'D':	/* -b binary option with output only */
7615 
7616 			GMT_Usage (API, 1, "\n%s", GMT_bo_OPT);
7617 			GMT_Usage (API, -2, "Select native binary table output; <record> is comma-separated groups of [<ncols>][<type>][w]; "
7618 				"<ncols> is number of consecutive columns of given <type> from c|u|h|H|i|I|l|L|f|d [d]. "
7619 				"A group may be byte-swapped by appending w. Available modifiers: ");
7620 			GMT_Usage (API, 3, "+b Write table in big-endian byte-order.");
7621 			GMT_Usage (API, 3, "+l Write table in little-endian byte-order.");
7622 			break;
7623 
7624 		case 'c':	/* -c option advances subplot panel focus under modern mode */
7625 
7626 			if (GMT->current.setting.run_mode == GMT_MODERN || GMT->current.setting.use_modern_name) {	/* -c has no use in classic */
7627 				GMT_Usage (API, 1, "\n%s", GMT_c0_OPT);
7628 				GMT_Usage (API, -2, "Move to next subplot panel [Default] or append <row>,<col> or <index> of desired panel.");
7629 			}
7630 			break;
7631 
7632 		case 'd':	/* -d option to tell GMT the relationship between NaN and a nan-proxy for input/output */
7633 
7634 			GMT_Usage (API, 1, "\n%s", GMT_d_OPT);
7635 			GMT_Usage (API, -2, "On (i)nput, replace <nodata> values with NaN; on (o)utput do the reverse.");
7636 			break;
7637 
7638 		case 'e':	/* -e option for ASCII grep operation on data records */
7639 
7640 			GMT_Usage (API, 1, "\n%s", GMT_e_OPT);
7641 			GMT_Usage (API, -2, "Only accept input data records that contain the string \"pattern\". "
7642 				"Use -e~\"pattern\" to only accept data records that do NOT contain this pattern. "
7643 				"If your pattern begins with ~, escape it with \\~.  To match against "
7644 				"extended regular expressions use -e[~]/<regexp>/[i] (i for case-insensitive). "
7645 				"Use +f<file> to read patterns from a file instead, one pattern per line.");
7646 			GMT_Usage (API, -2, "Note: To give a single pattern starting with +f, escape it with \\+f.");
7647 			break;
7648 
7649 		case 'k':	/* -di option to tell GMT the relationship between NaN and a nan-proxy for input */
7650 
7651 			GMT_Usage (API, 1, "\n%s Replace any <nodata> values in input data with NaN.", GMT_di_OPT);
7652 			GMT_Usage (API, 3, "+c Append first column to be affected [2].");
7653 			break;
7654 
7655 		case 'l':	/* -l option to set up auto-legend items*/
7656 
7657 			GMT_Usage (API, 1, "\n%s", GMT_l_OPT);
7658 			GMT_Usage (API, -2, "Add symbol, line or polygon to the legend. Optionally, append <label> and any of the optional legend codes:");
7659 			GMT_Usage (API, 3, "+D Draw horizontal line with specified <pen>.");
7660 			GMT_Usage (API, 3, "+G Add a vertical <gap> of given height.");
7661 			GMT_Usage (API, 3, "+H Place a <header> in the legend.");
7662 			GMT_Usage (API, 3, "+L Place a <label> with optional justification [L].");
7663 			GMT_Usage (API, 3, "+N Set current number of columns for symbols.");
7664 			GMT_Usage (API, 3, "+S Set the current symbol <size>.");
7665 			GMT_Usage (API, 3, "+V Enable vertical lines between columns, optionally append <pen>].");
7666 			GMT_Usage (API, 3, "+f Set <font> used for the label.");
7667 			GMT_Usage (API, 3, "+g Set a frame fill <fill> [white].");
7668 			GMT_Usage (API, 3, "+j Set the justification of the legend [BL].");
7669 			GMT_Usage (API, 3, "+o Shift legend placement from reference point by <dx>[/<dy>] [0.2c].");
7670 			GMT_Usage (API, 3, "+p Draw frame outline, optionally append <pen> [1p].");
7671 			GMT_Usage (API, 3, "+s Set an overall symbol scale [1].");
7672 			GMT_Usage (API, 3, "+w Set a specific legend width [auto].");
7673 			break;
7674 
7675 		case 'm':	/* -do option to tell GMT the relationship between NaN and a nan-proxy for output */
7676 
7677 			GMT_Usage (API, 1, "\n%s", GMT_do_OPT);
7678 			GMT_Usage (API, -2, "Replace any NaNs in output data with <nodata>.");
7679 			GMT_Usage (API, 3, "+c Append first column to be affected [0].");
7680 			break;
7681 
7682 		case 'f':	/* -f option to tell GMT which columns are time (and optionally geographical) */
7683 
7684 			GMT_Usage (API, 1, "\n%s", GMT_f_OPT);
7685 			GMT_Usage (API, -2, "Indicate content of input/output columns. Optionally append i(nput) or o(utput) [Default is both]. "
7686 				"Append <colinfo> as one or more comma-separated groups of <cols><type>, where <cols> is a column (or column ranges) "
7687 				"and <type> is the column type, chosen from T (Calendar format), t (time relative to TIME_EPOCH), f (floating point), "
7688 				"x (longitude), y (latitude), or d (dimension). You may also use s (string) to indicate the start column of trailing text. "
7689 				"Shortcuts: -f[i|o]g means -f[i|o]0x,1y (geographic, i.e., lon/lat coordinates), -f[i|o]c means -f[i|o]0:1f (Cartesian coordinates), "
7690 				"while -fp[<unit>] means input x,y are already in projected coordinates in <unit> [e].");
7691 			break;
7692 
7693 		case 'g':	/* -g option to tell GMT to identify data gaps based on point separation */
7694 
7695 			GMT_Usage (API, 1, "\n%s", GMT_g_OPT);
7696 			GMT_Usage (API, -2, "Use data point separations to identify data gaps. "
7697 				"Append x, y, or z to identify data gaps in x, y or z coordinates, "
7698 				"respectively, or append d for distance gaps.  Use upper case X|Y|D to "
7699 				"first project the points (requires -R -J).  Optional modifiers:");
7700 			GMT_Usage (API, 3, "+a All criteria must be met to declare a gap [Just one criterion must be met].");
7701 			GMT_Usage (API, 3, "+c Set alternative z-coordinate column [2].");
7702 			GMT_Usage (API, 3, "+n Let d = prev-curr; d must exceed <gap> to detect a gap [Default is d=|curr-prev|].");
7703 			GMT_Usage (API, 3, "+p Let d = curr-prev; d must exceed <gap> to detect a gap [Default is d=|curr-prev|].");
7704 			GMT_Usage (API, -2, "For geographic data: Append <unit> from %s [Default is meter (%c)]. "
7705 				"For gaps based on projected coordinates: Append unit from %s [%s]. "
7706 				"For time data the unit is determined by TIME_UNIT. "
7707 				"For Cartesian data a unit is not specified. "
7708 				"Repeat the -g option to specify multiple criteria.",
7709 				GMT_LEN_UNITS2_DISPLAY, GMT_MAP_DIST_UNIT, GMT_DIM_UNITS_DISPLAY, GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
7710 			break;
7711 
7712 		case 'h':	/* Header */
7713 
7714 			GMT_Usage (API, 1, "\n%s", GMT_h_OPT);
7715 			GMT_Usage (API, -2, "Input/output file has <nrecs> header record(s) [%s]. "
7716 				"Optionally, append i for input or o for output only and number of expected header records [%d]. "
7717 				"Note: -hi turns off the writing of all headers on output since none will be read.  Optional modifiers:",
7718 				GMT_choice[GMT->current.setting.io_header[GMT_IN]], GMT->current.setting.io_n_header_items);
7719 			GMT_Usage (API, 3, "+c Add header record with column information [none].");
7720 			GMT_Usage (API, 3, "+d Delete headers before adding new ones [Default will append headers].");
7721 			GMT_Usage (API, 3, "+m Insert a new segment header and <segheader> content after the headers [none].");
7722 			GMT_Usage (API, 3, "+r Add a <remark> comment to the output [none].");
7723 			GMT_Usage (API, 3, "+t Add a <title> comment to the output [none].");
7724 			GMT_Usage (API, -2, "Note: <remark> and <title> may contain \\n to indicate line-breaks. "
7725 				"For binary files, <nrecs> is considered to mean number of bytes instead of records.");
7726 			break;
7727 
7728 		case 'i':	/* -i option for input column order */
7729 
7730 			GMT_Usage (API, 1, "\n%s", GMT_i_OPT);
7731 			GMT_Usage (API, -2, "Set alternate input column order and optional transformations [Default reads all columns in original order]. "
7732 				"Append list or ranges of columns; t[<word>] selects the trailing text; append <word> to pick a word from the text. Use -in to select numerical input only. "
7733 				"Optional modifiers per column or column group:");
7734 			GMT_Usage (API, 3, "+l Take log10 of column before any other transformations.");
7735 			GMT_Usage (API, 3, "+d Divide column by appended <divisor>.");
7736 			GMT_Usage (API, 3, "+o Add to column the appended <offset>.");
7737 			GMT_Usage (API, 3, "+s Multiply column by appended <scale> or give d (convert km to degree) or k (degree to km)");
7738 			break;
7739 
7740 		case 'A':	/* -j option for spherical distance calculation mode */
7741 
7742 			GMT_Usage (API, 1, "\n%s", GMT_j_OPT);
7743 			GMT_Usage (API, -2, "Set spherical distance calculation mode for modules that offer that flexibility. "
7744 				"Append e for Ellipsoidal, f for Flat, or g for Spherical Earth (Great Circle) [Default].");
7745 			break;
7746 
7747 		case 'n':	/* -n option for grid resampling parameters in BCR */
7748 
7749 			GMT_Usage (API, 1, "\n%s", GMT_n_OPT);
7750 			GMT_Usage (API, -2, "Specify a grid interpolation directive:");
7751 			GMT_Usage (API, 3, "b: B-spline.");
7752 			GMT_Usage (API, 3, "c: Bicubic spline [Default].");
7753 			GMT_Usage (API, 3, "l: Bilinear spline.");
7754 			GMT_Usage (API, 3, "n: Nearest-neighbor value, i.e., no spline at all.");
7755 			GMT_Usage (API, -2, "Optional modifiers:");
7756 #ifdef DEBUG
7757 				GMT_Usage (API, 3, "+A Save the anti-aliasing counter to nz_grd_counter.grd for debugging.");
7758 #endif
7759 				GMT_Usage (API, 3, "+a Switch off anti-aliasing (except for linear) [Default is ON].");
7760 				GMT_Usage (API, 3, "+b Change boundary conditions, where <BC> can be either "
7761 					"g for geographic, p for periodic, or n for natural boundary conditions. "
7762 					"For p and n you may optionally append x or y [Default is both]: "
7763 					"x for periodic boundary conditions on x, "
7764 					"y for periodic boundary conditions on y "
7765 					"[Default is Natural conditions, unless grid is known to be geographic].");
7766 				GMT_Usage (API, 3, "+c Clip interpolated grid to input z-min/max [Default may exceed limits].");
7767 				GMT_Usage (API, 3, "+t Change the minimum weight in vicinity of NaNs. A <threshold> of "
7768 					"1.0 requires all nodes involved in interpolation to be non-NaN; 0.5 will interpolate "
7769 					"about half way from a non-NaN to a NaN node [Default is 0.5].");
7770 			break;
7771 
7772 		case 'o':	/* -o option for output column order */
7773 
7774 			GMT_Usage (API, 1, "\n%s", GMT_o_OPT);
7775 			GMT_Usage (API, -2, "Set alternate output column order [Default writes all columns in normal order]. "
7776 				"Append list or ranges of columns; t[<word>] selects the trailing text; append <word> for writing a single word from the text. "
7777 				"Use -on to select numerical output only.");
7778 			break;
7779 
7780 		case 'p':	/* Enhanced pseudo-perspective 3-D plot settings */
7781 		case 'E':	/* GMT4: For backward compatibility */
7782 			if (gmt_M_compat_check (GMT, 4) || options[k] == 'p') {
7783 				GMT_Usage (API, 1, "\n%s", GMT_p_OPT);
7784 				GMT_Usage (API, -2, "Select a 3-D pseudo perspective view.  Append the "
7785 					"<azimuth>/<elevation> of the viewpoint [180/90], and "
7786 					"when used with -Jz|Z, optionally add /<zlevel> for basemap level [bottom of z-axis]. "
7787 					"Prepend x or y to plot against the \"wall\" x = level or y = level [z]. "
7788 					"For a plain rotation about the z-axis, give rotation angle only "
7789 					"and optionally use modifiers +w or +v to select location of axis:");
7790 				GMT_Usage (API, 3, "+w Specify a fixed coordinate point [region center].");
7791 				GMT_Usage (API, 3, "+v Set a fixed projected point [panel center].");
7792 			}
7793 			break;
7794 
7795 		case 'q':	/* -q option for input/output row selection */
7796 
7797 			GMT_Usage (API, 1, "\n%s", GMT_q_OPT);
7798 			GMT_Usage (API, -2, "Select input (-q or -qi) or output (-qo) rows to process [Default reads or writes all rows]. "
7799 				"Append comma-separated lists or ranges of rows; prepend ~ to exclude those ranges instead. Optional modifiers:");
7800 			GMT_Usage (API, 3, "+c Set limits on data values for specified column <col> instead.");
7801 			GMT_Usage (API, 3, "+a Reset row counters per data set [Default].");
7802 			GMT_Usage (API, 3, "+f Reset row counters per file.");
7803 			GMT_Usage (API, 3, "+s Reset row counters per segment.");
7804 			break;
7805 
7806 		case 'u':	/* -qi option for input only */
7807 
7808 			GMT_Usage (API, 1, "\n%s", GMT_qi_OPT);
7809 			GMT_Usage (API, -2, "Select input rows to process [Default reads all rows]. "
7810 				"Append comma-separated lists or ranges of rows; prepend ~ to exclude those ranges instead. Optional modifiers:");
7811 			GMT_Usage (API, 3, "+c Set limits on data values for specified input column <col> instead.");
7812 			GMT_Usage (API, 3, "+a Reset row counters per data set [Default].");
7813 			GMT_Usage (API, 3, "+f Reset row counters per file.");
7814 			GMT_Usage (API, 3, "+s Reset row counters per segment.");
7815 			break;
7816 
7817 		case 'v':	/* -qo option for output only */
7818 
7819 			GMT_Usage (API, 1, "\n%s", GMT_qo_OPT);
7820 			GMT_Usage (API, -2, "Select output rows to process [Default writes all rows]. "
7821 				"Append comma-separated lists or ranges of rows; prepend ~ to exclude those ranges instead. Optional modifiers:");
7822 			GMT_Usage (API, 3, "+c Set limits on data values for specified output column <col> instead.");
7823 			GMT_Usage (API, 3, "+a Reset row counters per data set [Default].");
7824 			GMT_Usage (API, 3, "+f Reset row counters per file.");
7825 			GMT_Usage (API, 3, "+s Reset row counters per segment.");
7826 			break;
7827 
7828 		case 'w':	/* -w option for cyclicity */
7829 
7830 			GMT_Usage (API, 1, "\n%s", GMT_w_OPT);
7831 			GMT_Usage (API, -2, "Wrap selected column [0] with specified cyclicity. "
7832 				"Absolute time: Append y|a|w|d|h|m|s for year, annual (by month), week, day, hour, minute, or second cycles. "
7833 				"Alternatively, append c<period>[/<phase>] for custom cyclicity [Default <phase> = 0].");
7834 			GMT_Usage (API, 3, "+c<col> Select another column than 0 (first) for wrapping.");
7835 			break;
7836 
7837 		case 's':	/* Output control for records where z are NaN */
7838 
7839 			GMT_Usage (API, 1, "\n%s", GMT_s_OPT);
7840 			GMT_Usage (API, -2, "Suppress output of data records whose z-value(s) equal NaN [Default prints all records]. "
7841 				"Append <cols> to test all specified column(s) [2]. Optional modifiers:");
7842 			GMT_Usage (API, 3, "+a Suppress records where any column equals NaN [all columns must equal NaN].");
7843 			GMT_Usage (API, 3, "+r Reverse the test (only output record that fail the NaN-test).");
7844 			break;
7845 
7846 		case 'F':	/* -r grid registration option  */
7847 
7848 			GMT_Usage (API, 1, "\n%s", GMT_r_OPT);
7849 			GMT_Usage (API, -2, "Set (g)ridline- or (p)ixel-registration (-r with no argument sets pixel registration). "
7850 				"If -r is not given then we default to gridline registration.");
7851 			break;
7852 
7853 		case 't':	/* -t layer transparency option  */
7854 
7855 			GMT_Usage (API, 1, "\n%s", GMT_t_OPT);
7856 			GMT_Usage (API, -2, "Set layer transparency from 0-100 [Default is 0; opaque]. "
7857 				"For separate transparencies for fill and stroke, append /<transp2> [Default is same for both]. Optional modifiers:");
7858 			GMT_Usage (API, 3, "+f Let <transp> apply to fill only.");
7859 			GMT_Usage (API, 3, "+s Let <transp> apply to stroke only.");
7860 			GMT_Usage (API, -2, "Note: Transparency requires conversion to PDF or raster formats to take effect.");
7861 			break;
7862 
7863 		case 'T':	/* Same -t but with extension for variable fill/stroke transparency option  */
7864 
7865 			GMT_Usage (API, 1, "\n%s", GMT_tv_OPT);
7866 			GMT_Usage (API, -2, "Set layer transparency from 0-100 [Default is 0; opaque]. "
7867 				"For separate transparencies for fill and stroke, append /<transp2>. "
7868 				"For plotting symbols with variable transparency read from file, append no values "
7869 				"and give the transparency as the last numerical value(s) in the data record. Optional modifiers:");
7870 			GMT_Usage (API, 3, "+f Let transparency apply to fills only.");
7871 			GMT_Usage (API, 3, "+s Let transparency apply to strokes only.");
7872 			GMT_Usage (API, -2, "If no transparency is given then use +f, +s to tell if we expect one or two transparencies from file [1].");
7873 			GMT_Usage (API, -2, "Note: Transparency requires conversion to PDF or raster formats to take effect.");
7874 			break;
7875 
7876 		case ':':	/* lon/lat [x/y] or lat/lon [y/x] */
7877 
7878 			GMT_Usage (API, 1, "\n%s Swap 1st and 2nd column on input and/or output [%s/%s].", GMT_colon_OPT,
7879 				GMT_choice[GMT->current.setting.io_lonlat_toggle[GMT_IN]], GMT_choice[GMT->current.setting.io_lonlat_toggle[GMT_OUT]]);
7880 			break;
7881 
7882 		case '.':	/* Trailer message */
7883 
7884 			GMT_Usage (API, 1, "\n-^ (or -) Print short synopsis message.");
7885 			GMT_Usage (API, 1, "-+ (or +) Print longer synopsis message.");
7886 			GMT_Usage (API, 1, "-? (or no arguments) Print this usage message.");
7887 			GMT_Usage (API, 1, "--PAR=<value> Temporarily override GMT default setting(s) (repeatable).");
7888 			GMT_Usage (API, -2, "(See %s documentation for GMT default parameters).", GMT_SETTINGS_FILE);
7889 			break;
7890 
7891 		case ';':	/* Trailer message without --PAR=value etc */
7892 
7893 			GMT_Usage (API, 1, "\n-^ (or -) Print short synopsis message.");
7894 			GMT_Usage (API, 1, "-+ (or +) Print longer synopsis message.");
7895 			GMT_Usage (API, 1, "-? (or no arguments) Print this usage message.");
7896 			break;
7897 
7898 		case '<':	/* Table input */
7899 
7900 			GMT_Usage (API, 1, "\n<table> is one or more data files (in ASCII, binary, netCDF). "
7901 				"If no files are given, standard input is read.");
7902 			break;
7903 
7904 		case '-':	/* --PAR=arg message */
7905 			GMT_Usage (API, 1, "\n--PAR=<value> Temporarily override GMT default setting(s) (repeatable).");
7906 			GMT_Usage (API, -2, "(See %s documentation for GMT default parameters).", GMT_SETTINGS_FILE);
7907 			break;
7908 
7909 		default:	/* Pass through, no error, might be things like -y not available */
7910 			break;
7911 		}
7912 	}
7913 }
7914 
7915 /*! Output grid specification */
7916 /*!
7917 	\param GMT ...
7918 	\param message ...
7919 */
gmt_outgrid_syntax(struct GMTAPI_CTRL * API,char option,char * message)7920 void gmt_outgrid_syntax (struct GMTAPI_CTRL *API, char option, char *message) {
7921 	if (option == 0)	/* grid is an input file argument, not an option */
7922 		GMT_Usage (API, 1, "\n%s", GMT_OUTGRID);
7923 	else if (option == '=')	/* grdmath usees = instead of -option*/
7924 		GMT_Usage (API, 1, "\n= %s", GMT_OUTGRID);
7925 	else	/* All regular options */
7926 		GMT_Usage (API, 1, "\n-%c%s", option, GMT_OUTGRID);
7927 	if (message)
7928 		GMT_Usage (API, -2, "%s. Optionally append =<ID> for writing a specific file format and add any modifiers:", message);
7929 	else
7930 		GMT_Usage (API, -2, "Optionally append =<ID> for writing a specific file format and add any modifiers:");
7931 	GMT_Usage (API, 3, "+d Divide data values by the given <divisor> [1]");
7932 	GMT_Usage (API, 3, "+n Replace data values matching <invalid> with a NaN.");
7933 	GMT_Usage (API, 3, "+o Offset data values by the given <offset>, or append a for automatic range offset to preserve precision for integer grids [0].");
7934 	GMT_Usage (API, 3, "+s Scale data values by the given <scale>, or append a for automatic range scale to preserve precision for integer grids [1].");
7935 	GMT_Usage (API, -2, "Note: Any offset is added before any scaling, and +sa also sets +oa (unless overridden). "
7936 		"To write specific formats via GDAL, use <ID> = gd and supply <driver> (and optionally <dataType> and/or one or more concatenated GDAL -co <options> using +c).");
7937 }
7938 
7939 /*! Input grid specification */
7940 /*!
7941 	\param GMT ...
7942 	\param message ...
7943 */
gmt_ingrid_syntax(struct GMTAPI_CTRL * API,char option,char * message)7944 void gmt_ingrid_syntax (struct GMTAPI_CTRL *API, char option, char *message) {
7945 	if (option == 0)
7946 		GMT_Usage (API, 1, "\n%s", GMT_INGRID);
7947 	else
7948 		GMT_Usage (API, 1, "\n-%c%s", option, GMT_INGRID);
7949 	GMT_Usage (API, -2, "%s. Optionally append =<ID> for reading a specific file format or ?<varname> for a specific netCDF variable, and add any modifiers:", message);
7950 	GMT_Usage (API, 3, "+b Select a band (for images only) [0]");
7951 	GMT_Usage (API, 3, "+d Divide data values by the given <divisor> [1]");
7952 	GMT_Usage (API, 3, "+n Replace data values matching <invalid> with a NaN.");
7953 	GMT_Usage (API, 3, "+o Offset data values by the given <offset> [0].");
7954 	GMT_Usage (API, 3, "+s Scale data values by the given <scale> [1].");
7955 	GMT_Usage (API, -2, "Note: Any offset is added after any scaling.");
7956 }
7957 
7958 /*! GSHHG subset specification */
7959 /*!
7960 	\param GMT ...
7961 	\param option ...
7962 */
gmt_GSHHG_syntax(struct GMT_CTRL * GMT,char option)7963 void gmt_GSHHG_syntax (struct GMT_CTRL *GMT, char option) {
7964 	struct GMTAPI_CTRL *API = GMT->parent;
7965 	gmt_M_unused (option);
7966  	GMT_Usage (API, 1, "\n%s", GMT_A_OPT);
7967  	GMT_Usage (API, -2, "Place limits on coastline features from the GSHHG data base. "
7968 		"Features smaller than <min_area> (in km^2) or of levels (0-4) outside the <min_level>/<max_level> "
7969 		"range will be skipped [Default is 0/4 (0 is ocean and 4 means lake inside island inside lake)]. Optional modifiers:");
7970 		GMT_Usage (API, 3, "+a Control how Antarctica is handled: "
7971 		"Append g to use shelf ice grounding line for Antarctica coastline, or "
7972 		"append i to use ice/water front instead [Default]. "
7973 		"Append s to skip Antarctica (all data south of %dS), or "
7974 		"S to skip anything BUT Antarctica (all data north of %dS) [use all data]. ",
7975 			abs(GSHHS_ANTARCTICA_LIMIT), abs(GSHHS_ANTARCTICA_LIMIT));
7976 		GMT_Usage (API, 3, "+l Only get lakes from level 2 [riverlakes and lakes].");
7977 		GMT_Usage (API, 3, "+p Exclude features whose size is < <percent>%% of the full-resolution feature [use all features].");
7978 		GMT_Usage (API, 3, "+r Only get riverlakes from level 2  [riverlakes and lakes]");
7979 }
7980 
7981 /*! GSHHG resolution specification */
7982 /*! .
7983 	\param GMT ...
7984 	\param option ...
7985 	\param string ...
7986 */
gmt_GSHHG_resolution_syntax(struct GMT_CTRL * GMT,char option,char * string)7987 void gmt_GSHHG_resolution_syntax  (struct GMT_CTRL *GMT, char option, char *string) {
7988 	struct GMTAPI_CTRL *API = GMT->parent;
7989 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
7990 	GMT_Usage (API, 1, "\n-%c<resolution>[+f]", option);
7991 	GMT_Usage (API, -2, "Choose one of the following resolutions:");
7992 	GMT_Usage (API, 3, "f: Full resolution (may be very slow for large regions).");
7993 	GMT_Usage (API, 3, "h: High resolution (may be slow for large regions).");
7994 	GMT_Usage (API, 3, "i: Intermediate resolution.");
7995 	GMT_Usage (API, 3, "l: Low resolution [Default].");
7996 	GMT_Usage (API, 3, "c: Crude resolution, for tasks that need crude continent outlines only.");
7997 	GMT_Usage (API, -2, "Append +f to use a lower resolution should the chosen one not be available [abort]. %s", string);
7998 }
7999 
8000 /*! Contour/line specifications in *contour and psxy[z] */
8001 /*!
8002 	\param GMT ...
8003 	\param indent The number of spaces to indent after the TAB
8004 	\param kind  kind = 0 for *contour and 1 for psxy[z]
8005 */
gmt_label_syntax(struct GMT_CTRL * GMT,unsigned int indent,unsigned int kind)8006 void gmt_label_syntax (struct GMT_CTRL *GMT, unsigned int indent, unsigned int kind) {
8007 	struct GMTAPI_CTRL *API = GMT->parent;
8008 	static char *type[3] = {"Contour", "Line", "Decorated line"};
8009 	static char *feature[3] = {"label", "label", "symbol"};
8010 	indent++;
8011 	if (kind == 0) {
8012 		GMT_Usage (API, indent, "+a Place all %s at a fixed <angle>. "
8013 			"Or, specify +an (line-normal) or +ap (line-parallel) [Default]. "
8014 			"For +ap, you may optionally append u for up-hill"
8015 			"and d for down-hill cartographic annotations.\n", feature[kind]);
8016 	}
8017 	else {
8018 		GMT_Usage (API, indent, "+a Place all %s at a fixed <angle>. "
8019 			"Or, specify +an (line-normal) or +ap (line-parallel) [Default].", feature[kind]);
8020 	}
8021 	if (kind < 2) GMT_Usage (API, indent, "+c Set clearance <dx>[/<dy>] between label and text box [15%%].");
8022 	GMT_Usage (API, indent, "+d Debug mode which draws helper points and lines; optionally add a pen [%s].", gmt_putpen (GMT, &GMT->current.setting.map_default_pen));
8023 	if (kind < 2) GMT_Usage (API, indent, "+e Delay plotting of text as text clipping is set instead.");
8024 	if (kind < 2) GMT_Usage (API, indent, "+f Set specified label <font> [Default is %s].", gmt_putfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]));
8025 	if (kind < 2)
8026 		GMT_Usage (API, indent, "+g Paint text box [transparent]; optionally append <color> [white].");
8027 	else
8028 		GMT_Usage (API, indent, "+g Set the <fill> for the symbol [transparent]");
8029 	if (kind) GMT_Usage (API, indent, "+i Make the main line invisible [drawn using pen settings from -W].");
8030 	if (kind < 2) GMT_Usage (API, indent, "+j Set %s <justification> [Default is MC].", feature[kind]);
8031 	if (kind == 1) {
8032 		GMT_Usage (API, indent, "+l Use <text> as label (quote text if containing spaces).");
8033 		GMT_Usage (API, indent, "+L Set label according to given flag: "
8034 			"d: Cartesian plot distance; append a desired unit from %s, "
8035 			"D: Map distance; append a desired unit from %s, "
8036 			"f: Use given label location file with label in last column, "
8037 			"h: Use segment header labels (via -Lstring), "
8038 			"n: Use the current segment number (starting at 0), "
8039 			"N: Use current file number / segment number (starting at 0/0), or "
8040 			"x: Like h, but use headers in file with crossing lines instead.", GMT_DIM_UNITS_DISPLAY, GMT_LEN_UNITS_DISPLAY);
8041 	}
8042 	if (kind < 2)
8043 		GMT_Usage (API, indent, "+n Nudge label by <dx>[/<dy>] along line (+N for along x/y axis); ignored with +v.");
8044 	else
8045 		GMT_Usage (API, indent, "+n Nudge symbol by <dx>[/<dy>] along line (+N for along x/y axis).");
8046 	if (kind < 2) GMT_Usage (API, indent, "+o Use rounded rectangular text box [Default is rectangular].");
8047 	GMT_Usage (API, indent, "+p Draw outline of textbox [Default is no outline]. "
8048 		"Optionally append a <pen> [Default is default pen].");
8049 	if (kind == 2) GMT_Usage (API, indent, "+s Specify the decorative <symbol> and its <size>.");
8050 	if (kind < 2) {
8051 		GMT_Usage (API, indent, "+r Skip labels where radius of curvature < <rmin> [0].");
8052 		GMT_Usage (API, indent, "+t Save (x y angle label) to <file> [%s_labels.txt].", type[kind%2]);
8053 	}
8054 	if (kind == 0) {
8055 		GMT_Usage (API, indent, "+u Append <unit> to all labels. "
8056 			"If z is appended we use the z-unit from the grdfile [no unit].");
8057 	}
8058 	else
8059 		GMT_Usage (API, indent, "+u Append <unit> to all labels.");
8060 	if (kind < 2) GMT_Usage (API, indent, "+v Place curved text along path [Default is straight].");
8061 	GMT_Usage (API, indent, "+w Specify <n> (x,y) points to use for angle calculation [auto].");
8062 	if (kind == 1) {
8063 		GMT_Usage (API, indent, "+x Add <first>,<last> to these two labels [,']. "
8064 			"This modifier is only allowed if -SqN2 is used.");
8065 	}
8066 	if (kind < 2) GMT_Usage (API, indent, "+= Give all labels a <prefix>.");
8067 }
8068 
8069 /*! Contour/line label placement specifications in *contour and psxy[z] */
8070 /*!
8071 	\param GMT ...
8072 	\param indent The number of spaces to indent after the TAB
8073 	\param kind  kind = 0 for *contour and 1 for psxy[z]
8074 */
gmt_cont_syntax(struct GMT_CTRL * GMT,unsigned int indent,unsigned int kind)8075 void gmt_cont_syntax (struct GMT_CTRL *GMT, unsigned int indent, unsigned int kind) {
8076 	double gap;
8077 	char *type[3] = {"contour", "quoted line", "decorated line"};
8078 	char *feature[3] = {"label", "label", "symbol"};
8079 	struct GMTAPI_CTRL *API = GMT->parent;
8080 
8081 	gap = (GMT->current.setting.proj_length_unit == GMT_CM) ? 10.0 / 2.54 : 4.0;
8082 	gap *= GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
8083 
8084 	GMT_Usage (API, indent, "d: Append distance <dist> between %ss with specified map unit in %s  [Default is d%g%c]. "
8085 		"The first %s will appear at <frac>*<dist>; change this by appending /<frac> [0.25].",
8086 			feature[kind], GMT_DIM_UNITS_DISPLAY, gap, GMT->session.unit_name[GMT->current.setting.proj_length_unit][0], feature[kind]);
8087 	GMT_Usage (API, indent, "D: Same as +d, but append geographic distance between %ss with specified unit in %s.", feature[kind], GMT_LEN_UNITS_DISPLAY);
8088 	GMT_Usage (API, indent, "f: Append <file> with locations of individual points along the %ss where %ss should be placed.", type[kind], feature[kind]);
8089 	if (kind == 0) {
8090 		GMT_Usage (API, indent, "l: Append <line1>[,<line2>,...] to set coordinates for "
8091 		"straight line segments; %ss will be placed where these "
8092 		"lines intersect %ss.  The format of each <line> "
8093 		"is <start>/<stop>, where <start> or <stop> is either <lon/lat> or a "
8094 		"2-character key that uses the standard text justification codes "
8095 		"to specify a point on the map as [LCR][BMT]. In addition, you can use Z-, Z+ "
8096 		"to mean the global minimum and maximum locations in the grid.", feature[kind], type[kind]);
8097 	}
8098 	else {
8099 		GMT_Usage (API, indent, "l: Append <line1>[,<line2>,...] to set start and stop coordinates for "
8100 		"straight line segments; %ss will be placed where these "
8101 		"lines intersect %ss.  The format of each <line> "
8102 		"is <start>/<stop>, where <start> or <stop> is either <lon/lat> or a "
8103 		"2-character key that uses the standard text justification codes "
8104 		"to specify a point on the map as [LCR][BMT].", feature[kind], type[kind]);
8105 	}
8106 	GMT_Usage (API, indent, "L: Same as +l, but the point pairs define great circles instead of straight lines.");
8107 	GMT_Usage (API, indent, "n: Append number of centered equidistant %ss per %s. "
8108 		"Optionally, append /<min_dist> to enforce a minimum spacing between consecutive %ss [0].", feature[kind], type[kind], feature[kind]);
8109 	GMT_Usage (API, indent, "N: Same as +n, but start %s exactly at the start of %s. "
8110 		"Special cases: N-1 will place a single %s at start of the %s, while "
8111 		"N+1 will place a single %s at the end of the %s.",
8112 			feature[kind], type[kind], feature[kind], type[kind], feature[kind], type[kind]);
8113 	if (kind == 1) {
8114 		GMT_Usage (API, indent, "s: Append number of equidistant %ss per segmented %s. "
8115 			"Similar to +n, but splits input lines into a series of 2-point segments first.", feature[kind], type[kind]);
8116 		GMT_Usage (API, indent, "S: Same as +s, but with %s placement as described for +N.", feature[kind]);
8117 	}
8118 	GMT_Usage (API, indent, "x: Append name of a multi-segment <file> and place "
8119 		"%ss at intersections between %ss and lines in <file>.", feature[kind], type[kind]);
8120 	GMT_Usage (API, indent, "X: Same as +x, but will resample the lines first.");
8121 	if (kind < 2) {
8122 		GMT_Usage (API, -indent, "For all placement selections, append +r<radius> to specify minimum "
8123 			"radial separation between labels [0].");
8124 	}
8125 }
8126 
gmt_innercont_syntax(struct GMT_CTRL * GMT)8127 void gmt_innercont_syntax (struct GMT_CTRL *GMT) {
8128 	struct GMTAPI_CTRL *API = GMT->parent;
8129 	GMT_Usage (API, -2, "Embellish innermost, closed contours with ticks pointing in the downward direction. "
8130  		"User may specify to tick only highs (-Th) or lows (-Tl) [-T implies both extrema].");
8131  	GMT_Usage (API, 3, "+a Tick all closed contours.");
8132  	GMT_Usage (API, 3, "+d Append <spacing>[/<ticklength>] (with units) to change defaults [%gp/%gp].",
8133  		GMT_TICKED_SPACING, GMT_TICKED_LENGTH);
8134  	GMT_Usage (API, 3, "+l Append two characters (e.g., LH) or two comma-separated strings (e.g., \"low,high\") "
8135  		"to place labels at the center of local lows and highs [-+].");
8136 }
8137 
8138 /*! Widely used in most programs that need grid increments to be set */
8139 /*!
8140 	\param GMT ...
8141 	\param option ...
8142 	\param error ...
8143 */
gmt_inc_syntax(struct GMT_CTRL * GMT,char option,bool error)8144 void gmt_inc_syntax (struct GMT_CTRL *GMT, char option, bool error) {
8145 	struct GMTAPI_CTRL *API = GMT->parent;
8146 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8147 	GMT_Usage (API, 1, "\n-%c%s", option, GMT_inc_OPT);
8148 	GMT_Usage (API, -2, "Specify increment(s) and optionally append units or modifiers. "
8149 		"For geographic regions in degrees you can optionally append units from this list: "
8150 		"(d)egree [Default], (m)inute, (s)econd, m(e)ter, (f)oot, (k)ilometer, (M)ile, (n)autical mile, s(u)rvey foot.");
8151 	GMT_Usage (API, 3, "+e Adjust the region to fit increments [Adjust increment to fit domain].");
8152 	GMT_Usage (API, 3, "+n Increment specifies the number of nodes instead. Then, the actual increments "
8153 		"are calculated from the given domain and node-registration settings (see Appendix B for details).");
8154 	GMT_Usage (API, -2, "Note: If -R<grdfile> was used then -%c "
8155 		"(and -R and maybe -r) have been set; use -%c to override those increments.", option, option);
8156 }
8157 
8158 /*! .
8159 	\param GMT ...
8160 	\param option ...
8161 	\param string ...
8162 */
gmt_fill_syntax(struct GMT_CTRL * GMT,char option,char * longoption,char * string)8163 void gmt_fill_syntax (struct GMT_CTRL *GMT, char option, char *longoption, char *string) {
8164 	struct GMTAPI_CTRL *API = GMT->parent;
8165 	if (string[0] == ' ') {
8166 		if (longoption)
8167 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%s parsing failure.  Correct syntax:\n", longoption);
8168 		else
8169 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8170 	}
8171 	if (longoption)
8172 		GMT_Usage (API, 1, "\n-%s<fill>", longoption);
8173 	else
8174 		GMT_Usage (API, 1, "\n-%c<fill>", option);
8175 	GMT_Usage (API, -2, "%s Specify <fill> as one of:", string);
8176 	GMT_Usage (API, 3, "%s <gray> or <red>/<green>/<blue>, all in the range 0-255.", GMT_LINE_BULLET);
8177 	GMT_Usage (API, 3, "%s #rrggbb, all in the range 0-255 using hexadecimal numbers.", GMT_LINE_BULLET);
8178 	GMT_Usage (API, 3, "%s <cyan>/<magenta>/<yellow>/<black> in range 0-100%%.", GMT_LINE_BULLET);
8179 	GMT_Usage (API, 3, "%s <hue>-<saturation>-<value> in ranges 0-360, 0-1, 0-1.", GMT_LINE_BULLET);
8180 	GMT_Usage (API, 3, "%s A valid color name.", GMT_LINE_BULLET);
8181 	GMT_Usage (API, 3, "%s P|p<pattern>[+b<color>][+f<color>][+r<dpi>]. "
8182 		"Give <pattern> number from 1-90 or a filename. Optional modifiers:", GMT_LINE_BULLET);
8183 	GMT_Usage (API, 4, "+r Specify the <dpi> of the pattern [%g]. ", PSL_DOTS_PER_INCH_PATTERN);
8184 	GMT_Usage (API, 4, "+b Change the background <color> (no <color> sets transparency).");
8185 	GMT_Usage (API, 4, "+f Change the foreground <color> (no <color> sets transparency).");
8186 	GMT_Usage (API, -2, "For PDF fill transparency, append @<transparency> in the range 0-100 [0 = opaque].");
8187 }
8188 
8189 /*! .
8190 	\param GMT ...
8191 	\param option ...
8192 	\param string ...
8193 */
gmt_pen_syntax(struct GMT_CTRL * GMT,char option,char * longoption,char * string,char * prefix,unsigned int mode)8194 void gmt_pen_syntax (struct GMT_CTRL *GMT, char option, char *longoption, char *string, char *prefix, unsigned int mode) {
8195 	/* mode = 1 (bezier option), 2 = end trim, 4 = vector heads, 7 = all, 8 = CPT interactions */
8196 	struct GMTAPI_CTRL *API = GMT->parent;
8197 	char msg[GMT_LEN256] = {""};
8198 	char *args = (mode) ? "[<width>[c|i|p]],[<color>],[<style>[c|i|p]][+c[f|l][+o<offset><unit>][+s][+v[b|e]<specs>]" : "[<width>[c|i|p]],[<color>],[<style>[c|i|p]]";
8199 	if (string[0] == ' ') {
8200 		if (longoption)
8201 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%s parsing failure.  Correct syntax:\n", longoption);
8202 		else
8203 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8204 	}
8205 	if (strstr (string, "%s"))
8206 		sprintf (msg, string, gmt_putpen (GMT, &GMT->current.setting.map_default_pen));
8207 	else
8208 		strcpy (msg, string);
8209 	if (longoption) {
8210 		if (prefix)
8211 			GMT_Usage (API, 1, "\n-%s=%s%s", longoption, prefix, args);
8212 		else
8213 			GMT_Usage (API, 1, "\n-%s=%s", longoption, args);
8214 	}
8215 	else {
8216 		if (prefix)
8217 			GMT_Usage (API, 1, "\n-%c%s%s", option, prefix, args);
8218 		else
8219 			GMT_Usage (API, 1, "\n-%c%s", option, args);
8220 	}
8221 	GMT_Usage (API, -2, "%s", msg);
8222 	GMT_Usage (API, 2, "<pen> is a comma-separated list of three optional items in the order: "
8223 		"<width>[%s], <color>, and <style>[%s].", GMT_DIM_UNITS_DISPLAY, GMT_DIM_UNITS_DISPLAY);
8224 	GMT_Usage (API, 3, "<width> >= 0.0 sets pen width (Default unit is point); alternatively, give a pen "
8225 		"name: Choose from faint, default, [thin|thick|fat][er|est] or wide.");
8226 	GMT_Usage (API, 3, "<color> = <gray> or <red>/<green>/<blue>, each in the range 0-255; "
8227 		"#rrggbb, each in the range 00-FF using hexadecimal numbers; "
8228 		"<cyan>/<magenta>/<yellow>/<black> each in 0-100%% range; "
8229 		"<hue>-<saturation>-<value> in ranges 0-360, 0-1, 0-1, respectively; "
8230 		"a valid color name.");
8231 	GMT_Usage (API, 3, "<style> = pattern of dashes (-) and dots (.), scaled by <width>; "
8232 		"\"dashed\", \"dotted\", \"dashdot\", \"dotdash\", or \"solid\"; "
8233 		"<pattern>[:<offset>]; <pattern> holds lengths [Default unit is points] "
8234 		"of any number of lines and gaps separated by underscores. "
8235 		"The optional <offset> phase-shifts elements from start of the line [0].");
8236 	GMT_Usage (API, -2, "For PDF stroke transparency, append @<transparency> in the range 0-100%% [0 = opaque].");
8237 	if (mode)
8238 		GMT_Usage (API, -2, "Additional line attribute modifiers are also available:");
8239 	if (mode & 8) {
8240 		GMT_Usage (API, 3, "+c Control how pens and fills are affected if a CPT is specified via -C: "
8241 			"Append l to let pen colors follow the CPT setting, or "
8242 			"append f to let fill/font colors follow the CPT setting. "
8243 			"Default activates both effects.");
8244 	}
8245 	if (mode & 2) {
8246 		GMT_Usage (API, 3, "+o Trim the line from the end inward by the specified amount. "
8247 			"Choose <unit> in plot distances (%s) or map distances (%s) [Cartesian]. "
8248 			"To trim the two ends differently, give two offsets separated by a slash (/).", GMT_DIM_UNITS_DISPLAY, GMT_LEN_UNITS_DISPLAY);
8249 	}
8250 	if (mode & 1)
8251 		GMT_Usage (API, 3, "+s Draw line using a Bezier spline through projected coordinates [Linear spline].");
8252 	if (mode & 4) {
8253 		GMT_Usage (API, 3, "+v Add vector head with the given <specs> at the ends of lines. "
8254 			"Use +ve and +vb separately to give different endings (+v applies to both). "
8255 			"See vector specifications for details. Note: +v must be the last modifier for a pen.");
8256 	}
8257 }
8258 
8259 /*! .
8260 	\param GMT ...
8261 	\param option ...
8262 	\param string ...
8263 */
gmt_rgb_syntax(struct GMT_CTRL * GMT,char option,char * string)8264 void gmt_rgb_syntax (struct GMT_CTRL *GMT, char option, char *string) {
8265 	struct GMTAPI_CTRL *API = GMT->parent;
8266 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8267 	GMT_Usage (API, 1, "\n-%c<color>", option);
8268 	GMT_Usage (API, 2, "%s Specify <color> as one of: ", string);
8269 	GMT_Usage (API, 3, "%s <gray> or <red>/<green>/<blue>, all in range 0-255; ", GMT_LINE_BULLET);
8270 	GMT_Usage (API, 3, "%s <cyan>/<magenta>/<yellow>/<black> in range 0-100%%; ", GMT_LINE_BULLET);
8271 	GMT_Usage (API, 3, "%s <hue>-<saturation>-<value> in ranges 0-360, 0-1, 0-1; ", GMT_LINE_BULLET);
8272 	GMT_Usage (API, 3, "%s Any valid color name.", GMT_LINE_BULLET);
8273 	GMT_Usage (API, -2, "For PDF fill transparency, append @<transparency> in the range 0-100%% [0 = opaque].");
8274 }
8275 
gmt_refpoint_syntax(struct GMT_CTRL * GMT,char * option,char * string,unsigned int kind,unsigned int part)8276 void gmt_refpoint_syntax (struct GMT_CTRL *GMT, char *option, char *string, unsigned int kind, unsigned int part) {
8277 	/* For -Dg|j|J|n|x */
8278 	struct GMTAPI_CTRL *API = GMT->parent;
8279 	char *type[GMT_ANCHOR_NTYPES] = {"logo", "image", "legend", "color-bar", "inset", "map scale", "map rose", "vertical scale"};
8280 	unsigned int shift = (kind == GMT_ANCHOR_INSET) ? 1 : 0;	/* Add additional "tab" to front of message */
8281 	shift = 0;
8282 	if (part & 1) {	/* Here string is message, or NULL */
8283 		if (string) GMT_Usage (API, 1+shift, "%s %s", option, string);
8284 		GMT_Usage (API, 2+shift, "Positioning is specified via one of four coordinate systems:");
8285 		GMT_Usage (API, 3+shift, "g: Give <refpoint> in map coordinates.");
8286 		GMT_Usage (API, 3+shift, "j: Set inside-the-box <refpoint> via justification code (BL, MC, etc).");
8287 		GMT_Usage (API, 3+shift, "J: Set outside-the-box refpoint> via justification code (BL, MC, etc).");
8288 		GMT_Usage (API, 3+shift, "n: Give <refpoint> in normalized coordinates in 0-1 range.");
8289 		GMT_Usage (API, 3+shift, "x: Give <refpoint> in plot coordinates.");
8290 	}
8291 	/* May need to place other things in the middle */
8292 	if (part & 2) {	/* Here string is irrelevant */
8293 		char *just[GMT_ANCHOR_NTYPES] = {"BL", "BL", "BL", "BL", "BL", "MC", "MC", "ML"};
8294 		GMT_Usage (API, -(2+shift), "All systems except x require the -R and -J options to be set. Refpoint modifiers:");
8295 		GMT_Usage (API, 3+shift, "+j Append 2-char <justify> code to associate that anchor point on the %s with <refpoint>. "
8296 			"If +j<justify> is not given then <justify> will default to the same as <refpoint> (with j), "
8297 			"or the mirror opposite of <refpoint> (with -J), or %s (otherwise).", type[kind], just[kind]);
8298 		GMT_Usage (API, 3+shift, "+o Offset %s from <refpoint> by <dx>[/<dy>] in direction implied by <justify> [0/0].", type[kind]);
8299 	}
8300 	else
8301 		GMT_Usage (API, -(2+shift), "All systems except x require the -R and -J options to be set. ");
8302 }
8303 
8304 /*! .
8305 	\param GMT ...
8306 	\param option ...
8307 	\param string ...
8308 */
gmt_mapinset_syntax(struct GMT_CTRL * GMT,char option,char * string)8309 void gmt_mapinset_syntax (struct GMT_CTRL *GMT, char option, char *string) {
8310 	struct GMTAPI_CTRL *API = GMT->parent;
8311 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8312 	GMT_Usage (API, 1, "\n-%c %s", option, string);
8313 	GMT_Usage (API, 2, "Specify the map inset region using one of three specifications:");
8314 	GMT_Usage (API, 3, "%s Give <west>/<east>/<south>/<north> of geographic rectangle bounded by meridians and parallels. "
8315 		"Append +r if coordinates are the lower left and upper right corners of a rectangular area. ", GMT_LINE_BULLET);
8316 	GMT_Usage (API, 3, "%s Give <xmin>/<xmax>/<ymin>/<ymax>[+u<unit>] of bounding rectangle in projected coordinates [meters].", GMT_LINE_BULLET);
8317 	GMT_Usage (API, 3, "%s Set reference point and dimensions of the inset:", GMT_LINE_BULLET);
8318 	gmt_refpoint_syntax (GMT, "D", NULL, GMT_ANCHOR_INSET, 1);
8319 	GMT_Usage (API, 3, "Append +w<width>[<u>]/<height>[<u>] of bounding rectangle (<u> is a unit from %s).", GMT_DIM_UNITS_DISPLAY);
8320 	gmt_refpoint_syntax (GMT, "D", NULL, GMT_ANCHOR_INSET, 2);
8321 	if (GMT->current.setting.run_mode == GMT_CLASSIC) {
8322 		GMT_Usage (API, 2, "Append +s<file> to save inset lower left corner and dimensions to <file>.");
8323 		GMT_Usage (API, 2, "Append +t to translate plot origin to the lower left corner of the inset.");
8324 	}
8325 	GMT_Usage (API, 2, "Set panel attributes separately via the -F option.");
8326 }
8327 
8328 /*! .
8329 	\param GMT ...
8330 	\param option ...
8331 	\param string ...
8332 */
gmt_mapscale_syntax(struct GMT_CTRL * GMT,char option,char * string)8333 void gmt_mapscale_syntax (struct GMT_CTRL *GMT, char option, char *string) {
8334 	/* Used in psbasemap and pscoast */
8335 	struct GMTAPI_CTRL *API = GMT->parent;
8336 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8337 	GMT_Usage (API, 1, "\n-%c%s", option, GMT_SCALE);
8338 	GMT_Usage (API, -2, "%s", string);
8339 	gmt_refpoint_syntax (GMT, "L", NULL, GMT_ANCHOR_MAPSCALE, 3);
8340 	GMT_Usage (API, -2, "Set required scale via +w<length>, and (for geographic projection) append a unit from %s [km]. Other scale modifiers are optional:",
8341 		GMT_LEN_UNITS2_DISPLAY);
8342 	GMT_Usage (API, 3, "+a Append label alignment (choose among l(eft), r(ight), t(op), and b(ottom)) [t].");
8343 	GMT_Usage (API, 3, "+c Control where the map scale should apply. "
8344 		"If no arguments we select the center of the map. "
8345 		"Alternatively, give +c<slat> (with central longitude) or +c<slon>/<slat> [Default is at scale bar placement].");
8346 	GMT_Usage (API, 3, "+f Draw a \"fancy\" scale [Default is plain].");
8347 	GMT_Usage (API, 3, "+l Set scale <label> to use [By default, the label equals the distance unit name and is placed on top [+at].  Use the +l<label> "
8348 		"and +a<align> mechanisms to specify another label and placement.  For the fancy scale, "
8349 		"+u appends units to annotations while for plain scale it uses unit abbreviation instead of name as label.");
8350 	GMT_Usage (API, 3, "+u Append unit set by +w to all distance annotations (for the plain scale, +u will select unit to be appended to the distance length.");
8351 	GMT_Usage (API, 3, "+v Select a vertical scale instead for Cartesian plots.");
8352 }
8353 
8354 /*! .
8355 	\param GMT ...
8356 	\param option ...
8357 	\param string ...
8358 */
gmt_maprose_syntax(struct GMT_CTRL * GMT,char type,char * string)8359 void gmt_maprose_syntax (struct GMT_CTRL *GMT, char type, char *string) {
8360 	/* Used in psbasemap and pscoast -T option.  pass type as m or d */
8361 	struct GMTAPI_CTRL *API = GMT->parent;
8362 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -T%c parsing failure.  Correct syntax:\n", type);
8363 	if (type == 'm')
8364 		GMT_Usage (API, 1, "\n-T%c%s", type, GMT_TROSE_MAG);
8365 	else
8366 		GMT_Usage (API, 1, "\n-T%c%s", type, GMT_TROSE_DIR);
8367 	GMT_Usage (API, -2, "%s", string);
8368 	gmt_refpoint_syntax (GMT, "Td|m", NULL, GMT_ANCHOR_MAPROSE, 3);
8369 	GMT_Usage (API, -2, "Set required size of the rose via +w<diameter>. Other rose modifiers are optional:");
8370 	if (type == 'm') {
8371 		GMT_Usage (API, 3, "+d Set magnetic <declination>[/<dlabel>], where "
8372 			"<dlabel> is an optional label for the magnetic compass needle. "
8373 			"If +d does not include <dlabel> we default to \"delta = <declination>\". "
8374 			"Set <dlabel> to \"-\" to disable the declination label. ");
8375 		GMT_Usage (API, 3, "+i Draw outline of primary (inner) circle with <pen> [no circle].");
8376 		GMT_Usage (API, 3, "+l Place the letters W, E, S, N at the cardinal points. "
8377 			"Optionally, append comma-separated west, east, south, north custom labels instead.");
8378 		GMT_Usage (API, 3, "+p Draw outline of secondary (outer) circle with <pen> [no circle].");
8379 		GMT_Usage (API, 3, "+t Override default annotation and primary and secondary tick internals [30/5/1].");
8380 	}
8381 	else {
8382 		GMT_Usage (API, 3, "+f Draws a \"fancy\" rose [Default is plain]. "
8383 			"Optionally, add <level> of fancy rose: 1 draws E-W, N-S directions [Default], "
8384 			"2 adds NW-SE and NE-SW, while 3 adds WNW-ESE, NNW-SSE, NNE-SSW, and ENE-WSW directions.");
8385 		GMT_Usage (API, 3, "+l Place the letters W, E, S, N at the cardinal points. "
8386 			"Optionally, append comma-separated west, east, south, north custom labels instead.");
8387 	}
8388 	GMT_Usage (API, -2, "Note: If the North label = \'*\' then a north star is plotted instead of the label.");
8389 }
8390 
8391 /*! .
8392 	\param GMT ...
8393 	\param option ...
8394 	\param string ...
8395 */
gmt_mappanel_syntax(struct GMT_CTRL * GMT,char option,char * string,unsigned int kind)8396 void gmt_mappanel_syntax (struct GMT_CTRL *GMT, char option, char *string, unsigned int kind) {
8397 	/* Called by gmtlogo, psimage, pslegend, psscale */
8398 	struct GMTAPI_CTRL *API = GMT->parent;
8399 	static char *type[5] = {"logo", "image", "legend", "scale", "vertical scale"};
8400 	assert (kind < 5);
8401 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option);
8402 	GMT_Usage (API, 1, "\n-%c%s", option, GMT_PANEL);
8403 	GMT_Usage (API, -2, "%s", string);
8404 	GMT_Usage (API, -2, "Without further options: draw frame around the %s panel (using MAP_FRAME_PEN) "
8405 		"[Default is no frame].  Available modifiers:", type[kind]);
8406 	GMT_Usage (API, 3, "+c Set <clearance> as either <gap>, <xgap>/<ygap>, or <lgap>/<rgap>/<bgap>/<tgap> [%gp]. "
8407 		"Note: For a map inset the default <clearance> is zero.", GMT_FRAME_CLEARANCE);
8408 #ifdef DEBUG
8409 	GMT_Usage (API, 3, "+d Draw guide lines for debugging.");
8410 #endif
8411 	GMT_Usage (API, 3, "+g Set the <fill> for the %s panel [Default is no fill].", type[kind]);
8412 	GMT_Usage (API, 3, "+i Draw secondary inner frame boundary [Default gap is %gp].", GMT_FRAME_GAP);
8413 	GMT_Usage (API, 3, "+p Draw the border and optionally change the border <pen> [%s]. ", gmt_putpen (GMT, &GMT->current.setting.map_frame_pen));
8414 	GMT_Usage (API, 3, "+r Draw rounded rectangles instead [Default <radius> is %gp].", GMT_FRAME_RADIUS);
8415 	GMT_Usage (API, 3, "+s Place a shadow behind the %s panel [Default is %gp/%gp/gray50].",
8416 		type[kind], GMT_FRAME_CLEARANCE, -GMT_FRAME_CLEARANCE);
8417 }
8418 /*! .
8419 	\param GMT ...
8420 	\param option ...
8421 	\param string ...
8422 */
gmt_dist_syntax(struct GMT_CTRL * GMT,char * option,char * string)8423 void gmt_dist_syntax (struct GMT_CTRL *GMT, char *option, char *string) {
8424 	/* Used by many modules */
8425 	struct GMTAPI_CTRL *API = GMT->parent;
8426 	if (string[0] == ' ') GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure.  Correct syntax:\n", option[0]);
8427 	GMT_Usage (API, 1, "\n-%s", option);
8428 	GMT_Usage (API, -2, "%s "
8429 		"Append e (meter), f (foot), k (km), M (mile), n (nautical mile), u (survey foot), "
8430 		"d (arc degree), m (arc minute), or s (arc second) [%c]. "
8431 		"Spherical distances are based on great-circle calculations; "
8432 		"see -j<mode> for other modes of measurements.", string, GMT_MAP_DIST_UNIT);
8433 }
8434 
8435 /*! Use mode to control which options are displayed */
gmt_vector_syntax(struct GMT_CTRL * GMT,unsigned int mode,int level)8436 void gmt_vector_syntax (struct GMT_CTRL *GMT, unsigned int mode, int level) {
8437 	/* Mode is composed of bit-flags to control which lines are printed.
8438 	 * Items without if-test are common to all vectors.
8439 	 * 1	= Accepts +j (not mathangle)
8440 	 * 2	= Accepts +s (not mathangle)
8441 	 * 4	= Accepts +p (not mathangle)
8442 	 * 8	= Accepts +g (not mathangle)
8443 	 * 16	= Accepts +z (not mathangle, geovector)
8444 	 */
8445 	struct GMTAPI_CTRL *API = GMT->parent;
8446 	GMT_Usage (API, -level, "Append length of vector head. Note: "
8447 		"Left and right sides are defined by looking from start to end of vector. Optional modifiers:");
8448 	GMT_Usage (API, level, "+a Set <angle> of the vector head apex [30]");
8449 	GMT_Usage (API, level, "+b Place a vector head at the beginning of the vector [none]. "
8450 		"Append t for terminal, c for circle, s for square, a for arrow [Default], "
8451 		"i for tail, A for plain arrow, and I for plain tail. "
8452 		"Append l|r to only draw left or right side of this head [both sides].");
8453 	GMT_Usage (API, level, "+e Place a vector head at the end of the vector [none]. "
8454 		"Append t for terminal, c for circle, s for square, a for arrow [Default], "
8455 		"i for tail, A for plain arrow, and I for plain tail. "
8456 		"Append l|r to only draw left or right side of this head [both sides].");
8457 	if (mode & 8) GMT_Usage (API, level, "+g Set head <fill>; exclude <fill> to turn off fill [Default fill].");
8458 	GMT_Usage (API, level, "+h Set vector head shape in -2/2 range [%g].", GMT->current.setting.map_vector_shape);
8459 	if (mode & 1) GMT_Usage (API, level, "+j Justify vector at (b)eginning [Default], (e)nd, or (c)enter.");
8460 	GMT_Usage (API, level, "+l Only draw left side of all specified vector heads [both sides].");
8461 	GMT_Usage (API, level, "+m Place vector head at mid-point of segment [Default expects +b|+e]. "
8462 		"Append f or r for forward|reverse direction [forward]. "
8463 		"Append t for terminal, c for circle, s for square, or a for arrow [Default]. "
8464 		"Append l|r to only draw left or right side of this head [both sides].");
8465 	GMT_Usage (API, level, "+n Shrink attributes if vector length < <norm> [none].");
8466 	GMT_Usage (API, level, "+o Set pole <plon/plat> [Default is north pole] for great or small circles; only give length via input.");
8467 	if (mode & 4) GMT_Usage (API, level, "+p Set pen attributes; exclude <pen> to turn off head outlines [Default pen and outline].");
8468 	GMT_Usage (API, level, "+q Start and stop opening angles are given instead of (azimuth,length) on input.");
8469 	GMT_Usage (API, level, "+r Only draw right side of all specified vector heads [both sides].");
8470 	if (mode & 2) GMT_Usage (API, level, "+s Give (x,y) coordinates of tip instead of (azimuth,length) on input.");
8471 	GMT_Usage (API, level, "+t Shift (b)egin or (e)nd position along vector by given <trim(s)> [no shifting].");
8472 	if (mode & 16) GMT_Usage (API, level, "+z Give (dx,dy) vector components instead of (azimuth,length) on input. "
8473 		"Append <scale> to convert components to length in given unit.");
8474 }
8475 
8476 /*! Use mode to control which options are displayed */
gmt_segmentize_syntax(struct GMT_CTRL * GMT,char option,unsigned int mode)8477 void gmt_segmentize_syntax (struct GMT_CTRL *GMT, char option, unsigned int mode) {
8478 	/* mode == 0 for formatting and 1 for plotting */
8479 	struct GMTAPI_CTRL *API = GMT->parent;
8480 	char *verb[2] = {"Form", "Draw"}, *count[2] = {"four", "three"};
8481 	char *syntax = (mode) ? GMT_SEGMENTIZE3 : GMT_SEGMENTIZE4;
8482 	GMT_Usage (API, 1, "\n-%c%s", option, syntax);
8483 	GMT_Usage (API, -2, "Alter the way points are connected and the data are segmented. "
8484 		"Append one of %s line connection schemes: ", count[mode]);
8485 	GMT_Usage (API, 3, "c: %s continuous line segments for each group [Default].", verb[mode]);
8486 	GMT_Usage (API, 3, "p: %s line segments from a reference point reset for each group.", verb[mode]);
8487 	GMT_Usage (API, 3, "n: %s networks of line segments between all points in each group.", verb[mode]);
8488 	if (mode == 0) GMT_Usage (API, 3, "v: Form vector line segments suitable for psxy -Sv|=<size>+s");
8489 	GMT_Usage (API, 2, "Optionally, append one of five ways to define a \"group\":");
8490 	GMT_Usage (API, 3, "a: Data set is considered a single group; reference point is first point in the group.");
8491 	GMT_Usage (API, 3, "f: Each file is a separate group; reference point is reset to first point in the group.");
8492 	GMT_Usage (API, 3, "s: Each segment is a group; reference point is reset to first point in the group [Default].");
8493 	GMT_Usage (API, 3, "r: Each segment is a group, but reference point is reset to each point in the group.");
8494 	GMT_Usage (API, 3, "Alternatively, append a fixed external reference point instead.");
8495 }
8496 
8497 /*! For programs that can read *.img grids */
gmt_img_syntax(struct GMT_CTRL * GMT,int indent)8498 void gmt_img_syntax (struct GMT_CTRL *GMT, int indent) {
8499 	struct GMTAPI_CTRL *API = GMT->parent;
8500 	GMT_Usage (API, indent, "Give filename and append comma-separated scale, mode, and optionally max latitude. "
8501 		"The scale (typically 0.1 or 1) is used to multiply after read; give mode as follows:");
8502 	GMT_Usage (API, indent+1, "0: img file with no constraints coded, interpolate to get data at track.");
8503 	GMT_Usage (API, indent+1, "1: img file with constraints coded, interpolate to get data at track.");
8504 	GMT_Usage (API, indent+1, "2: img file with constraints coded, gets data only at constrained points, NaN elsewhere.");
8505 	GMT_Usage (API, indent+1, "3: img file with constraints coded, gets 1 at constraints, 0 elsewhere.");
8506 	GMT_Usage (API, -indent, "For mode 2|3 you may want to consider the -n+t<threshold> setting.");
8507 }
8508 
8509 /*! . */
gmt_syntax(struct GMT_CTRL * GMT,char option)8510 void gmt_syntax (struct GMT_CTRL *GMT, char option) {
8511 	/* The function print to stderr the syntax for the option indicated by
8512 	 * the variable <option>.  Only the common parameter options are covered.
8513 	 * It basically selects the shorter version of -B -J and calls GMT_Option.
8514 	 */
8515 	char arg[3] = {""};
8516 	arg[0] = option;
8517 	if (strchr ("BJ", option)) arg[1] = '-';	/* Get short version of these two */
8518 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c parsing failure. Correct syntax:\n", option);
8519 	GMT_Option (GMT->parent, arg);
8520 }
8521 
gmt_default_error(struct GMT_CTRL * GMT,char option)8522 int gmt_default_error (struct GMT_CTRL *GMT, char option) {
8523 	/* gmt_default_error ignores all the common options that have already been processed and returns
8524 	 * true if the option is not an already processed common option.
8525 	 */
8526 
8527 	int error = 0;
8528 
8529 	switch (option) {
8530 
8531 		case '-': break;	/* Skip indiscriminately */
8532 		case '=': break;	/* Skip indiscriminately */
8533 		case '>': break;	/* Skip indiscriminately since dealt with internally */
8534 		case 'B': error += (GMT->common.B.active[GMT_PRIMARY] == false && GMT->common.B.active[GMT_SECONDARY] == false); break;
8535 		case 'J': error += GMT->common.J.active == false; break;
8536 		case 'K': error += GMT->common.K.active == false; break;
8537 		case 'O': error += GMT->common.O.active == false; break;
8538 		case 'P': error += GMT->common.P.active == false; break;
8539 		case 'R': error += GMT->common.R.active[RSET] == false; break;
8540 		case 'U': error += GMT->common.U.active == false; break;
8541 		case 'V': error += GMT->common.V.active == false; break;
8542 		case 'X': error += GMT->common.X.active == false; break;
8543 		case 'Y': error += GMT->common.Y.active == false; break;
8544 		case 'a': error += GMT->common.a.active == false; break;
8545 		case 'b': error += ((GMT->common.b.active[GMT_IN] == false && GMT->common.b.nc[GMT_IN] == false) \
8546 			&& (GMT->common.b.active[GMT_OUT] == false && GMT->common.b.nc[GMT_OUT] == false)); break;
8547 		case 'd': error += (GMT->common.d.active[GMT_IN] == false && GMT->common.d.active[GMT_OUT] == false); break;
8548 		case 'e': error += GMT->common.e.active == false; break;
8549 		case 'f': error += (GMT->common.f.active[GMT_IN] == false &&  GMT->common.f.active[GMT_OUT] == false); break;
8550 		case 'g': error += GMT->common.g.active == false; break;
8551 		case 'H':
8552 			if (gmt_M_compat_check (GMT, 4)) {
8553 				error += GMT->common.h.active == false;
8554 			}
8555 			else
8556 				error++;
8557 			break;
8558 		case 'h': error += GMT->common.h.active == false; break;
8559 		case 'i': error += GMT->common.i.active == false; break;
8560 		case 'j': error += GMT->common.j.active == false; break;
8561 		case 'l': error += GMT->common.l.active == false; break;
8562 		case 'n': error += GMT->common.n.active == false; break;
8563 		case 'o': error += GMT->common.o.active == false; break;
8564 		case 'Z':
8565 			if (!gmt_M_compat_check (GMT, 4)) error++;
8566 			break;
8567 		case 'E':
8568 			if (gmt_M_compat_check (GMT, 4))
8569 				error += GMT->common.p.active == false;
8570 			else
8571 				error++;
8572 			break;
8573 		case 'p': error += GMT->common.p.active == false; break;
8574 		case 'q': error += (GMT->common.q.active[GMT_IN] == false && GMT->common.q.active[GMT_OUT] == false); break;
8575 		case 'm': if (!gmt_M_compat_check (GMT, 4)) error++; break;
8576 		case 'S': if (!gmt_M_compat_check (GMT, 4)) error++; break;
8577 		case 'F': if (!gmt_M_compat_check (GMT, 4)) error++; break;
8578 		case 'r': error += GMT->common.R.active[GSET] == false; break;
8579 		case 's': error += GMT->common.s.active == false; break;
8580 		case 't': error += GMT->common.t.active == false; break;
8581 		case 'w': error += GMT->common.w.active == false; break;
8582 #ifdef GMT_MP_ENABLED
8583 		case 'x': error += GMT->common.x.active == false; break;
8584 #endif
8585 		case ':': error += GMT->common.colon.active == false; break;
8586 
8587 		default:
8588 			/* Not a processed common options */
8589 			error++;
8590 			break;
8591 	}
8592 
8593 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized option -%c\n", option);
8594 	return (error);
8595 }
8596 
gmt_parse_region_extender(struct GMT_CTRL * GMT,char option,char * arg,unsigned int * mode,double inc[])8597 unsigned int gmt_parse_region_extender (struct GMT_CTRL *GMT, char option, char *arg, unsigned int *mode, double inc[]) {
8598 	/* If given +e|r|R<incs> we must parse and get the mode and 1, 2, or 4 increments */
8599 	unsigned int n_errors = 0, k;
8600 	if (arg == NULL || arg[0] == '\0') return GMT_NOERROR;	/* Nothing to do */
8601 	k = (arg[0] == '+') ? 1 : 0;	/* We may get +r or e, for instance, depending on upstream strtok */
8602 	if (strchr ("erR", arg[k])) {	/* Want to extend the final region before reporting */
8603 		int j = GMT_Get_Values (GMT->parent, &arg[k+1], inc, 4);
8604 		*mode = (arg[k] == 'e') ? GMT_REGION_ROUND_EXTEND : ((arg[k] == 'r') ? GMT_REGION_ROUND : GMT_REGION_ADD);
8605 		if (j == 1)	/* Same increments in all directions */
8606 			inc[XHI] = inc[YLO] = inc[YHI] = inc[XLO];
8607 		else if (j == 2) {	/* Separate increments in x and y */
8608 			inc[YLO] = inc[YHI] = inc[XHI];
8609 			inc[XHI] = inc[XLO];
8610 		}
8611 		else if (j != 4) {	/* The only other option is 4 but somehow we failed */
8612 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad number of increment to modifier +%c.\n", option, arg[k]);
8613 			n_errors++;
8614 		}
8615 	}
8616 	return (n_errors);
8617 }
8618 
8619 /*! If region is given then we must have w < e and s <= n */
gmt_check_region(struct GMT_CTRL * GMT,double wesn[])8620 bool gmt_check_region (struct GMT_CTRL *GMT, double wesn[]) {
8621 	gmt_M_unused(GMT);
8622 	if (!strncmp (GMT->init.module_name, "pshistogram", 11U))
8623 		return ((wesn[XLO] >= wesn[XHI] || wesn[YLO] > wesn[YHI]));
8624 	else
8625 		return ((wesn[XLO] >= wesn[XHI] || wesn[YLO] >= wesn[YHI]));
8626 }
8627 
8628 /*! . */
gmt_parse_R_option(struct GMT_CTRL * GMT,char * arg)8629 int gmt_parse_R_option (struct GMT_CTRL *GMT, char *arg) {
8630 	unsigned int i, icol, pos, error = 0, n_slash = 0, first = 0;
8631 	int got, col_type[2], expect_to_read;
8632 	size_t length;
8633 	bool inv_project = false, scale_coord = false, got_r, got_country, no_T = false, done[3] = {false, false, false};
8634 	char text[GMT_BUFSIZ] = {""}, item[GMT_BUFSIZ] = {""}, string[GMT_BUFSIZ] = {""}, r_unit = 0, *c = NULL, *d = NULL, *ptr = NULL;
8635 	double p[6];
8636 
8637 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* Got nothing */
8638 	strncpy (item, arg, GMT_BUFSIZ-1);	/* Copy locally */
8639 
8640 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Must handle any internal history regarding increments and registration */
8641 		/* Here, item may be of the form <region>[+I<incs>][+G[P|G][B|T]] if -R was given to a non-plotting module */
8642 		if ((c = strstr (item, "+G")) != NULL) {	/* Got grid registration */
8643 			GMT->common.R.active[GSET] = true;
8644 			GMT->common.R.registration = strchr(&c[2], 'P') != NULL || strchr(&c[2], 'p') != NULL ? GMT_GRID_PIXEL_REG : GMT_GRID_NODE_REG;
8645 			GMT->common.R.row_order = strchr(&c[2], 'T') != NULL || strchr(&c[2], 't') != NULL ? k_nc_start_north : k_nc_start_south;
8646 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT modern: Obtained grid registration and/or row order from -R%s\n", item);
8647 		}
8648 		if ((d = strstr (item, "+I")) != NULL) {	/* Got grid increments */
8649 			if (gmt_getinc (GMT, &d[2], GMT->common.R.inc)) {
8650 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in GMT modern while parsing the grid spacing.\n");
8651 				return (GMT_PARSE_ERROR);
8652 			}
8653 			GMT->common.R.active[ISET] = true;
8654 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "GMT modern: Obtained grid spacing from -R%s\n", item);
8655 		}
8656 		/* Chop off modifiers. This has to be after the above ifs, to allow +I and +G in any order */
8657 		if (c) c[0] = '\0';
8658 		if (d) d[0] = '\0';
8659 	}
8660 
8661 	/* Parse the -R option.  Full syntax:
8662 	 * -R<west/east/south/north>
8663 	 * -R<LLx/LLy/URx/URy>+r
8664 	 * -R<xmin/xmax/ymin/ymax>[+u<unit>]
8665 	 * -R<grdfile>[+u<unit>]
8666 	 * -Rg|d as global shorthand for 0/360/-90/90 or -180/180/-90/90
8667 	 * -R[L|C|R][B|M|T]<x0>/<y0>/<n_columns>/<n_rows>
8668 	 * -R[g|d]w/e/s/n[/z0/z1][+r]
8669 	 * -R<countrycodes>
8670 	 */
8671 
8672 	length = strlen (item) - 1;
8673 	n_slash = gmt_count_char (GMT, item, '/');
8674 	got_r = (strstr (item, "+r") != NULL);
8675 	got_country = (got_r || (strstr (item, "+R") != NULL));	/* May have given DCW (true of +R, maybe if +r since the latter also means oblique) */
8676 
8677 	strncpy (GMT->common.R.string, item, GMT_LEN256-1);	/* Verbatim copy */
8678 
8679 	if (n_slash == 3 && !got_country && ((strchr ("LCR", item[0]) && strchr ("TMB", item[1])) || (strchr ("LCR", item[1]) && strchr ("TMB", item[0])))) {	/* Extended -R option using coordinate codes and grid increments */
8680 		char X[2][GMT_LEN64] = {"", ""}, code[3] = {""};
8681 		double xdim, ydim, orig[2] = {0.0, 0.0};
8682 		int n_columns, n_rows, just, part;
8683 		gmt_M_memcpy (code, item, 2, char);
8684 		if ((just = gmt_just_decode (GMT, code, PSL_NO_DEF)) == -99) {	/* Since justify not in correct format */
8685 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R: Unrecognized justification code %s\n", code);
8686 			return (GMT_PARSE_ERROR);
8687 		}
8688 		if (sscanf (&item[2], "%[^/]/%[^/]/%d/%d", X[0], X[1], &n_columns, &n_rows) != 4) {
8689 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R%s<lon0>/<lat0>/<n_columns>/<n_rows>: Did not get 4 items\n", code);
8690 			return (GMT_PARSE_ERROR);
8691 		}
8692 		for (icol = GMT_X; icol <= GMT_Y; icol++) {
8693 			if (gmt_M_type (GMT, GMT_IN, icol) == GMT_IS_UNKNOWN) {	/* No -J or -f set, proceed with caution */
8694 				got = gmt_scanf_arg (GMT, X[icol], gmt_M_type (GMT, GMT_IN, icol), true, &orig[icol]);
8695 				if (got & GMT_IS_GEO)
8696 					gmt_set_column_type (GMT, GMT_IN, icol, got);
8697 				else if (got & GMT_IS_RATIME) {
8698 					gmt_set_column_type (GMT, GMT_IN, icol, got);
8699 					GMT->current.proj.xyz_projection[icol] = GMT_TIME;
8700 				}
8701 			}
8702 			else {	/* Things are set, do or die */
8703 				expect_to_read = (gmt_M_type (GMT, GMT_IN, icol) & GMT_IS_RATIME) ? GMT_IS_ARGTIME : gmt_M_type (GMT, GMT_IN, icol);
8704 				error += gmt_verify_expectations (GMT, expect_to_read, gmt_scanf (GMT, X[icol], expect_to_read, &orig[icol]), X[icol]);
8705 			}
8706 		}
8707 		if (error) {
8708 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in -R%s<lon0>/<lat0>/<n_columns>/<n_rows>: Could not parse coordinate pair\n", code);
8709 			return (GMT_PARSE_ERROR);
8710 		}
8711 		if (n_columns <= 0 || n_rows <= 0) {
8712 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in -R%s<lon0>/<lat0>/<n_columns>/<n_rows>: Must have positive dimensions\n", code);
8713 			return (GMT_PARSE_ERROR);
8714 		}
8715 		/* Finally set up -R */
8716 		if (!GMT->common.R.active[GSET]) n_columns--, n_rows--;	/* Needed to get correct dimensions */
8717 		xdim = n_columns * GMT->common.R.inc[GMT_X];
8718 		ydim = n_rows * GMT->common.R.inc[GMT_Y];
8719 		part = just / 4;	/* Need any multiples of 4 in just */
8720 		GMT->common.R.wesn[XLO] = orig[GMT_X] - 0.5 * ((just%4)-1) * xdim;
8721 		GMT->common.R.wesn[YLO] = orig[GMT_Y] - 0.5 * part * ydim;
8722 		GMT->common.R.wesn[XHI] = GMT->common.R.wesn[XLO] + xdim;
8723 		GMT->common.R.wesn[YHI] = GMT->common.R.wesn[YLO] + ydim;
8724 		return (GMT_NOERROR);
8725 	}
8726 	if ((item[0] == 'g' || item[0] == 'd') && item[1] == '\0') {	/* Check -Rd|g separately in case user has files called d or g */
8727 		if (item[0] == 'g') {	/* -Rg is shorthand for -R0/360/-90/90 */
8728 			GMT->common.R.wesn[XLO] = 0.0, GMT->common.R.wesn[XHI] = 360.0;
8729 			GMT->current.io.geo.range = GMT_IS_0_TO_P360_RANGE;
8730 		}
8731 		else {			/* -Rd is shorthand for -R-180/180/-90/90 */
8732 			GMT->common.R.wesn[XLO] = -180.0, GMT->common.R.wesn[XHI] = 180.0;
8733 			GMT->current.io.geo.range = GMT_IS_M180_TO_P180_RANGE;
8734 		}
8735 		GMT->common.R.wesn[YLO] = -90.0;	GMT->common.R.wesn[YHI] = +90.0;
8736 		gmt_set_geographic (GMT, GMT_IN);
8737 		return (GMT_NOERROR);
8738 	}
8739 	ptr = item;	/* To avoid compiler warning that item cannot be NULL */
8740 	if ((c = strstr (item, "+u"))) {	/* Got +u<unit> appended to something */
8741 		c[0] = '\0';	/* Chop off all modifiers so range can be determined */
8742 		r_unit = c[2];	/* The data unit */
8743 		if (!strchr (GMT_LEN_UNITS2, r_unit)) {	/* +u is meant for projected distances only */
8744 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -R: The +u<unit> modifier only applies to projected coordinates. Your unit %c is ignored\n", r_unit);
8745 			r_unit = '\0';
8746 		}
8747 		else if (gmt_M_is_linear (GMT))	/* Just scale up the values */
8748 			scale_coord = true;
8749 		else
8750 			inv_project = true;	/* Flag here that we already now we want to invert these units to degrees */
8751 	}
8752 	if (!gmt_M_file_is_memory (ptr) && ptr[0] == '@') {	/* Must be a cache file */
8753 		first = gmt_download_file_if_not_found (GMT, item, 0);
8754 	}
8755 	if (!gmt_access (GMT, &item[first], R_OK)) {	/* Gave a readable file, presumably a grid */
8756 		struct GMT_GRID *G = NULL;
8757 		if ((G = GMT_Read_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, NULL, &item[first], NULL)) == NULL) {	/* Read header */
8758 			return (GMT->parent->error);
8759 		}
8760 		if (gmt_M_y_is_lat (GMT, GMT_IN)) {	/* Handle round-off in actual_range for latitudes */
8761 			if (gmt_M_is_Npole (G->header->wesn[YHI])) G->header->wesn[YHI] = +90.0;
8762 			if (gmt_M_is_Spole (G->header->wesn[YLO])) G->header->wesn[YLO] = -90.0;
8763 		}
8764 		/* If we did not see +u<unit> yet above, we do allow +ue to be implicit for TM. UTM, and STEREO which often use meter grids */
8765 		if (!inv_project && (GMT->current.proj.projection_GMT == GMT_UTM || GMT->current.proj.projection_GMT == GMT_TM || GMT->current.proj.projection_GMT == GMT_STEREO)) {	/* Perhaps we got an [U]TM or stereographic grid? */
8766 			if (fabs (G->header->wesn[XLO]) > 360.0 || fabs (G->header->wesn[XHI]) > 360.0 \
8767 			  || fabs (G->header->wesn[YLO]) > 90.0 || fabs (G->header->wesn[YHI]) > 90.0) {	/* Yes we probably did, but cannot be sure */
8768 				inv_project = true;
8769 				r_unit = 'e';	/* Must specify the "missing" leading e for meter */
8770 			}
8771 		}
8772 		if (inv_project)	/* Either set explicitly via +u or implicitly via us */
8773 			snprintf (string, GMT_BUFSIZ, "%.16g/%.16g/%.16g/%.16g", G->header->wesn[XLO], G->header->wesn[XHI], G->header->wesn[YLO], G->header->wesn[YHI]);
8774 		else {	/* Got grid with degrees or regular Cartesian */
8775 			struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
8776 			gmt_M_memcpy (GMT->common.R.wesn, G->header->wesn, 4, double);
8777 			if (GMT->common.R.active[ISET] == false) gmt_M_memcpy (GMT->common.R.inc, G->header->inc, 2, double);	/* Do not override settings given via -I */
8778 			GMT->common.R.registration = (GMT->common.R.active[GSET]) ? !G->header->registration : G->header->registration;	/* Set, or toggle registration if -r was set */
8779 			GMT->common.R.row_order = HH->row_order;	/* Copy the row order */
8780 			GMT_Report (GMT->parent, GMT_MSG_DEBUG,
8781 				"-R<grdfile> converted to -R%.16g/%.16g/%.16g/%.16g\n", GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
8782 #if 0
8783 			/* Next bit removed because of issue #592. Should not change the boundaries of the grid */
8784 			if (GMT->common.R.registration == GMT_GRID_NODE_REG && doubleAlmostEqualZero (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO] + GMT->common.R.inc[GMT_X], 360.0)) {
8785 				/* Geographic grid with gridline registration that does not contain the repeating column, but is still 360 range */
8786 				GMT_Report (GMT->parent, GMT_MSG_DEBUG,
8787 				            "-R<file> with gridline registration and non-repeating column detected; return full 360 degree range for -R\n");
8788 				if (gmt_M_is_zero (GMT->common.R.wesn[XLO]) || doubleAlmostEqualZero (GMT->common.R.wesn[XLO], -180.0))
8789 					GMT->common.R.wesn[XHI] = GMT->common.R.wesn[XLO] + 360.0;
8790 				else
8791 					GMT->common.R.wesn[XLO] = GMT->common.R.wesn[XHI] - 360.0;
8792 			}
8793 #endif
8794 			GMT->common.R.wesn[ZLO] = G->header->z_min;	GMT->common.R.wesn[ZHI] = G->header->z_max;
8795 			GMT->common.R.active[ISET] = GMT->common.R.active[GSET] = GMT->common.R.active[FSET] = true;
8796 			if (GMT_Destroy_Data (GMT->parent, &G) != GMT_OK)
8797 				return (GMT->parent->error);
8798 			return (GMT_NOERROR);
8799 		}
8800 		if (c) c[0] = '+';	/* Restore */
8801 	}
8802 	else if ((item[0] == 'g' || item[0] == 'd') && n_slash == 3) {	/* Here we have a region appended to -Rd|g */
8803 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Got global region (%s)\n", item);
8804 		gmt_set_geographic (GMT, GMT_IN);
8805 		strncpy (string, &item[1], GMT_BUFSIZ-1);
8806 		GMT->current.io.geo.range = (item[0] == 'g') ? GMT_IS_0_TO_P360_RANGE : GMT_IS_M180_TO_P180_RANGE;
8807 	}
8808 	else if ((isupper ((int)item[0]) && isupper ((int)item[1])) || item[0] == '=' || strchr (item, ',')) {
8809 		/* Region specified via country codes with optional round off/extension, e.g., -RNO+r1 or -R=EU */
8810 		struct GMT_DCW_SELECT info;
8811 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Got country code for region (%s)\n", item);
8812 		gmt_M_memset (&info, 1, struct GMT_DCW_SELECT);	/* To ensure it is all NULL, 0 */
8813 		if ((error = gmt_DCW_parse (GMT, 'R', item, &info))) return error;
8814 		(void) gmt_DCW_operation (GMT, &info, GMT->common.R.wesn, GMT_DCW_REGION);	/* Get region */
8815 		gmt_DCW_free (GMT, &info);
8816 		if (fabs (GMT->common.R.wesn[XLO]) > 1000.0) return (GMT_MAP_NO_REGION);
8817 		if (strstr (item, "=AN")) {	/* Antarctica is the only polar cap polygon so w,e,s must be reset */
8818 			GMT->common.R.wesn[XLO] = -180.0;
8819 			GMT->common.R.wesn[XHI] = +180.0;
8820 			GMT->common.R.wesn[YLO] = -90.0;
8821 		}
8822 		if (GMT->common.R.wesn[XLO] < 0.0 && GMT->common.R.wesn[XHI] > 0.0)
8823 			GMT->current.io.geo.range = GMT_IS_M180_TO_P180_RANGE;
8824 		else
8825 			GMT->current.io.geo.range = GMT_IS_0_TO_P360_RANGE;
8826 		gmt_set_geographic (GMT, GMT_IN);
8827 		GMT->common.R.via_polygon = true;
8828 		return (GMT_NOERROR);
8829 	}
8830 	else if (strchr (GMT_LEN_UNITS2, item[0])) {	/* Obsolete: Specified min/max in projected distance units */
8831 		strncpy (string, &item[1], GMT_BUFSIZ-1);
8832 		r_unit = item[0];	/* The leading unit */
8833 		if (gmt_M_is_linear (GMT))	/* Just scale up the values */
8834 			scale_coord = true;
8835 		else
8836 			inv_project = true;
8837 	}
8838 	else if (item[length] != 'r' && !strstr (item, "+r") && (GMT->current.proj.projection_GMT == GMT_UTM || GMT->current.proj.projection_GMT == GMT_TM || GMT->current.proj.projection_GMT == GMT_STEREO)) {
8839 		/* Just _might_ be getting -R in meters, better check if argument is too large to be degrees */
8840 		double rect[4] = {0.0, 0.0, 0.0, 0.0};
8841 		int n_rect_read;
8842 		strncpy (string, item, GMT_BUFSIZ-1);	/* Try to read these as 4 limits in meters */
8843 		n_rect_read = sscanf (string, "%lg/%lg/%lg/%lg", &rect[XLO], &rect[XHI], &rect[YLO], &rect[YHI]);
8844 		if (!inv_project && n_rect_read == 4 && (fabs (rect[XLO]) > 360.0 || fabs (rect[XHI]) > 360.0 || fabs (rect[YLO]) > 90.0 || fabs (rect[YHI]) > 90.0)) {	/* Oh, yeah... */
8845 			inv_project = true;
8846 			r_unit = 'e';	/* Must specify the "missing" leading e for meter */
8847 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "For a UTM or TM projection, your region %s is too large to be in degrees and thus assumed to be in meters\n", string);
8848 		}
8849 	}
8850 	else {	/* Plain old -Rw/e/s/n */
8851 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Got regular w/e/s/n for region (%s)\n", item);
8852 		strncpy (string, item, GMT_BUFSIZ-1);
8853 	}
8854 
8855 	/* Now decode the string */
8856 
8857 	length = strlen (string) - 1;
8858 	col_type[0] = col_type[1] = 0;
8859 	if ((c = strstr (string, "+r"))) {	/* Got +r */
8860 		GMT->common.R.oblique = true;
8861 		c[0] = '\0';	/* Remove the trailing +r so gmt_scanf will work */
8862 	}
8863 	else if (string[length] == 'r') {	/* Obsolete */
8864 		GMT->common.R.oblique = true;
8865 		string[strlen(string)-1] = '\0';	/* Remove the trailing r so gmt_scanf will work */
8866 	}
8867 	else
8868 		GMT->common.R.oblique = false;
8869 	i = pos = 0;
8870 	gmt_M_memset (p, 6, double);
8871 	while ((gmt_strtok (string, "/", &pos, text))) {
8872 		if (i > 5) {
8873 			error++;
8874 			return (error);		/* Have to break out here to avoid segv on p[6]  */
8875 		}
8876 		/* Figure out what column corresponds to a token to get col_type[GMT_IN] flag  */
8877 		if (i > 3)
8878 			icol = 2;
8879 		else if (GMT->common.R.oblique)
8880 			icol = i%2;
8881 		else
8882 			icol = i/2;
8883 		if (icol < 2 && GMT->current.setting.io_lonlat_toggle[GMT_IN]) icol = 1 - icol;	/* col_types were swapped */
8884 		if (inv_project)	/* input is distance units */
8885 			p[i] = atof (text);
8886 		else {
8887 			bool maybe_time = gmtlib_maybe_abstime (GMT, text, &no_T);	/* Will flag things like 1999-12-03 or 2000/09/4 as abstime with missing T */
8888 			if (maybe_time || gmt_M_type (GMT, GMT_IN, icol) == GMT_IS_UNKNOWN || gmt_M_type (GMT, GMT_IN, icol) == GMT_IS_FLOAT) {	/* Because abstime specs must be parsed carefully we must do more checking */
8889 				/* Here, -J or -f may have ben set, proceed with caution */
8890 				if (no_T) strcat (text, "T");	/* Add the missing trailing 'T' in an ISO date */
8891 				got = gmt_scanf_arg (GMT, text, GMT_IS_UNKNOWN, true, &p[i]);
8892 				if (got == GMT_IS_LON || got == GMT_IS_LAT)	/* If clearly lon or lat we say so */
8893 					gmt_set_column_type (GMT, GMT_IN, icol, got), done[icol] = true;
8894 				else if (got == GMT_IS_GEO)	/* If clearly geographical we say so */
8895 					gmt_set_column_type (GMT, GMT_IN, icol, got), done[icol] = true;
8896 				else if ((got & GMT_IS_RATIME) || got == GMT_IS_ARGTIME) {	/* If we got time then be specific */
8897 					if (got == GMT_IS_ARGTIME) got = GMT_IS_ABSTIME;
8898 					/* While the -R arg may be abstime, -J or -f may have indicated that data are relative time, so we must check before imposing our got: */
8899 					if (!(gmt_M_type (GMT, GMT_IN, icol) & GMT_IS_RATIME)) gmt_set_column_type (GMT, GMT_IN, icol, got), done[icol] = true;	/* Only set if not already set to a time flavor */
8900 					GMT->current.proj.xyz_projection[icol] = GMT_TIME;
8901 				}
8902 				else if (got == GMT_IS_FLOAT) {	/* Don't want to set col type prematurely ince -R0/13:30E/... would first find Cartesian and then fail on 13:30E */
8903 					if (done[icol] && gmt_M_type (GMT, GMT_IN, icol) == GMT_IS_UNKNOWN)	/* 2nd time for this column and still not set, then FLOAT is OK */
8904 						gmt_set_column_type (GMT, GMT_IN, icol, got);
8905 				}
8906 				else {	/* Testing this, could we ever get here with sane data? */
8907 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not parse %s into Geographical, Cartesian, or Temporal coordinates!\n", text);
8908 					error++;
8909 				}
8910 			}
8911 			else {	/* Things are already prescribed by -J or -f, do or die */
8912 				expect_to_read = (gmt_M_type (GMT, GMT_IN, icol) & GMT_IS_RATIME) ? GMT_IS_ARGTIME : gmt_M_type (GMT, GMT_IN, icol);
8913 				error += gmt_verify_expectations (GMT, expect_to_read, gmt_scanf (GMT, text, expect_to_read, &p[i]), text);
8914 			}
8915 		}
8916 		if (error) return (error);
8917 
8918 		i++;
8919 	}
8920 	if (GMT->common.R.oblique) {
8921 		gmt_M_double_swap (p[2], p[1]);	/* So w/e/s/n makes sense */
8922 		gmt_M_memcpy (GMT->common.R.wesn_orig, p, 4, double);	/* Save these in case they get enlarged by oblique projections */
8923 	}
8924 	if (inv_project) {	/* Convert rectangular distances to geographic corner coordinates */
8925 		double wesn[4] = {0.0, 0.0, 0.0, 0.0};
8926 		GMT->common.R.oblique = false;
8927 		error += gmtinit_rectR_to_geoR (GMT, r_unit, p, wesn, true);
8928 		gmt_M_memcpy (p, wesn, 4, double);
8929 		GMT->common.R.oblique = true;
8930 		gmt_set_geographic (GMT, GMT_IN);
8931 	}
8932 	else if (scale_coord) {	/* Just scale x/y coordinates to meters according to given unit */
8933 		double fwd_scale, inv_scale = 0.0, inch_to_unit = 0, unit_to_inch = 0;
8934 		int k_unit = gmtlib_get_unit_number (GMT, r_unit);
8935 		if ((error = gmt_init_scales (GMT, k_unit, &fwd_scale, &inv_scale, &inch_to_unit, &unit_to_inch, NULL)))
8936 			return (error);
8937 		for (pos = 0; pos < 4; pos++) p[pos] *= inv_scale;
8938 	}
8939 	if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_GEO && gmt_M_type (GMT, GMT_IN, GMT_Y) == GMT_IS_GEO) {	/* Two geographical coordinates means lon,lat */
8940 		gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_LON);
8941 		gmt_set_column_type (GMT, GMT_IN, GMT_Y, GMT_IS_LAT);
8942 	}
8943 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* Arrange so geographic region always has w < e */
8944 		double w = p[0], e = p[1];
8945 		if (p[0] <= -360.0 || p[1] > 360.0) {	/* Arrange so geographic region always has |w,e| <= 360 */
8946 			double shift = (p[0] <= -360.0) ? 360.0 : -360.0;
8947 			p[0] += shift;	p[1] += shift;
8948 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION,
8949 				"Option -R: Given west and east values [%g %g] were adjusted so not exceed multiples of 360 [%g %g]\n", w, e, p[0], p[1]);
8950 		}
8951 		else if (p[0] > p[1] && GMT->common.R.oblique && !GMT->common.J.active) {	/* Used -Rw/s/e/n+r for non mapping */
8952 			if (GMT->current.io.geo.range == GMT_IS_M180_TO_P180_RANGE) p[0] -= 360.0; else p[1] += 360.0;
8953 		}
8954 		else if (p[0] > p[1] && strchr (string, 'W') && strchr (string, 'E')) {	/* Used -R<lon>E/<lon>W so we must add 360 to east */
8955 			p[1] += 360.0;
8956 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION,
8957 				"Option -R: Mix W and E longitudes in region setting, adjusted to [%g %g]\n", p[0], p[1]);
8958 		}
8959 		if (gmt_M_is_conical (GMT) && gmt_M_360_range (p[0], p[1]) && !doubleAlmostEqualZero (0.5 * (p[0] + p[1]), GMT->current.proj.pars[0])) {
8960 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Conical projections with full 360 longitude range require the projection central longitude to be at the mid-point\n");
8961 			error++;
8962 		}
8963 #if 0	/* This causes too much trouble: Better to annoy the person wishing this to work vs annoy all those who made an honest error.  We cannot be mind-readers here so we insist on e > w */
8964 		else if (p[0] > p[1]) {	/* Arrange so geographic region always has w < e */
8965 			if (GMT->current.io.geo.range == GMT_IS_M180_TO_P180_RANGE) p[0] -= 360.0; else p[1] += 360.0;
8966 			GMT_Report (GMT->parent, GMT_MSG_WARNING,
8967 				"Warning -R: Given west and east values [%g %g] were adjusted so west < east [%g %g]\n", w, e, p[0], p[1]);
8968 		}
8969 #endif
8970 	}
8971 	if (i < 4 || i > 6 || ((!GMT->common.R.oblique && gmt_check_region (GMT, p)) || (i == 6 && p[4] >= p[5]))) error++;
8972 	gmt_M_memcpy (GMT->common.R.wesn, p, 6, double);	/* This will probably be reset by gmt_map_setup */
8973 	if (i == 6 && strncmp (GMT->init.module_name, "grdinfo", 7U) && !GMT->current.proj.JZ_set && GMT->current.setting.run_mode == GMT_CLASSIC) {
8974 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "-R with six parameters but no -Jz|Z given - ignore zmin/zmax\n");
8975 		GMT->common.R.wesn[ZLO] = GMT->common.R.wesn[ZHI] = 0.0;
8976 	}
8977 	return (error);
8978 }
8979 
8980 //*! . */
gmtlib_parse_index_range(struct GMT_CTRL * GMT,char * p,int64_t * start,int64_t * stop)8981 int64_t gmtlib_parse_index_range (struct GMT_CTRL *GMT, char *p, int64_t *start, int64_t *stop) {
8982 	/* Parse p looking for range or columns or individual columns.
8983 	 * If neither then we just increment both start and stop. */
8984 	int64_t inc = 1, r = 0;
8985 	int got;
8986 	char *c = NULL;
8987 	if ((c = strchr (p, '-')) || (c = strchr (p, '/'))) {	/* Range of columns given. e.g., 7-9 or 14/19 */
8988 		char q = c[0];
8989 		if (p[0] == q) {	/* Got no start - set to 0 */
8990 			*start = 0;
8991 			got = sscanf (&p[1], "%" PRIu64, stop) + 1;
8992 		}
8993 		else {
8994 			c[0] = ' ';	/* Replace - or . with a space */
8995 			got = sscanf (p, "%" PRIu64 " %" PRIu64, start, stop);
8996 			if (c[1] == '\0') *stop = INTMAX_MAX;	/* Did not specify stop, set to max */
8997 			else if (got != 2) inc = 0L;	/* Error flag */
8998 			c[0] = q;	/* Restore the character */
8999 		}
9000 	}
9001 	else if ((c = strchr (p, ':'))) {	/* Range of columns given. e.g., 7:9 or 1:2:5 */
9002 		if (p[0] == ':') {	/* Did not give the start value */
9003 			*start = 0;
9004 			got = sscanf (&p[1], "%" PRIu64 ":%" PRIu64, &inc, stop) + 1;
9005 		}
9006 		else
9007 			got = sscanf (p, "%" PRIu64 ":%" PRIu64 ":%" PRIu64, start, &inc, stop);
9008 		if (p[strlen(p)-1] == ':') *stop = INTMAX_MAX;	/* Did not specify stop, set to max */
9009 		else if (got == 2) { *stop = inc; inc = 1L;}	/* Just got start:stop with implicit inc = 1 */
9010 		else if (got != 3 || inc < 1) inc = 0L;	/* Error flag */
9011 	}
9012 	else if (isdigit ((int)p[0]))	/* Just a single column, e.g., 3 */
9013 		*start = *stop = atol (p);
9014 	else				/* Just assume it goes column by column */
9015 		(*start)++, (*stop)++;
9016 	if ((*stop) < (*start)) inc = 0L;	/* Not good */
9017 	if (inc > 1 && (*stop != INTMAX_MAX) && (r = ((*stop - *start) % inc)) != 0) {	/* Must adjust stop to fit the sequence */
9018 		*stop -= r;
9019 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "For -i: Sequence %s does not end at given stop value, reduced to %" PRIu64 "\n", p, *stop);
9020 	}
9021 	if (inc == 0)
9022 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad range [%s]: col, start-stop, start:stop, or start:step:stop must yield monotonically increasing positive selections\n", p);
9023 	return (inc);	/* Either > 0 or 0 for error */
9024 }
9025 
9026 /*! . */
gmtinit_parse_data_range(struct GMT_CTRL * GMT,char * arg,unsigned int type,double * start,double * stop)9027 GMT_LOCAL int gmtinit_parse_data_range (struct GMT_CTRL *GMT, char *arg, unsigned int type, double *start, double *stop) {
9028 	/* Parses p looking for range of data or individual values.
9029 	  * Because values may be negative we cannot use - as a range separator here */
9030 	int result = GMT_NOERROR;
9031 	char *c = NULL, A[GMT_LEN64] = {""}, B[GMT_LEN64] = {""}, q;
9032 	if (type == GMT_IS_ABSTIME) {	/* yyyy-mm-ddThh:mm:ss.xxx/yyyy-mm-ddThh:mm:ss.xxx format so must insist on slash separation */
9033 		if ((c = strchr (arg, '/'))) q = '/';
9034 	}
9035 	else if (type & GMT_IS_GEO && strchr (arg, ':')) {	/* Geographical lon/lat arguments contain dddd:mm:ss probably, so also use slash if found */
9036 		char *d = strchr (arg, '-');
9037 		if ((c = strchr (arg, '/'))) q = '/';
9038 		if (c == NULL && d && (d > arg)) c = d, q = c[0];	/* Used hyphens as separator so we are OK using it since no slash was found */
9039 	}
9040 	else {	/* Just floating point numbers, can use :, or / */
9041 		char *d = strchr (arg, '-');
9042 		if ((c = strchr (arg, '/')) || (c = strchr (arg, ':'))) q = c[0];	/* Range of values given. e.g., 14.5/3.44 or 14:20 */
9043 		if (c == NULL && d && (d > arg)) c = d, q = c[0];	/* Used hyphens as separator so we are OK using it since no slash or colon was found */
9044 	}
9045 	if (c) {	/* Got a range */
9046 		if (arg[0] == q) {	/* Got :B which means we did not get a start so we start at -infinity */
9047 			*start = -DBL_MAX;
9048 			gmt_scanf_arg (GMT, &arg[1], type, false, stop);	/* Decode B according to type */
9049 		}
9050 		else {	/* Got A:[B] */
9051 			c[0] = ' ';	/* Temporarily remove the range divider */
9052 			sscanf (arg, "%s %s", A, B);	/* Split into A and B strings */
9053 			gmt_scanf_arg (GMT, A, type, false, start);	/* Decode A according to type */
9054 			if (c[1] == '\0') *stop = DBL_MAX;	/* Did not specify stop, set to +infinity */
9055 			else
9056 				gmt_scanf_arg (GMT, B, type, false, stop);	/* Decode B according to type */
9057 			c[0] = q;	/* Restore divisor */
9058 		}
9059 	}
9060 	else {	/* A single value (?) */
9061 		gmt_scanf_arg (GMT, arg, type, false, start);
9062 		*stop = *start;	/* A single point in time */
9063 	}
9064 	if ((*stop) < (*start)) { /* Not good */
9065 		result = GMT_PARSE_ERROR;
9066 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad range [%s]: value, start:stop, or datetime1/datetime2 must yield monotonically increasing positive selections\n", arg);
9067 	}
9068 	return (result);	/* Either > 0 or 0 for error */
9069 }
9070 
9071 /*! . */
gmt_parse_d_option(struct GMT_CTRL * GMT,char * arg)9072 unsigned int gmt_parse_d_option (struct GMT_CTRL *GMT, char *arg) {
9073 	unsigned int dir, first, last, col, def_col[2] = {GMT_Z, GMT_X};
9074 	char *c = NULL, *q = NULL;
9075 
9076 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -d requires an argument */
9077 	if ((q = strstr (arg, "+c"))) {	/* Want to override start columns */
9078 		col = atoi (&q[2]);
9079 		q[0] = '\0';	/* Chop off for now */
9080 	}
9081 	if (arg[0] == 'i') {
9082 		first = last = GMT_IN;
9083 		c = &arg[1];
9084 		if (q) def_col[GMT_IN] = col;
9085 	}
9086 	else if (arg[0] == 'o') {
9087 		first = last = GMT_OUT;
9088 		c = &arg[1];
9089 		if (q) def_col[GMT_OUT] = col;
9090 	}
9091 	else {
9092 		first = GMT_IN;	last = GMT_OUT;
9093 		c = arg;
9094 		if (q) def_col[GMT_IN] = def_col[GMT_OUT] = col;
9095 	}
9096 
9097 	for (dir = first; dir <= last; dir++) {
9098 		GMT->common.d.active[dir] = true;
9099 		GMT->common.d.nan_proxy[dir] = atof (c);
9100 		/* Need to know if 0 is used as NaN proxy since we must use a different comparison macro later */
9101 		GMT->common.d.is_zero[dir] = doubleAlmostEqualZero (0.0, GMT->common.d.nan_proxy[dir]);
9102 		GMT->common.d.first_col[dir] = def_col[dir];
9103 	}
9104 	if (q) q[0] = '+';	/* Restore modifier */
9105 	if (first == GMT_IN) strncpy (GMT->common.d.string, arg, GMT_LEN64-1);	/* Verbatim copy */
9106 	return (GMT_NOERROR);
9107 }
9108 
9109 /*! . */
gmtinit_parse_e_option(struct GMT_CTRL * GMT,char * arg)9110 GMT_LOCAL unsigned int gmtinit_parse_e_option (struct GMT_CTRL *GMT, char *arg) {
9111 	/* Parse the -e option.  Full syntax: -e[~]\"search string\" */
9112 
9113 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -e requires an argument */
9114 	strncpy (GMT->common.e.string, arg, GMT_LEN64-1);	/* Make copy of -e argument verbatim */
9115 	GMT->common.e.select = gmt_set_text_selection (GMT, arg);
9116 
9117 	return (GMT_NOERROR);
9118 }
9119 
9120 /*! Routine will decode the -i<col>|<colrange>|t[+l][+d<divisor>][+s<scale>][+o<offset>],... arguments or just -in */
gmt_parse_i_option(struct GMT_CTRL * GMT,char * arg)9121 int gmt_parse_i_option (struct GMT_CTRL *GMT, char *arg) {
9122 
9123 	char copy[GMT_BUFSIZ] = {""}, p[GMT_BUFSIZ] = {""}, word[GMT_LEN256] = {""}, *c = NULL;
9124 	bool new_style;
9125 	unsigned int k = 0, pos = 0, pos_p, uerr = 0;
9126 	int convert;
9127 	int64_t i, start = GMT_NOTSET, stop = GMT_NOTSET, inc;
9128 	double scale, offset;
9129 
9130 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -i requires an argument */
9131 
9132 	strncpy (copy, arg, GMT_BUFSIZ-1);
9133 
9134 	GMT->current.io.trailing_text[GMT_IN] = GMT->current.io.trailing_text[GMT_OUT] = false;	/* When using -i you have to specifically add column t to parse trailing text */
9135 	GMT->common.i.end = false;
9136 	if (!strcmp (arg, "n")) return GMT_NOERROR;	/* We just wanted to process the numerical columns */
9137 	if (!strcmp (arg, "t") || !strcmp (arg, ",t")) {	/* Cannot just input trailing text, must use -ot instead */
9138 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Selection -i%s (just trailing text, no numerical input) is not allowed.  Consider using -ot instead, if available.\n", arg);
9139 		return GMT_PARSE_ERROR;
9140 	}
9141 	if (!strcmp (arg, "n,t")) {	/* This is the default when -i is not called except it does not change default -o setting [no trailing text] */
9142 		GMT->current.io.trailing_text[GMT_IN] = true;
9143 		return GMT_NOERROR;
9144 	}
9145 
9146 	new_style = gmt_found_modifier (GMT, arg, "dlos");
9147 
9148 	strncpy (GMT->common.i.string, arg, GMT_LEN64-1);	/* Verbatim copy */
9149 	for (i = 0; i < GMT_MAX_COLUMNS; i++) GMT->current.io.col_skip[i] = true;	/* Initially, no input column is requested */
9150 
9151 	while ((gmt_strtok (copy, ",", &pos, p))) {	/* While it is not empty, process the comma-separated sections */
9152 		convert = 0, scale = 1.0, offset = 0.0;	/* Reset for next column selection */
9153 		if (new_style) {	/* New format as of 5.4: -i<col>|<colrange>[+l][+d<divide>][+s<scale>][+o<offset>],... */
9154 			if ((c = gmt_first_modifier (GMT, p, "dlos"))) {	/* Process modifiers */
9155 				pos_p = 0;	/* Reset to start of new word */
9156 				while (gmt_getmodopt (GMT, 'i', c, "dlos", &pos_p, word, &uerr) && uerr == 0) {
9157 					switch (word[0]) {
9158 						case 'd': convert |= 1;	scale = 1.0 / atof (&word[1]); break;
9159 						case 'l': convert |= 2; break;
9160 						case 'o': convert |= 1; offset = atof (&word[1]); break;
9161 						case 's':	/* Must check for special codes k (convert degrees to km) and d (convert km to degrees) */
9162 							convert |= 1;
9163 							switch (word[1]) {
9164 								case 'k': scale = GMT->current.proj.DIST_KM_PR_DEG; break;
9165 								case 'd': scale = 1.0 / GMT->current.proj.DIST_KM_PR_DEG; break;
9166 								default: scale  = atof (&word[1]); break;
9167 							}
9168 							break;
9169 						default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
9170 					}
9171 				}
9172 				c[0] = '\0';	/* Chop off all modifiers so range can be determined */
9173 				if (uerr) return (GMT_PARSE_ERROR);
9174 			}
9175 		}
9176 		else {	/* Old-style: -i<col>|<colrange>[l][s<scale>][o<offset>],... */
9177 			char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
9178 			if ((c = strchr (p, 'o'))) {	/* Look for offset */
9179 				c[0] = '\0';	/* Wipe out the 'o' so that next scan terminates there */
9180 				convert |= 1;
9181 				offset = atof (&c[1]);
9182 			}
9183 			if ((c = strchr (p, 's'))) {	/* Look for scale factor */
9184 				c[0] = '\0';	/* Wipe out the 's' so that next scan terminates there */
9185 				if (gmt_M_compat_check (GMT, 4)) {	/* GMT4 */
9186 					i = (int)strlen (p) - 1;
9187 					convert = (p[i] == 'l') ? 2 : 1;
9188 					i = sscanf (&c[1], "%[^/]/%[^l]", txt_a, txt_b);
9189 					if (i == 0) {
9190 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "-i...s contains bad scale info\n");
9191 						return (GMT_PARSE_ERROR);
9192 					}
9193 					scale = atof (txt_a);
9194 					if (i == 2) offset = atof (txt_b);
9195 				} else {
9196 					convert |= 1;
9197 					scale = atof (&c[1]);
9198 				}
9199 			}
9200 			if ((c = strchr (p, 'l'))) {	/* Look for log indicator */
9201 				c[0] = '\0';	/* Wipe out the 's' so that next scan terminates there */
9202 				convert |= 2;
9203 			}
9204 		}
9205 
9206 		if (p[0] == 't') {	/* Got the trailing test "column" */
9207 			GMT->current.io.trailing_text[GMT_IN] = GMT->current.io.trailing_text[GMT_OUT] = true;
9208 			if (p[1]) {	/* Want a specific word (0-(nwords-1)) from the trailing text */
9209 				int64_t k = atol (&p[1]);
9210 				if (k < 0) {
9211 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot give negative word position\n");
9212 					return GMT_PARSE_ERROR;
9213 				}
9214 				else {
9215 					GMT->common.i.word = true;
9216 					GMT->common.i.w_col = k + 1;	/* Store as 1-nwords */
9217 				}
9218 			}
9219 		}
9220 		else {	/* Now process column range */
9221 			if ((inc = gmtlib_parse_index_range (GMT, p, &start, &stop)) == 0) return (GMT_PARSE_ERROR);
9222 			if (stop == INTMAX_MAX) {	/* Gave an open interval, e.g., 3: or 4- to mean "until last column" */
9223 				GMT->common.i.end = true;
9224 				stop = GMT_MAX_COLUMNS - 1;	/* Set to last column */
9225 			}
9226 			/* Now set the code for these columns */
9227 
9228 			for (i = start; i <= stop; i += inc, k++) {
9229 				GMT->current.io.col_skip[i] = false;	/* Do not skip these */
9230 				GMT->current.io.col[GMT_IN][k].col = (unsigned int)i;	/* Requested physical column */
9231 				GMT->current.io.col[GMT_IN][k].order = k;		/* Requested logical order of columns */
9232 				GMT->current.io.col[GMT_IN][k].convert = convert;
9233 				GMT->current.io.col[GMT_IN][k].scale = scale;
9234 				GMT->current.io.col[GMT_IN][k].offset = offset;
9235 			}
9236 		}
9237 	}
9238 	/* Use mergesort since qsort is unstable (i.e., unpredictable order) when items are identical */
9239 	mergesort (GMT->current.io.col[GMT_IN], k, sizeof (struct GMT_COL_INFO), gmtinit_compare_cols);
9240 	GMT->common.i.n_cols = k;
9241 	if (k) {	/* Because the user may have repeated some columns we also determine how many unique columns were requested */
9242 		GMT->common.i.n_actual_cols = 1;
9243 		for (i = 1; i < k; i++) if (GMT->current.io.col[GMT_IN][i].col != GMT->current.io.col[GMT_IN][i-1].col)
9244 			GMT->common.i.n_actual_cols++;
9245 	}
9246 	GMT->common.i.orig = GMT->common.i.select = true;
9247 
9248 #if 0
9249 	if (GMT->common.i.n_cols == 0 && GMT->current.io.trailing_text[GMT_IN]) {
9250 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "-it is not allowed, need at least 1-2 leading numerical columns\n");
9251 		return (GMT_PARSE_ERROR);
9252 	}
9253 #endif
9254 	return (GMT_NOERROR);
9255 }
9256 
gmt_parse_j_option(struct GMT_CTRL * GMT,char * arg)9257 int gmt_parse_j_option (struct GMT_CTRL *GMT, char *arg) {
9258 	int err = GMT_NOERROR;
9259 	if (arg == NULL) return GMT_PARSE_ERROR;	/* Must supply the arg */
9260 	switch (arg[0]) {
9261 		case 'c': GMT->common.j.mode = GMT_NO_MODE; break;
9262 		case 'e': GMT->common.j.mode = GMT_GEODESIC; break;
9263 		case 'f': GMT->common.j.mode = GMT_FLATEARTH; break;
9264 		case 'g': case '\0': GMT->common.j.mode = GMT_GREATCIRCLE; break;
9265 		default:
9266 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "-j argument %s is not one of the valid modes e|f|g\n", arg);
9267 			err = GMT_PARSE_ERROR;
9268 			break;
9269 	}
9270 	strncpy (GMT->common.j.string, arg, GMT_LEN8-1);
9271 	return (err);
9272 }
9273 
9274 #define GMT_OPTION_L_MODIFIERS "DGHLNSVfgjopws"	/* All the modifiers available to -l */
9275 
9276 /*! Parse the legend-building option -l */
gmtinit_parse_l_option(struct GMT_CTRL * GMT,char * arg)9277 GMT_LOCAL int gmtinit_parse_l_option (struct GMT_CTRL *GMT, char *arg) {
9278 	char *c = NULL, *q = NULL;
9279 	if (GMT->current.setting.run_mode == GMT_CLASSIC) {     /* Not in modern mode */
9280 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "-l is only recognized in modern mode\n");
9281 		return GMT_PARSE_ERROR;
9282 	}
9283 	if (arg == NULL) return GMT_PARSE_ERROR;		/* Must supply the label arg */
9284 	gmt_M_memset (&(GMT->common.l.item), 1, struct GMT_LEGEND_ITEM);	/* Initialize */
9285 
9286 	if ((c = gmt_first_modifier (GMT, arg, GMT_OPTION_L_MODIFIERS))) {	/* Got modifiers */
9287 		unsigned int pos = 0, n_errors = 0;
9288 		char txt[GMT_LEN128] = {""};
9289 		while (gmt_getmodopt (GMT, 'l', c, GMT_OPTION_L_MODIFIERS, &pos, txt, &n_errors) && n_errors == 0) {
9290 			switch (txt[0]) {
9291 				case 'G': strncpy (GMT->common.l.item.gap, &txt[1], GMT_LEN32-1);		break;	/* Gap before next item */
9292 				case 'D': /* Draw horizontal line */
9293 					GMT->common.l.item.draw |= GMT_LEGEND_DRAW_D;
9294 					if (&txt[1]) strncpy (GMT->common.l.item.pen[GMT_LEGEND_PEN_D], &txt[1], GMT_LEN32-1);
9295 					break;
9296 				case 'H': strncpy (GMT->common.l.item.header, &txt[1], GMT_LEN128-1); 		break;	/* Legend header */
9297 				case 'L': if (txt[2] == '/') {	/* Gave <code>/<label> */
9298 						if (!strchr ("LCRlcr", txt[1])) {
9299 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "-l +l<just>/<label> has bad justification %c\n", txt[1]);
9300 							return GMT_PARSE_ERROR;
9301 						}
9302 						strncpy (GMT->common.l.item.subheader, &txt[3], GMT_LEN128-1);	/* Legend label */
9303 						GMT->common.l.item.code = txt[1];	/* Justification code */
9304 					}
9305 					else {
9306 						strncpy (GMT->common.l.item.subheader, &txt[1], GMT_LEN128-1);	/* Legend label default left justified */
9307 						GMT->common.l.item.code = 'L';
9308 					}
9309 					break;
9310 				case 'N': GMT->common.l.item.ncols = atoi (&txt[1]);			break;	/* Number of columns */
9311 				case 'S':	/* Fixed size for a symbol */
9312 						if ((q = strchr (&txt[1], '/'))) {	/* Got two dimensions, e.g. width/height */
9313 							q[0] = '\0';
9314 							GMT->common.l.item.size  = gmt_M_to_inch (GMT, &txt[1]);
9315 							GMT->common.l.item.size2 = gmt_M_to_inch (GMT, &q[1]);
9316 							q[0] = '/';
9317 						}
9318 						else
9319 							GMT->common.l.item.size = gmt_M_to_inch (GMT, &txt[1]);
9320 						break;
9321 				case 'V': /* Draw vertical line(s) */
9322 					GMT->common.l.item.draw |= GMT_LEGEND_DRAW_V;
9323 					if (&txt[1]) strncpy (GMT->common.l.item.pen[GMT_LEGEND_PEN_V], &txt[1], GMT_LEN32-1);
9324 					break;
9325 				case 'f': strncpy (GMT->common.l.item.font, &txt[1], GMT_LEN32-1);		break;	/* Font to use for this -l entry */
9326 				case 'g': /* Frame fill */
9327 					if (&txt[1]) strncpy (GMT->common.l.item.fill, &txt[1], GMT_LEN32-1);
9328 					break;
9329 				case 'j': GMT->common.l.item.just = gmt_just_decode (GMT, &txt[1], PSL_TR);	break;	/* legend placement */
9330 				case 'o': /* Frame offset */
9331 					if (&txt[1]) strncpy (GMT->common.l.item.off, &txt[1], GMT_LEN32-1);
9332 					break;
9333 				case 'p': /* Frame pen */
9334 					if (&txt[1]) strncpy (GMT->common.l.item.pen[GMT_LEGEND_PEN_P], &txt[1], GMT_LEN32-1);
9335 					break;
9336 				case 's': GMT->common.l.item.scale = atof (&txt[1]);			break;	/* Scale all symbols by this factor */
9337 				case 'w': GMT->common.l.item.width = gmt_M_to_inch (GMT, &txt[1]);		break;	/* Legend width */
9338 				default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
9339 			}
9340 		}
9341 		c[0] = '\0';	/* Chop'em off */
9342 	}
9343 	if (arg[0]) {	/* Gave a label, which may be a constant string, code, or format statement */
9344 		char *d = strchr (arg, '%');
9345 		if (d && strchr (d, 'd')) {
9346 			GMT->common.l.item.label_type = GMT_LEGEND_LABEL_FORMAT;
9347 			strncpy (GMT->common.l.item.label, arg, GMT_LEN128-1);
9348 		}
9349 		else if (arg[strlen(arg)-1] == '#') {	/* Short hand for integer %d */
9350 			GMT->common.l.item.label_type = GMT_LEGEND_LABEL_FORMAT;
9351 			arg[strlen(arg)-1] = '\0';
9352 			snprintf (GMT->common.l.item.label, GMT_LEN128, "%s%%d", arg);
9353 			arg[strlen(arg)-1] = '#';
9354 		}
9355 		else if (strchr (arg, ',')) {
9356 			GMT->common.l.item.label_type = GMT_LEGEND_LABEL_LIST;
9357 			strncpy (GMT->common.l.item.label, arg, GMT_LEN128-1);
9358 		}
9359 		else {	/* Static string */
9360 			GMT->common.l.item.label_type = GMT_LEGEND_LABEL_FIXED;
9361 			strncpy (GMT->common.l.item.label, arg, GMT_LEN128-1);
9362 		}
9363 	}
9364 	else	/* Try segment header label if present */
9365 			GMT->common.l.item.label_type = GMT_LEGEND_LABEL_HEADER;
9366 	if (c) c[0] = '+';	/* Restore */
9367 	return (GMT_NOERROR);
9368 }
9369 
9370 /*! Routine will decode the -[<col>|<colrange>|t,... arguments or just -on */
gmt_parse_o_option(struct GMT_CTRL * GMT,char * arg)9371 int gmt_parse_o_option (struct GMT_CTRL *GMT, char *arg) {
9372 
9373 	char copy[GMT_BUFSIZ] = {""}, p[GMT_BUFSIZ] = {""};
9374 	unsigned int pos = 0;
9375 	uint64_t k = 0;
9376 	int64_t i, start = GMT_NOTSET, stop = GMT_NOTSET, inc;
9377 
9378 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -o requires an argument */
9379 
9380 	if (gmt_found_modifier (GMT, arg, "dlos")) {
9381 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "The -o option does not take +d|l|o|s modifiers; consider -i instead.\n");
9382 		return GMT_PARSE_ERROR;
9383 	}
9384 
9385 	strncpy (copy, arg, GMT_BUFSIZ-1);
9386 	strncpy (GMT->common.o.string, arg, GMT_LEN64-1);	/* Verbatim copy */
9387 
9388 	GMT->current.io.trailing_text[GMT_OUT] = false;	/* When using -o you have to specifically add column t to parse trailing text */
9389 	if (! strcmp (arg, "n")) return GMT_NOERROR;	/* We just wanted to select numerical output only */
9390 	if (arg[0] == 't') {	/* Just wants trailing text, no numerical columns */
9391 		GMT->current.io.trailing_text[GMT_OUT] = true;
9392 		GMT->common.o.text = true;	/* Special flag to switch to gmtlib_ascii_output_trailing_text output function later */
9393 		if (arg[1]) {	/* Want a specific word (0-(nwords-1)) from the trailing text */
9394 			int64_t kk = atol (&arg[1]);
9395 			if (kk < 0) {
9396 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot give negative word position\n");
9397 				return GMT_PARSE_ERROR;
9398 			}
9399 			else {
9400 				GMT->common.o.word = true;
9401 				GMT->common.o.w_col = kk + 1;	/* Store as 1-nwords */
9402 			}
9403 		}
9404 		return GMT_NOERROR;
9405 	}
9406 	if (! strncmp (arg, "n,t", 3U)) {	/* Odd, but user wants both numerical and trailing text, possibly specific word */
9407 		GMT->current.io.trailing_text[GMT_OUT] = true;
9408 		if (arg[3]) {	/* Want a specific word (0-(nwords-1)) from the trailing text */
9409 			int64_t kk = atol (&arg[3]);
9410 			if (kk < 0) {
9411 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot give negative word position\n");
9412 				return GMT_PARSE_ERROR;
9413 			}
9414 			else {
9415 				GMT->common.o.word = true;
9416 				GMT->common.o.w_col = kk + 1;	/* Store as 1-nwords */
9417 			}
9418 		}
9419 		return GMT_NOERROR;
9420 	}
9421 
9422 	/* Regular parsing of -ocols */
9423 
9424 	while ((gmt_strtok (copy, ",", &pos, p))) {	/* While it is not empty, process it */
9425 		if (p[0] == 't') {
9426 			GMT->current.io.trailing_text[GMT_OUT] = true;	/* Include trailing text */
9427 			if (p[1]) {	/* Want a specific word (0-(nwords-1)) from the trailing text */
9428 				int64_t kk = atol (&p[1]);
9429 				if (kk < 0) {
9430 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot give negative word position\n");
9431 					return GMT_PARSE_ERROR;
9432 				}
9433 				else {
9434 					GMT->common.o.word = true;
9435 					GMT->common.o.w_col = kk + 1;	/* Store as 1-nwords */
9436 				}
9437 			}
9438 		}
9439 		else {
9440 			if ((inc = gmtlib_parse_index_range (GMT, p, &start, &stop)) == 0) return (GMT_PARSE_ERROR);
9441 			if (stop == INTMAX_MAX) {	/* Gave an open-ended interval, e.g., 3: or 3- to mean "from 3 until last column" */
9442 				GMT->common.o.end = true;
9443 				stop = GMT_MAX_COLUMNS - 1;	/* Set to last column */
9444 			}
9445 
9446 			/* Now set the code for these columns */
9447 
9448 			for (i = start; i <= stop; i += inc, k++) {
9449 				GMT->current.io.col[GMT_OUT][k].col = (unsigned int)i;	/* Requested order of columns */
9450 				GMT->current.io.col[GMT_OUT][k].order = (unsigned int)k;		/* Requested order of columns */
9451 			}
9452 		}
9453 	}
9454 	GMT->common.o.n_cols = k;
9455 	if (GMT->common.b.active[GMT_OUT] && GMT->common.b.ncol[GMT_OUT] == 0) GMT->common.b.ncol[GMT_OUT] = GMT->common.b.ncol[GMT_IN];	/* Since -o machinery will march through */
9456 	GMT->common.o.orig = GMT->common.o.select = true;
9457 	return (GMT_NOERROR);
9458 }
9459 
9460 /*! Routine to decode the [~]<row>|<rowrange>|,... arguments */
gmtinit_parse_q_option_r(struct GMT_CTRL * GMT,unsigned int direction,char * arg)9461 GMT_LOCAL int gmtinit_parse_q_option_r (struct GMT_CTRL *GMT, unsigned int direction, char *arg) {
9462 	/* Any modifiers +a|f|s have been stripped off by now */
9463 	char p[GMT_LEN64] = {""};
9464 	unsigned int pos = 0, n = 0, j = 0;
9465 	struct GMT_ROW_RANGE *R = NULL;
9466 
9467 	R = GMT->current.io.row_range[direction];	/* Just a short-hand */
9468 	if (arg[0] == '~') GMT->common.q.inverse[direction] = true, j = 1;
9469 
9470 	/* Parsing of <rows> sequences */
9471 	while ((gmt_strtok (&arg[j], ",", &pos, p))) {	/* While it is not empty, process another range */
9472 		if (p[0] == '~') {
9473 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Only one reverse-the-test sign (~) is allowed before the first range in -q\n");
9474 			return (GMT_PARSE_ERROR);
9475 		}
9476 		/* We can process A or A: or A- or A/ or A:B or A-B or A/B or :B or -B or /B */
9477 		if ((R[n].inc = gmtlib_parse_index_range (GMT, p, &R[n].first, &R[n].last)) == 0) return (GMT_PARSE_ERROR);
9478 		R[n].first++;	/* Since the i/o machinery increments the current record number before we compare, we switch to 1 being the first row */
9479 		if (R[n].last != INTMAX_MAX) R[n].last++;	/* Same for last row unless infinite */
9480 		if ((++n) == GMT_MAX_RANGES) {
9481 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Exceeded max number of row range arguments (%d)\n", GMT_MAX_RANGES);
9482 			return (GMT_PARSE_ERROR);
9483 		}
9484 	}
9485 	GMT->current.io.n_row_ranges[direction] = n;	/* Number of sequences given */
9486 	return (GMT_NOERROR);
9487 }
9488 
9489 /*! Routine will decode the [~]<first/last>,|,... arguments */
gmtinit_parse_q_option_z(struct GMT_CTRL * GMT,unsigned int direction,unsigned int col,char * arg)9490 GMT_LOCAL int gmtinit_parse_q_option_z (struct GMT_CTRL *GMT, unsigned int direction, unsigned int col, char *arg) {
9491 	/* Any modifier +ac<col> has been stripped off by now */
9492 	char p[GMT_LEN64] = {""};
9493 	unsigned int answer, pos = 0, n = 0, j = 0;
9494 	struct GMT_DATA_RANGE *R = NULL;
9495 
9496 	R = GMT->current.io.data_range[direction];	/* Just a short-hand */
9497 	if (arg[0] == '~') GMT->common.q.inverse[direction] = true, j = 1;
9498 
9499 	/* Parsing of <range> sequences */
9500 	while ((gmt_strtok (&arg[j], ",", &pos, p))) {	/* While it is not empty, process another range */
9501 		if (p[0] == '~') {
9502 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Only one reverse-the-test sign (~) is allowed before the first range in -q\n");
9503 			return (GMT_PARSE_ERROR);
9504 		}
9505 		/* We can process A or A/ or A/B or /B */
9506 		if ((answer = gmtinit_parse_data_range (GMT, p, gmt_M_type (GMT, direction, col), &R[n].first, &R[n].last)) == GMT_PARSE_ERROR) return (answer);
9507 		if ((++n) == GMT_MAX_RANGES) {
9508 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Exceeded max number of row range arguments (%d)\n", GMT_MAX_RANGES);
9509 			return (GMT_PARSE_ERROR);
9510 		}
9511 	}
9512 	GMT->current.io.n_row_ranges[direction] = n;	/* Number of sequences given */
9513 	return (GMT_NOERROR);
9514 }
9515 
9516 /*! Routine will decode the -q[i|o\[<row>|<rowrange>|,...[+a|f|s] arguments */
gmtinit_parse_q_option(struct GMT_CTRL * GMT,char * arg)9517 GMT_LOCAL int gmtinit_parse_q_option (struct GMT_CTRL *GMT, char *arg) {
9518 
9519 	char *c = NULL;
9520 	unsigned int answer, k = 0, direction = GMT_IN;	/* Default direction is input rows */
9521 
9522 	if (!arg || !arg[0]) return (GMT_PARSE_ERROR);	/* -q requires an argument */
9523 
9524 	if (arg[0] == 'i') k = 1;	/* Specifically gave input rows [Default] */
9525 	else if (arg[0] == 'o') k = 1, direction = GMT_OUT;	/* Specifically gave output rows */
9526 	strncpy (GMT->common.q.string[direction], arg, GMT_LEN64-1);	/* Verbatim copy of the option args */
9527 
9528 	if ((c = strstr (arg, "+c"))) {	/* Want row limits based on data values, not row row counters */
9529 		unsigned int col = atol (&c[2]);	/* Examine the data in this column */
9530 		c[0] = '\0';	/* Chop off column indicator */
9531 		answer = gmtinit_parse_q_option_z (GMT, direction, col, &arg[k]);
9532 		c[0] = '+';	/* Restore modifier */
9533 		GMT->common.q.col = col;	/* Remember the column */
9534 		GMT->common.q.mode = GMT_RANGE_DATA_IN + 2 * direction;	/* Mode flag is 2 (input range) or 4 (output range) */
9535 	}
9536 	else {	/* Got -q for integer rows */
9537 		if ((c = strstr (arg, "+a")) || (c = strstr (arg, "+f")) || (c = strstr (arg, "+s"))) {
9538 			switch (c[1]) {
9539 				case 'a': GMT->common.q.rec = &(GMT->current.io.data_record_number_in_set[direction]);	break; /* For the whole data set */
9540 				case 'f': GMT->common.q.rec = &(GMT->current.io.data_record_number_in_tbl[direction]);	break; /* Reset counter per table */
9541 				case 's': GMT->common.q.rec = &(GMT->current.io.data_record_number_in_seg[direction]);	break; /* Reset counter per segment */
9542 				default: break;	/* Cannot get here but Coverity does not know that... */
9543 			}
9544 			c[0] = '\0';	/* Chop off modifier */
9545 		}
9546 		else	/* Default assignment for -q counter */
9547 			GMT->common.q.rec = &(GMT->current.io.data_record_number_in_set[direction]);
9548 		answer = gmtinit_parse_q_option_r (GMT, direction, &arg[k]);
9549 		GMT->common.q.mode = GMT_RANGE_ROW_IN + 2 * direction;	/* Mode flag is 1 (input rows) or 3 (output rows) */
9550 		if (c) c[0] = '+';	/* Restore modifier */
9551 	}
9552 	if (GMT->current.io.n_row_ranges[direction] == 0) {
9553 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No valid ranges given [-q%s]!\n", arg);
9554 		answer = GMT_PARSE_ERROR;
9555 	}
9556 	return (answer);
9557 }
9558 
9559 /*! . */
gmt_parse_model(struct GMT_CTRL * GMT,char option,char * in_arg,unsigned int dim,struct GMT_MODEL * M)9560 int gmt_parse_model (struct GMT_CTRL *GMT, char option, char *in_arg, unsigned int dim, struct GMT_MODEL *M) {
9561 	/* This may eventually switch on dim, but for now it is just 1D */
9562 	gmt_M_unused(dim);
9563 	return (gmtinit_parse_model1d (GMT, option, in_arg, M));
9564 }
9565 
9566 /*! . */
gmt_parse_segmentize(struct GMT_CTRL * GMT,char option,char * in_arg,unsigned int mode,struct GMT_SEGMENTIZE * S)9567 unsigned int gmt_parse_segmentize (struct GMT_CTRL *GMT, char option, char *in_arg, unsigned int mode, struct GMT_SEGMENTIZE *S) {
9568 	/* Parse segmentizing options in gmt convert (mode == 0) or psxy (mode == 1).
9569 	 * Syntax is given below (assuming option = -F here):
9570 	 * -F<scheme><method: i.e., -F[c|n|p[<origin>|v][a|f|s|r] or -Fp<origin>
9571 	 * where <scheme> is c = continuous [Default], n = network, r = reference point, and v = vectors.
9572 	 * and impact is a = all files, f = per file, s = per segment [Default], r = per record.
9573 	 * Four different segmentizing schemes:
9574 	 * 1) -Fc: Continuous lines.  By default, lines are drawn on a segment by segment basis.
9575 	 *    Thus, -F or -Fc or -Fcs or -Fs is the standard default.  However, if we use
9576 	 *    -Fcf or -Ff then we ignore segment headers WITHIN each file, except for the first header
9577 	 *   in each file.  In other words, all points in a file will be considered continuous.
9578 	 *   Finally, using -Fca or -Fa then all points in all fields are considered continuous and
9579 	 *   only the first segment header in the first file is considered.  So only a|f|r is allowed.
9580 	 * 2) -Fn: Network.  For each group of points we connect each point with every other point.
9581 	 *   The modifiers a,f,s control what the "group" is.  With s, we construct a separate
9582 	 *   network for each segment, with f we group all segments in a file and construct a
9583 	 *   network for all those points, while with a with consider all points in the dataset
9584 	 *   to be one group. So only a|f|s is allowed.
9585 	 * 3) -Fp: Ref point.  Here, we construct line segments from the given reference point to
9586 	 *   each of the points in the file.  If refpoint is given as two slash-separated coordinates
9587 	 *   then the refpoint is fixed throughout this construction.  However, refpoint may also be
9588 	 *   given as a, f, s and if so we pick the first point in the dataset, or first point in each
9589 	 *   file, or the first point in each segment to update the actual reference point.
9590 	 * 4) -Fv: Vectorize.  Here, consecutive points are turned into vector segments such as used
9591 	 *   by psxy -Sv+s or external applications.  Again, appending a|f|s controls if we should
9592 	 *   honor the segment headers [Default is -Fvs if -Fv is given]. Only a|f|s is allowed.
9593 	 */
9594 
9595 	unsigned int k, errors = 0;
9596 	switch (in_arg[0]) {	/* First set method */
9597 		case 'c': k = 1;	S->method  = SEGM_CONTINUOUS;	break;
9598 		case 'n': k = 1;	S->method  = SEGM_NETWORK;	break;
9599 		case 'p': case 'r': k = 1;	S->method  = SEGM_REFPOINT;	break;	/* Backwards support for r for refpoint, now p for point */
9600 		case 'v': k = 1;	S->method  = SEGM_VECTOR;	break;
9601 		default:  k = 0;	S->method  = SEGM_CONTINUOUS;	break;
9602 	}
9603 
9604 	switch (in_arg[k]) {	/* Now set level */
9605 		case 's': case '\0': S->level = SEGM_SEGMENT;	break;	/* Default is segment */
9606 		case 'a': S->level = SEGM_DATASET;	break;
9607 		case 'f': S->level = SEGM_TABLE;	break;
9608 		case 'r': S->level = SEGM_RECORD;	break;
9609 		default:	/* Must be a reference point but only if method is refpoint */
9610 			if (S->method == SEGM_REFPOINT && strchr (in_arg, '/')) {	/* Gave arguments for an origin */
9611 				S->level = SEGM_ORIGIN;
9612 				if ((k = gmt_get_pair (GMT, &in_arg[k], GMT_PAIR_COORD, S->origin)) < 2) errors++;
9613 			}
9614 			else {
9615 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Expected reference point coordinates but got this: %s\n", option, &in_arg[k]);
9616 				errors++;
9617 			}
9618 			break;
9619 	}
9620 	if (S->method == SEGM_CONTINUOUS && S->level == SEGM_SEGMENT) {
9621 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Selecting -Fc, -Fs, or -Fcs yields no change\n", option);
9622 		errors++;
9623 	}
9624 	if (S->method != SEGM_REFPOINT && S->level == SEGM_RECORD) {
9625 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Only -Fp may accept refpoint = r\n", option);
9626 		errors++;
9627 	}
9628 	if (mode == 1 && S->method == SEGM_VECTOR)	/* Only available for gmtconvert */
9629 		errors++;
9630 	return (errors);
9631 }
9632 
9633 /*! . */
gmt_check_binary_io(struct GMT_CTRL * GMT,uint64_t n_req)9634 int gmt_check_binary_io (struct GMT_CTRL *GMT, uint64_t n_req) {
9635 	int n_errors = 0;
9636 
9637 	/* Check the binary options that are used with most GMT programs.
9638 	 * GMT is the pointer to the GMT structure.
9639 	 * n_req is the number of required columns. If 0 then it relies on
9640 	 *    GMT->common.b.ncol[GMT_IN] to be non-zero.
9641 	 * Return value is the number of errors that where found.
9642 	 */
9643 
9644 	if (!GMT->common.b.active[GMT_IN]) return (GMT_NOERROR);	/* Let machinery figure out input cols for ASCII */
9645 
9646 	/* These are specific tests for binary input */
9647 
9648 	if (GMT->common.b.ncol[GMT_IN] == 0) GMT->common.b.ncol[GMT_IN] = n_req;
9649 	if (GMT->common.b.ncol[GMT_IN] == 0) {
9650 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Must specify number of columns in binary input data (-bi)\n");
9651 		n_errors++;
9652 	}
9653 	else if (n_req > GMT->common.b.ncol[GMT_IN]) {
9654 		GMT_Report (GMT->parent, GMT_MSG_ERROR,
9655 			"Binary input data (-bi) provides %d but must have at least %d columns\n",
9656 			GMT->common.b.ncol[GMT_IN], n_req);
9657 		n_errors++;
9658 	}
9659 	if (GMT->common.b.ncol[GMT_IN] < GMT->common.i.n_actual_cols) {
9660 		GMT_Report (GMT->parent, GMT_MSG_ERROR,
9661 			"Binary input data (-bi) provides %d but column selection (-i) asks for %d columns\n",
9662 			GMT->common.b.ncol[GMT_IN], GMT->common.i.n_actual_cols);
9663 		n_errors++;
9664 	}
9665 	if (GMT->common.b.active[GMT_OUT] && GMT->common.b.ncol[GMT_OUT] && (GMT->common.b.ncol[GMT_OUT] < GMT->common.o.n_cols)) {
9666 		GMT_Report (GMT->parent, GMT_MSG_ERROR,
9667 			"Binary output data (-bo) provides %d but column selection (-o) asks for %d columns\n",
9668 			GMT->common.b.ncol[GMT_OUT], GMT->common.o.n_cols);
9669 		n_errors++;
9670 	}
9671 
9672 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Provides %d, expects %d-column binary data\n", GMT->common.b.ncol[GMT_IN], n_req);
9673 
9674 	return (n_errors);
9675 }
9676 
9677 /*! . */
gmt_parse_g_option(struct GMT_CTRL * GMT,char * txt)9678 int gmt_parse_g_option (struct GMT_CTRL *GMT, char *txt) {
9679 	int i, kk, k = 0, c, gap = 0, z_col = GMT_Z;
9680 	char *t = NULL;
9681 	/* Process the GMT gap detection option for parameters (leading [+|-] for gap is deprecated) */
9682 	/* Syntax, e.g., -gx|y|z|d|X|Y|D<gap>[d|m|s|e|f|k|M|n|c|i|p][+a][+c<col>][+n|p] */
9683 	/* Deprecated: -gx|X|y|Y|d|D|[<col>]z<gap>[d|m|s|e|f|k|M|n|c|i|p][+n|p] or -ga */
9684 
9685 	if (!txt || !txt[0]) return (GMT_PARSE_ERROR);	/* -g requires an argument */
9686 	if ((i = GMT->common.g.n_methods) == GMT_N_GAP_METHODS) {
9687 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot specify more than %d gap criteria\n", GMT_N_GAP_METHODS);
9688 		return (1);
9689 	}
9690 	strncpy (GMT->common.g.string, txt, GMT_LEN64-1);	/* Verbatim copy */
9691 
9692 	gmt_set_segmentheader (GMT, GMT_OUT, true);	/* -g gap checking implies -mo if not already set */
9693 
9694 	if (txt[0] == 'a') {	/* Deprecated syntax for multiple criteria, specify that all criteria be met [Default is any] */
9695 		k++;
9696 		GMT->common.g.match_all = true;
9697 		if (!txt[k]) return (1);	/* Just a single -ga */
9698 	}
9699 
9700 	if ((t = gmt_first_modifier (GMT, txt, "acnp"))) {	/* Process any modifiers */
9701 		char p[GMT_LEN16] = {""};
9702 		unsigned int pos = 0, n_errors = 0;
9703 		while (gmt_getmodopt (GMT, 'g', t, "acnp", &pos, p, &n_errors) && n_errors == 0) {
9704 			switch (p[0]) {
9705 				case 'a':	GMT->common.g.match_all = true;	break;	/* All criteria must be met */
9706 				case 'c':	/* Specify alternate z-column */
9707 					z_col = (p[1]) ? atoi (&p[1]) : GMT_Z;
9708 					if (z_col < GMT_Z) {
9709 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -g: +c<col> given a <col> less than 2, which is x,y territory\n");
9710 						n_errors++;
9711 					}
9712 					break;
9713 				case 'n':	gap = -1;	break;	/* prev - current */
9714 				case 'p':	gap = +1;	break;	/* current - prev */
9715 				default:
9716 					break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
9717 			}
9718 		}
9719 		t[0] = '\0';	/* Chop off the modifiers */
9720 	}
9721 
9722 	/* Check if we have deprecated syntax and if that is allowed by the compatibility setting */
9723 	kk = k;
9724 	while (txt[kk] && isdigit ((int)txt[kk])) kk++;	/* Skip past until we find z */
9725 	if (strchr ("xXyYdDz", txt[kk]) && strchr ("-+", txt[kk+1])) {	/* Deprecated way of setting the gap */
9726 		if (gmt_M_compat_check (GMT, 6))
9727 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Leading sign for gaps in -g is deprecated; use modifiers +n or +p instead\n");
9728 		else if (txt[kk+1] == '-') {
9729 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gap value is negative; see -g syntax\n");
9730 			if (t) t[0] = '+';	/* Restore modifiers */
9731 			return (1);
9732 		}
9733 		if (gap == 0) gap = (txt[kk+1] == '-') ? -1 : +1;	/* Use the gap variable in the tests below */
9734 	}
9735 
9736 	switch (txt[k]) {	/* Determine method used for gap detection */
9737 		case 'x':	/* Difference in user's x-coordinates used for test */
9738 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_COL : ((gap == +1) ? GMT_POSGAP_IN_COL : GMT_ABSGAP_IN_COL);
9739 			GMT->common.g.col[i] = GMT_X;
9740 			break;
9741 		case 'X':	/* Difference in user's mapped x-coordinates used for test */
9742 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_MAP_COL : ((gap == +1) ? GMT_POSGAP_IN_MAP_COL : GMT_ABSGAP_IN_MAP_COL);
9743 			GMT->common.g.col[i] = GMT_X;
9744 			break;
9745 		case 'y':	/* Difference in user's y-coordinates used for test */
9746 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_COL : ((gap == +1) ? GMT_POSGAP_IN_COL : GMT_ABSGAP_IN_COL);
9747 			GMT->common.g.col[i] = GMT_Y;
9748 			break;
9749 		case 'Y':	/* Difference in user's mapped y-coordinates used for test */
9750 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_MAP_COL : ((gap == +1) ? GMT_POSGAP_IN_MAP_COL : GMT_ABSGAP_IN_MAP_COL);
9751 			GMT->common.g.col[i] = GMT_Y;
9752 			break;
9753 		case 'd':	/* Great circle (if geographic data) or Cartesian distance used for test */
9754 			GMT->common.g.method[i] = (gmt_M_is_geographic (GMT, GMT_IN)) ? GMT_GAP_IN_GDIST : GMT_GAP_IN_CDIST;
9755 			GMT->common.g.col[i] = -1;
9756 			break;
9757 		case 'D':	/* Cartesian mapped distance used for test */
9758 			GMT->common.g.method[i] = GMT_GAP_IN_PDIST;
9759 			GMT->common.g.col[i] = -1;
9760 			break;
9761 		case 'z':	/* Difference in user's z-coordinates used for test */
9762 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_COL : ((gap == +1) ? GMT_POSGAP_IN_COL : GMT_ABSGAP_IN_COL);
9763 			GMT->common.g.col[i] = z_col;
9764 			break;
9765 		case '1':	/* Difference in a specified z-column's coordinates used for test [Deprecated: Use +c<col> instead] */
9766 		case '2':
9767 		case '3':
9768 		case '4':
9769 		case '5':
9770 		case '6':
9771 		case '7':
9772 		case '8':
9773 		case '9':
9774 		case '0':
9775 			c = k;
9776 			while (txt[k] && isdigit ((int)txt[k])) k++;	/* Skip past until we find z */
9777 			if (txt[k] != 'z') {
9778 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad gap selector (%c).  Choose from x|y|d|X|Y|D|z\n", txt[k]);
9779 				if (t) t[0] = '+';	/* Restore modifiers */
9780 				return (1);
9781 			}
9782 			GMT->common.g.method[i] = (gap == -1) ? GMT_NEGGAP_IN_COL : ((gap == +1) ? GMT_POSGAP_IN_COL : GMT_ABSGAP_IN_COL);
9783 			GMT->common.g.col[i] = atoi (&txt[c]);
9784 			break;
9785 		default:
9786 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad gap selector (%c).  Choose from x|y|d|X|Y|D|z\n", txt[0]);
9787 			if (t) t[0] = '+';	/* Restore modifiers */
9788 			return (1);
9789 			break;
9790 	}
9791 	switch (GMT->common.g.method[i]) {
9792 		case GMT_NEGGAP_IN_COL:
9793 			GMT->common.g.get_dist[i] = &gmtinit_neg_col_dist;
9794 			break;
9795 		case GMT_POSGAP_IN_COL:
9796 			GMT->common.g.get_dist[i] = &gmtinit_pos_col_dist;
9797 			break;
9798 		case GMT_ABSGAP_IN_COL:
9799 			GMT->common.g.get_dist[i] = &gmtinit_abs_col_dist;
9800 			break;
9801 		case GMT_NEGGAP_IN_MAP_COL:
9802 			GMT->common.g.get_dist[i] = &gmtinit_neg_col_map_dist;
9803 			break;
9804 		case GMT_POSGAP_IN_MAP_COL:
9805 			GMT->common.g.get_dist[i] = &gmtinit_pos_col_map_dist;
9806 			break;
9807 		case GMT_ABSGAP_IN_MAP_COL:
9808 			GMT->common.g.get_dist[i] = &gmtinit_abs_col_map_dist;
9809 			break;
9810 		case GMT_GAP_IN_GDIST:
9811 			GMT->common.g.get_dist[i] = &gmtinit_xy_true_dist;
9812 			break;
9813 		case GMT_GAP_IN_CDIST:
9814 			GMT->common.g.get_dist[i] = &gmtinit_xy_cart_dist;
9815 			break;
9816 		case GMT_GAP_IN_PDIST:
9817 			GMT->common.g.get_dist[i] = &gmtinit_xy_map_dist;
9818 			break;
9819 		default:
9820 			break;	/* Already set, or will be reset below  */
9821 	}
9822 	k++;	/* Skip to start of gap value */
9823 	if (txt[k] == '-' || txt[k] == '+') k++;	/* Skip deprecated sign */
9824 	if ((GMT->common.g.gap[i] = atof (&txt[k])) == 0.0) {
9825 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Gap value must be non-zero\n");
9826 		if (t) t[0] = '+';	/* Restore modifiers */
9827 		return (1);
9828 	}
9829 	if (GMT->common.g.method[i] == GMT_GAP_IN_GDIST) {	/* Convert any gap given to meters */
9830 		switch (txt[strlen(txt)-1]) {	/* Process unit information */
9831 			case 'd':	/* Arc degrees, reset pointer */
9832 				GMT->common.g.get_dist[i] = &gmtinit_xy_deg_dist;
9833 				GMT->common.g.method[i] = GMT_GAP_IN_DDIST;
9834 				break;
9835 			case 'm':	/* Arc minutes, reset pointer */
9836 				GMT->common.g.get_dist[i] = &gmtinit_xy_deg_dist;
9837 				GMT->common.g.method[i] = GMT_GAP_IN_DDIST;
9838 				GMT->common.g.gap[i] *= GMT_MIN2DEG;
9839 				break;
9840 			case 's':	/* Arc seconds, reset pointer */
9841 				GMT->common.g.get_dist[i] = &gmtinit_xy_deg_dist;
9842 				GMT->common.g.method[i] = GMT_GAP_IN_DDIST;
9843 				GMT->common.g.gap[i] *= GMT_SEC2DEG;
9844 				break;
9845 			case 'f':	/* Feet  */
9846 				GMT->common.g.gap[i] *= METERS_IN_A_FOOT;
9847 				break;
9848 			case 'k':	/* Km  */
9849 				GMT->common.g.gap[i] *= 1000.0;
9850 				break;
9851 			case 'M':	/* Miles */
9852 				GMT->common.g.gap[i] *= METERS_IN_A_MILE;
9853 				break;
9854 			case 'n':	/* Nautical miles */
9855 				GMT->common.g.gap[i] *= METERS_IN_A_NAUTICAL_MILE;
9856 				break;
9857 			case 'u':	/* Survey feet  */
9858 				GMT->common.g.gap[i] *= METERS_IN_A_SURVEY_FOOT;
9859 				break;
9860 			default:	/* E.g., meters or junk */
9861 				break;
9862 		}
9863 	}
9864 	else if (GMT->common.g.method[i] == GMT_GAP_IN_PDIST){	/* Cartesian plot distance stuff */
9865 		switch (txt[strlen(txt)-1]) {	/* Process unit information */
9866 			case 'c':	/* cm */
9867 				GMT->common.g.gap[i] /= 2.54;
9868 				break;
9869 			case 'p':	/* Points */
9870 				GMT->common.g.gap[i] /= 72.0;
9871 				break;
9872 			default:	/* E.g., inch or junk */
9873 				break;
9874 		}
9875 	}
9876 	if ((uint64_t)(GMT->common.g.col[i] + 1) > GMT->common.g.n_col) GMT->common.g.n_col = (uint64_t)(GMT->common.g.col[i] + 1);	/* Needed when checking since it may otherwise not be read */
9877 	GMT->common.g.n_methods++;
9878 	GMT->common.g.selected = true;
9879 	if (t) t[0] = '+';	/* Restore modifiers */
9880 	return (GMT_NOERROR);
9881 }
9882 
9883 /*! . */
gmt_set_V(int mode)9884 char gmt_set_V (int mode) {
9885 	char val = 0;
9886 	switch (mode) {
9887 		case GMT_MSG_QUIET:		val = 'q'; break;
9888 		case GMT_MSG_ERROR:		val = 'e'; break;
9889 		case GMT_MSG_WARNING:		val = 'w'; break;
9890 		case GMT_MSG_TICTOC:		val = 't'; break;
9891 		case GMT_MSG_INFORMATION:	val = 'i'; break;
9892 		case GMT_MSG_COMPAT:		val = 'c'; break;
9893 		case GMT_MSG_DEBUG:		val = 'd'; break;
9894 		default: break;
9895 	}
9896 	return val;
9897 }
9898 
9899 GMT_LOCAL int gmtinit_update_theme (struct GMT_CTRL *GMT);	/* Must set this here since the next two functions call each other */
9900 
9901 /*! . */
gmtinit_loaddefaults(struct GMT_CTRL * GMT,char * file,bool theme)9902 GMT_LOCAL int gmtinit_loaddefaults (struct GMT_CTRL *GMT, char *file, bool theme) {
9903 	static int gmt_version_major = GMT_PACKAGE_VERSION_MAJOR;
9904 	unsigned int error = 0, rec = 0, ver;
9905 	char line[GMT_BUFSIZ] = {""}, keyword[GMT_LEN256] = {""}, value[GMT_BUFSIZ] = {""};
9906 	FILE *fp = NULL;
9907 
9908 	if ((fp = fopen (file, "r")) == NULL) return (GMT_NOTSET);
9909 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reading GMT Default parameters from file: %s\n", file);
9910 	while (fgets (line, GMT_BUFSIZ, fp)) {
9911 		rec++;
9912 		gmt_chop (line); /* Get rid of [\r]\n */
9913 		if (rec == 1 && (line[0] == 'S' || line[0] == 'U'))	{	/* An old GMT4 gmt.conf got in the way */
9914 			fclose (fp);
9915 			return (GMT_NOERROR);
9916 		}
9917 
9918 		if (!theme) {	/* Must check validity and version */
9919 			if (rec != 2) { /* Nothing */ }
9920 			else if (strlen (line) < 7 || (ver = strtol (&line[6], NULL, 10)) < 5 )
9921 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your %s file (%s) may not be GMT %d compatible\n", GMT_SETTINGS_FILE, file, gmt_version_major);
9922 			else if (!strncmp (&line[6], "5.0.0", 5))
9923 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your %s file (%s) is of version 5.0.0 and may need to be updated. Use \"gmtset -G%s\"\n", GMT_SETTINGS_FILE, file, file);
9924 		}
9925 		if (line[0] == '#') continue;	/* Skip comments */
9926 		if (line[0] == '\0') continue;	/* Skip Blank lines */
9927 
9928 		keyword[0] = value[0] = '\0';	/* Initialize */
9929 		sscanf (line, "%s = %[^\n]", keyword, value);
9930 
9931 		if (gmtlib_setparameter (GMT, keyword, value, true))
9932 			error++;
9933 		else {
9934 			int case_val = gmt_hash_lookup (GMT, keyword, keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
9935 			if (case_val >= 0) GMT_keyword_updated[case_val] = true;		/* Leave a record that this keyword is no longer a default one */
9936 		}
9937 	}
9938 	fclose (fp);
9939 
9940 	error += gmtinit_update_theme (GMT);	/* If we got a GMT_THEME setting, take delayed action now */
9941 
9942 	gmtinit_verify_encodings (GMT);
9943 
9944 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "%d GMT Defaults conversion errors in file %s!\n", error, file);
9945 
9946 	return (GMT_NOERROR);
9947 }
9948 
gmtinit_update_theme(struct GMT_CTRL * GMT)9949 GMT_LOCAL int gmtinit_update_theme (struct GMT_CTRL *GMT) {
9950 	int error = GMT_NOERROR;
9951 	char theme_file[PATH_MAX] = {""};
9952 
9953 	if (!GMT->current.setting.update_theme) return GMT_NOERROR;	/* Nothing to do */
9954 	if (!strcmp (GMT->current.setting.theme, "off")) return GMT_NOERROR;	/* Nothing to do */
9955 
9956 	/* Got a GMT_THEME setting, take delayed action now */
9957 	GMT->current.setting.update_theme = false;
9958 	if (!strcmp (GMT->current.setting.theme, "classic"))	/* Just reload the classic defaults */
9959 		gmtinit_conf_classic (GMT);
9960 	else if (!strcmp (GMT->current.setting.theme, "modern"))
9961 		gmtinit_conf_modern (GMT);
9962 	else if (gmt_getsharepath (GMT, "themes", GMT->current.setting.theme, ".conf", theme_file, R_OK)) {	/* Load given theme */
9963 		error = gmtinit_loaddefaults (GMT, theme_file, true);
9964 	}
9965 	else
9966 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Theme %s file not found - ignored\n", GMT->current.setting.theme);
9967 	return (error);
9968 }
9969 
gmtinit_update_keys(struct GMT_CTRL * GMT,bool arg)9970 void gmtinit_update_keys (struct GMT_CTRL *GMT, bool arg) {
9971 	gmt_M_unused(GMT);
9972 	if (arg == false)
9973 		gmt_M_memset (GMT_keyword_updated, GMT_N_KEYS, bool);
9974 	else {
9975 		for (unsigned int k = 0; k < GMT_N_KEYS; k++)
9976 			GMT_keyword_updated[k] = true;
9977 	}
9978 }
9979 
9980 /*! . */
gmt_setdefaults(struct GMT_CTRL * GMT,struct GMT_OPTION * options)9981 unsigned int gmt_setdefaults (struct GMT_CTRL *GMT, struct GMT_OPTION *options) {
9982 	unsigned int p, n_errors = 0;
9983 	struct GMT_OPTION *opt = NULL;
9984 	char *param = NULL;
9985 
9986 	for (opt = options; opt; opt = opt->next) {
9987 		if (!(opt->option == '<' || opt->option == '#') || !opt->arg) continue;		/* Skip other and empty options */
9988 		if (!strcmp (opt->arg, "=")) continue;			/* User forgot and gave parameter = value (3 words) */
9989 		if (opt->arg[0] != '=' && strchr (opt->arg, '=') && (!param || !strstr (param, "FONT_"))) {	/* User forgot and gave parameter=value (1 word); OK except for FONTS */
9990 			p = 0;
9991 			while (opt->arg[p] && opt->arg[p] != '=') p++;
9992 			opt->arg[p] = '\0';	/* Temporarily remove the equal sign */
9993 			n_errors += gmtlib_setparameter (GMT, opt->arg, &opt->arg[p+1], true);
9994 			opt->arg[p] = '=';	/* Restore the equal sign */
9995 		}
9996 		else if (!param)			/* Keep parameter name */
9997 			param = opt->arg;
9998 		else {					/* This must be value */
9999 			n_errors += gmtlib_setparameter (GMT, param, opt->arg, true);
10000 			param = NULL;	/* Get ready for next parameter */
10001 		}
10002 	}
10003 
10004 	n_errors += gmtinit_update_theme (GMT);	/* If we got a GMT_THEME setting, take delayed action now */
10005 
10006 	if (param != NULL)	/* param should be NULL unless no value were added */
10007 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Last GMT Defaults parameter from command options had no value\n");
10008 
10009 	if (n_errors) GMT_Report (GMT->parent, GMT_MSG_ERROR, " %d GMT Defaults conversion errors from command options\n", n_errors);
10010 	return (n_errors);
10011 }
10012 
gmt_set_undefined_axes(struct GMT_CTRL * GMT,bool conf_update)10013 void gmt_set_undefined_axes (struct GMT_CTRL *GMT, bool conf_update) {
10014 	char axes[GMT_LEN32] = {""};
10015 	double az = (gmt_M_is_zero (GMT->common.p.z_rotation)) ? GMT->current.proj.z_project.view_azimuth : GMT->common.p.z_rotation;
10016 	if (strcmp (GMT->current.setting.map_frame_axes, "auto") || !GMT->current.map.frame.draw) return;
10017 
10018 	/* Determine suitable MAP_FRAME_AXES for plot */
10019 	if (GMT->current.proj.projection == GMT_POLAR) {	/* May need to switch what is south and north */
10020 		strcpy (axes, GMT->current.proj.flip ? "WrStZ" : "WrbNZ");
10021 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given polar projection flip = %d, set MAP_FRAME_AXES = %s\n", GMT->current.proj.flip, axes);
10022 	}
10023 	else if (GMT->current.proj.projection == GMT_GNOMONIC || GMT->current.proj.projection == GMT_GENPER)	/* Need to relax to all since hard to guess what works */
10024 		strcpy (axes, "WESNZ");
10025 	else if (!doubleAlmostEqual (az, 180.0)) {	/* Rotated, so must adjust */
10026 		unsigned int quadrant = urint (floor (az / 90.0)) + 1;
10027 		switch (quadrant) {
10028 			case 1: strcpy (axes, "lEbNZ"); break;
10029 			case 2: strcpy (axes, "lEStZ"); break;
10030 			case 3: strcpy (axes, "WrStZ"); break;
10031 			case 4: strcpy (axes, "WrbNZ"); break;
10032 		}
10033 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given view angle = %g, set MAP_FRAME_AXES = %s\n", az, axes);
10034 	}
10035 	else if (!strncmp (GMT->init.module_name, "psternary", 9U))	/* Make exception for ternary diagrams since S,E,W are the three sides of triangle */
10036 		strcpy (axes, "WEStZ");
10037 	else	/* Default modern mode setting */
10038 		strcpy (axes, "WrStZ");
10039 	gmtlib_setparameter (GMT, "MAP_FRAME_AXES", axes, conf_update);
10040 	(void)gmtinit_decode5_wesnz (GMT, axes, false);
10041 }
10042 
gmt_set_undefined_defaults(struct GMT_CTRL * GMT,double plot_dim,bool conf_update)10043 void gmt_set_undefined_defaults (struct GMT_CTRL *GMT, double plot_dim, bool conf_update) {
10044 	/* We must adjust all frame items with unspecified size according to plot dimension */
10045 	bool geo_frame = false;
10046 	bool auto_scale = false;
10047 	char style[GMT_PEN_LEN] = {""};
10048 	double fontsize, scale;
10049 	double const pt = 1.0/72.0;	/* points to inch */
10050 
10051 #if NO_THEMES
10052 	return;		/* Ignore all the modern theme stuff */
10053 #endif
10054 
10055 	/* Refuse to do this in gmtset */
10056 	if (!strcmp (GMT->init.module_name, "gmtset")) {fprintf (stderr, "Not doing it\n"); return; }
10057 
10058 	gmt_set_undefined_axes (GMT, conf_update);	/* Determine suitable MAP_FRAME_AXES for plot if still auto */
10059 
10060 	/* If a geographic map frame is fancy then we cannot have lrbt regardless of mode */
10061 
10062 	geo_frame = (gmt_M_is_geographic (GMT, GMT_IN) && (GMT->current.setting.map_frame_type == GMT_IS_FANCY || GMT->current.setting.map_frame_type == GMT_IS_ROUNDED));
10063 	if (geo_frame) {	/* Turn any l,r,b,t to w,e,s,n */
10064 		for (unsigned int k = 0; k < 4; k++)
10065 			if (GMT->current.map.frame.side[k] & GMT_AXIS_DRAW) GMT->current.map.frame.side[k] |= GMT_AXIS_BARB;
10066 	}
10067 
10068 	/* Use this equation for fontsize to compute the primary annotation font size given map max dimension */
10069 
10070 	if (gmt_M_is_zero (plot_dim)) {	/* Get nominal reference values */
10071 		fontsize = 10;
10072 		scale = 1.0;
10073 	}
10074 	else {	/* Use map dimensions to get scale */
10075 		double map_dim_cm = plot_dim * GMT->session.u2u[GMT_INCH][GMT_CM];
10076 		fontsize = (2.0/15.0) * (map_dim_cm - 10.0) + 9;	/* Gives result in points for plot dimension in cm */
10077 		scale = fontsize / 10.0;	/* scaling for offsets, pen widths and lengths normalized to the modern 10p size */
10078 	}
10079 
10080 	/* Only apply the automatic scaling to items NOT specifically set via a --PAR=value option */
10081 
10082 	if (gmt_M_is_dnan (GMT->current.setting.font_annot[GMT_PRIMARY].size)) {
10083 		GMT->current.setting.font_annot[GMT_PRIMARY].size = fontsize;
10084 		auto_scale = true;
10085 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_ANNOT_PRIMARY] = true;
10086 	}
10087 	if (gmt_M_is_dnan (GMT->current.setting.font_annot[GMT_SECONDARY].size)) {
10088 		GMT->current.setting.font_annot[GMT_SECONDARY].size = scale * 12.0;	/* Modern 12p vs 10p */
10089 		auto_scale = true;
10090 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_ANNOT_SECONDARY] = true;
10091 	}
10092 	if (gmt_M_is_dnan (GMT->current.setting.font_label.size)) {
10093 		GMT->current.setting.font_label.size = scale * 14.0;	/* Modern 14p vs 10p */
10094 		auto_scale = true;
10095 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_LABEL] = true;
10096 	}
10097 	if (gmt_M_is_dnan (GMT->current.setting.font_heading.size)) {
10098 		GMT->current.setting.font_heading.size = scale * 28.0;	/* Modern 28p vs 10p */
10099 		auto_scale = true;
10100 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_HEADING] = true;
10101 	}
10102 	if (gmt_M_is_dnan (GMT->current.setting.font_tag.size)) {
10103 		GMT->current.setting.font_tag.size = scale * 16.0;		/* Modern 16p vs 10p */
10104 		auto_scale = true;
10105 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_TAG] = true;
10106 	}
10107 	if (gmt_M_is_dnan (GMT->current.setting.font_title.size)) {
10108 		GMT->current.setting.font_title.size = scale * 22.0;	/* Modern 22p vs 10p */
10109 		auto_scale = true;
10110 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_TITLE] = true;
10111 	}
10112 	if (gmt_M_is_dnan (GMT->current.setting.font_subtitle.size)) {
10113 		GMT->current.setting.font_subtitle.size = scale * 18.0;	/* Modern 18p vs 10p */
10114 		auto_scale = true;
10115 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_SUBTITLE] = true;
10116 	}
10117 	if (gmt_M_is_dnan (GMT->current.setting.font_logo.size)) {
10118 		GMT->current.setting.font_logo.size = scale * 8.0;		/* Classic 8p vs 10p */
10119 		auto_scale = true;
10120 		if (conf_update) GMT_keyword_updated[GMTCASE_FONT_LOGO] = true;
10121 	}
10122 
10123 	/* Offsets */
10124 
10125 	if (gmt_M_is_dnan (GMT->current.setting.map_annot_offset[GMT_PRIMARY])) {
10126 		GMT->current.setting.map_annot_offset[GMT_PRIMARY] = 3 * pt * scale; /* 3p */
10127 		auto_scale = true;
10128 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_ANNOT_OFFSET_PRIMARY] = true;
10129 	}
10130 	if (gmt_M_is_dnan (GMT->current.setting.map_annot_offset[GMT_SECONDARY])) {
10131 		GMT->current.setting.map_annot_offset[GMT_SECONDARY] = 3 * pt * scale; /* 3p */
10132 		auto_scale = true;
10133 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_ANNOT_OFFSET_SECONDARY] = true;
10134 	}
10135 	if (gmt_M_is_dnan (GMT->current.setting.map_label_offset[GMT_X])) {
10136 		if (GMT->current.setting.map_label_mode[GMT_X] == GMT_LABEL_AXIS)
10137 			GMT->current.setting.map_label_offset[GMT_X] = 32 * pt;
10138 		else
10139 			GMT->current.setting.map_label_offset[GMT_X] = 6 * pt * scale;	/* 6p */
10140 		auto_scale = true;
10141 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_LABEL_OFFSET] = true;
10142 	}
10143 	if (gmt_M_is_dnan (GMT->current.setting.map_label_offset[GMT_Y])) {
10144 		if (GMT->current.setting.map_label_mode[GMT_Y] == GMT_LABEL_AXIS)
10145 			GMT->current.setting.map_label_offset[GMT_Y] = 32 * pt;
10146 		else
10147 			GMT->current.setting.map_label_offset[GMT_Y] = 6 * pt * scale;	/* 6p */
10148 		auto_scale = true;
10149 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_LABEL_OFFSET] = true;
10150 	}
10151 	if (gmt_M_is_dnan (GMT->current.setting.map_title_offset)) {
10152 		GMT->current.setting.map_title_offset = 12 * pt * scale;	/* 12p */
10153 		auto_scale = true;
10154 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_TITLE_OFFSET] = true;
10155 	}
10156 	if (gmt_M_is_dnan (GMT->current.setting.map_heading_offset)) {
10157 		GMT->current.setting.map_heading_offset = 16 * pt * scale;	/* 16p */
10158 		auto_scale = true;
10159 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_HEADING_OFFSET] = true;
10160 	}
10161 	if (gmt_M_is_dnan (GMT->current.setting.map_annot_min_spacing)) {
10162 		GMT->current.setting.map_annot_min_spacing = 28 * pt * scale; /* 28p */
10163 		auto_scale = true;
10164 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_ANNOT_MIN_SPACING] = true;
10165 		snprintf (GMT->current.setting.map_annot_min_spacing_txt, GMT_LEN16, "%.6gp", GMT->current.setting.map_annot_min_spacing / pt);
10166 	}
10167 
10168 	/* Must first do map_frame_width since it may be used below */
10169 
10170 	if (gmt_M_is_dnan (GMT->current.setting.map_frame_width)) {
10171 		GMT->current.setting.map_frame_width = 3 * pt * scale; /* 3p */
10172 		auto_scale = true;
10173 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_FRAME_WIDTH] = true;
10174 	}
10175 
10176 	/* Tick lengths */
10177 
10178 	if (gmt_M_is_dnan (GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER])) {
10179 		if (geo_frame && GMT->current.setting.run_mode == GMT_MODERN) {
10180 			/* Use 50% lengths but extend ticks by the width of the fancy frame */
10181 			GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = 2 * pt * scale + GMT->current.setting.map_frame_width;
10182 			GMT->current.setting.map_tick_length[GMT_TICK_UPPER]  = 1 * pt * scale + GMT->current.setting.map_frame_width;
10183 		}
10184 		else {
10185 			GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = 4 * pt * scale;	/* 4p */
10186 			GMT->current.setting.map_tick_length[GMT_TICK_UPPER]  = 2 * pt * scale;	/* 2p */
10187 		}
10188 		auto_scale = true;
10189 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_TICK_LENGTH_PRIMARY] = true;
10190 	}
10191 	if (gmt_M_is_dnan (GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER])) {
10192 		if (geo_frame && GMT->current.setting.run_mode == GMT_MODERN) {
10193 			/* Use 50% lengths but extend ticks by the width of the fancy frame */
10194 			GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = 6   * pt * scale + GMT->current.setting.map_frame_width;
10195 			GMT->current.setting.map_tick_length[GMT_TICK_LOWER]  = 1.5 * pt * scale + GMT->current.setting.map_frame_width;
10196 		}
10197 		else {
10198 			GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = 12 * pt * scale;	/* 12p */
10199 			GMT->current.setting.map_tick_length[GMT_TICK_LOWER]  = 3  * pt * scale;	/* 3p */
10200 		}
10201 		auto_scale = true;
10202 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_TICK_LENGTH_SECONDARY] = true;
10203 	}
10204 
10205 	if (gmt_M_is_dnan (GMT->current.setting.map_polar_cap[0])) {
10206 		/* Must estimate a suitable parallel for a polar cap given area */
10207 		double p_range = MIN (90.0 - GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI] + 90.0);	/* SHortest distance from far latitude to pole */
10208 		double f = 1.0, reach = MIN (5.0, 0.25 * p_range); /* Max 5 degrees from pole, but aim for 25% of that range */
10209 		if (reach < 1.0) reach *= 60.0, f *= 60.0;	/* Deal with sub-degree estimates and at least make them integer units */
10210 		if (reach < 1.0) reach *= 60.0, f *= 60.0;
10211 		reach = rint (reach) / f;	/* Integer degrees, minutes or seconds */
10212 		GMT->current.setting.map_polar_cap[0] = 90.0 - reach; /* Max 5 degrees from pole */
10213 		auto_scale = true;
10214 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_POLAR_CAP] = true;
10215 	}
10216 
10217 	/* Frame, tick and gridline pens */
10218 
10219 	if (gmt_M_is_dnan (GMT->current.setting.map_frame_pen.width)) {
10220 		GMT->current.setting.map_frame_pen.width = 1.5 * scale; /* 1.5p (thicker) */
10221 		strncpy (style, GMT->current.setting.map_frame_pen.style, GMT_PEN_LEN);
10222 		gmtlib_getpenstyle (GMT, style, &GMT->current.setting.map_frame_pen);
10223 		auto_scale = true;
10224 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_FRAME_PEN] = true;
10225 	}
10226 	if (gmt_M_is_dnan (GMT->current.setting.map_tick_pen[GMT_PRIMARY].width)) {
10227 		GMT->current.setting.map_tick_pen[GMT_PRIMARY].width = 0.5 * scale;	/* 0.5p (thinner) */
10228 		strncpy (style, GMT->current.setting.map_tick_pen[GMT_PRIMARY].style, GMT_PEN_LEN);
10229 		gmtlib_getpenstyle (GMT, style, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
10230 		auto_scale = true;
10231 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_TICK_PEN_PRIMARY] = true;
10232 	}
10233 	if (gmt_M_is_dnan (GMT->current.setting.map_tick_pen[GMT_SECONDARY].width)) {
10234 		GMT->current.setting.map_tick_pen[GMT_SECONDARY].width = 0.5 * scale;	/* 0.5p (thinner) */
10235 		strncpy (style, GMT->current.setting.map_tick_pen[GMT_SECONDARY].style, GMT_PEN_LEN);
10236 		gmtlib_getpenstyle (GMT, style, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
10237 		auto_scale = true;
10238 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_TICK_PEN_SECONDARY] = true;
10239 	}
10240 	if (gmt_M_is_dnan (GMT->current.setting.map_grid_pen[GMT_PRIMARY].width)) {
10241 		GMT->current.setting.map_grid_pen[GMT_PRIMARY].width = 0.25 * scale;	/* 0.25p (default) */
10242 		strncpy (style, GMT->current.setting.map_grid_pen[GMT_PRIMARY].style, GMT_PEN_LEN);
10243 		gmtlib_getpenstyle (GMT, style, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
10244 		auto_scale = true;
10245 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_GRID_PEN_PRIMARY] = true;
10246 	}
10247 	if (gmt_M_is_dnan (GMT->current.setting.map_grid_pen[GMT_SECONDARY].width)) {
10248 		GMT->current.setting.map_grid_pen[GMT_SECONDARY].width = 0.5 * scale;	/* 0.5p (thinner) */
10249 		strncpy (style, GMT->current.setting.map_grid_pen[GMT_SECONDARY].style, GMT_PEN_LEN);
10250 		gmtlib_getpenstyle (GMT, style, &GMT->current.setting.map_grid_pen[GMT_SECONDARY]);
10251 		auto_scale = true;
10252 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_GRID_PEN_SECONDARY] = true;
10253 	}
10254 
10255 	if (gmt_M_is_dnan (GMT->current.setting.map_vector_shape)) {
10256 		GMT->current.setting.map_vector_shape = 0.5;
10257 		auto_scale = true;
10258 		if (conf_update) GMT_keyword_updated[GMTCASE_MAP_VECTOR_SHAPE] = true;
10259 	}
10260 
10261 	if (auto_scale) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Computed automatic parameters using dimension scaling: %g\n", scale);
10262 }
10263 
gmtinit_parse_map_annot_oblique(struct GMT_CTRL * GMT,char * text)10264 GMT_LOCAL unsigned int gmtinit_parse_map_annot_oblique (struct GMT_CTRL *GMT, char *text) {
10265 	/* Break down a comma-separated list of keywords for MAP_ANNOT_OBLIQUE or
10266 	 * just return the integer if old-fashion bit-sum
10267 	 */
10268 	char *string = NULL, *token = NULL, *tofree = NULL;
10269 	unsigned int bits = 0, k, found;
10270 
10271 	if (isdigit (text[0])) return (atoi (text));	/* That was easy */
10272 
10273 	tofree = string = strdup (text);
10274 	while ((token = strsep (&string, ",")) != NULL) {
10275 		for (k = 0, found = UINT_MAX; found == UINT_MAX && k < N_MAP_ANNOT_OBLIQUE_ITEMS; k++)
10276 			if (!strcmp (token, map_annot_oblique_item[k])) found = k;
10277 		if (found != UINT_MAX)
10278 			bits += (found == 0) ? 0 : urint (pow (2.0, found-1));
10279 		else
10280 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtinit_parse_map_annot_oblique: Unrecognized flag name %s - ignored\n", token);
10281 	}
10282 	gmt_M_str_free (tofree);
10283 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtinit_parse_map_annot_oblique: Converted %s to %d\n", text, bits);
10284 
10285 	return (bits);
10286 }
10287 
gmtinit_print_map_annot_oblique(struct GMT_CTRL * GMT,unsigned int val)10288 GMT_LOCAL char * gmtinit_print_map_annot_oblique (struct GMT_CTRL *GMT, unsigned int val) {
10289 	/* Create text-equivalent using keywords for MAP_ANNOT_OBLIQUE */
10290 	char string[GMT_LEN128] = {""};
10291 	unsigned int bit, k, first = 1;
10292 
10293 	if ((val & 1) == 0) {	/* separate and anywhere are mutually exclusive */
10294 		strncpy (string, map_annot_oblique_item[0], GMT_LEN128-1);
10295 		first = 0;
10296 	}
10297 	for (k = 1; k < N_MAP_ANNOT_OBLIQUE_ITEMS; k++) {
10298 		bit = urint (pow (2.0, k-1));
10299 		if (val & bit) {	/* This one was set */
10300 			if (!first) strcat (string, ",");
10301 			strcat (string, map_annot_oblique_item[k]);
10302 			first = 0;
10303 		}
10304 	}
10305 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtinit_print_map_annot_oblique: Converted %d to %s\n", val, string);
10306 	return (strdup (string));
10307 }
10308 
10309 /*! . */
gmtlib_setparameter(struct GMT_CTRL * GMT,const char * keyword,char * value,bool core)10310 unsigned int gmtlib_setparameter (struct GMT_CTRL *GMT, const char *keyword, char *value, bool core) {
10311 	/* core is true if we are calling gmtlib_setparameter from gmtinit_loaddefaults, while it is
10312 	 * false when just called once, such as via --PAR=value on the comment line.  The reason is
10313 	 * that when GMT_THEME=theme is given we want to wait to address the theme change until all the items
10314 	 * in gmt.conf has been read, but must act right away if a single entry. */
10315 
10316 	unsigned int pos;
10317 	size_t len;
10318 	int i, ival, case_val, manual, limit;
10319 	bool error = false, tf_answer = false;
10320 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, lower_value[GMT_BUFSIZ] = {""};
10321 
10322 	double dval;
10323 
10324 	if (!value) return (1);		/* value argument missing */
10325 	strncpy (lower_value, value, GMT_BUFSIZ-1);	/* Get a lower case version */
10326 	gmt_str_tolower (lower_value);
10327 	len = strlen (value);
10328 
10329 	case_val = gmt_hash_lookup (GMT, keyword, keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10330 
10331 	switch (case_val) {
10332 		/* FORMAT GROUP */
10333 		case GMTCASE_INPUT_CLOCK_FORMAT:
10334 			gmt_M_compat_translate ("FORMAT_CLOCK_IN");
10335 			break;
10336 		case GMTCASE_FORMAT_CLOCK_IN:
10337 			strncpy (GMT->current.setting.format_clock_in, value, GMT_LEN64-1);
10338 			error = gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_in, &GMT->current.io.clock_input, 0);
10339 			break;
10340 		case GMTCASE_INPUT_DATE_FORMAT:
10341 			gmt_M_compat_translate ("FORMAT_DATE_IN");
10342 			break;
10343 		case GMTCASE_FORMAT_DATE_IN:
10344 			strncpy (GMT->current.setting.format_date_in, value, GMT_LEN64-1);
10345 			error = gmtlib_date_C_format (GMT, GMT->current.setting.format_date_in, &GMT->current.io.date_input, 0);
10346 			break;
10347 		case GMTCASE_OUTPUT_CLOCK_FORMAT:
10348 			gmt_M_compat_translate ("FORMAT_CLOCK_OUT");
10349 			break;
10350 		case GMTCASE_FORMAT_CLOCK_OUT:
10351 			strncpy (GMT->current.setting.format_clock_out, value, GMT_LEN64-1);
10352 			gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_out, &GMT->current.io.clock_output, 1);
10353 			break;
10354 		case GMTCASE_OUTPUT_DATE_FORMAT:
10355 			gmt_M_compat_translate ("FORMAT_DATE_OUT");
10356 			break;
10357 		case GMTCASE_FORMAT_DATE_OUT:
10358 			strncpy (GMT->current.setting.format_date_out, value, GMT_LEN64-1);
10359 			error = gmtlib_date_C_format (GMT, GMT->current.setting.format_date_out, &GMT->current.io.date_output, 1);
10360 			break;
10361 		case GMTCASE_OUTPUT_DEGREE_FORMAT:
10362 			gmt_M_compat_translate ("FORMAT_GEO_OUT");
10363 			break;
10364 		case GMTCASE_FORMAT_GEO_OUT:
10365 			strncpy (GMT->current.setting.format_geo_out, value, GMT_LEN64-1);
10366 			error = gmtlib_geo_C_format (GMT);	/* Can fail if FORMAT_FLOAT_OUT not yet set, but is repeated at the end of gmt_begin */
10367 			break;
10368 		case GMTCASE_PLOT_CLOCK_FORMAT:
10369 			gmt_M_compat_translate ("FORMAT_CLOCK_MAP");
10370 			break;
10371 		case GMTCASE_FORMAT_CLOCK_MAP:
10372 			strncpy (GMT->current.setting.format_clock_map, value, GMT_LEN64-1);
10373 			gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_map, &GMT->current.plot.calclock.clock, 2);
10374 			break;
10375 		case GMTCASE_PLOT_DATE_FORMAT:
10376 			gmt_M_compat_translate ("FORMAT_DATE_MAP");
10377 			break;
10378 		case GMTCASE_FORMAT_DATE_MAP:
10379 			strncpy (GMT->current.setting.format_date_map, value, GMT_LEN64-1);
10380 			error = gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
10381 			break;
10382 		case GMTCASE_PLOT_DEGREE_FORMAT:
10383 			gmt_M_compat_translate ("FORMAT_GEO_MAP");
10384 			break;
10385 		case GMTCASE_FORMAT_GEO_MAP:
10386 			strncpy (GMT->current.setting.format_geo_map, value, GMT_LEN64-1);
10387 			error = gmtlib_plot_C_format (GMT);	/* Update format statements */
10388 			break;
10389 		case GMTCASE_FORMAT_TIME_MAP:
10390 			error = gmtlib_setparameter (GMT, "FORMAT_TIME_PRIMARY_MAP", value, core) +
10391 			        gmtlib_setparameter (GMT, "FORMAT_TIME_SECONDARY_MAP", value, core);
10392 			break;
10393 		case GMTCASE_TIME_FORMAT_PRIMARY:
10394 			gmt_M_compat_translate ("FORMAT_TIME_PRIMARY_MAP");
10395 			break;
10396 		case GMTCASE_FORMAT_TIME_PRIMARY_MAP:
10397 			strncpy (GMT->current.setting.format_time[GMT_PRIMARY], value, GMT_LEN64-1);
10398 			break;
10399 		case GMTCASE_TIME_FORMAT_SECONDARY:
10400 			gmt_M_compat_translate ("FORMAT_TIME_SECONDARY_MAP");
10401 			break;
10402 		case GMTCASE_FORMAT_TIME_SECONDARY_MAP:
10403 			strncpy (GMT->current.setting.format_time[GMT_SECONDARY], value, GMT_LEN64-1);
10404 			break;
10405 		case GMTCASE_D_FORMAT:
10406 			gmt_M_compat_translate ("FORMAT_FLOAT_OUT");
10407 			break;
10408 		case GMTCASE_FORMAT_FLOAT_OUT:
10409 			gmtinit_parse_format_float_out (GMT, value);
10410 			break;
10411 		case GMTCASE_FORMAT_FLOAT_MAP:
10412 			strncpy (GMT->current.setting.format_float_map, value, GMT_LEN64-1);
10413 			break;
10414 		case GMTCASE_UNIX_TIME_FORMAT:
10415 			gmt_M_compat_translate ("FORMAT_TIME_STAMP");
10416 			break;
10417 		case GMTCASE_FORMAT_TIME_STAMP:
10418 			strncpy (GMT->current.setting.format_time_stamp, value, GMT_LEN256-1);
10419 			break;
10420 
10421 		/* FONT GROUP */
10422 
10423 		case GMTCASE_FONT:	/* Special to set all fonts */
10424 			error = gmtlib_setparameter (GMT, "FONT_ANNOT_PRIMARY", value, core) +
10425 				gmtlib_setparameter (GMT, "FONT_ANNOT_SECONDARY", value, core) +
10426 				gmtlib_setparameter (GMT, "FONT_SUBTITLE", value, core) +
10427 				gmtlib_setparameter (GMT, "FONT_TITLE", value, core) +
10428 				gmtlib_setparameter (GMT, "FONT_TAG", value, core) +
10429 				gmtlib_setparameter (GMT, "FONT_HEADING", value, core) +
10430 				gmtlib_setparameter (GMT, "FONT_LABEL", value, core);
10431 			/*      FONT_LOGO is purposely skipped */
10432 			break;
10433 		case GMTCASE_FONT_ANNOT:
10434 			error = gmtlib_setparameter (GMT, "FONT_ANNOT_PRIMARY", value, core) +
10435 			        gmtlib_setparameter (GMT, "FONT_ANNOT_SECONDARY", value, core);
10436 			break;
10437 		case GMTCASE_ANNOT_FONT_PRIMARY:
10438 			gmt_M_compat_translate ("FONT_ANNOT_PRIMARY");
10439 			break;
10440 		case GMTCASE_FONT_ANNOT_PRIMARY:
10441 			if (value[0] == '+') {
10442 				/* When + is prepended, scale fonts, offsets and ticklengths relative to FONT_ANNOT_PRIMARY (except LOGO font) */
10443 				double scale = GMT->current.setting.font_annot[GMT_PRIMARY].size;
10444 				if (gmt_getfont (GMT, &value[1], &GMT->current.setting.font_annot[GMT_PRIMARY])) error = true;
10445 				scale = GMT->current.setting.font_annot[GMT_PRIMARY].size / scale;
10446 				GMT->current.setting.font_annot[GMT_SECONDARY].size *= scale;
10447 				GMT->current.setting.font_label.size *= scale;
10448 				GMT->current.setting.font_heading.size *= scale;
10449 				GMT->current.setting.font_tag.size *= scale;
10450 				GMT->current.setting.font_subtitle.size *= scale;
10451 				GMT->current.setting.font_title.size *= scale;
10452 				GMT->current.setting.map_annot_offset[GMT_PRIMARY] *= scale;
10453 				GMT->current.setting.map_annot_offset[GMT_SECONDARY] *= scale;
10454 				GMT->current.setting.map_heading_offset *= scale;
10455 				GMT->current.setting.map_label_offset[GMT_X] *= scale;
10456 				GMT->current.setting.map_label_offset[GMT_Y] *= scale;
10457 				GMT->current.setting.map_title_offset *= scale;
10458 				GMT->current.setting.map_frame_width *= scale;
10459 				GMT->current.setting.map_tick_length[GMT_PRIMARY] *= scale;
10460 				GMT->current.setting.map_tick_length[GMT_SECONDARY] *= scale;
10461 				GMT->current.setting.map_tick_length[2] *= scale;
10462 				GMT->current.setting.map_tick_length[3] *= scale;
10463 				if (core) {		/* Need to update more than just FONT_ANNOT_PRIMARY */
10464 					int p = gmt_hash_lookup (GMT, "FONT_ANNOT_SECONDARY", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10465 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10466 					p = gmt_hash_lookup (GMT, "MAP_ANNOT_OFFSET_PRIMARY", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10467 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10468 					p = gmt_hash_lookup (GMT, "MAP_ANNOT_OFFSET_SECONDARY", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10469 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10470 					p = gmt_hash_lookup (GMT, "FONT_LABEL", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10471 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10472 					p = gmt_hash_lookup (GMT, "MAP_LABEL_OFFSET", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10473 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10474 					p = gmt_hash_lookup (GMT, "FONT_SUBTITLE", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10475 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10476 					p = gmt_hash_lookup (GMT, "FONT_TITLE", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10477 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10478 					p = gmt_hash_lookup (GMT, "MAP_TITLE_OFFSET", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10479 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10480 					p = gmt_hash_lookup (GMT, "MAP_TICK_LENGTH_PRIMARY", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10481 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10482 					p = gmt_hash_lookup (GMT, "MAP_TICK_LENGTH_SECONDARY", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10483 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10484 					p = gmt_hash_lookup (GMT, "MAP_FRAME_WIDTH", keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
10485 					if (p >= 0) GMT_keyword_updated[p] = true;		/* Leave a record that this keyword is no longer a default one */
10486 				}
10487 			}
10488 			else {
10489 				if (gmt_getfont (GMT, value, &GMT->current.setting.font_annot[GMT_PRIMARY])) error = true;
10490 			}
10491 			break;
10492 		case GMTCASE_ANNOT_FONT_SECONDARY:
10493 			gmt_M_compat_translate ("FONT_ANNOT_SECONDARY");
10494 			break;
10495 		case GMTCASE_FONT_ANNOT_SECONDARY:
10496 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_annot[GMT_SECONDARY])) error = true;
10497 			break;
10498 		case GMTCASE_FONT_HEADING:
10499 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_heading)) error = true;
10500 			break;
10501 		case GMTCASE_FONT_SUBTITLE:
10502 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_subtitle)) error = true;
10503 			break;
10504 		case GMTCASE_FONT_TITLE:
10505 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_title)) error = true;
10506 			break;
10507 		case GMTCASE_FONT_TAG:
10508 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_tag)) error = true;
10509 			break;
10510 		case GMTCASE_LABEL_FONT:
10511 			gmt_M_compat_translate ("FONT_LABEL");
10512 			break;
10513 		case GMTCASE_FONT_LABEL:
10514 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_label)) error = true;
10515 			break;
10516 		case GMTCASE_FONT_LOGO:
10517 			if (gmt_getfont (GMT, value, &GMT->current.setting.font_logo)) error = true;
10518 			break;
10519 
10520 		/* FONT GROUP ... obsolete options */
10521 
10522 		case GMTCASE_ANNOT_FONT_SIZE_PRIMARY:
10523 			if (gmt_M_compat_check (GMT, 4)) {
10524 				gmt_M_compat_change ("FONT_ANNOT_PRIMARY");
10525 				dval = gmt_convert_units (GMT, value, GMT_PT, GMT_PT);
10526 				if (dval > 0.0) {
10527 					GMT->current.setting.font_annot[GMT_PRIMARY].size = dval;
10528 					gmt_M_keyword_update (GMTCASE_FONT_ANNOT_PRIMARY);
10529 				}
10530 				else
10531 					error = true;
10532 			}
10533 			else	/* Not recognized so give error message */
10534 				error = gmtinit_badvalreport (GMT, keyword);
10535 			break;
10536 		case GMTCASE_ANNOT_FONT_SIZE_SECONDARY:
10537 			if (gmt_M_compat_check (GMT, 4)) {
10538 				gmt_M_compat_change ("FONT_ANNOT_SECONDARY");
10539 				dval = gmt_convert_units (GMT, value, GMT_PT, GMT_PT);
10540 				if (dval > 0.0) {
10541 					GMT->current.setting.font_annot[GMT_SECONDARY].size = dval;
10542 					gmt_M_keyword_update (GMTCASE_FONT_ANNOT_SECONDARY);
10543 				}
10544 				else
10545 					error = true;
10546 			}
10547 			else	/* Not recognized so give error message */
10548 				error = gmtinit_badvalreport (GMT, keyword);
10549 			break;
10550 		case GMTCASE_HEADER_FONT_SIZE:
10551 			if (gmt_M_compat_check (GMT, 4)) {
10552 				gmt_M_compat_change ("FONT_TITLE");
10553 				dval = gmt_convert_units (GMT, value, GMT_PT, GMT_PT);
10554 				if (dval > 0.0) {
10555 					GMT->current.setting.font_title.size = dval;
10556 					gmt_M_keyword_update (GMTCASE_FONT_TITLE);
10557 				}
10558 				else
10559 					error = true;
10560 			}
10561 			else	/* Not recognized so give error message */
10562 				error = gmtinit_badvalreport (GMT, keyword);
10563 			break;
10564 		case GMTCASE_LABEL_FONT_SIZE:
10565 			if (gmt_M_compat_check (GMT, 4)) {
10566 				gmt_M_compat_change ("FONT_LABEL");
10567 				dval = gmt_convert_units (GMT, value, GMT_PT, GMT_PT);
10568 				if (dval > 0.0) {
10569 					GMT->current.setting.font_label.size = dval;
10570 					gmt_M_keyword_update (GMTCASE_FONT_LABEL);
10571 				}
10572 				else
10573 					error = true;
10574 			}
10575 			else	/* Not recognized so give error message */
10576 				error = gmtinit_badvalreport (GMT, keyword);
10577 			break;
10578 
10579 		/* MAP GROUP */
10580 
10581 		case GMTCASE_ANNOT_OFFSET_PRIMARY:
10582 			gmt_M_compat_translate ("MAP_ANNOT_OFFSET_PRIMARY");
10583 			break;
10584 		case GMTCASE_MAP_ANNOT_OFFSET:
10585 			error = gmtlib_setparameter (GMT, "MAP_ANNOT_OFFSET_PRIMARY", value, core) +
10586 			        gmtlib_setparameter (GMT, "MAP_ANNOT_OFFSET_SECONDARY", value, core);
10587 			break;
10588 		case GMTCASE_MAP_ANNOT_OFFSET_PRIMARY:
10589 			GMT->current.setting.map_annot_offset[GMT_PRIMARY] = gmt_M_to_inch (GMT, value);
10590 			break;
10591 		case GMTCASE_ANNOT_OFFSET_SECONDARY:
10592 			gmt_M_compat_translate ("MAP_ANNOT_OFFSET_SECONDARY");
10593 			break;
10594 		case GMTCASE_MAP_ANNOT_OFFSET_SECONDARY:
10595 			GMT->current.setting.map_annot_offset[GMT_SECONDARY] = gmt_M_to_inch (GMT, value);
10596 			break;
10597 		case GMTCASE_OBLIQUE_ANNOTATION:
10598 			gmt_M_compat_translate ("MAP_ANNOT_OBLIQUE");
10599 			break;
10600 		case GMTCASE_MAP_ANNOT_OBLIQUE:
10601 			ival = gmtinit_parse_map_annot_oblique (GMT, value);
10602 			if (ival >= GMT_OBL_ANNOT_LON_X_LAT_Y && ival < GMT_OBL_ANNOT_FLAG_LIMIT)  {
10603 				GMT->current.setting.map_annot_oblique = ival;
10604 				GMT->current.setting.map_annot_oblique_set = true;
10605 			}
10606 			else
10607 				error = true;
10608 			break;
10609 		case GMTCASE_ANNOT_MIN_ANGLE:
10610 			gmt_M_compat_translate ("MAP_ANNOT_MIN_ANGLE");
10611 			break;
10612 		case GMTCASE_MAP_ANNOT_MIN_ANGLE:
10613 			dval = atof (value);
10614 			if (dval < 0.0)
10615 				error = true;
10616 			else
10617 				GMT->current.setting.map_annot_min_angle = dval;
10618 			break;
10619 		case GMTCASE_ANNOT_MIN_SPACING:
10620 			gmt_M_compat_translate ("MAP_ANNOT_MIN_SPACING");
10621 			break;
10622 		case GMTCASE_MAP_ANNOT_MIN_SPACING:
10623 			if (value[0] == '-')	/* Negative */
10624 				error = true;
10625 			else {
10626 				GMT->current.setting.map_annot_min_spacing = gmt_M_to_inch (GMT, value);
10627 				strncpy (GMT->current.setting.map_annot_min_spacing_txt, value, GMT_LEN16);
10628 			}
10629 			break;
10630 		case GMTCASE_Y_AXIS_TYPE:
10631 			if (gmt_M_compat_check (GMT, 4)) {
10632 				gmt_M_compat_change ("MAP_ANNOT_ORTHO");
10633 				if (!strcmp (lower_value, "ver_text")) {
10634 					strncpy (GMT->current.setting.map_annot_ortho, "", 5U);
10635 					gmt_M_keyword_update (GMTCASE_MAP_ANNOT_ORTHO);
10636 				}
10637 				else if (!strcmp (lower_value, "hor_text")) {
10638 					strncpy (GMT->current.setting.map_annot_ortho, "we", 5U);
10639 					gmt_M_keyword_update (GMTCASE_MAP_ANNOT_ORTHO);
10640 				}
10641 				else
10642 					error = true;
10643 			}
10644 			else	/* Not recognized so give error message */
10645 				error = gmtinit_badvalreport (GMT, keyword);
10646 			break;
10647 		case GMTCASE_MAP_ANNOT_ORTHO:
10648 			strncpy (GMT->current.setting.map_annot_ortho, lower_value, 5U);
10649 			break;
10650 		case GMTCASE_DEGREE_SYMBOL:
10651 			gmt_M_compat_translate ("MAP_DEGREE_SYMBOL");
10652 			break;
10653 		case GMTCASE_MAP_DEGREE_SYMBOL:
10654 			if (value[0] == '\0' || !strcmp (lower_value, "degree"))	/* Default */
10655 				GMT->current.setting.map_degree_symbol = gmt_degree;
10656 			else if (!strcmp (lower_value, "ring"))
10657 				GMT->current.setting.map_degree_symbol = gmt_ring;
10658 			else if (!strcmp (lower_value, "colon"))
10659 				GMT->current.setting.map_degree_symbol = gmt_colon;
10660 			else if (!strcmp (lower_value, "none"))
10661 				GMT->current.setting.map_degree_symbol = gmt_none;
10662 			else
10663 				error = true;
10664 			error += gmtlib_plot_C_format (GMT);	/* Update format statement since degree symbol may have changed */
10665 			break;
10666 		case GMTCASE_BASEMAP_AXES:
10667 			gmt_M_compat_translate ("MAP_FRAME_AXES");
10668 			break;
10669 		case GMTCASE_MAP_FRAME_AXES:
10670 			strncpy (GMT->current.setting.map_frame_axes, value, 5U);
10671 			error += (bool)gmtinit_decode5_wesnz (GMT, value, false);
10672 			break;
10673 
10674 		case GMTCASE_BASEMAP_FRAME_RGB:
10675 			gmt_M_compat_translate ("MAP_DEFAULT_PEN");
10676 			break;
10677 		case GMTCASE_MAP_DEFAULT_PEN:
10678 			i = (value[0] == '+') ? 1 : 0;	/* If plus is added, copy color to MAP_*_PEN settings */
10679 			error = gmt_getpen (GMT, &value[i], &GMT->current.setting.map_default_pen);
10680 			if (i == 1) {
10681 				gmt_M_rgb_copy (&GMT->current.setting.map_grid_pen[GMT_PRIMARY].rgb, &GMT->current.setting.map_default_pen.rgb);
10682 				gmt_M_rgb_copy (&GMT->current.setting.map_grid_pen[GMT_SECONDARY].rgb, &GMT->current.setting.map_default_pen.rgb);
10683 				gmt_M_rgb_copy (&GMT->current.setting.map_frame_pen.rgb  , &GMT->current.setting.map_default_pen.rgb);
10684 				gmt_M_rgb_copy (&GMT->current.setting.map_tick_pen[GMT_PRIMARY].rgb, &GMT->current.setting.map_default_pen.rgb);
10685 				gmt_M_rgb_copy (&GMT->current.setting.map_tick_pen[GMT_SECONDARY].rgb, &GMT->current.setting.map_default_pen.rgb);
10686 			}
10687 			break;
10688 		case GMTCASE_FRAME_PEN:
10689 			gmt_M_compat_translate ("MAP_FRAME_PEN");
10690 			break;
10691 		case GMTCASE_MAP_FRAME_PEN:
10692 			error = gmt_getpen (GMT, value, &GMT->current.setting.map_frame_pen);
10693 			break;
10694 		case GMTCASE_MAP_FRAME_PERCENT:
10695 			dval = atof (value);
10696 			if (dval <= 0.0 || dval > 100.0)
10697 				error = true;
10698 			else
10699 				GMT->current.setting.map_frame_percent = dval;
10700 			break;
10701 		case GMTCASE_BASEMAP_TYPE:
10702 			gmt_M_compat_translate ("MAP_FRAME_TYPE");
10703 			break;
10704 		case GMTCASE_MAP_FRAME_TYPE:
10705 			if (!strcmp (lower_value, "plain"))
10706 				GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
10707 			else if (!strncmp (lower_value, "graph", 5U)) {
10708 				char *c = NULL;
10709 				GMT->current.setting.map_frame_type = GMT_IS_GRAPH;
10710 				if ((c = strchr (lower_value, ','))) {	/* Also specified vector extension setting */
10711 					size_t last = strlen (lower_value) - 1;
10712 					char unit = lower_value[last];
10713 					if (unit == GMT_GRAPH_EXTENSION_UNIT) {	/* Want extension as percentage of axis length */
10714 						GMT->current.setting.map_graph_extension_unit = unit;
10715 						GMT->current.setting.map_graph_extension = atof (&c[1]);
10716 					}
10717 					else {	/* Read and convert value with unit to inches */
10718 						GMT->current.setting.map_graph_extension_unit = gmtlib_unit_lookup (GMT, unit, GMT->current.setting.proj_length_unit);	/* Will warn if c is not 0, 'c', 'i', 'p' */
10719 						GMT->current.setting.map_graph_extension = gmt_M_to_inch (GMT, &c[1]);
10720 					}
10721 				}
10722 				else {	/* Reset to default settings */
10723 					GMT->current.setting.map_graph_extension_unit = GMT_GRAPH_EXTENSION_UNIT;
10724 					GMT->current.setting.map_graph_extension = GMT_GRAPH_EXTENSION;
10725 				}
10726 			}
10727 			else if (!strcmp (lower_value, "fancy"))
10728 				GMT->current.setting.map_frame_type = GMT_IS_FANCY;
10729 			else if (!strcmp (lower_value, "fancy-rounded"))
10730 				GMT->current.setting.map_frame_type = GMT_IS_ROUNDED;
10731 			else if (!strcmp (lower_value, "fancy+"))	/* Deprecated */
10732 				GMT->current.setting.map_frame_type = GMT_IS_ROUNDED;
10733 			else if (!strcmp (lower_value, "inside"))
10734 				GMT->current.setting.map_frame_type = GMT_IS_INSIDE;
10735 			else
10736 				error = true;
10737 			break;
10738 		case GMTCASE_FRAME_WIDTH:
10739 			gmt_M_compat_translate ("MAP_FRAME_WIDTH");
10740 			break;
10741 		case GMTCASE_MAP_FRAME_WIDTH:
10742 			GMT->current.setting.map_frame_width = gmt_M_to_inch (GMT, value);
10743 			break;
10744 		case GMTCASE_GRID_CROSS_SIZE_PRIMARY:
10745 			gmt_M_compat_translate ("MAP_GRID_CROSS_SIZE_PRIMARY");
10746 			break;
10747 		case GMTCASE_MAP_GRID_CROSS_SIZE:
10748 			error = gmtlib_setparameter (GMT, "MAP_GRID_CROSS_SIZE_PRIMARY", value, core) +
10749 			        gmtlib_setparameter (GMT, "MAP_GRID_CROSS_SIZE_SECONDARY", value, core);
10750 			break;
10751 		case GMTCASE_MAP_GRID_CROSS_SIZE_PRIMARY:
10752 			dval = gmt_M_to_inch (GMT, value);
10753 			ival = (value[0] == '-') ? GMT_CROSS_ASYMM : ((value[0] == '+') ? GMT_CROSS_SYMM : GMT_CROSS_NORMAL);
10754 			GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] = dval;
10755 			GMT->current.setting.map_grid_cross_type[GMT_PRIMARY] = ival;
10756 			break;
10757 		case GMTCASE_GRID_CROSS_SIZE_SECONDARY:
10758 			gmt_M_compat_translate ("MAP_GRID_CROSS_SIZE_SECONDARY");
10759 			break;
10760 		case GMTCASE_MAP_GRID_CROSS_SIZE_SECONDARY:
10761 			dval = gmt_M_to_inch (GMT, value);
10762 			ival = (value[0] == '-') ? GMT_CROSS_ASYMM : ((value[0] == '+') ? GMT_CROSS_SYMM : GMT_CROSS_NORMAL);
10763 			GMT->current.setting.map_grid_cross_size[GMT_SECONDARY] = dval;
10764 			GMT->current.setting.map_grid_cross_type[GMT_SECONDARY] = ival;
10765 			break;
10766 		case GMTCASE_MAP_GRID_PEN:
10767 			error = gmtlib_setparameter (GMT, "MAP_GRID_PEN_PRIMARY", value, core) +
10768 			        gmtlib_setparameter (GMT, "MAP_GRID_PEN_SECONDARY", value, core);
10769 			break;
10770 		case GMTCASE_GRID_PEN_PRIMARY:
10771 			gmt_M_compat_translate ("MAP_GRID_PEN_PRIMARY");
10772 			break;
10773 		case GMTCASE_MAP_GRID_PEN_PRIMARY:
10774 			error = gmt_getpen (GMT, value, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
10775 			break;
10776 		case GMTCASE_GRID_PEN_SECONDARY:
10777 			gmt_M_compat_translate ("MAP_GRID_PEN_SECONDARY");
10778 			break;
10779 		case GMTCASE_MAP_GRID_PEN_SECONDARY:
10780 			error = gmt_getpen (GMT, value, &GMT->current.setting.map_grid_pen[GMT_SECONDARY]);
10781 			break;
10782 		case GMTCASE_MAP_HEADING_OFFSET:
10783 			GMT->current.setting.map_heading_offset = gmt_M_to_inch (GMT, value);
10784 			break;
10785 		case GMTCASE_MAP_LABEL_MODE:
10786 			if ((i = sscanf (value, "%[^/]/%s", txt_a, txt_b)) == 2) {	/* Separate settings for x and y */
10787 				if (strcmp (txt_a, "annot") && strcmp(txt_a,"axis") && strcmp(txt_b,"annot") && strcmp(txt_b,"axis"))
10788 					error = true;
10789 				else {
10790 					GMT->current.setting.map_label_mode[GMT_X] = (!strcmp (txt_a, "axis") ? GMT_LABEL_AXIS : GMT_LABEL_ANNOT);
10791 					GMT->current.setting.map_label_mode[GMT_Y] = (!strcmp (txt_b, "axis") ? GMT_LABEL_AXIS : GMT_LABEL_ANNOT);
10792 				}
10793 			}
10794 			else {
10795 				if (strcmp (value, "annot") && strcmp(value,"axis"))
10796 					error = true;
10797 				else
10798 					GMT->current.setting.map_label_mode[GMT_X] = GMT->current.setting.map_label_mode[GMT_Y] = (!strcmp (value, "axis") ? GMT_LABEL_AXIS : GMT_LABEL_ANNOT);
10799 			}
10800 			break;
10801 		case GMTCASE_LABEL_OFFSET:
10802 			gmt_M_compat_translate ("MAP_LABEL_OFFSET");
10803 			break;
10804 		case GMTCASE_MAP_LABEL_OFFSET:
10805 			if ((i = sscanf (value, "%[^/]/%s", txt_a, txt_b)) == 2) {	/* Separate settings for x and y */
10806 				GMT->current.setting.map_label_offset[GMT_X] = gmt_M_to_inch (GMT, txt_a);
10807 				GMT->current.setting.map_label_offset[GMT_Y] = gmt_M_to_inch (GMT, txt_b);
10808 			}
10809 			else
10810 				GMT->current.setting.map_label_offset[GMT_X] = GMT->current.setting.map_label_offset[GMT_Y] = gmt_M_to_inch (GMT, value);
10811 			break;
10812 		case GMTCASE_LINE_STEP:
10813 			gmt_M_compat_translate ("MAP_LINE_STEP");
10814 			break;
10815 		case GMTCASE_MAP_LINE_STEP:
10816 			if ((GMT->current.setting.map_line_step = gmt_M_to_inch (GMT, value)) <= 0.0) {
10817 				GMT->current.setting.map_line_step = 0.01;
10818 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s <= 0, reset to %g %s\n", keyword, GMT->current.setting.map_line_step, GMT->session.unit_name[GMT_INCH]);
10819 			}
10820 			break;
10821 		case GMTCASE_UNIX_TIME:
10822 			gmt_M_compat_translate ("MAP_LOGO");
10823 			break;
10824 		case GMTCASE_MAP_LOGO:
10825 			error = gmtinit_true_false_or_error (lower_value, &GMT->current.setting.map_logo);
10826 			break;
10827 		case GMTCASE_UNIX_TIME_POS:
10828 			gmt_M_compat_translate ("MAP_LOGO_POS");
10829 			break;
10830 		case GMTCASE_MAP_LOGO_POS:
10831 			i = sscanf (value, "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
10832 			if (i == 1) {
10833 				GMT->current.setting.map_logo_justify = gmt_just_decode (GMT, txt_a, PSL_NO_DEF);
10834 			} else if (i == 2) {
10835 				GMT->current.setting.map_logo_pos[GMT_X] = gmt_M_to_inch (GMT, txt_a);
10836 				GMT->current.setting.map_logo_pos[GMT_Y] = gmt_M_to_inch (GMT, txt_b);
10837 			}
10838 			else if (i == 3) {	/* New style, includes justification, introduced in GMT 4.3.0 */
10839 				GMT->current.setting.map_logo_justify = gmt_just_decode (GMT, txt_a, PSL_NO_DEF);
10840 				GMT->current.setting.map_logo_pos[GMT_X] = gmt_M_to_inch (GMT, txt_b);
10841 				GMT->current.setting.map_logo_pos[GMT_Y] = gmt_M_to_inch (GMT, txt_c);
10842 			}
10843 			else
10844 				error = true;
10845 			break;
10846 		case GMTCASE_X_ORIGIN:
10847 			gmt_M_compat_translate ("MAP_ORIGIN_X");
10848 			break;
10849 		case GMTCASE_MAP_ORIGIN_X:
10850 			GMT->current.setting.map_origin[GMT_X] = gmt_M_to_inch (GMT, value);
10851 			break;
10852 		case GMTCASE_Y_ORIGIN:
10853 			gmt_M_compat_translate ("MAP_ORIGIN_Y");
10854 			break;
10855 		case GMTCASE_MAP_ORIGIN_Y:
10856 			GMT->current.setting.map_origin[GMT_Y] = gmt_M_to_inch (GMT, value);
10857 			break;
10858 		case GMTCASE_POLAR_CAP:
10859 			gmt_M_compat_translate ("MAP_POLAR_CAP");
10860 			break;
10861 		case GMTCASE_MAP_POLAR_CAP:
10862 			if (!strcmp (lower_value, "none")) {	/* Means reset to no cap -> lat = 90, dlon = 0 */
10863 				GMT->current.setting.map_polar_cap[0] = 90.0;
10864 				GMT->current.setting.map_polar_cap[1] = 0.0;
10865 			}
10866 			else if (!strcmp (lower_value, "auto")) {	/* Means reset cap pending region */
10867 				GMT->current.setting.map_polar_cap[0] = GMT->session.d_NaN;
10868 				GMT->current.setting.map_polar_cap[1] = 90.0;
10869 			}
10870 			else {
10871 				double inc[2];
10872 				i = sscanf (lower_value, "%[^/]/%s", txt_a, txt_b);
10873 				if (i != 2)
10874 					error = true;
10875 				else
10876 					error = gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_a, GMT_IS_LAT, &GMT->current.setting.map_polar_cap[0]), txt_a);
10877 				if (gmt_getinc (GMT, txt_b, inc)) error = true;
10878 				GMT->current.setting.map_polar_cap[1] = inc[GMT_X];
10879 			}
10880 			break;
10881 		case GMTCASE_MAP_SCALE_HEIGHT:
10882 			dval = gmt_M_to_inch (GMT, value);
10883 			if (dval <= 0.0)
10884 				error = true;
10885 			else
10886 				GMT->current.setting.map_scale_height = dval;
10887 			break;
10888 		case GMTCASE_TICK_LENGTH:
10889 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
10890 				gmt_M_compat_change ("MAP_TICK_LENGTH");
10891 				GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = gmt_M_to_inch (GMT, value);
10892 				GMT->current.setting.map_tick_length[GMT_TICK_UPPER]  = 0.50 * GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
10893 				GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = 3.00 * GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
10894 				GMT->current.setting.map_tick_length[GMT_TICK_LOWER]  = 0.75 * GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
10895 				gmt_M_keyword_update (GMTCASE_MAP_TICK_LENGTH_PRIMARY);
10896 				gmt_M_keyword_update (GMTCASE_MAP_TICK_LENGTH_SECONDARY);
10897 			}
10898 			else	/* Not recognized so give error message */
10899 				error = gmtinit_badvalreport (GMT, keyword);
10900 			break;
10901 		case GMTCASE_MAP_TICK_LENGTH:
10902 			error = gmtlib_setparameter (GMT, "MAP_TICK_LENGTH_PRIMARY", value, core) +
10903 			        gmtlib_setparameter (GMT, "MAP_TICK_LENGTH_SECONDARY", value, core);
10904 			break;
10905 		case GMTCASE_MAP_TICK_LENGTH_PRIMARY:
10906 			i = sscanf (value, "%[^/]/%s", txt_a, txt_b);
10907 			GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] = gmt_M_to_inch (GMT, txt_a);
10908 			GMT->current.setting.map_tick_length[GMT_TICK_UPPER]  = (i > 1) ? gmt_M_to_inch (GMT, txt_b) : 0.50 * GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
10909 			break;
10910 		case GMTCASE_MAP_TICK_LENGTH_SECONDARY:
10911 			i = sscanf (value, "%[^/]/%s", txt_a, txt_b);
10912 			GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] = gmt_M_to_inch (GMT, txt_a);
10913 			GMT->current.setting.map_tick_length[GMT_TICK_LOWER]  = (i > 1) ? gmt_M_to_inch (GMT, txt_b) : 0.25 * GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER];
10914 			break;
10915 		case GMTCASE_TICK_PEN:
10916 			gmt_M_compat_translate ("MAP_TICK_PEN");
10917 			break;
10918 		case GMTCASE_MAP_TICK_PEN:
10919 			error = gmtlib_setparameter (GMT, "MAP_TICK_PEN_PRIMARY", value, core) +
10920 			        gmtlib_setparameter (GMT, "MAP_TICK_PEN_SECONDARY", value, core);
10921 			break;
10922 		case GMTCASE_MAP_TICK_PEN_PRIMARY:
10923 			error = gmt_getpen (GMT, value, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
10924 			break;
10925 		case GMTCASE_MAP_TICK_PEN_SECONDARY:
10926 			error = gmt_getpen (GMT, value, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
10927 			break;
10928 		case GMTCASE_HEADER_OFFSET:
10929 			gmt_M_compat_translate ("MAP_TITLE_OFFSET");
10930 			break;
10931 		case GMTCASE_MAP_TITLE_OFFSET:
10932 			GMT->current.setting.map_title_offset = gmt_M_to_inch (GMT, value);
10933 			break;
10934 		case GMTCASE_VECTOR_SHAPE:
10935 			gmt_M_compat_translate ("MAP_VECTOR_SHAPE");
10936 			break;
10937 		case GMTCASE_MAP_VECTOR_SHAPE:
10938 			dval = atof (value);
10939 			if (dval < -2.0 || dval > 2.0)
10940 				error = true;
10941 			else
10942 				GMT->current.setting.map_vector_shape = dval;
10943 			break;
10944 
10945 		/* COLOR GROUP */
10946 
10947 		case GMTCASE_COLOR_BACKGROUND:
10948 			error = gmt_getrgb (GMT, value, GMT->current.setting.color_patch[GMT_BGD]);
10949 			break;
10950 		case GMTCASE_COLOR_FOREGROUND:
10951 			error = gmt_getrgb (GMT, value, GMT->current.setting.color_patch[GMT_FGD]);
10952 			break;
10953 		case GMTCASE_COLOR_CPT:
10954 			if (strlen (value) >= GMT_LEN64) {
10955 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "COLOR_CPT = %s exceeds max name length of %d\n", value, GMT_LEN64);
10956 				error = true;
10957 			}
10958 			else
10959 				strncpy (GMT->current.setting.cpt, value, GMT_LEN64-1);
10960 			break;
10961 		case GMTCASE_COLOR_MODEL:
10962 			if (!strcmp (lower_value, "none"))
10963 				GMT->current.setting.color_model = GMT_RGB;
10964 			else if (!strcmp (lower_value, "rgb"))
10965 				GMT->current.setting.color_model = GMT_RGB + GMT_COLORINT;
10966 			else if (!strcmp (lower_value, "cmyk"))
10967 				GMT->current.setting.color_model = GMT_CMYK + GMT_COLORINT;
10968 			else if (!strcmp (lower_value, "hsv"))
10969 				GMT->current.setting.color_model = GMT_HSV + GMT_COLORINT;
10970 			else if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
10971 				if (!strcmp (lower_value, "+rgb")) {
10972 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "warning: COLOR_MODEL = %s is deprecated, use COLOR_MODEL = %s instead\n" GMT_COMPAT_INFO, value, &lower_value[1]);
10973 					GMT->current.setting.color_model = GMT_RGB + GMT_COLORINT;
10974 				}
10975 				else if (!strcmp (lower_value, "+cmyk")) {
10976 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "warning: COLOR_MODEL = %s is deprecated, use COLOR_MODEL = %s instead\n" GMT_COMPAT_INFO, value, &lower_value[1]);
10977 					GMT->current.setting.color_model = GMT_CMYK + GMT_COLORINT;
10978 				}
10979 				else if (!strcmp (lower_value, "+hsv")) {
10980 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "warning: COLOR_MODEL = %s is deprecated, use COLOR_MODEL = %s instead\n" GMT_COMPAT_INFO, value, &lower_value[1]);
10981 					GMT->current.setting.color_model = GMT_HSV + GMT_COLORINT;
10982 				}
10983 				else
10984 					error = true;
10985 			}
10986 			else
10987 				error = true;
10988 			break;
10989 		case GMTCASE_COLOR_NAN:
10990 			error = gmt_getrgb (GMT, value, GMT->current.setting.color_patch[GMT_NAN]);
10991 			break;
10992 		case GMTCASE_HSV_MIN_SATURATION:
10993 			gmt_M_compat_translate ("COLOR_HSV_MIN_S");
10994 			break;
10995 		case GMTCASE_COLOR_HSV_MIN_S:
10996 			dval = atof (value);
10997 			if (dval < 0.0 || dval > 1.0)
10998 				error = true;
10999 			else
11000 				GMT->current.setting.color_hsv_min_s = dval;
11001 			break;
11002 		case GMTCASE_HSV_MAX_SATURATION:
11003 			gmt_M_compat_translate ("COLOR_HSV_MAX_S");
11004 			break;
11005 		case GMTCASE_COLOR_HSV_MAX_S:
11006 			dval = atof (value);
11007 			if (dval < 0.0 || dval > 1.0)
11008 				error = true;
11009 			else
11010 				GMT->current.setting.color_hsv_max_s = dval;
11011 			break;
11012 		case GMTCASE_HSV_MIN_VALUE:
11013 			gmt_M_compat_translate ("COLOR_HSV_MIN_V");
11014 			break;
11015 		case GMTCASE_COLOR_HSV_MIN_V:
11016 			dval = atof (value);
11017 			if (dval < 0.0 || dval > 1.0)
11018 				error = true;
11019 			else
11020 				GMT->current.setting.color_hsv_min_v = dval;
11021 			break;
11022 		case GMTCASE_HSV_MAX_VALUE:
11023 			gmt_M_compat_translate ("COLOR_HSV_MAX_V");
11024 			break;
11025 		case GMTCASE_COLOR_HSV_MAX_V:
11026 			dval = atof (value);
11027 			if (dval < 0.0 || dval > 1.0)
11028 				error = true;
11029 			else
11030 				GMT->current.setting.color_hsv_max_v = dval;
11031 			break;
11032 		case GMTCASE_COLOR_SET:
11033 			if (strlen (value) >= GMT_LEN256) {	/* Tool long string */
11034 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "COLOR_SET = %s exceeds max name length of %d\n", value, GMT_LEN256);
11035 				error = true;
11036 			}
11037 			else if (!strncmp (value, "default", 7U))	/* Reset to GMT defaults */
11038 				strncpy (GMT->current.setting.color_set, GMT_DEFAULT_COLOR_SET, GMT_LEN256-1);
11039 			else if (strchr (value, ',')) {	/* Gave comma-separated list of colors, check that they are all valid */
11040 				char *word = NULL, *trail = NULL, *orig = strdup (value);
11041 				trail = orig;
11042 				while ((word = strsep (&trail, ",")) != NULL) {
11043 					if (*word != '\0') {	/* Skip empty strings */
11044 						if (!gmtlib_is_color (GMT, word)) {
11045 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "COLOR_SET list-item %s is not a valid color\n", word);
11046 							error = true;
11047 						}
11048 					}
11049 					else {
11050 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "COLOR_SET list %s has a missing entry\n", value);
11051 						error = true;
11052 					}
11053 				}
11054 				gmt_M_str_free (orig);
11055 			}
11056 			else {	/* Gave a single name, presumably a CPT */
11057 				if (value[0] != '@' && !gmt_is_cpt_master (GMT, value) && gmt_access (GMT, value, F_OK)) {	/* Check that a local CPT can be found */
11058 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "COLOR_SET selection of CPT %s cannot be found\n", value);
11059 					error = true;
11060 				}
11061 			}
11062 			if (!error)
11063 				strncpy (GMT->current.setting.color_set, value, GMT_LEN256-1);
11064 
11065 			break;
11066 
11067 		/* PS GROUP */
11068 
11069 		case GMTCASE_CHAR_ENCODING:
11070 			gmt_M_compat_translate ("PS_CHAR_ENCODING");
11071 			break;
11072 		case GMTCASE_PS_CHAR_ENCODING:
11073 			strncpy (GMT->current.setting.ps_encoding.name, value, GMT_LEN64-1);
11074 			gmtinit_load_encoding (GMT);
11075 			error = gmtlib_plot_C_format (GMT);	/* Since a chance in char set chances what is degree, ring, etc. */
11076 			break;
11077 		case GMTCASE_PS_COLOR:
11078 			gmt_M_compat_translate ("PS_COLOR_MODEL");
11079 			break;
11080 		case GMTCASE_PS_COLOR_MODEL:
11081 			if (!strcmp (lower_value, "rgb"))
11082 				GMT->current.setting.ps_color_mode = PSL_RGB;
11083 			else if (!strcmp (lower_value, "cmyk"))
11084 				GMT->current.setting.ps_color_mode = PSL_CMYK;
11085 			else if (!strcmp (lower_value, "hsv"))
11086 				GMT->current.setting.ps_color_mode = PSL_HSV;
11087 			else if (!strcmp (lower_value, "gray") || !strcmp (lower_value, "grey"))
11088 				GMT->current.setting.ps_color_mode = PSL_GRAY;
11089 			else
11090 				error = true;
11091 			break;
11092 		case GMTCASE_N_COPIES:
11093 		case GMTCASE_PS_COPIES:
11094 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
11095 				GMT_COMPAT_WARN;
11096 				ival = atoi (value);
11097 				if (ival > 0)
11098 					GMT->current.setting.ps_copies = ival;
11099 				else
11100 					error = true;
11101 			}
11102 			else	/* Not recognized so give error message */
11103 				error = gmtinit_badvalreport (GMT, keyword);
11104 			break;
11105 		case GMTCASE_DOTS_PR_INCH:
11106 		case GMTCASE_PS_DPI:
11107 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
11108 				GMT_COMPAT_WARN;
11109 			else	/* Not recognized so give error message */
11110 				error = gmtinit_badvalreport (GMT, keyword);
11111 			break;
11112 		case GMTCASE_PS_EPS:
11113 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
11114 				GMT_COMPAT_WARN;
11115 			else	/* Not recognized so give error message */
11116 				error = gmtinit_badvalreport (GMT, keyword);
11117 			break;
11118 		case GMTCASE_PS_IMAGE_COMPRESS:
11119 			if (!GMT->PSL) return (0);	/* Not using PSL in this session */
11120 			if (!strcmp (lower_value, "none"))
11121 				GMT->PSL->internal.compress = PSL_NONE;
11122 			else if (!strcmp (lower_value, "rle"))
11123 				GMT->PSL->internal.compress = PSL_RLE;
11124 			else if (!strcmp (lower_value, "lzw"))
11125 				GMT->PSL->internal.compress = PSL_LZW;
11126 			else if (!strncmp (lower_value, "deflate", 7)) {
11127 #ifdef HAVE_ZLIB
11128 				GMT->PSL->internal.compress = PSL_DEFLATE;
11129 				if ((sscanf (value + 7, " , %u", &GMT->PSL->internal.deflate_level) != 1)
11130 						|| GMT->PSL->internal.deflate_level > 9)
11131 					/* Compression level out of range or not provided, using default */
11132 					GMT->PSL->internal.deflate_level = 0;
11133 #else
11134 				/* Silently fall back to LZW compression when ZLIB not available */
11135 				GMT->PSL->internal.compress = PSL_LZW;
11136 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "PS_IMAGE_COMPRESS = DEFLATE not available, falling back to LZW.\n");
11137 #endif
11138 			}
11139 			else
11140 				error = true;
11141 			break;
11142 		case GMTCASE_PS_LINE_CAP:
11143 			if (!GMT->PSL) return (0);	/* Not using PSL in this session */
11144 			if (!strcmp (lower_value, "butt"))
11145 				GMT->PSL->internal.line_cap = PSL_BUTT_CAP;
11146 			else if (!strcmp (lower_value, "round"))
11147 				GMT->PSL->internal.line_cap = PSL_ROUND_CAP;
11148 			else if (!strcmp (lower_value, "square"))
11149 				GMT->PSL->internal.line_cap = PSL_SQUARE_CAP;
11150 			else
11151 				error = true;
11152 			break;
11153 		case GMTCASE_PS_LINE_JOIN:
11154 			if (!GMT->PSL) return (0);	/* Not using PSL in this session */
11155 			if (!strcmp (lower_value, "miter"))
11156 				GMT->PSL->internal.line_join = PSL_MITER_JOIN;
11157 			else if (!strcmp (lower_value, "round"))
11158 				GMT->PSL->internal.line_join = PSL_ROUND_JOIN;
11159 			else if (!strcmp (lower_value, "bevel"))
11160 				GMT->PSL->internal.line_join = PSL_BEVEL_JOIN;
11161 			else
11162 				error = true;
11163 			break;
11164 		case GMTCASE_PS_MITER_LIMIT:
11165 			if (!GMT->PSL) return (0);	/* Not using PSL in this session */
11166 			ival = atoi (value);
11167 			if (ival >= 0 && ival <= 180)
11168 				GMT->PSL->internal.miter_limit = ival;
11169 			else
11170 				error = true;
11171 			break;
11172 		case GMTCASE_PAGE_COLOR:
11173 			gmt_M_compat_translate ("PS_PAGE_COLOR");
11174 			break;
11175 		case GMTCASE_PS_PAGE_COLOR:
11176 			error = gmt_getrgb (GMT, value, GMT->current.setting.ps_page_rgb);
11177 			break;
11178 		case GMTCASE_PAGE_ORIENTATION:
11179 			gmt_M_compat_translate ("PS_PAGE_ORIENTATION");
11180 			break;
11181 		case GMTCASE_PS_PAGE_ORIENTATION:
11182 			if (!strcmp (lower_value, "landscape"))
11183 				GMT->current.setting.ps_orientation = PSL_LANDSCAPE;
11184 			else if (!strcmp (lower_value, "portrait"))
11185 				GMT->current.setting.ps_orientation = PSL_PORTRAIT;
11186 			else
11187 				error = true;
11188 			break;
11189 		case GMTCASE_PAPER_MEDIA:
11190 			gmt_M_compat_translate ("PS_MEDIA");
11191 			break;
11192 		case GMTCASE_PS_MEDIA:
11193 			if (GMT->current.setting.run_mode == GMT_MODERN && !strcmp (lower_value, "auto")) {
11194 				gmtinit_setautopagesize (GMT);
11195 				break;
11196 			}
11197 			manual = false;
11198 			len--;
11199 			if (lower_value[len] == '-') {	/* Manual Feed selected */
11200 				lower_value[len] = '\0';
11201 				manual = true;
11202 			}
11203 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
11204 				if (lower_value[len] == '+') {	/* EPS format selected */
11205 					lower_value[len] = '\0';
11206 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Production of EPS format is no longer supported, remove + after paper size\n");
11207 				}
11208 			}
11209 			if ((i = gmtinit_key_lookup (lower_value, GMT_media_name, GMT_N_MEDIA)) < GMT_N_MEDIA) {
11210 				/* Use the specified standard format */
11211 				GMT->current.setting.ps_media = i;
11212 				GMT->current.setting.ps_page_size[0] = GMT_media[i].width;
11213 				GMT->current.setting.ps_page_size[1] = GMT_media[i].height;
11214 			}
11215 			else if (gmtinit_load_user_media (GMT) &&
11216 				(pos = gmtinit_key_lookup (lower_value, GMT->session.user_media_name, GMT->session.n_user_media)) < GMT->session.n_user_media) {
11217 				/* Use a user-specified format */
11218 					GMT->current.setting.ps_media = pos + GMT_USER_MEDIA_OFFSET;
11219 					GMT->current.setting.ps_page_size[0] = GMT->session.user_media[pos].width;
11220 					GMT->current.setting.ps_page_size[1] = GMT->session.user_media[pos].height;
11221 				}
11222 			else {
11223 				/* A custom paper size in W x H points (or in inch/c if units are appended) */
11224 				if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
11225 					pos = (strncmp (lower_value, "custom_", 7U) ? 0 : 7);
11226 				}
11227 				else
11228 					pos = 0;
11229 				if (gmt_strtok (lower_value, "x", &pos, txt_a))	/* Returns width and update pos */
11230 					GMT->current.setting.ps_page_size[0] = gmt_convert_units (GMT, txt_a, GMT_PT, GMT_PT);
11231 				if (gmt_strtok (lower_value, "x", &pos, txt_b))	/* Returns height and update pos */
11232 					GMT->current.setting.ps_page_size[1] = gmt_convert_units (GMT, txt_b, GMT_PT, GMT_PT);
11233 				if (GMT->current.setting.ps_page_size[0] <= 0.0) error = true;
11234 				if (GMT->current.setting.ps_page_size[1] <= 0.0) error = true;
11235 				GMT->current.setting.ps_media = -GMT_USER_MEDIA_OFFSET;
11236 			}
11237 			if (!error && manual) GMT->current.setting.ps_page_size[0] = -GMT->current.setting.ps_page_size[0];
11238 			GMT->current.setting.ps_def_page_size[0] = GMT->current.setting.ps_page_size[0];
11239 			GMT->current.setting.ps_def_page_size[1] = GMT->current.setting.ps_page_size[1];
11240 			break;
11241 		case GMTCASE_GLOBAL_X_SCALE:
11242 			gmt_M_compat_translate ("PS_SCALE_X");
11243 			break;
11244 		case GMTCASE_PS_SCALE_X:
11245 			dval = atof (value);
11246 			if (dval > 0.0)
11247 				GMT->current.setting.ps_magnify[GMT_X] = dval;
11248 			else
11249 				error = true;
11250 			break;
11251 		case GMTCASE_GLOBAL_Y_SCALE:
11252 			gmt_M_compat_translate ("PS_SCALE_Y");
11253 			break;
11254 		case GMTCASE_PS_SCALE_Y:
11255 			dval = atof (value);
11256 			if (dval > 0.0)
11257 				GMT->current.setting.ps_magnify[GMT_Y] = dval;
11258 			else
11259 				error = true;
11260 			break;
11261 		case GMTCASE_TRANSPARENCY:
11262 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
11263 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Transparency is now part of pen and fill specifications.  TRANSPARENCY is ignored\n");
11264 			else
11265 				error = gmtinit_badvalreport (GMT, keyword);
11266 			break;
11267 		case GMTCASE_PS_TRANSPARENCY:
11268 			strncpy (GMT->current.setting.ps_transpmode, value, GMT_LEN16-1);
11269 			break;
11270 		case GMTCASE_PS_CONVERT:
11271 			strncpy (GMT->current.setting.ps_convert, value, GMT_LEN256-1);
11272 			break;
11273 		case GMTCASE_PS_VERBOSE:
11274 			gmt_M_compat_translate ("PS_COMMENTS");
11275 			break;
11276 		case GMTCASE_PS_COMMENTS:
11277 			if (!GMT->PSL) return (0);	/* Not using PSL in this session */
11278 			error = gmtinit_true_false_or_error (lower_value, &tf_answer);
11279 			GMT->PSL->internal.comments = (tf_answer) ? 1 : 0;
11280 			break;
11281 
11282 		/* IO GROUP */
11283 
11284 		case GMTCASE_FIELD_DELIMITER:
11285 			gmt_M_compat_translate ("IO_COL_SEPARATOR");
11286 			break;
11287 		case GMTCASE_IO_COL_SEPARATOR:
11288 			if (value[0] == '\0' || !strcmp (lower_value, "tab"))	/* DEFAULT */
11289 				strncpy (GMT->current.setting.io_col_separator, "\t", 8U);
11290 			else if (!strcmp (lower_value, "space"))
11291 				strncpy (GMT->current.setting.io_col_separator, " ", 8U);
11292 			else if (!strcmp (lower_value, "comma"))
11293 				strncpy (GMT->current.setting.io_col_separator, ",", 8U);
11294 			else if (!strcmp (lower_value, "none"))
11295 				GMT->current.setting.io_col_separator[0] = 0;
11296 #ifdef WIN32
11297 			/* Fix crazy MinGW obsession of replacing / with C:/somepath... [https://github.com/GenericMappingTools/gmt/issues/1054] */
11298 			else if (strlen (lower_value) > 1 && lower_value[1] == ':')
11299 				strncpy (GMT->current.setting.io_col_separator, "/", 8U);
11300 #endif
11301 			else
11302 				strncpy (GMT->current.setting.io_col_separator, value, 8U);
11303 			GMT->current.setting.io_col_separator[7] = 0;	/* Just a precaution */
11304 			break;
11305 		case GMTCASE_IO_FIRST_HEADER:
11306 			if (!strcmp (lower_value, "maybe"))
11307 				GMT->current.setting.io_first_header = GMT_FIRST_SEGHEADER_MAYBE;
11308 			else if (!strcmp (lower_value, "always"))
11309 				GMT->current.setting.io_first_header = GMT_FIRST_SEGHEADER_ALWAYS;
11310 			else if (!strcmp (lower_value, "never"))
11311 				GMT->current.setting.io_first_header = GMT_FIRST_SEGHEADER_NEVER;
11312 			else
11313 				error = true;
11314 			break;
11315 		case GMTCASE_GRIDFILE_FORMAT:
11316 			gmt_M_compat_translate ("IO_GRIDFILE_FORMAT");
11317 			break;
11318 		case GMTCASE_IO_GRIDFILE_FORMAT:
11319 			strncpy (GMT->current.setting.io_gridfile_format, value, GMT_LEN64-1);
11320 			break;
11321 		case GMTCASE_GRIDFILE_SHORTHAND:
11322 			gmt_M_compat_translate ("IO_GRIDFILE_SHORTHAND");
11323 			break;
11324 		case GMTCASE_IO_GRIDFILE_SHORTHAND:
11325 			error = gmtinit_true_false_or_error (lower_value, &GMT->current.setting.io_gridfile_shorthand);
11326 			break;
11327 		case GMTCASE_IO_HEADER:
11328 			error = gmtinit_true_false_or_error (lower_value, &GMT->current.setting.io_header[GMT_IN]);
11329 			GMT->current.setting.io_header[GMT_OUT] = GMT->current.setting.io_header[GMT_IN];
11330 			break;
11331 		case GMTCASE_IO_HEADER_MARKER:
11332 			if (len == 0) {	/* Blank gives default */
11333 				strcpy (GMT->current.setting.io_head_marker_in, DEF_HEADER_MARKERS);	/* Handle GMT and MATLAB headers and comments */
11334 				GMT->current.setting.io_head_marker_out = '#';
11335 			}
11336 			else {
11337 				char txt[2][GMT_LEN32];
11338 				if (strchr (value, ',')) {	/* Got separate header record markers for input,output */
11339 					sscanf (value, "%[^,],%s", txt[GMT_IN], txt[GMT_OUT]);
11340 				}
11341 				else {	/* Just duplicate */
11342 					strncpy (txt[GMT_IN], value, GMT_LEN32-1);	strncpy (txt[GMT_OUT], value, GMT_LEN32-1);
11343 				}
11344 				strcpy (GMT->current.setting.io_head_marker_in, txt[GMT_IN]);
11345 				GMT->current.setting.io_head_marker_out = txt[GMT_OUT][0];	/* Only pick the first character */
11346 			}
11347 			if (gmtinit_check_markers (GMT)) error = true;
11348 			break;
11349 		case GMTCASE_N_HEADER_RECS:
11350 			gmt_M_compat_translate ("IO_N_HEADER_RECS");
11351 			break;
11352 		case GMTCASE_IO_N_HEADER_RECS:
11353 			ival = atoi (value);
11354 			if (ival < 0)
11355 				error = true;
11356 			else
11357 				GMT->current.setting.io_n_header_items = ival;
11358 			break;
11359 		case GMTCASE_NAN_RECORDS:
11360 			gmt_M_compat_translate ("IO_NAN_RECORDS");
11361 			break;
11362 		case GMTCASE_IO_NAN_RECORDS:
11363 			if (!strcmp (lower_value, "pass"))
11364 				GMT->current.setting.io_nan_records = true;
11365 			else if (!strcmp (lower_value, "skip"))
11366 				GMT->current.setting.io_nan_records = false;
11367 			else
11368 				error = true;
11369 			break;
11370 		case GMTCASE_IO_NC4_CHUNK_SIZE:
11371 				if (*lower_value == 'a') /* auto */
11372 				GMT->current.setting.io_nc4_chunksize[0] = k_netcdf_io_chunked_auto;
11373 			else if (*lower_value == 'c') /* classic */
11374 				GMT->current.setting.io_nc4_chunksize[0] = k_netcdf_io_classic;
11375 			else if ((i = sscanf (value, "%" PRIuS " , %" PRIuS, /* Chunk size: vert,hor */
11376 			         &GMT->current.setting.io_nc4_chunksize[0], &GMT->current.setting.io_nc4_chunksize[1])) > 0) {
11377 				if (i == 1) /* Use chunk size for both horizontal and vertical dimension */
11378 					GMT->current.setting.io_nc4_chunksize[1] = GMT->current.setting.io_nc4_chunksize[0];
11379 				if (GMT->current.setting.io_nc4_chunksize[0] <= k_netcdf_io_chunked_auto ||
11380 				    GMT->current.setting.io_nc4_chunksize[1] <= k_netcdf_io_chunked_auto)
11381 					/* Chunk size too small */
11382 					error = true;
11383 			}
11384 			else
11385 				error = true;
11386 			break;
11387 		case GMTCASE_IO_NC4_DEFLATION_LEVEL:
11388 			if (!strcmp (lower_value, "false"))
11389 				ival = 0;
11390 			else
11391 				ival = atoi (value);
11392 			if (ival >= 0 && ival <= 9)
11393 				GMT->current.setting.io_nc4_deflation_level = ival;
11394 			else
11395 				error = true;
11396 			break;
11397 		case GMTCASE_XY_TOGGLE:
11398 			gmt_M_compat_translate ("IO_LONLAT_TOGGLE");
11399 			break;
11400 		case GMTCASE_IO_LONLAT_TOGGLE:
11401 			if (!gmtinit_true_false_or_error (lower_value, &GMT->current.setting.io_lonlat_toggle[GMT_IN]))
11402 				/* We got false/f/0 or true/t/1. Set outgoing setting to the same as the ingoing. */
11403 				GMT->current.setting.io_lonlat_toggle[GMT_OUT] = GMT->current.setting.io_lonlat_toggle[GMT_IN];
11404 			else if (!strcmp (lower_value, "in")) {
11405 				GMT->current.setting.io_lonlat_toggle[GMT_IN] = true;
11406 				GMT->current.setting.io_lonlat_toggle[GMT_OUT] = false;
11407 			}
11408 			else if (!strcmp (lower_value, "out")) {
11409 				GMT->current.setting.io_lonlat_toggle[GMT_IN] = false;
11410 				GMT->current.setting.io_lonlat_toggle[GMT_OUT] = true;
11411 			}
11412 			else
11413 				error = true;
11414 			break;
11415 
11416 		case GMTCASE_IO_SEGMENT_BINARY:
11417 			if (!strcmp (lower_value, "off"))
11418 				GMT->current.setting.n_bin_header_cols = 0;	/* 0 means do not consider nans to mean segment header */
11419 			else {	/* Read the minimum columns a binary record must have to be examined for segment headers */
11420 				ival = atoi (value);
11421 				if (ival < 0) {
11422 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while decoding IO_SEGMENT_BINARY: Cannot be negative.\n");
11423 					error = true;
11424 				}
11425 				else
11426 					GMT->current.setting.n_bin_header_cols = (uint64_t)ival;	/* Only do it for files with at least this many cols */
11427 			}
11428 			break;
11429 
11430 		case GMTCASE_IO_SEGMENT_MARKER:
11431 			if (len == 0)	/* Blank gives default */
11432 				GMT->current.setting.io_seg_marker[GMT_OUT] = GMT->current.setting.io_seg_marker[GMT_IN] = '>';
11433 			else {
11434 				int dir, k;
11435 				char txt[2][GMT_LEN256];
11436 				if (strchr (value, ',')) {	/* Got separate markers for input,output */
11437 					sscanf (value, "%[^,],%s", txt[GMT_IN], txt[GMT_OUT]);
11438 				}
11439 				else {	/* Just duplicate */
11440 					strncpy (txt[GMT_IN], value, GMT_LEN256-1);	strncpy (txt[GMT_OUT], value, GMT_LEN256-1);
11441 				}
11442 				for (dir = 0; dir < 2; dir++) {
11443 					switch (txt[dir][0]) {
11444 						case 'B':
11445 							GMT->current.setting.io_blankline[dir] = true;
11446 							GMT->current.setting.io_seg_marker[dir] = 'B';
11447 							break;
11448 						case 'N':
11449 							GMT->current.setting.io_nanline[dir] = true;
11450 							GMT->current.setting.io_seg_marker[dir] = 'N';
11451 							break;
11452 						default:
11453 							k = (txt[dir][0] == '\\') ? 1 : 0;
11454 							GMT->current.setting.io_seg_marker[dir] = txt[dir][k];
11455 							break;
11456 					}
11457 				}
11458 			}
11459 			if (gmtinit_check_markers (GMT)) error = true;
11460 			break;
11461 
11462 		/* PROJ GROUP */
11463 
11464 		case GMTCASE_PROJ_AUX_LATITUDE:
11465 			if (!strncmp (lower_value, "none", 4U)) /* Use lat as is [backwards compatibility] */
11466 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_NONE;
11467 			else if (!strncmp (lower_value, "geodetic", 8U)) /* Use lat as is */
11468 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_NONE;
11469 			else if (!strncmp (lower_value, "authalic", 8U)) /* Authalic latitude */
11470 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2A;
11471 			else if (!strncmp (lower_value, "conformal", 9U)) /* Conformal latitude */
11472 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2C;
11473 			else if (!strncmp (lower_value, "geocentric", 10U)) /* Geocentric latitude */
11474 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2O;
11475 			else if (!strncmp (lower_value, "meridional", 10U)) /* Meridional latitude */
11476 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2M;
11477 			else if (!strncmp (lower_value, "parametric", 10U)) /* Parametric latitude */
11478 				GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2P;
11479 			else
11480 				error = true;
11481 			gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid */
11482 			break;
11483 
11484 		case GMTCASE_ELLIPSOID:
11485 			gmt_M_compat_translate ("PROJ_ELLIPSOID");
11486 			break;
11487 		case GMTCASE_PROJ_ELLIPSOID:
11488 			ival = gmt_get_ellipsoid (GMT, value);
11489 			if (ival < 0)
11490 				error = true;
11491 			else
11492 				GMT->current.setting.proj_ellipsoid = ival;
11493 			gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid */
11494 			break;
11495 		case GMTCASE_PROJ_DATUM:	/* Not implemented yet */
11496 			break;
11497 		case GMTCASE_PROJ_GEODESIC:
11498 			if (!strncmp (lower_value, "vincenty", 8U)) /* Same as exact*/
11499 				GMT->current.setting.proj_geodesic = GMT_GEODESIC_VINCENTY;
11500 			else if (!strncmp (lower_value, "andoyer", 7U)) /* Andoyer approximation */
11501 				GMT->current.setting.proj_geodesic = GMT_GEODESIC_ANDOYER;
11502 			else if (!strncmp (lower_value, "rudoe", 5U)) /* Volumetric radius R_3 */
11503 				GMT->current.setting.proj_geodesic = GMT_GEODESIC_RUDOE;
11504 			else
11505 				error = true;
11506 			gmtlib_init_geodesic (GMT);	/* Set function pointer depending on the geodesic selected */
11507 			break;
11508 
11509 		case GMTCASE_MEASURE_UNIT:
11510 			gmt_M_compat_translate ("PROJ_LENGTH_UNIT");
11511 			break;
11512 		case GMTCASE_PROJ_LENGTH_UNIT:
11513 			if (gmt_set_length_unit (GMT, lower_value[0]) == GMT_NOTSET)
11514 					error = true;
11515 			break;
11516 		case GMTCASE_PROJ_MEAN_RADIUS:
11517 			if (!strncmp (lower_value, "mean", 4U)) /* Mean radius R_1 */
11518 				GMT->current.setting.proj_mean_radius = GMT_RADIUS_MEAN;
11519 			else if (!strncmp (lower_value, "authalic", 8U)) /* Authalic radius R_2 */
11520 				GMT->current.setting.proj_mean_radius = GMT_RADIUS_AUTHALIC;
11521 			else if (!strncmp (lower_value, "volumetric", 10U)) /* Volumetric radius R_3 */
11522 				GMT->current.setting.proj_mean_radius = GMT_RADIUS_VOLUMETRIC;
11523 			else if (!strncmp (lower_value, "meridional", 10U)) /* Meridional radius */
11524 				GMT->current.setting.proj_mean_radius = GMT_RADIUS_MERIDIONAL;
11525 			else if (!strncmp (lower_value, "quadratic", 9U)) /* Quadratic radius */
11526 				GMT->current.setting.proj_mean_radius = GMT_RADIUS_QUADRATIC;
11527 			else
11528 				error = true;
11529 			gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid */
11530 			break;
11531 
11532 		case GMTCASE_MAP_SCALE_FACTOR:
11533 			gmt_M_compat_translate ("PROJ_SCALE_FACTOR");
11534 			break;
11535 		case GMTCASE_PROJ_SCALE_FACTOR:
11536 			if (!strncmp (value, "def", 3U)) /* Default scale for chosen projection */
11537 				GMT->current.setting.proj_scale_factor = -1.0;
11538 			else {
11539 				dval = atof (value);
11540 				if (dval <= 0.0)
11541 					error = true;
11542 				else
11543 					GMT->current.setting.proj_scale_factor = dval;
11544 			}
11545 			break;
11546 
11547 		/* GMT GROUP */
11548 
11549 		case GMTCASE_GMT_COMPATIBILITY:
11550 			ival = (int)atof (value);
11551 			limit = (GMT->current.setting.run_mode == GMT_CLASSIC) ? 4 : 6;
11552 			if (ival < limit) {
11553 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT_COMPATIBILITY: Expects values from %d to %d; reset to %d.\n", limit, GMT_MAJOR_VERSION, limit);
11554 				GMT->current.setting.compatibility = 4;
11555 			}
11556 			else if (ival > GMT_MAJOR_VERSION) {
11557 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT_COMPATIBILITY: Expects values from %d to %d; reset to %d.\n", limit, GMT_MAJOR_VERSION, GMT_MAJOR_VERSION);
11558 				GMT->current.setting.compatibility = GMT_MAJOR_VERSION;
11559 			}
11560 			else
11561 				GMT->current.setting.compatibility = ival;
11562 			break;
11563 
11564 		case GMTCASE_GMT_AUTO_DOWNLOAD:
11565 			/* Deprecated as of 6.2: we only use GMT_DATA_UPDATE_INTERVAL to control this feature now, but silently process for backwards compatibility */
11566 			if (!strncmp (lower_value, "on", 3))
11567 				GMT->current.setting.auto_download = GMT_YES_DOWNLOAD;
11568 			else if (!strncmp (lower_value, "off", 3))
11569 				GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
11570 			else {
11571 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT_AUTO_DOWNLOAD [deprecated]: Expects either on or off - set to off\n");
11572 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "See GMT_DATA_UPDATE_INTERVAL instead for controlling down-load frequency\n");
11573 				GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
11574 			}
11575 			break;
11576 
11577 		case GMTCASE_GMT_DATA_URL:	/* Deprecated in 6.0.0 */
11578 		case GMTCASE_GMT_DATA_SERVER:	/* The default is set by cmake, see ConfigDefault.cmake */
11579 			if (*value) {
11580 				if (GMT->session.DATASERVER) {
11581 					if ((strcmp (GMT->session.DATASERVER, value) == 0))
11582 						break; /* stop here if string in place is equal */
11583 					gmt_M_str_free (GMT->session.DATASERVER);
11584 				}
11585 				/* Set session DATASERVER dir */
11586 				GMT->session.DATASERVER = strdup (value);
11587 			}
11588 			break;
11589 
11590 		case GMTCASE_GMT_DATA_URL_LIMIT:	/* Deprecated in 6.0.0 */
11591 		case GMTCASE_GMT_DATA_SERVER_LIMIT:	/* The default is set by cmake, see ConfigDefault.cmake */
11592 			if (!strcmp (lower_value, "0") || !strncmp (lower_value, "unlim", 5U))
11593 				GMT->current.setting.url_size_limit = 0;
11594 			else {
11595 				size_t f, k = len - 1;
11596 				if (k && (lower_value[k] == 'b' || lower_value[k] == 'B')) k--;
11597 				switch (lower_value[k]) {
11598 					case 'k':	case 'K':	f = 1024;		break;
11599 					case 'm':	case 'M':	f = 1024*1024;		break;
11600 					case 'g':	case 'G':	f = 1024*1024*1024;	break;
11601 					default:	f = 1;	break;
11602 				}
11603 				GMT->current.setting.url_size_limit = atoi (lower_value) * f;
11604 			}
11605 			break;
11606 
11607 		case GMTCASE_GMT_DATA_UPDATE_INTERVAL:
11608 			if (!strncmp (lower_value, "off", 3U) || !strncmp (lower_value, "infinity", 8U) || !strncmp (lower_value, "never", 5U)) {
11609 				/* Many ways to turn download off entirely */
11610 				GMT->current.setting.refresh_time = 0;
11611 				GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
11612 			}
11613 			else if (lower_value[0]) {
11614 				size_t f, k = len - 1;
11615 				switch (lower_value[k]) {
11616 					case 'd':	f = 1;	break;
11617 					case 'w':	f = 7;	break;
11618 					case 'o':	f = 30;	break;
11619 					default:	f = 1;	break;
11620 				}
11621 				GMT->current.setting.refresh_time = (unsigned int )(atoi (lower_value) * f);
11622 				if (GMT->current.setting.refresh_time == 0)	/* 0 means no auto download */
11623 					GMT->current.setting.auto_download = GMT_NO_DOWNLOAD;
11624 			}
11625 			else
11626 				error = true;
11627 			break;
11628 
11629 		case GMTCASE_GMT_CUSTOM_LIBS:
11630 			if (*value) {
11631 				if (GMT->session.CUSTOM_LIBS) {
11632 					if ((strcmp (GMT->session.CUSTOM_LIBS, value) == 0))
11633 						break; /* stop here if string in place is equal */
11634 					gmt_M_str_free (GMT->session.CUSTOM_LIBS);
11635 				}
11636 				/* Set Extension shared libraries */
11637 				GMT->session.CUSTOM_LIBS = strdup (value);
11638 			}
11639 			break;
11640 
11641 		case GMTCASE_GMT_EXPORT_TYPE:
11642 			if (!strncmp (lower_value, "double", 6U))
11643 				GMT->current.setting.export_type = GMT_DOUBLE;
11644 			else if (!strncmp (lower_value, "single", 6U))
11645 				GMT->current.setting.export_type = GMT_FLOAT;
11646 			else if (!strncmp (lower_value, "float", 5U))
11647 				GMT->current.setting.export_type = GMT_FLOAT;
11648 			else if (!strncmp (lower_value, "long", 4U))
11649 				GMT->current.setting.export_type = GMT_LONG;
11650 			else if (!strncmp (lower_value, "ulong", 5U))
11651 				GMT->current.setting.export_type = GMT_ULONG;
11652 			else if (!strncmp (lower_value, "int", 3U))
11653 				GMT->current.setting.export_type = GMT_INT;
11654 			else if (!strncmp (lower_value, "uint", 4U))
11655 				GMT->current.setting.export_type = GMT_UINT;
11656 			else if (!strncmp (lower_value, "short", 5U))
11657 				GMT->current.setting.export_type = GMT_SHORT;
11658 			else if (!strncmp (lower_value, "ushort", 6U))
11659 				GMT->current.setting.export_type = GMT_USHORT;
11660 			else if (!strncmp (lower_value, "char", 4U))
11661 				GMT->current.setting.export_type = GMT_CHAR;
11662 			else if (!strncmp (lower_value, "byte", 4U))
11663 				GMT->current.setting.export_type = GMT_UCHAR;
11664 			else if (!strncmp (lower_value, "uchar", 5U))
11665 				GMT->current.setting.export_type = GMT_UCHAR;
11666 			else
11667 				error = true;
11668 			break;
11669 
11670 		case GMTCASE_GMT_EXTRAPOLATE_VAL:
11671 			if (!strcmp (lower_value, "nan"))
11672 				GMT->current.setting.extrapolate_val[0] = GMT_EXTRAPOLATE_NONE;
11673 			else if (!strcmp (lower_value, "extrap"))
11674 				GMT->current.setting.extrapolate_val[0] = GMT_EXTRAPOLATE_SPLINE;
11675 			else if (!strncmp (lower_value, "extrapval",9)) {
11676 				GMT->current.setting.extrapolate_val[0] = GMT_EXTRAPOLATE_CONSTANT;
11677 				GMT->current.setting.extrapolate_val[1] = atof (&lower_value[10]);
11678 				if (lower_value[9] != ',') {
11679 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while decoding GMT_EXTRAPOLATE_VAL for 'val' value. Comma out of place.\n");
11680 					error = true;
11681 				}
11682 			}
11683 			else
11684 				error = true;
11685 			if (error) {
11686 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT_EXTRAPOLATE_VAL: resetting to 'extrapolated is NaN' to avoid later crash.\n");
11687 				GMT->current.setting.extrapolate_val[0] = GMT_EXTRAPOLATE_NONE;
11688 			}
11689 			break;
11690 
11691 		case GMTCASE_GMT_FFT:
11692 			if (!strncmp (lower_value, "auto", 4))
11693 				GMT->current.setting.fft = k_fft_auto;
11694 			else if (!strncmp (lower_value, "fftw", 4)) { /* complete name: fftw */
11695 				GMT->current.setting.fft = k_fft_fftw;
11696 #ifdef HAVE_FFTW3F
11697 				/* FFTW planner flags supported by the planner routines in FFTW
11698 				 * FFTW_ESTIMATE:   pick a (probably sub-optimal) plan quickly
11699 				 * FFTW_MEASURE:    find optimal plan by computing several FFTs and measuring their execution time
11700 				 * FFTW_PATIENT:    like FFTW_MEASURE, but considers a wider range of algorithms
11701 				 * FFTW_EXHAUSTIVE: like FFTW_PATIENT, but considers an even wider range of algorithms */
11702 				GMT->current.setting.fftw_plan = FFTW_ESTIMATE; /* default planner flag */
11703 				{
11704 					char *c;
11705 					if ((c = strchr (lower_value, ',')) != NULL) { /* Parse FFTW planner flags */
11706 						c += strspn(c, ", \t"); /* advance past ',' and whitespace */
11707 						if (!strncmp (c, "m", 1)) /* complete: measure */
11708 							GMT->current.setting.fftw_plan = FFTW_MEASURE;
11709 						else if (!strncmp (c, "p", 1)) /* complete: patient */
11710 							GMT->current.setting.fftw_plan = FFTW_PATIENT;
11711 						else if (!strncmp (c, "ex", 2)) /* complete: exhaustive */
11712 							GMT->current.setting.fftw_plan = FFTW_EXHAUSTIVE;
11713 					}
11714 				}
11715 #endif /* HAVE_FFTW3F */
11716 			}
11717 			else if (!strncmp (lower_value, "ac", 2))   /* complete name: accelerate */
11718 				GMT->current.setting.fft = k_fft_accelerate;
11719 			else if (!strncmp (lower_value, "kiss", 4)) /* complete name: kissfft */
11720 				GMT->current.setting.fft = k_fft_kiss;
11721 			else if (!strcmp (lower_value, "brenner"))
11722 				GMT->current.setting.fft = k_fft_brenner;
11723 			else
11724 				error = true;
11725 			break;
11726 		case GMTCASE_GMT_GRAPHICS_DPU:
11727 		 	if (value[0] == '\0') {
11728 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "No value given to GMT_GRAPHICS_DPU\n");
11729 				error = true;
11730 			}
11731 			else {
11732 				GMT->current.setting.graphics_dpu = atof (value);
11733 				GMT->current.setting.graphics_dpu_unit = (!strcmp ("ci", &value[strlen(value)-1])) ? value[strlen(value)-1] : GMT_IMAGE_DPU_UNIT;
11734 			}
11735 			break;
11736 		case GMTCASE_GMT_GRAPHICS_FORMAT:
11737 		 	if ((ival = gmt_get_graphics_id (GMT, value)) == GMT_NOTSET) {
11738 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized graphics format %s\n", value);
11739 				error = true;
11740 			}
11741 			else
11742 				GMT->current.setting.graphics_format = ival;
11743 			break;
11744 		case GMTCASE_HISTORY:
11745 			gmt_M_compat_translate ("GMT_HISTORY");
11746 			break;
11747 		case GMTCASE_GMT_HISTORY:
11748 			if (!strcmp (lower_value, "1") || !strcmp (lower_value, "true"))
11749 				GMT->current.setting.history = (GMT_HISTORY_READ | GMT_HISTORY_WRITE);
11750 			else if (!strcmp (lower_value, "readonly"))
11751 				GMT->current.setting.history = GMT_HISTORY_READ;
11752 			else if (!strcmp (lower_value, "0") || !strcmp (lower_value, "false"))
11753 				GMT->current.setting.history = GMT_HISTORY_OFF;
11754 			else
11755 				error = true;
11756 			break;
11757 		case GMTCASE_INTERPOLANT:
11758 			gmt_M_compat_translate ("GMT_INTERPOLANT");
11759 			break;
11760 		case GMTCASE_GMT_INTERPOLANT:
11761 			if (!strcmp (lower_value, "linear"))
11762 				GMT->current.setting.interpolant = GMT_SPLINE_LINEAR;
11763 			else if (!strcmp (lower_value, "akima"))
11764 				GMT->current.setting.interpolant = GMT_SPLINE_AKIMA;
11765 			else if (!strcmp (lower_value, "cubic"))
11766 				GMT->current.setting.interpolant = GMT_SPLINE_CUBIC;
11767 			else if (!strcmp (lower_value, "none"))
11768 				GMT->current.setting.interpolant = GMT_SPLINE_NONE;
11769 			else
11770 				error = true;
11771 			break;
11772 		case GMTCASE_GMT_LANGUAGE:
11773 			strncpy (GMT->current.setting.language, lower_value, GMT_LEN64-1);
11774 			gmtinit_get_language (GMT);	/* Load in names and abbreviations in chosen language */
11775 			break;
11776 		case GMTCASE_GMT_MAX_CORES:
11777 			if ((ival = atoi (value)) < 0) {
11778 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT_MAX_CORES must be 0 or positive\n");
11779 				error = true;
11780 			}
11781 			else
11782 				GMT->current.setting.max_cores = ival;
11783 			break;
11784 		case GMTCASE_GMT_TRIANGULATE:
11785 			if (!strcmp (lower_value, "watson"))
11786 				GMT->current.setting.triangulate = GMT_TRIANGLE_WATSON;
11787 			else if (!strcmp (lower_value, "shewchuk"))
11788 				GMT->current.setting.triangulate = GMT_TRIANGLE_SHEWCHUK;
11789 			else
11790 				error = true;
11791 			break;
11792 		case GMTCASE_GMT_THEME:
11793 			if (strlen (value) < GMT_LEN64) {
11794 				strncpy (GMT->current.setting.theme, value, GMT_LEN64-1);
11795 				GMT->current.setting.update_theme = (strcmp (GMT->current.setting.theme, "off") != 0);
11796 				error = gmtinit_update_theme (GMT);
11797 			}
11798 			else {
11799 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT_THEME must be less than %d characters\n", GMT_LEN64);
11800 				error = true;
11801 			}
11802 			break;
11803 		case GMTCASE_VERBOSE:
11804 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
11805 				gmt_M_compat_change ("GMT_VERBOSE");
11806 				ival = atoi (value) + 2;
11807 				if (ival >= GMT_MSG_QUIET && ival <= GMT_MSG_DEBUG) {
11808 					GMT->current.setting.verbose = ival;
11809 					gmt_M_keyword_update (GMTCASE_GMT_VERBOSE);
11810 				}
11811 				else
11812 					error = true;
11813 			}
11814 			else
11815 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
11816 			break;
11817 		case GMTCASE_GMT_VERBOSE:
11818 			error = gmtinit_parse_V_option (GMT, lower_value[0]);
11819 			break;
11820 
11821 		/* DIR GROUP */
11822 
11823 		case GMTCASE_DIR_CACHE:
11824 			if (*value) {
11825 				if (GMT->session.CACHEDIR) {
11826 					if ((strcmp (GMT->session.CACHEDIR, value) == 0))
11827 						break; /* stop here if string in place is equal */
11828 					gmt_M_str_free (GMT->session.CACHEDIR);
11829 				}
11830 				/* Set session CACHEDIR dir */
11831 				GMT->session.CACHEDIR = strdup (value);
11832 			}
11833 			break;
11834 		case GMTCASE_DIR_DATA:
11835 			if (*value) {
11836 				if (GMT->session.DATADIR) {
11837 					if ((strcmp (GMT->session.DATADIR, value) == 0))
11838 						break; /* stop here if string in place is equal */
11839 					gmt_M_str_free (GMT->session.DATADIR);
11840 				}
11841 				/* Set session DATADIR dir */
11842 				GMT->session.DATADIR = strdup (value);
11843 			}
11844 			break;
11845 		case GMTCASE_DIR_DCW:
11846 			if (*value) {
11847 				if (GMT->session.DCWDIR) {
11848 					if ((strcmp (GMT->session.DCWDIR, value) == 0))
11849 						break; /* stop here if string in place is equal */
11850 					gmt_M_str_free (GMT->session.DCWDIR);
11851 				}
11852 				/* Set session DCW dir */
11853 				GMT->session.DCWDIR = strdup (value);
11854 			}
11855 			break;
11856 		case GMTCASE_DIR_GSHHG:
11857 			if (*value) {
11858 				if (GMT->session.GSHHGDIR) {
11859 					if ((strcmp (GMT->session.GSHHGDIR, value) == 0))
11860 						break; /* stop here if string in place is equal */
11861 					gmt_M_str_free (GMT->session.GSHHGDIR);
11862 				}
11863 				/* Set session GSHHG dir */
11864 				GMT->session.GSHHGDIR = strdup (value);
11865 			}
11866 			break;
11867 
11868 		/* TIME GROUP */
11869 
11870 		case GMTCASE_TIME_EPOCH:
11871 			strncpy (GMT->current.setting.time_system.epoch, lower_value, GMT_LEN64-1);
11872 			(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
11873 			break;
11874 		case GMTCASE_TIME_IS_INTERVAL:
11875 			if (value[0] == '+' || value[0] == '-') {	/* OK, gave +<n>u or -<n>u, check for unit */
11876 				sscanf (&lower_value[1], "%d%c", &GMT->current.time.truncate.T.step, &GMT->current.time.truncate.T.unit);
11877 				switch (GMT->current.time.truncate.T.unit) {
11878 					case 'y': case 'o': case 'd': case 'h': case 'm': case 'c':
11879 						GMT->current.time.truncate.direction = (lower_value[0] == '+') ? 0 : 1;
11880 						break;
11881 					default:
11882 						error = true;
11883 						break;
11884 				}
11885 				if (GMT->current.time.truncate.T.step == 0) error = true;
11886 				GMT->current.setting.time_is_interval = true;
11887 			}
11888 			else if (!strcmp (lower_value, "off"))
11889 				GMT->current.setting.time_is_interval = false;
11890 			else
11891 				error = true;
11892 			break;
11893 		case GMTCASE_TIME_INTERVAL_FRACTION:
11894 			GMT->current.setting.time_interval_fraction = atof (value);
11895 			break;
11896 		case GMTCASE_TIME_LANGUAGE:
11897 			gmt_M_compat_translate ("GMT_LANGUAGE");
11898 			break;
11899 		case GMTCASE_WANT_LEAP_SECONDS:
11900 			gmt_M_compat_translate ("TIME_LEAP_SECONDS");
11901 			break;
11902 		case GMTCASE_TIME_LEAP_SECONDS:
11903 			error = gmtinit_true_false_or_error (lower_value, &GMT->current.setting.time_leap_seconds);
11904 			break;
11905 		case GMTCASE_TIME_REPORT:
11906 			if (!strncmp (lower_value, "none", 4U))
11907 				GMT->current.setting.timer_mode = GMT_NO_TIMER;
11908 			else if (!strncmp (lower_value, "clock", 5U))
11909 				GMT->current.setting.timer_mode = GMT_ABS_TIMER;
11910 			else if (!strncmp (lower_value, "elapsed", 7U))
11911 				GMT->current.setting.timer_mode = GMT_ELAPSED_TIMER;
11912 			else
11913 				error = true;
11914 			break;
11915 		case GMTCASE_TIME_UNIT:
11916 			GMT->current.setting.time_system.unit = lower_value[0];
11917 			(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
11918 			break;
11919 		case GMTCASE_TIME_SYSTEM:
11920 			error = gmt_get_time_system (GMT, lower_value, &GMT->current.setting.time_system);
11921 			(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
11922 			gmt_M_keyword_update (GMTCASE_TIME_UNIT);
11923 			gmt_M_keyword_update (GMTCASE_TIME_EPOCH);
11924 			break;
11925 		case GMTCASE_TIME_WEEK_START:
11926 			ival = gmtinit_key_lookup (value, GMT_weekdays, 7);
11927 			if (ival < 0 || ival >= 7) {
11928 				error = true;
11929 				GMT->current.setting.time_week_start = 0;
11930 			}
11931 			else
11932 				GMT->current.setting.time_week_start = ival;
11933 			break;
11934 		case GMTCASE_Y2K_OFFSET_YEAR:
11935 			gmt_M_compat_translate ("TIME_Y2K_OFFSET_YEAR");
11936 			break;
11937 		case GMTCASE_TIME_Y2K_OFFSET_YEAR:
11938 			if ((ival = atoi (value)) < 0) error = true;
11939 			else GMT->current.setting.time_Y2K_offset_year = ival;
11940 			/* Set the Y2K conversion parameters */
11941 			GMT->current.time.Y2K_fix.y2_cutoff = GMT->current.setting.time_Y2K_offset_year % 100;
11942 			GMT->current.time.Y2K_fix.y100 = GMT->current.setting.time_Y2K_offset_year - GMT->current.time.Y2K_fix.y2_cutoff;
11943 			GMT->current.time.Y2K_fix.y200 = GMT->current.time.Y2K_fix.y100 + 100;
11944 			break;
11945 
11946 		/* Obsolete */
11947 
11948 		case GMTCASE_PS_IMAGE_FORMAT:
11949 			/* Setting ignored, now always ASCII85 encoding */
11950 		case GMTCASE_X_AXIS_LENGTH:
11951 		case GMTCASE_Y_AXIS_LENGTH:
11952 			/* Setting ignored: x- and/or y scale are required inputs on -J option */
11953 		case GMTCASE_COLOR_IMAGE:
11954 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
11955 				GMT_COMPAT_WARN;
11956 			else	/* Not recognized so give error message */
11957 				error = gmtinit_badvalreport (GMT, keyword);
11958 			break;
11959 		case GMTCASE_DIR_TMP:
11960 		case GMTCASE_DIR_USER:
11961 			/* Setting ignored, were active previously in GMT5 but no longer */
11962 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Parameter %s (previously introduced in GMT5) is deprecated.\n" GMT_COMPAT_INFO, GMT_keyword[case_val]);
11963 			break;
11964 
11965 		default:
11966 			error = true;
11967 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized keyword %s.\n", keyword);
11968 			break;
11969 	}
11970 
11971 	/* Store possible unit.  For most cases these are irrelevant as no unit is expected */
11972 	if (case_val >= 0) {
11973 		if (len && strchr (GMT_DIM_UNITS, value[len-1])) GMT->current.setting.given_unit[case_val] = value[len-1];
11974 
11975 		if (error)
11976 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "%s given illegal value (%s)!\n", keyword, value);
11977 		else
11978 			gmt_M_keyword_update (case_val);
11979 	}
11980 	return ((error) ? 1 : 0);
11981 }
11982 
gmtinit_place_value(struct GMT_CTRL * GMT,double dim,int kase,char * value)11983 GMT_LOCAL void gmtinit_place_value (struct GMT_CTRL *GMT, double dim, int kase, char *value) {
11984 	if (gmt_M_is_dnan (dim))
11985 		snprintf (value, GMT_LEN256, "auto");
11986 	else
11987 		snprintf (value, GMT_LEN256, "%g%c", dim * gmt_M_def_scale(kase), gmt_M_def_unit(kase));
11988 }
11989 
11990 /*! . */
gmtlib_getparameter(struct GMT_CTRL * GMT,const char * keyword)11991 char *gmtlib_getparameter (struct GMT_CTRL *GMT, const char *keyword) {
11992 	/* value must hold at least GMT_BUFSIZ chars */
11993 	static char value[GMT_BUFSIZ] = {""}, txt[GMT_LEN8], *PRE[3] = {"", "-", "+"}, *string = NULL;
11994 	int case_val;
11995 	bool error = false;
11996 	char pm[2] = {'+', '-'}, *ft[2] = {"false", "true"};
11997 
11998 	gmt_M_memset (value, GMT_BUFSIZ, char);
11999 	if (!keyword) return (value);		/* keyword argument missing */
12000 
12001 	case_val = gmt_hash_lookup (GMT, keyword, keys_hashnode, GMT_N_KEYS, GMT_N_KEYS);
12002 
12003 	switch (case_val) {
12004 		/* FORMAT GROUP */
12005 		case GMTCASE_INPUT_CLOCK_FORMAT:
12006 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12007 				GMT_COMPAT_WARN;
12008 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12009 			/* Intentionally fall through */
12010 		case GMTCASE_FORMAT_CLOCK_IN:
12011 			strncpy (value, GMT->current.setting.format_clock_in,  GMT_BUFSIZ-1);
12012 			break;
12013 		case GMTCASE_INPUT_DATE_FORMAT:
12014 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12015 				GMT_COMPAT_WARN;
12016 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12017 			/* Intentionally fall through */
12018 		case GMTCASE_FORMAT_DATE_IN:
12019 			strncpy (value, GMT->current.setting.format_date_in, GMT_BUFSIZ-1);
12020 			break;
12021 		case GMTCASE_OUTPUT_CLOCK_FORMAT:
12022 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12023 				GMT_COMPAT_WARN;
12024 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12025 			/* Intentionally fall through */
12026 		case GMTCASE_FORMAT_CLOCK_OUT:
12027 			strncpy (value, GMT->current.setting.format_clock_out, GMT_BUFSIZ-1);
12028 			break;
12029 		case GMTCASE_OUTPUT_DATE_FORMAT:
12030 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12031 				GMT_COMPAT_WARN;
12032 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12033 			/* Intentionally fall through */
12034 		case GMTCASE_FORMAT_DATE_OUT:
12035 			strncpy (value, GMT->current.setting.format_date_out, GMT_BUFSIZ-1);
12036 			break;
12037 		case GMTCASE_OUTPUT_DEGREE_FORMAT:
12038 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12039 				GMT_COMPAT_WARN;
12040 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12041 			/* Intentionally fall through */
12042 		case GMTCASE_FORMAT_GEO_OUT:
12043 			strncpy (value, GMT->current.setting.format_geo_out, GMT_BUFSIZ-1);
12044 			break;
12045 		case GMTCASE_PLOT_CLOCK_FORMAT:
12046 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12047 				GMT_COMPAT_WARN;
12048 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12049 			/* Intentionally fall through */
12050 		case GMTCASE_FORMAT_CLOCK_MAP:
12051 			strncpy (value, GMT->current.setting.format_clock_map, GMT_BUFSIZ-1);
12052 			break;
12053 		case GMTCASE_PLOT_DATE_FORMAT:
12054 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12055 				GMT_COMPAT_WARN;
12056 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12057 			/* Intentionally fall through */
12058 		case GMTCASE_FORMAT_DATE_MAP:
12059 			strncpy (value, GMT->current.setting.format_date_map, GMT_BUFSIZ-1);
12060 			break;
12061 		case GMTCASE_PLOT_DEGREE_FORMAT:
12062 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12063 				GMT_COMPAT_WARN;
12064 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12065 			/* Intentionally fall through */
12066 		case GMTCASE_FORMAT_GEO_MAP:
12067 			strncpy (value, GMT->current.setting.format_geo_map, GMT_BUFSIZ-1);
12068 			break;
12069 		case GMTCASE_TIME_FORMAT_PRIMARY:
12070 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12071 				GMT_COMPAT_WARN;
12072 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12073 			/* Intentionally fall through */
12074 		case GMTCASE_FORMAT_TIME_PRIMARY_MAP:
12075 			strncpy (value, GMT->current.setting.format_time[GMT_PRIMARY], GMT_BUFSIZ-1);
12076 			break;
12077 		case GMTCASE_TIME_FORMAT_SECONDARY:
12078 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12079 				GMT_COMPAT_WARN;
12080 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12081 			/* Intentionally fall through */
12082 		case GMTCASE_FORMAT_TIME_SECONDARY_MAP:
12083 			strncpy (value, GMT->current.setting.format_time[GMT_SECONDARY], GMT_BUFSIZ-1);
12084 			break;
12085 		case GMTCASE_D_FORMAT:
12086 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12087 				GMT_COMPAT_WARN;
12088 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12089 			/* Intentionally fall through */
12090 		case GMTCASE_FORMAT_FLOAT_OUT:
12091 			strncpy (value, GMT->current.setting.format_float_out_orig, GMT_BUFSIZ-1);
12092 			break;
12093 		case GMTCASE_FORMAT_FLOAT_MAP:
12094 			strncpy (value, GMT->current.setting.format_float_map, GMT_BUFSIZ-1);
12095 			break;
12096 		case GMTCASE_UNIX_TIME_FORMAT:
12097 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12098 				GMT_COMPAT_WARN;
12099 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12100 			/* Intentionally fall through */
12101 		case GMTCASE_FORMAT_TIME_STAMP:
12102 			strncpy (value, GMT->current.setting.format_time_stamp, GMT_BUFSIZ-1);
12103 			break;
12104 
12105 		/* FONT GROUP */
12106 
12107 		case GMTCASE_ANNOT_FONT_PRIMARY:
12108 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12109 				GMT_COMPAT_WARN;
12110 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12111 			/* Intentionally fall through */
12112 		case GMTCASE_FONT_ANNOT_PRIMARY:
12113 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]), GMT_BUFSIZ-1);
12114 			break;
12115 		case GMTCASE_ANNOT_FONT_SECONDARY:
12116 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12117 				GMT_COMPAT_WARN;
12118 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12119 			/* Intentionally fall through */
12120 		case GMTCASE_FONT_ANNOT_SECONDARY:
12121 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_annot[GMT_SECONDARY]), GMT_BUFSIZ-1);
12122 			break;
12123 		case GMTCASE_FONT_HEADING:
12124 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_heading), GMT_BUFSIZ-1);
12125 			break;
12126 		case GMTCASE_HEADER_FONT:
12127 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12128 				GMT_COMPAT_WARN;
12129 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12130 			/* Intentionally fall through */
12131 		case GMTCASE_FONT_SUBTITLE:
12132 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_subtitle), GMT_BUFSIZ-1);
12133 			break;
12134 		case GMTCASE_FONT_TITLE:
12135 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_title), GMT_BUFSIZ-1);
12136 			break;
12137 		case GMTCASE_FONT_TAG:
12138 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_tag), GMT_BUFSIZ-1);
12139 			break;
12140 		case GMTCASE_LABEL_FONT:
12141 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12142 				GMT_COMPAT_WARN;
12143 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12144 			/* Intentionally fall through */
12145 		case GMTCASE_FONT_LABEL:
12146 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_label), GMT_BUFSIZ-1);
12147 			break;
12148 
12149 		case GMTCASE_FONT_LOGO:
12150 			strncpy (value, gmt_putfont (GMT, &GMT->current.setting.font_logo), GMT_BUFSIZ-1);
12151 			break;
12152 
12153 		/* FONT GROUP ... obsolete options */
12154 
12155 		case GMTCASE_ANNOT_FONT_SIZE_PRIMARY:
12156 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
12157 				GMT_COMPAT_WARN;
12158 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.font_annot[GMT_PRIMARY].size);
12159 			}
12160 			else
12161 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12162 			break;
12163 		case GMTCASE_ANNOT_FONT_SIZE_SECONDARY:
12164 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
12165 				GMT_COMPAT_WARN;
12166 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.font_annot[GMT_SECONDARY].size);
12167 			}
12168 			else
12169 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12170 			break;
12171 		case GMTCASE_HEADER_FONT_SIZE:
12172 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
12173 				GMT_COMPAT_WARN;
12174 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.font_title.size);
12175 			}
12176 			else
12177 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12178 			break;
12179 		case GMTCASE_LABEL_FONT_SIZE:
12180 			if (gmt_M_compat_check (GMT, 4)) {	/* GMT4: */
12181 				GMT_COMPAT_WARN;
12182 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.font_label.size);
12183 			}
12184 			else
12185 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12186 			break;
12187 
12188 		/* MAP GROUP */
12189 
12190 		case GMTCASE_ANNOT_OFFSET_PRIMARY:
12191 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12192 				GMT_COMPAT_WARN;
12193 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12194 			/* Intentionally fall through */
12195 		case GMTCASE_MAP_ANNOT_OFFSET_PRIMARY:
12196 			gmtinit_place_value (GMT, GMT->current.setting.map_annot_offset[GMT_PRIMARY], GMTCASE_MAP_ANNOT_OFFSET_PRIMARY, value);
12197 			break;
12198 		case GMTCASE_ANNOT_OFFSET_SECONDARY:
12199 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12200 				GMT_COMPAT_WARN;
12201 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12202 			/* Intentionally fall through */
12203 		case GMTCASE_MAP_ANNOT_OFFSET_SECONDARY:
12204 			gmtinit_place_value (GMT, GMT->current.setting.map_annot_offset[GMT_SECONDARY], GMTCASE_MAP_ANNOT_OFFSET_SECONDARY, value);
12205 			break;
12206 		case GMTCASE_OBLIQUE_ANNOTATION:
12207 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12208 				GMT_COMPAT_WARN;
12209 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12210 			/* Intentionally fall through */
12211 		case GMTCASE_MAP_ANNOT_OBLIQUE:
12212 			string = gmtinit_print_map_annot_oblique (GMT, GMT->current.setting.map_annot_oblique);
12213 			snprintf (value, GMT_LEN256, "%s", string);
12214 			gmt_M_str_free (string);
12215 			break;
12216 		case GMTCASE_ANNOT_MIN_ANGLE:
12217 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12218 				GMT_COMPAT_WARN;
12219 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12220 			/* Intentionally fall through */
12221 		case GMTCASE_MAP_ANNOT_MIN_ANGLE:
12222 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.map_annot_min_angle);
12223 			break;
12224 		case GMTCASE_ANNOT_MIN_SPACING:
12225 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12226 				GMT_COMPAT_WARN;
12227 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12228 			/* Intentionally fall through */
12229 		case GMTCASE_MAP_ANNOT_MIN_SPACING:
12230 			gmtinit_place_value (GMT, GMT->current.setting.map_annot_min_spacing, GMTCASE_MAP_ANNOT_MIN_SPACING, value);
12231 			break;
12232 		case GMTCASE_Y_AXIS_TYPE:
12233 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12234 				GMT_COMPAT_WARN;
12235 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12236 			/* Intentionally fall through */
12237 		case GMTCASE_MAP_ANNOT_ORTHO:
12238 			strncpy (value, GMT->current.setting.map_annot_ortho, GMT_BUFSIZ-1);
12239 			break;
12240 		case GMTCASE_DEGREE_SYMBOL:
12241 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12242 				GMT_COMPAT_WARN;
12243 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12244 			/* Intentionally fall through */
12245 		case GMTCASE_MAP_DEGREE_SYMBOL:
12246 			switch (GMT->current.setting.map_degree_symbol) {
12247 				case gmt_ring:		strcpy (value, "ring");		break;
12248 				case gmt_degree:	strcpy (value, "degree");	break;
12249 				case gmt_colon:		strcpy (value, "colon");	break;
12250 				case gmt_none:		strcpy (value, "none");		break;
12251 				default: strcpy (value, "undefined");
12252 			}
12253 			break;
12254 		case GMTCASE_BASEMAP_AXES:
12255 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12256 				GMT_COMPAT_WARN;
12257 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12258 			/* Intentionally fall through */
12259 		case GMTCASE_MAP_FRAME_AXES:
12260 			strncpy (value, GMT->current.setting.map_frame_axes, GMT_BUFSIZ-1);
12261 			break;
12262 		case GMTCASE_BASEMAP_FRAME_RGB:
12263 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12264 				GMT_COMPAT_WARN;
12265 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12266 			/* Intentionally fall through */
12267 		case GMTCASE_MAP_DEFAULT_PEN:
12268 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_default_pen));
12269 			break;
12270 		case GMTCASE_FRAME_PEN:
12271 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12272 				GMT_COMPAT_WARN;
12273 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12274 			/* Intentionally fall through */
12275 		case GMTCASE_MAP_FRAME_PEN:
12276 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_frame_pen));
12277 			break;
12278 		case GMTCASE_MAP_FRAME_PERCENT:
12279 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.map_frame_percent);
12280 			break;
12281 		case GMTCASE_BASEMAP_TYPE:
12282 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12283 				GMT_COMPAT_WARN;
12284 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12285 			/* Intentionally fall through */
12286 		case GMTCASE_MAP_FRAME_TYPE:
12287 			if (GMT->current.setting.map_frame_type == GMT_IS_PLAIN)
12288 				strcpy (value, "plain");
12289 			else if (GMT->current.setting.map_frame_type == GMT_IS_GRAPH) {
12290 				strcpy (value, "graph");
12291 				if (GMT->current.setting.map_graph_extension_unit != GMT_GRAPH_EXTENSION_UNIT || !doubleAlmostEqual (GMT->current.setting.map_graph_extension, GMT_GRAPH_EXTENSION)) {
12292 					char tmp[GMT_LEN32] = {""};
12293 					/* Not the default, specify what we are using */
12294 					if (GMT->current.setting.map_graph_extension_unit == GMT_GRAPH_EXTENSION_UNIT)	/* Extension in percent */
12295 						snprintf (tmp, GMT_LEN32, ",%g%%", GMT->current.setting.map_graph_extension);
12296 					else {
12297 						double s = GMT->session.u2u[GMT_INCH][GMT->current.setting.map_graph_extension_unit];
12298 						snprintf (tmp, GMT_LEN32, ",%g%c", s*GMT->current.setting.map_graph_extension, GMT->session.unit_name[GMT->current.setting.map_graph_extension_unit][0]);
12299 					}
12300 					strcat (value, tmp);
12301 				}
12302 			}
12303 			else if (GMT->current.setting.map_frame_type == GMT_IS_FANCY)
12304 				strcpy (value, "fancy");
12305 			else if (GMT->current.setting.map_frame_type == GMT_IS_ROUNDED)
12306 				strcpy (value, "fancy-rounded");
12307 			else if (GMT->current.setting.map_frame_type == GMT_IS_INSIDE)
12308 				strcpy (value, "inside");
12309 			else
12310 				strcpy (value, "undefined");
12311 			break;
12312 		case GMTCASE_FRAME_WIDTH:
12313 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12314 				GMT_COMPAT_WARN;
12315 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12316 			/* Intentionally fall through */
12317 		case GMTCASE_MAP_FRAME_WIDTH:
12318 			gmtinit_place_value (GMT, GMT->current.setting.map_frame_width, GMTCASE_MAP_FRAME_WIDTH, value);
12319 			break;
12320 		case GMTCASE_GRID_CROSS_SIZE_PRIMARY:
12321 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12322 				GMT_COMPAT_WARN;
12323 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12324 			/* Intentionally fall through */
12325 		case GMTCASE_MAP_GRID_CROSS_SIZE_PRIMARY:
12326 			snprintf (value, GMT_LEN256, "%s%g%c", PRE[GMT->current.setting.map_grid_cross_type[GMT_PRIMARY]], GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] * gmt_M_def_scale(GMTCASE_MAP_GRID_CROSS_SIZE_PRIMARY), gmt_M_def_unit(GMTCASE_MAP_GRID_CROSS_SIZE_PRIMARY));
12327 			break;
12328 		case GMTCASE_GRID_CROSS_SIZE_SECONDARY:
12329 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12330 				GMT_COMPAT_WARN;
12331 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12332 			/* Intentionally fall through */
12333 		case GMTCASE_MAP_GRID_CROSS_SIZE_SECONDARY:
12334 			snprintf (value, GMT_LEN256, "%s%g%c", PRE[GMT->current.setting.map_grid_cross_type[GMT_PRIMARY]], GMT->current.setting.map_grid_cross_size[GMT_SECONDARY] * gmt_M_def_scale(GMTCASE_MAP_GRID_CROSS_SIZE_SECONDARY), gmt_M_def_unit(GMTCASE_MAP_GRID_CROSS_SIZE_SECONDARY));
12335 			break;
12336 		case GMTCASE_GRID_PEN_PRIMARY:
12337 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12338 				GMT_COMPAT_WARN;
12339 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12340 			/* Intentionally fall through */
12341 		case GMTCASE_MAP_GRID_PEN_PRIMARY:
12342 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]));
12343 			break;
12344 		case GMTCASE_GRID_PEN_SECONDARY:
12345 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12346 				GMT_COMPAT_WARN;
12347 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12348 			/* Intentionally fall through */
12349 		case GMTCASE_MAP_GRID_PEN_SECONDARY:
12350 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_grid_pen[GMT_SECONDARY]));
12351 			break;
12352 		case GMTCASE_MAP_HEADING_OFFSET:
12353 			gmtinit_place_value (GMT, GMT->current.setting.map_heading_offset, GMTCASE_MAP_HEADING_OFFSET, value);
12354 			break;
12355 		case GMTCASE_MAP_LABEL_MODE:
12356 			snprintf (value, GMT_LEN256, (GMT->current.setting.map_label_mode[GMT_X]) ? "axis" : "annot");
12357 			if (GMT->current.setting.map_label_mode[GMT_X] != GMT->current.setting.map_label_mode[GMT_Y]) {
12358 				strcat (value, "/");
12359 				if (GMT->current.setting.map_label_mode[GMT_Y])
12360 					strcat (value, "axis");
12361 				else
12362 					strcat (value, "annot");
12363 			}
12364 			break;
12365 		case GMTCASE_LABEL_OFFSET:
12366 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12367 				GMT_COMPAT_WARN;
12368 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12369 			/* Intentionally fall through */
12370 		case GMTCASE_MAP_LABEL_OFFSET:
12371 			if (doubleAlmostEqualZero (GMT->current.setting.map_label_offset[GMT_X], GMT->current.setting.map_label_offset[GMT_Y]))
12372 				gmtinit_place_value (GMT, GMT->current.setting.map_label_offset[GMT_X], GMTCASE_MAP_LABEL_OFFSET, value);
12373 			else {
12374 				snprintf (value, GMT_LEN256, "%g%c/%g%c", GMT->current.setting.map_label_offset[GMT_X] * gmt_M_def_scale(GMTCASE_MAP_LABEL_OFFSET), gmt_M_def_unit(GMTCASE_MAP_LABEL_OFFSET),
12375 					 GMT->current.setting.map_label_offset[GMT_Y] * gmt_M_def_scale(GMTCASE_MAP_LABEL_OFFSET), gmt_M_def_unit(GMTCASE_MAP_LABEL_OFFSET));
12376 			}
12377 			break;
12378 		case GMTCASE_LINE_STEP:
12379 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12380 				GMT_COMPAT_WARN;
12381 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12382 			/* Intentionally fall through */
12383 		case GMTCASE_MAP_LINE_STEP:
12384 			snprintf (value, GMT_LEN256, "%g%c", GMT->current.setting.map_line_step * gmt_M_def_scale(GMTCASE_MAP_LINE_STEP), gmt_M_def_unit(GMTCASE_MAP_LINE_STEP));
12385 			break;
12386 		case GMTCASE_UNIX_TIME:
12387 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12388 				GMT_COMPAT_WARN;
12389 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12390 			/* Intentionally fall through */
12391 		case GMTCASE_MAP_LOGO:
12392 			snprintf (value, GMT_LEN256, "%s", ft[GMT->current.setting.map_logo]);
12393 			break;
12394 		case GMTCASE_UNIX_TIME_POS:
12395 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12396 				GMT_COMPAT_WARN;
12397 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12398 			/* Intentionally fall through */
12399 		case GMTCASE_MAP_LOGO_POS:
12400 			snprintf (value, GMT_LEN256, "%s/%g%c/%g%c", GMT_just_string[GMT->current.setting.map_logo_justify],
12401 			GMT->current.setting.map_logo_pos[GMT_X] * gmt_M_def_scale(GMTCASE_MAP_LOGO_POS), gmt_M_def_unit(GMTCASE_MAP_LOGO_POS),
12402 			GMT->current.setting.map_logo_pos[GMT_Y] * gmt_M_def_scale(GMTCASE_MAP_LOGO_POS), gmt_M_def_unit(GMTCASE_MAP_LOGO_POS));
12403 			break;
12404 		case GMTCASE_X_ORIGIN:
12405 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12406 				GMT_COMPAT_WARN;
12407 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12408 			/* Intentionally fall through */
12409 		case GMTCASE_MAP_ORIGIN_X:
12410 			snprintf (value, GMT_LEN256, "%g%c", GMT->current.setting.map_origin[GMT_X] * gmt_M_def_scale(GMTCASE_MAP_ORIGIN_X), gmt_M_def_unit(GMTCASE_MAP_ORIGIN_X));
12411 			break;
12412 		case GMTCASE_Y_ORIGIN:
12413 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12414 				GMT_COMPAT_WARN;
12415 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12416 			/* Intentionally fall through */
12417 		case GMTCASE_MAP_ORIGIN_Y:
12418 			snprintf (value, GMT_LEN256, "%g%c", GMT->current.setting.map_origin[GMT_Y] * gmt_M_def_scale(GMTCASE_MAP_ORIGIN_Y), gmt_M_def_unit(GMTCASE_MAP_ORIGIN_Y));
12419 			break;
12420 		case GMTCASE_POLAR_CAP:
12421 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12422 				GMT_COMPAT_WARN;
12423 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12424 			/* Intentionally fall through */
12425 		case GMTCASE_MAP_POLAR_CAP:
12426 			if (gmt_M_is_dnan (GMT->current.setting.map_polar_cap[0]))
12427 				snprintf (value, GMT_LEN256, "auto");
12428 			else if (doubleAlmostEqual (GMT->current.setting.map_polar_cap[0], 90.0))
12429 				snprintf (value, GMT_LEN256, "none");
12430 			else
12431 				snprintf (value, GMT_LEN256, "%g/%g", GMT->current.setting.map_polar_cap[0], GMT->current.setting.map_polar_cap[1]);
12432 			break;
12433 		case GMTCASE_MAP_SCALE_HEIGHT:
12434 			snprintf (value, GMT_LEN256, "%g%c", GMT->current.setting.map_scale_height * gmt_M_def_scale(GMTCASE_MAP_SCALE_HEIGHT), gmt_M_def_unit(GMTCASE_MAP_SCALE_HEIGHT));
12435 			break;
12436 		case GMTCASE_MAP_TICK_LENGTH:
12437 		case GMTCASE_TICK_LENGTH:
12438 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12439 				GMT_COMPAT_WARN;
12440 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12441 			/* Intentionally fall through */
12442 		case GMTCASE_MAP_TICK_LENGTH_PRIMARY:
12443 			if (gmt_M_is_dnan (GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER]))
12444 				snprintf (value, GMT_LEN256, "auto");
12445 			else {
12446 				snprintf (value, GMT_LEN256, "%g%c/%g%c",
12447 				GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER] * gmt_M_def_scale(GMTCASE_MAP_TICK_LENGTH_PRIMARY), gmt_M_def_unit(GMTCASE_MAP_TICK_LENGTH_PRIMARY),
12448 				GMT->current.setting.map_tick_length[GMT_TICK_UPPER] * gmt_M_def_scale(GMTCASE_MAP_TICK_LENGTH_PRIMARY), gmt_M_def_unit(GMTCASE_MAP_TICK_LENGTH_PRIMARY));
12449 			}
12450 			break;
12451 		case GMTCASE_MAP_TICK_LENGTH_SECONDARY:
12452 			if (gmt_M_is_dnan (GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER]))
12453 				snprintf (value, GMT_LEN256, "auto");
12454 			else {
12455 				snprintf (value, GMT_LEN256, "%g%c/%g%c",
12456 				GMT->current.setting.map_tick_length[GMT_ANNOT_LOWER] * gmt_M_def_scale(GMTCASE_MAP_TICK_LENGTH_SECONDARY), gmt_M_def_unit(GMTCASE_MAP_TICK_LENGTH_SECONDARY),
12457 				GMT->current.setting.map_tick_length[GMT_TICK_LOWER] * gmt_M_def_scale(GMTCASE_MAP_TICK_LENGTH_SECONDARY), gmt_M_def_unit(GMTCASE_MAP_TICK_LENGTH_SECONDARY));
12458 			}
12459 			break;
12460 		case GMTCASE_MAP_TICK_PEN:
12461 		case GMTCASE_TICK_PEN:
12462 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12463 				GMT_COMPAT_WARN;
12464 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12465 			/* Intentionally fall through */
12466 		case GMTCASE_MAP_TICK_PEN_PRIMARY:
12467 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]));
12468 			break;
12469 		case GMTCASE_MAP_TICK_PEN_SECONDARY:
12470 			snprintf (value, GMT_LEN256, "%s", gmt_putpen (GMT, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]));
12471 			break;
12472 		case GMTCASE_HEADER_OFFSET:
12473 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12474 				GMT_COMPAT_WARN;
12475 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12476 			/* Intentionally fall through */
12477 		case GMTCASE_MAP_TITLE_OFFSET:
12478 			gmtinit_place_value (GMT, GMT->current.setting.map_title_offset, GMTCASE_MAP_TITLE_OFFSET, value);
12479 			break;
12480 		case GMTCASE_VECTOR_SHAPE:
12481 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12482 				GMT_COMPAT_WARN;
12483 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12484 			/* Intentionally fall through */
12485 		case GMTCASE_MAP_VECTOR_SHAPE:
12486 			if (gmt_M_is_dnan (GMT->current.setting.map_vector_shape))
12487 				snprintf (value, GMT_LEN256, "auto");
12488 			else
12489 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.map_vector_shape);
12490 			break;
12491 
12492 		/* COLOR GROUP */
12493 
12494 		case GMTCASE_COLOR_BACKGROUND:
12495 			snprintf (value, GMT_LEN256, "%s", gmt_putcolor (GMT, GMT->current.setting.color_patch[GMT_BGD]));
12496 			break;
12497 		case GMTCASE_COLOR_FOREGROUND:
12498 			snprintf (value, GMT_LEN256, "%s", gmt_putcolor (GMT, GMT->current.setting.color_patch[GMT_FGD]));
12499 			break;
12500 		case GMTCASE_COLOR_CPT:
12501 			snprintf (value, GMT_LEN64, "%s", GMT->current.setting.cpt);
12502 			break;
12503 		case GMTCASE_COLOR_MODEL:
12504 			if (GMT->current.setting.color_model == GMT_RGB)
12505 				strcpy (value, "none");
12506 			else if (GMT->current.setting.color_model == (GMT_RGB + GMT_COLORINT))
12507 				strcpy (value, "rgb");
12508 			else if (GMT->current.setting.color_model == (GMT_CMYK + GMT_COLORINT))
12509 				strcpy (value, "cmyk");
12510 			else if (GMT->current.setting.color_model == (GMT_HSV + GMT_COLORINT))
12511 				strcpy (value, "hsv");
12512 			else
12513 				strcpy (value, "undefined");
12514 			break;
12515 		case GMTCASE_COLOR_NAN:
12516 			snprintf (value, GMT_LEN256, "%s", gmt_putcolor (GMT, GMT->current.setting.color_patch[GMT_NAN]));
12517 			break;
12518 		case GMTCASE_HSV_MIN_SATURATION:
12519 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12520 				GMT_COMPAT_WARN;
12521 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12522 			/* Intentionally fall through */
12523 		case GMTCASE_COLOR_HSV_MIN_S:
12524 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.color_hsv_min_s);
12525 			break;
12526 		case GMTCASE_HSV_MAX_SATURATION:
12527 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12528 				GMT_COMPAT_WARN;
12529 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12530 			/* Intentionally fall through */
12531 		case GMTCASE_COLOR_HSV_MAX_S:
12532 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.color_hsv_max_s);
12533 			break;
12534 		case GMTCASE_HSV_MIN_VALUE:
12535 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12536 				GMT_COMPAT_WARN;
12537 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12538 			/* Intentionally fall through */
12539 		case GMTCASE_COLOR_HSV_MIN_V:
12540 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.color_hsv_min_v);
12541 			break;
12542 		case GMTCASE_HSV_MAX_VALUE:
12543 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12544 				GMT_COMPAT_WARN;
12545 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12546 			/* Intentionally fall through */
12547 		case GMTCASE_COLOR_HSV_MAX_V:
12548 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.color_hsv_max_v);
12549 			break;
12550 		case GMTCASE_COLOR_SET:
12551 			snprintf (value, GMT_LEN256, "%s", GMT->current.setting.color_set);
12552 			break;
12553 
12554 		/* PS GROUP */
12555 
12556 		case GMTCASE_CHAR_ENCODING:
12557 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12558 				GMT_COMPAT_WARN;
12559 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12560 			/* Intentionally fall through */
12561 		case GMTCASE_PS_CHAR_ENCODING:
12562 			strncpy (value, GMT->current.setting.ps_encoding.name, GMT_BUFSIZ-1);
12563 			break;
12564 		case GMTCASE_PS_COLOR:
12565 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12566 				GMT_COMPAT_WARN;
12567 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12568 			/* Intentionally fall through */
12569 		case GMTCASE_PS_COLOR_MODEL:
12570 			if (GMT->current.setting.ps_color_mode == PSL_RGB)
12571 				strcpy (value, "rgb");
12572 			else if (GMT->current.setting.ps_color_mode == PSL_CMYK)
12573 				strcpy (value, "cmyk");
12574 			else if (GMT->current.setting.ps_color_mode == PSL_HSV)
12575 				strcpy (value, "hsv");
12576 			else if (GMT->current.setting.ps_color_mode == PSL_GRAY)
12577 				strcpy (value, "gray");
12578 			else
12579 				strcpy (value, "undefined");
12580 			break;
12581 		case GMTCASE_N_COPIES:
12582 		case GMTCASE_PS_COPIES:
12583 			if (gmt_M_compat_check (GMT, 4)) {
12584 				GMT_COMPAT_WARN;
12585 				snprintf (value, GMT_LEN256, "%d", GMT->current.setting.ps_copies);
12586 			}
12587 			else
12588 				error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12589 			break;
12590 		case GMTCASE_DOTS_PR_INCH:
12591 		case GMTCASE_PS_DPI: GMT_COMPAT_WARN;
12592 			if (!gmt_M_compat_check (GMT, 4)) error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12593 			break;
12594 		case GMTCASE_PS_EPS: GMT_COMPAT_WARN;
12595 			if (!gmt_M_compat_check (GMT, 4)) error = gmtinit_badvalreport (GMT, keyword);	/* Not recognized so give error message */
12596 			break;
12597 		case GMTCASE_PS_IMAGE_COMPRESS:
12598 			if (!GMT->PSL) return (NULL);	/* Not using PSL in this session */
12599 			if (GMT->PSL->internal.compress == PSL_NONE)
12600 				strcpy (value, "none");
12601 			else if (GMT->PSL->internal.compress == PSL_RLE)
12602 				strcpy (value, "rle");
12603 			else if (GMT->PSL->internal.compress == PSL_LZW)
12604 				strcpy (value, "lzw");
12605 			else if (GMT->PSL->internal.compress == PSL_DEFLATE) {
12606 				if (GMT->PSL->internal.deflate_level != 0)
12607 					snprintf (value, GMT_LEN256, "deflate,%u", GMT->PSL->internal.deflate_level);
12608 				else
12609 					strcpy (value, "deflate");
12610 			}
12611 			else
12612 				strcpy (value, "undefined");
12613 			break;
12614 		case GMTCASE_PS_LINE_CAP:
12615 			if (!GMT->PSL) return (NULL);	/* Not using PSL in this session */
12616 			if (GMT->PSL->internal.line_cap == PSL_BUTT_CAP)
12617 				strcpy (value, "butt");
12618 			else if (GMT->PSL->internal.line_cap == PSL_ROUND_CAP)
12619 				strcpy (value, "round");
12620 			else if (GMT->PSL->internal.line_cap == PSL_SQUARE_CAP)
12621 				strcpy (value, "square");
12622 			else
12623 				strcpy (value, "undefined");
12624 			break;
12625 		case GMTCASE_PS_LINE_JOIN:
12626 			if (!GMT->PSL) return (NULL);	/* Not using PSL in this session */
12627 			if (GMT->PSL->internal.line_join == PSL_MITER_JOIN)
12628 				strcpy (value, "miter");
12629 			else if (GMT->PSL->internal.line_join == PSL_ROUND_JOIN)
12630 				strcpy (value, "round");
12631 			else if (GMT->PSL->internal.line_join == PSL_BEVEL_JOIN)
12632 				strcpy (value, "bevel");
12633 			else
12634 				strcpy (value, "undefined");
12635 			break;
12636 		case GMTCASE_PS_MITER_LIMIT:
12637 			if (!GMT->PSL) return (NULL);	/* Not using PSL in this session */
12638 			snprintf (value, GMT_LEN256, "%d", GMT->PSL->internal.miter_limit);
12639 			break;
12640 		case GMTCASE_PAGE_COLOR:
12641 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12642 				GMT_COMPAT_WARN;
12643 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12644 			/* Intentionally fall through */
12645 		case GMTCASE_PS_PAGE_COLOR:
12646 			snprintf (value, GMT_LEN256, "%s", gmt_putcolor (GMT, GMT->current.setting.ps_page_rgb));
12647 			break;
12648 		case GMTCASE_PAGE_ORIENTATION:
12649 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12650 				GMT_COMPAT_WARN;
12651 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12652 			/* Intentionally fall through */
12653 		case GMTCASE_PS_PAGE_ORIENTATION:
12654 			if (GMT->current.setting.ps_orientation == PSL_LANDSCAPE)
12655 				strcpy (value, "landscape");
12656 			else if (GMT->current.setting.ps_orientation == PSL_PORTRAIT)
12657 				strcpy (value, "portrait");
12658 			else
12659 				strcpy (value, "undefined");
12660 			break;
12661 		case GMTCASE_PAPER_MEDIA:
12662 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12663 				GMT_COMPAT_WARN;
12664 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12665 			/* Intentionally fall through */
12666 		case GMTCASE_PS_MEDIA:
12667 			if (GMT->current.setting.ps_media == -GMT_USER_MEDIA_OFFSET)
12668 				snprintf (value, GMT_LEN256, "%gx%g", fabs (GMT->current.setting.ps_page_size[0]), fabs (GMT->current.setting.ps_page_size[1]));
12669 			else if (GMT->current.setting.ps_media >= GMT_USER_MEDIA_OFFSET)
12670 				snprintf (value, GMT_LEN256, "%s", GMT->session.user_media_name[GMT->current.setting.ps_media-GMT_USER_MEDIA_OFFSET]);
12671 			else if (GMT->current.setting.ps_media < GMT_N_MEDIA)
12672 				snprintf (value, GMT_LEN256, "%s", GMT_media_name[GMT->current.setting.ps_media]);
12673 			if (GMT->current.setting.ps_page_size[0] < 0.0)
12674 				strcat (value, "-");
12675 			else if (GMT->current.setting.ps_page_size[1] < 0.0)
12676 				strcat (value, "+");
12677 			break;
12678 		case GMTCASE_GLOBAL_X_SCALE:
12679 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12680 				GMT_COMPAT_WARN;
12681 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12682 			/* Intentionally fall through */
12683 		case GMTCASE_PS_SCALE_X:
12684 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.ps_magnify[GMT_X]);
12685 			break;
12686 		case GMTCASE_GLOBAL_Y_SCALE:
12687 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12688 				GMT_COMPAT_WARN;
12689 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12690 			/* Intentionally fall through */
12691 		case GMTCASE_PS_SCALE_Y:
12692 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.ps_magnify[GMT_Y]);
12693 			break;
12694 		case GMTCASE_TRANSPARENCY:
12695 			if (gmt_M_compat_check (GMT, 4))
12696 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Transparency is now part of pen and fill specifications.  TRANSPARENCY is ignored\n");
12697 			else
12698 				error = gmtinit_badvalreport (GMT, keyword);
12699 			break;
12700 		case GMTCASE_PS_TRANSPARENCY:
12701 			strncpy (value, GMT->current.setting.ps_transpmode, GMT_LEN16-1);
12702 			break;
12703 		case GMTCASE_PS_CONVERT:
12704 			strncpy (value, GMT->current.setting.ps_convert, GMT_BUFSIZ-1);
12705 			break;
12706 		case GMTCASE_PS_VERBOSE:
12707 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12708 				GMT_COMPAT_WARN;
12709 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12710 			/* Intentionally fall through */
12711 		case GMTCASE_PS_COMMENTS:
12712 			if (!GMT->PSL) return (NULL);	/* Not using PSL in this session */
12713 			snprintf (value, GMT_LEN256, "%s", ft[GMT->PSL->internal.comments]);
12714 			break;
12715 
12716 		/* IO GROUP */
12717 
12718 		case GMTCASE_FIELD_DELIMITER:
12719 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12720 				GMT_COMPAT_WARN;
12721 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12722 			/* Intentionally fall through */
12723 		case GMTCASE_IO_COL_SEPARATOR:
12724 			if (GMT->current.setting.io_col_separator[0] == '\t')	/* DEFAULT */
12725 				strcpy (value, "tab");
12726 			else if (GMT->current.setting.io_col_separator[0] == ' ')
12727 				strcpy (value, "space");
12728 			else if (GMT->current.setting.io_col_separator[0] == ',')
12729 				strcpy (value, "comma");
12730 			else if (!GMT->current.setting.io_col_separator[0])
12731 				strcpy (value, "none");
12732 			else
12733 				strncpy (value, GMT->current.setting.io_col_separator, GMT_BUFSIZ-1);
12734 			break;
12735 		case GMTCASE_IO_FIRST_HEADER:
12736 			if (GMT->current.setting.io_first_header == GMT_FIRST_SEGHEADER_MAYBE)
12737 				strcpy (value, "maybe");
12738 			else if (GMT->current.setting.io_first_header == GMT_FIRST_SEGHEADER_ALWAYS)
12739 				strcpy (value, "always");
12740 			else
12741 				strcpy (value, "never");
12742 			break;
12743 		case GMTCASE_GRIDFILE_FORMAT:
12744 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12745 				GMT_COMPAT_WARN;
12746 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12747 			/* Intentionally fall through */
12748 		case GMTCASE_IO_GRIDFILE_FORMAT:
12749 			strncpy (value, GMT->current.setting.io_gridfile_format, GMT_BUFSIZ-1);
12750 			break;
12751 		case GMTCASE_GRIDFILE_SHORTHAND:
12752 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12753 				GMT_COMPAT_WARN;
12754 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12755 			/* Intentionally fall through */
12756 		case GMTCASE_IO_GRIDFILE_SHORTHAND:
12757 			snprintf (value, GMT_LEN256, "%s", ft[GMT->current.setting.io_gridfile_shorthand]);
12758 			break;
12759 		case GMTCASE_IO_HEADER:
12760 			snprintf (value, GMT_LEN256, "%s", ft[GMT->current.setting.io_header[GMT_IN]]);
12761 			break;
12762 		case GMTCASE_IO_HEADER_MARKER:
12763 			value[0] = '\0';
12764 			if (strlen (GMT->current.setting.io_head_marker_in) > 1 || GMT->current.setting.io_head_marker_in[0] != GMT->current.setting.io_head_marker_out) {
12765 				snprintf (txt, 8U, "%s,", GMT->current.setting.io_head_marker_in);	strcat (value, txt);
12766 				snprintf (txt, 8U, "%c", GMT->current.setting.io_head_marker_out);	strcat (value, txt);
12767 			}
12768 			else {	/* Just a single character for both ways */
12769 				snprintf (txt, 8U, "%c", GMT->current.setting.io_head_marker_out);	strcat (value, txt);
12770 			}
12771 			break;
12772 		case GMTCASE_N_HEADER_RECS:
12773 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12774 				GMT_COMPAT_WARN;
12775 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12776 			/* Intentionally fall through */
12777 		case GMTCASE_IO_N_HEADER_RECS:
12778 			snprintf (value, GMT_LEN256, "%d", GMT->current.setting.io_n_header_items);
12779 			break;
12780 		case GMTCASE_NAN_RECORDS:
12781 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12782 				GMT_COMPAT_WARN;
12783 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12784 			/* Intentionally fall through */
12785 		case GMTCASE_IO_NAN_RECORDS:
12786 			if (GMT->current.setting.io_nan_records)
12787 				strcpy (value, "pass");
12788 			else
12789 				strcpy (value, "skip");
12790 			break;
12791 		case GMTCASE_IO_NC4_CHUNK_SIZE:
12792 			if (GMT->current.setting.io_nc4_chunksize[0] == k_netcdf_io_chunked_auto)
12793 				strcpy (value, "auto");
12794 			else if (GMT->current.setting.io_nc4_chunksize[0] == k_netcdf_io_classic)
12795 				strcpy (value, "classic");
12796 			else
12797 				snprintf (value, GMT_LEN256, "%" PRIuS ",%" PRIuS, /* chunk size: lat,lon */
12798 						 GMT->current.setting.io_nc4_chunksize[0],
12799 						 GMT->current.setting.io_nc4_chunksize[1]);
12800 			break;
12801 		case GMTCASE_IO_NC4_DEFLATION_LEVEL:
12802 			snprintf (value, GMT_LEN256, "%u", GMT->current.setting.io_nc4_deflation_level);
12803 			break;
12804 		case GMTCASE_XY_TOGGLE:
12805 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12806 				GMT_COMPAT_WARN;
12807 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12808 			/* Intentionally fall through */
12809 		case GMTCASE_IO_LONLAT_TOGGLE:
12810 			if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && GMT->current.setting.io_lonlat_toggle[GMT_OUT])
12811 				strcpy (value, "true");
12812 			else if (!GMT->current.setting.io_lonlat_toggle[GMT_IN] && !GMT->current.setting.io_lonlat_toggle[GMT_OUT])
12813 				strcpy (value, "false");
12814 			else if (GMT->current.setting.io_lonlat_toggle[GMT_IN] && !GMT->current.setting.io_lonlat_toggle[GMT_OUT])
12815 				strcpy (value, "in");
12816 			else
12817 				strcpy (value, "out");
12818 			break;
12819 
12820 		case GMTCASE_IO_SEGMENT_BINARY:
12821 			if (GMT->current.setting.n_bin_header_cols == 0)
12822 				strcpy (value, "off");
12823 			else
12824 				snprintf (value, GMT_LEN256, "%" PRIu64, GMT->current.setting.n_bin_header_cols);
12825 			break;
12826 
12827 		case GMTCASE_IO_SEGMENT_MARKER:
12828 			value[0] = '\0';
12829 			if (GMT->current.setting.io_seg_marker[GMT_OUT] != GMT->current.setting.io_seg_marker[GMT_IN]) {
12830 				if ((GMT->current.setting.io_seg_marker[GMT_IN] == 'N' && !GMT->current.setting.io_nanline[GMT_IN]) || (GMT->current.setting.io_seg_marker[GMT_IN] == 'B' && !GMT->current.setting.io_blankline[GMT_IN])) value[0] = '\\';
12831 				snprintf (txt, 8U, "%c,", GMT->current.setting.io_seg_marker[GMT_IN]);	strcat (value, txt);
12832 				if ((GMT->current.setting.io_seg_marker[GMT_IN] == 'N' && !GMT->current.setting.io_nanline[GMT_IN]) || (GMT->current.setting.io_seg_marker[GMT_IN] == 'B' && !GMT->current.setting.io_blankline[GMT_IN])) strcat (value, "\\");
12833 				snprintf (txt, 8U, "%c", GMT->current.setting.io_seg_marker[GMT_OUT]);	strcat (value, txt);
12834 			}
12835 			else {
12836 				if ((GMT->current.setting.io_seg_marker[GMT_IN] == 'N' && !GMT->current.setting.io_nanline[GMT_IN]) || (GMT->current.setting.io_seg_marker[GMT_IN] == 'B' && !GMT->current.setting.io_blankline[GMT_IN])) value[0] = '\\';
12837 				snprintf (txt, 8U, "%c", GMT->current.setting.io_seg_marker[GMT_IN]);	strcat (value, txt);
12838 			}
12839 			break;
12840 
12841 		/* PROJ GROUP */
12842 
12843 		case GMTCASE_PROJ_AUX_LATITUDE:
12844 			switch (GMT->current.setting.proj_aux_latitude) {
12845 				case GMT_LATSWAP_NONE:
12846 					strcpy (value, "geodetic");
12847 					break;
12848 				case GMT_LATSWAP_G2A:
12849 					strcpy (value, "authalic");
12850 					break;
12851 				case GMT_LATSWAP_G2C:
12852 					strcpy (value, "conformal");
12853 					break;
12854 				case GMT_LATSWAP_G2M:
12855 					strcpy (value, "meridional");
12856 					break;
12857 				case GMT_LATSWAP_G2O:
12858 					strcpy (value, "geocentric");
12859 					break;
12860 				case GMT_LATSWAP_G2P:
12861 					strcpy (value, "parametric");
12862 					break;
12863 				default:
12864 					strcpy (value, "undefined");
12865 			}
12866 			break;
12867 
12868 		case GMTCASE_ELLIPSOID:
12869 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12870 				GMT_COMPAT_WARN;
12871 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12872 			/* Intentionally fall through */
12873 		case GMTCASE_PROJ_ELLIPSOID:
12874 			if (GMT->current.setting.proj_ellipsoid < GMT_N_ELLIPSOIDS - 1)	/* Custom ellipse */
12875 				snprintf (value, GMT_LEN256, "%s", GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].name);
12876 			else if (gmt_M_is_spherical (GMT))
12877 				snprintf (value, GMT_LEN256, "%f", GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius);
12878 			else
12879 				snprintf (value, GMT_LEN256, "%f,%f", GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius,
12880 					1.0/GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening);
12881 			break;
12882 		case GMTCASE_PROJ_DATUM:	/* Not implemented yet */
12883 			break;
12884 		case GMTCASE_PROJ_GEODESIC:
12885 			switch (GMT->current.setting.proj_geodesic) {
12886 				case GMT_GEODESIC_VINCENTY:
12887 					strcpy (value, "Vincenty");
12888 					break;
12889 				case GMT_GEODESIC_ANDOYER:
12890 					strcpy (value, "Andoyer");
12891 					break;
12892 				case GMT_GEODESIC_RUDOE:
12893 					strcpy (value, "Rudoe");
12894 					break;
12895 				default:
12896 					strcpy (value, "undefined");
12897 			}
12898 			break;
12899 
12900 		case GMTCASE_MEASURE_UNIT:
12901 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12902 				GMT_COMPAT_WARN;
12903 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12904 			/* Intentionally fall through */
12905 		case GMTCASE_PROJ_LENGTH_UNIT:
12906 			snprintf (value, GMT_LEN256, "%s", GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
12907 			break;
12908 		case GMTCASE_PROJ_MEAN_RADIUS:
12909 			switch (GMT->current.setting.proj_mean_radius) {
12910 				case GMT_RADIUS_MEAN:
12911 					strcpy (value, "mean");
12912 					break;
12913 				case GMT_RADIUS_AUTHALIC:
12914 					strcpy (value, "authalic");
12915 					break;
12916 				case GMT_RADIUS_VOLUMETRIC:
12917 					strcpy (value, "volumetric");
12918 					break;
12919 				case GMT_RADIUS_MERIDIONAL:
12920 					strcpy (value, "meridional");
12921 					break;
12922 				case GMT_RADIUS_QUADRATIC:
12923 					strcpy (value, "quadratic");
12924 					break;
12925 				default:
12926 					strcpy (value, "undefined");
12927 			}
12928 			break;
12929 		case GMTCASE_MAP_SCALE_FACTOR:
12930 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
12931 				GMT_COMPAT_WARN;
12932 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
12933 			/* Intentionally fall through */
12934 		case GMTCASE_PROJ_SCALE_FACTOR:
12935 			if (doubleAlmostEqual (GMT->current.setting.proj_scale_factor, -1.0)) /* Default scale for chosen projection */
12936 				strcpy (value, "default"); /* Default scale for chosen projection */
12937 			else
12938 				snprintf (value, GMT_LEN256, "%g", GMT->current.setting.proj_scale_factor);
12939 			break;
12940 
12941 		/* GMT GROUP */
12942 
12943 		case GMTCASE_GMT_AUTO_DOWNLOAD:
12944 			/* Deprecated as of 6.2: we only use GMT_DATA_UPDATE_INTERVAL to control this feature now, so just break here */
12945 			break;
12946 		case GMTCASE_GMT_COMPATIBILITY:
12947 			snprintf (value, GMT_LEN256, "%u", GMT->current.setting.compatibility);
12948 			break;
12949 
12950 		case GMTCASE_GMT_DATA_SERVER:	/* The default is set by cmake, see ConfigDefault.cmake */
12951 			strncpy (value, (GMT->session.DATASERVER) ? GMT->session.DATASERVER : "", GMT_BUFSIZ-1);
12952 			break;
12953 
12954 		case GMTCASE_GMT_DATA_SERVER_LIMIT:
12955 			if (GMT->current.setting.url_size_limit == 0)
12956 				strcpy (value, "unlimited");
12957 			else if (GMT->current.setting.url_size_limit < 1024)
12958 				snprintf (value, GMT_BUFSIZ, "%" PRIu64, (uint64_t)GMT->current.setting.url_size_limit);
12959 			else if (GMT->current.setting.url_size_limit < 1024*1024)
12960 				snprintf (value, GMT_BUFSIZ, "%" PRIu64 "Kb", (uint64_t)GMT->current.setting.url_size_limit/1024);
12961 			else if (GMT->current.setting.url_size_limit < 1024*1024*1024)
12962 				snprintf (value, GMT_BUFSIZ, "%" PRIu64 "Mb", (uint64_t)GMT->current.setting.url_size_limit/(1024*1024));
12963 			else
12964 				snprintf (value, GMT_BUFSIZ, "%" PRIu64 "Gb", (uint64_t)GMT->current.setting.url_size_limit/(1024*1024*1024));
12965 			break;
12966 
12967 		case GMTCASE_GMT_DATA_UPDATE_INTERVAL:
12968 			if (GMT->current.setting.refresh_time == 0)	/* Currently deactivated */
12969 				strcpy (value, "off");
12970 			else if ((GMT->current.setting.refresh_time % 30) == 0)	/* Whole "months" = 30 days */
12971 				snprintf (value, GMT_BUFSIZ, "%do", GMT->current.setting.refresh_time / 30);
12972 			else if ((GMT->current.setting.refresh_time % 7) == 0)	/* Whole weeks */
12973 				snprintf (value, GMT_BUFSIZ, "%dw", GMT->current.setting.refresh_time / 7);
12974 			else /* Number of days */
12975 				snprintf (value, GMT_BUFSIZ, "%dd", GMT->current.setting.refresh_time);
12976 			break;
12977 
12978 		case GMTCASE_GMT_CUSTOM_LIBS:
12979 			strncpy (value, (GMT->session.CUSTOM_LIBS) ? GMT->session.CUSTOM_LIBS : "", GMT_BUFSIZ-1);
12980 			break;
12981 
12982 		case GMTCASE_GMT_EXPORT_TYPE:
12983 			if (GMT->current.setting.export_type == GMT_DOUBLE)
12984 				strcpy (value, "double");
12985 			else if (GMT->current.setting.export_type == GMT_FLOAT)
12986 				strcpy (value, "single");
12987 			else if (GMT->current.setting.export_type == GMT_LONG)
12988 				strcpy (value, "long");
12989 			else if (GMT->current.setting.export_type == GMT_ULONG)
12990 				strcpy (value, "ulong");
12991 			else if (GMT->current.setting.export_type == GMT_INT)
12992 				strcpy (value, "int");
12993 			else if (GMT->current.setting.export_type == GMT_UINT)
12994 				strcpy (value, "uint");
12995 			else if (GMT->current.setting.export_type == GMT_SHORT)
12996 				strcpy (value, "short");
12997 			else if (GMT->current.setting.export_type == GMT_USHORT)
12998 				strcpy (value, "ushort");
12999 			else if (GMT->current.setting.export_type == GMT_CHAR)
13000 				strcpy (value, "char");
13001 			else if (GMT->current.setting.export_type == GMT_UCHAR)
13002 				strcpy (value, "byte");
13003 			break;
13004 
13005 		case GMTCASE_GMT_EXTRAPOLATE_VAL:
13006 			if (GMT->current.setting.extrapolate_val[0] == GMT_EXTRAPOLATE_NONE)
13007 				strcpy (value, "NaN");
13008 			else if (GMT->current.setting.extrapolate_val[0] == GMT_EXTRAPOLATE_SPLINE)
13009 				strcpy (value, "extrap");
13010 			else if (GMT->current.setting.extrapolate_val[0] == GMT_EXTRAPOLATE_CONSTANT)
13011 				snprintf (value, GMT_LEN256, "extrapval,%g", GMT->current.setting.extrapolate_val[1]);
13012 			break;
13013 		case GMTCASE_GMT_FFT:
13014 			switch (GMT->current.setting.fft) {
13015 				case k_fft_auto:
13016 					strcpy (value, "auto");
13017 					break;
13018 				case k_fft_kiss:
13019 					strcpy (value, "kissfft");
13020 					break;
13021 				case k_fft_brenner:
13022 					strcpy (value, "brenner");
13023 					break;
13024 				case k_fft_fftw:
13025 					strcpy (value, "fftw");
13026 #ifdef HAVE_FFTW3F
13027 					switch (GMT->current.setting.fftw_plan) {
13028 						case FFTW_MEASURE:
13029 							strcat (value, ",measure");
13030 							break;
13031 						case FFTW_PATIENT:
13032 							strcat (value, ",patient");
13033 							break;
13034 						case FFTW_EXHAUSTIVE:
13035 							strcat (value, ",exhaustive");
13036 							break;
13037 						default:
13038 							strcat (value, ",estimate");
13039 					}
13040 #endif /* HAVE_FFTW3F */
13041 					break;
13042 				case k_fft_accelerate:
13043 					strcpy (value, "accelerate");
13044 					break;
13045 				default:
13046 					strcpy (value, "undefined");
13047 			}
13048 			break;
13049 		case GMTCASE_GMT_GRAPHICS_DPU:
13050 			sprintf (value, "%g%c", GMT->current.setting.graphics_dpu, GMT->current.setting.graphics_dpu_unit);
13051 			break;
13052 		case GMTCASE_GMT_GRAPHICS_FORMAT:
13053 			strncpy (value, gmt_session_format[GMT->current.setting.graphics_format], GMT_LEN256-1);
13054 			break;
13055 		case GMTCASE_HISTORY:
13056 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
13057 				GMT_COMPAT_WARN;
13058 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
13059 			/* Intentionally fall through */
13060 		case GMTCASE_GMT_HISTORY:
13061 			if (GMT->current.setting.history & GMT_HISTORY_WRITE)
13062 				snprintf (value, GMT_LEN256, "true");
13063 			else if (GMT->current.setting.history & GMT_HISTORY_READ)
13064 				snprintf (value, GMT_LEN256, "readonly");
13065 			else
13066 				snprintf (value, GMT_LEN256, "false");
13067 			break;
13068 		case GMTCASE_INTERPOLANT:
13069 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
13070 				GMT_COMPAT_WARN;
13071 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
13072 			/* Intentionally fall through */
13073 		case GMTCASE_GMT_INTERPOLANT:
13074 			if (GMT->current.setting.interpolant == GMT_SPLINE_LINEAR)
13075 				strcpy (value, "linear");
13076 			else if (GMT->current.setting.interpolant == GMT_SPLINE_AKIMA)
13077 				strcpy (value, "akima");
13078 			else if (GMT->current.setting.interpolant == GMT_SPLINE_CUBIC)
13079 				strcpy (value, "cubic");
13080 			else if (GMT->current.setting.interpolant == GMT_SPLINE_NONE)
13081 				strcpy (value, "none");
13082 			else
13083 				strcpy (value, "undefined");
13084 			break;
13085 		case GMTCASE_GMT_MAX_CORES:
13086 			sprintf (value, "%d", GMT->current.setting.max_cores);
13087 			break;
13088 		case GMTCASE_TIME_LANGUAGE:
13089 		case GMTCASE_GMT_LANGUAGE:
13090 			strncpy (value, GMT->current.setting.language, GMT_LEN64-1);
13091 			gmtinit_get_language (GMT);	/* Load in names and abbreviations in chosen language */
13092 			break;
13093 		case GMTCASE_GMT_THEME:
13094 			strncpy (value, GMT->current.setting.theme, GMT_LEN256-1);
13095 			break;
13096 		case GMTCASE_GMT_TRIANGULATE:
13097 			if (GMT->current.setting.triangulate == GMT_TRIANGLE_WATSON)
13098 				strcpy (value, "Watson");
13099 			else if (GMT->current.setting.triangulate == GMT_TRIANGLE_SHEWCHUK)
13100 				strcpy (value, "Shewchuk");
13101 			else
13102 				strcpy (value, "undefined");
13103 			break;
13104 		case GMTCASE_VERBOSE:
13105 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
13106 				GMT_COMPAT_WARN;
13107 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
13108 			/* Intentionally fall through */
13109 		case GMTCASE_GMT_VERBOSE:
13110 			switch (GMT->current.setting.verbose) {
13111 				case GMT_MSG_QUIET:			strcpy (value, "quiet");		break;
13112 				case GMT_MSG_ERROR:			strcpy (value, "error");		break;
13113 				case GMT_MSG_WARNING:		strcpy (value, "warning");		break;
13114 				case GMT_MSG_TICTOC:		strcpy (value, "timing");		break;
13115 				case GMT_MSG_INFORMATION:	strcpy (value, "information");	break;
13116 				case GMT_MSG_DEBUG:			strcpy (value, "debug");		break;
13117 				default:					strcpy (value, "compat");		break;
13118 			}
13119 			break;
13120 
13121 		/* DIR GROUP */
13122 
13123 		case GMTCASE_DIR_CACHE:
13124 			/* Force update of session.CACHEDIR before copying the string */
13125 			strncpy (value, (GMT->session.CACHEDIR) ? GMT->session.CACHEDIR : "", GMT_BUFSIZ-1);
13126 			break;
13127 		case GMTCASE_DIR_DATA:
13128 			/* Force update of session.DATADIR before copying the string */
13129 			strncpy (value, (GMT->session.DATADIR) ? GMT->session.DATADIR : "", GMT_BUFSIZ-1);
13130 			break;
13131 		case GMTCASE_DIR_DCW:
13132 			/* Force update of session.DCWDIR before copying the string */
13133 			strncpy (value, (GMT->session.DCWDIR) ? GMT->session.DCWDIR : "", GMT_BUFSIZ-1);
13134 			break;
13135 		case GMTCASE_DIR_GSHHG:
13136 			/* Force update of session.GSHHGDIR before copying the string */
13137 			gmt_shore_adjust_res (GMT, 'c', false);
13138 			strncpy (value, (GMT->session.GSHHGDIR) ? GMT->session.GSHHGDIR : "", GMT_BUFSIZ-1);
13139 			break;
13140 
13141 		/* TIME GROUP */
13142 
13143 		case GMTCASE_TIME_EPOCH:
13144 			strncpy (value, GMT->current.setting.time_system.epoch, GMT_LEN64-1);
13145 			break;
13146 		case GMTCASE_TIME_IS_INTERVAL:
13147 			if (GMT->current.setting.time_is_interval)
13148 				snprintf (value, GMT_LEN256, "%c%d%c", pm[GMT->current.time.truncate.direction], GMT->current.time.truncate.T.step, GMT->current.time.truncate.T.unit);
13149 			else
13150 				snprintf (value, GMT_LEN256, "off");
13151 			break;
13152 		case GMTCASE_TIME_INTERVAL_FRACTION:
13153 			snprintf (value, GMT_LEN256, "%g", GMT->current.setting.time_interval_fraction);
13154 			break;
13155 		case GMTCASE_WANT_LEAP_SECONDS:
13156 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
13157 				GMT_COMPAT_WARN;
13158 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
13159 			/* Intentionally fall through */
13160 		case GMTCASE_TIME_LEAP_SECONDS:
13161 			snprintf (value, GMT_LEN256, "%s", ft[GMT->current.setting.time_leap_seconds]);
13162 			break;
13163 		case GMTCASE_TIME_REPORT:
13164 			if (GMT->current.setting.timer_mode == GMT_NO_TIMER)
13165 				strcpy (value, "none");
13166 			else if (GMT->current.setting.timer_mode == GMT_ABS_TIMER)
13167 				strcpy (value, "clock");
13168 			else if (GMT->current.setting.timer_mode == GMT_ELAPSED_TIMER)
13169 				strcpy (value, "elapsed");
13170 			break;
13171 		case GMTCASE_TIME_UNIT:
13172 			value[0] = GMT->current.setting.time_system.unit;
13173 			break;
13174 		case GMTCASE_TIME_WEEK_START:
13175 			snprintf (value, GMT_LEN256, "%s", GMT_weekdays[GMT->current.setting.time_week_start]);
13176 			break;
13177 		case GMTCASE_Y2K_OFFSET_YEAR:
13178 			if (gmt_M_compat_check (GMT, 4))	/* GMT4: */
13179 				GMT_COMPAT_WARN;
13180 			else { error = gmtinit_badvalreport (GMT, keyword); break; }	/* Not recognized so give error message */
13181 			/* Intentionally fall through */
13182 		case GMTCASE_TIME_Y2K_OFFSET_YEAR:
13183 			snprintf (value, GMT_LEN256, "%d", GMT->current.setting.time_Y2K_offset_year);
13184 			break;
13185 
13186 		default:
13187 			error = true; /* keyword not known */
13188 			break;
13189 	}
13190 	if (error)
13191 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized keyword %s\n", keyword);
13192 	return (value);
13193 }
13194 
13195 /*! . */
gmt_pickdefaults(struct GMT_CTRL * GMT,bool lines,struct GMT_OPTION * options)13196 int gmt_pickdefaults (struct GMT_CTRL *GMT, bool lines, struct GMT_OPTION *options) {
13197 	int error = GMT_OK, n = 0;
13198 	struct GMT_OPTION *opt = NULL;
13199 	struct GMT_RECORD Out;
13200 	char *param, record[GMT_BUFSIZ] = {""};
13201 
13202 	if (GMT_Init_IO (GMT->parent, GMT_IS_DATASET, GMT_IS_TEXT, GMT_OUT, GMT_ADD_DEFAULT, 0, options) != GMT_OK) {	/* Establishes data output */
13203 		return (GMT->parent->error);
13204 	}
13205 	if (GMT_Begin_IO (GMT->parent, GMT_IS_DATASET, GMT_OUT, GMT_HEADER_ON) != GMT_OK) {
13206 		return (GMT->parent->error);	/* Enables data output and sets access mode */
13207 	}
13208 	if (GMT_Set_Geometry (GMT->parent, GMT_OUT, GMT_IS_TEXT) != GMT_NOERROR) {	/* Sets output geometry */
13209 		return (GMT->parent->error);
13210 	}
13211 	Out.data = NULL;
13212 	for (opt = options; opt; opt = opt->next) {
13213 		if (!(opt->option == '<' || opt->option == '#') || !opt->arg)
13214 			continue;		/* Skip other and empty options */
13215 		if (lines) record[0] = '\0';	/* Start over */
13216 		if (!lines && n)
13217 			strcat (record, " ");	/* Separate by spaces */
13218 		param = gmtlib_getparameter (GMT, opt->arg);
13219 		if (*param == '\0') {
13220 			/* if keyword unknown */
13221 			error = GMT_OPTION_NOT_FOUND;
13222 			break;
13223 		}
13224 		if (lines) {	/* Separate lines */
13225 			Out.text = param;
13226 			GMT_Put_Record (GMT->parent, GMT_WRITE_DATA, &Out);
13227 		}
13228 		else
13229 			strncat (record, param, GMT_BUFSIZ-1);
13230 		n++;
13231 	}
13232 	if (!lines && n) {
13233 		Out.text = record;
13234 		GMT_Put_Record (GMT->parent, GMT_WRITE_DATA, &Out);		/* Separate lines */
13235 	}
13236 	if (GMT_End_IO (GMT->parent, GMT_OUT, 0) != GMT_OK) {	/* Disables further data output */
13237 		return (GMT->parent->error);
13238 	}
13239 	return error;
13240 }
13241 
13242 /*! Dumps the GMT parameters to file or standard output */
gmt_putdefaults(struct GMT_CTRL * GMT,char * this_file)13243 void gmt_putdefaults (struct GMT_CTRL *GMT, char *this_file) {
13244 	/* ONLY USED BY GMTSET AND GMTDEFAULTS */
13245 	if (this_file)	/* File name is defined: use it as is */
13246 		gmtinit_savedefaults (GMT, this_file);
13247 	else {	/* Use local dir, tempdir, or workflow dir */
13248 		char path[PATH_MAX] = {""}, tag[GMT_LEN32] = {""};
13249 		if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Modern mode: Use the workflow directory and below */
13250 			gmt_hierarchy_tag (GMT->parent, GMT_SETTINGS_FILE, GMT_OUT, tag);
13251 			snprintf (path, PATH_MAX, "%s/%s%s", GMT->parent->gwf_dir, GMT_SETTINGS_FILE, tag);
13252 		}
13253 		else if (GMT->session.TMPDIR)	/* Write GMT->session.TMPDIR/gmt.conf */
13254 			snprintf (path, PATH_MAX, "%s/%s", GMT->session.TMPDIR, GMT_SETTINGS_FILE);
13255 		else	/* Write gmt.conf in current directory */
13256 			strcpy (path, GMT_SETTINGS_FILE);
13257 		gmtinit_savedefaults (GMT, path);
13258 	}
13259 }
13260 
13261 /*! Read user's gmt.conf file and initialize parameters */
gmt_getdefaults(struct GMT_CTRL * GMT,char * this_file)13262 int gmt_getdefaults (struct GMT_CTRL *GMT, char *this_file) {
13263 	char file[PATH_MAX];
13264 	int err = GMT_NOTSET;	/* Returned if this_file == NULL, classic mode, and no gmt.conf found */
13265 
13266 	if (this_file)	/* Defaults file is specified */
13267 		err = gmtinit_loaddefaults (GMT, this_file, false);
13268 	else {	/* Use local dir, tempdir, or workflow dir (modern mode) */
13269 		if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Modern mode: Use the workflow directory */
13270 			char path[PATH_MAX] = {""}, tag[GMT_LEN32] = {""};
13271 			gmt_hierarchy_tag (GMT->parent, GMT_SETTINGS_FILE, GMT_IN, tag);
13272 			snprintf (path, PATH_MAX, "%s/%s%s", GMT->parent->gwf_dir, GMT_SETTINGS_FILE, tag);
13273 			err = gmtinit_loaddefaults (GMT, path, false);
13274 		}
13275 		else if (gmtlib_getuserpath (GMT, GMT_SETTINGS_FILE, file))
13276 			err = gmtinit_loaddefaults (GMT, file, false);
13277 	}
13278 	return (err);
13279 }
13280 
13281 /*! Creates the name (if equivalent) or the string r[/g/b] corresponding to the RGB triplet or a pattern.
13282  * Example: gmtlib_putfill (GMT, fill) may produce "white" or "1/2/3" or "p300/7"
13283  */
gmtlib_putfill(struct GMT_CTRL * GMT,struct GMT_FILL * F)13284 char *gmtlib_putfill (struct GMT_CTRL *GMT, struct GMT_FILL *F) {
13285 
13286 	static char text[PATH_MAX+GMT_LEN256] = {""};
13287 	int i;
13288 
13289 	if (F == NULL) {	/* Mostly for the benefit of cppcheck */
13290 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtlib_putfill called with NULL fill pointer!\n");
13291 		return (text);
13292 	}
13293 	if (F->use_pattern) {
13294 		if (F->pattern_no)
13295 			snprintf (text, PATH_MAX+GMT_LEN256, "P%d/%d", F->dpi, F->pattern_no);
13296 		else
13297 			snprintf (text, PATH_MAX+GMT_LEN256, "P%d/%s", F->dpi, F->pattern);
13298 	}
13299 	else if (F->rgb[0] < -0.5)
13300 		strcpy (text, "-");
13301 	else if ((i = gmtlib_getrgb_index (GMT, F->rgb)) >= 0)
13302 		snprintf (text, PATH_MAX+GMT_LEN256, "%s", gmt_M_color_name[i]);
13303 	else if (gmt_M_is_gray (F->rgb))
13304 		snprintf (text, PATH_MAX+GMT_LEN256, "%.5g", gmt_M_t255(F->rgb,0));
13305 	else
13306 		snprintf (text, PATH_MAX+GMT_LEN256, "%.5g/%.5g/%.5g", gmt_M_t255(F->rgb,0), gmt_M_t255(F->rgb,1), gmt_M_t255(F->rgb,2));
13307 	gmtinit_append_trans (text, F->rgb[3]);
13308 	return (text);
13309 }
13310 
13311 /*! Creates the name (if equivalent) or the string r[/g/b] corresponding to the RGB triplet.
13312  * Example: gmt_putcolor (GMT, rgb) may produce "white" or "1/2/3"
13313  */
gmt_putcolor(struct GMT_CTRL * GMT,double * rgb)13314 char *gmt_putcolor (struct GMT_CTRL *GMT, double *rgb) {
13315 
13316 	static char text[GMT_LEN256] = {""};
13317 	int i;
13318 
13319 	if (rgb[0] < -0.5)
13320 		strcpy (text, "-");
13321 	else if ((i = gmtlib_getrgb_index (GMT, rgb)) >= 0)
13322 		snprintf (text, GMT_LEN256, "%s", gmt_M_color_name[i]);
13323 	else if (gmt_M_is_gray(rgb))
13324 		snprintf (text, GMT_LEN256, "%.5g", gmt_M_t255(rgb,0));
13325 	else
13326 		snprintf (text, GMT_LEN256, "%.5g/%.5g/%.5g", gmt_M_t255(rgb,0), gmt_M_t255(rgb,1), gmt_M_t255(rgb,2));
13327 	gmtinit_append_trans (text, rgb[3]);
13328 	return (text);
13329 }
13330 
13331 /*! Creates t the string r/g/b corresponding to the RGB triplet */
gmt_putrgb(struct GMT_CTRL * GMT,double * rgb)13332 char *gmt_putrgb (struct GMT_CTRL *GMT, double *rgb) {
13333 
13334 	static char text[GMT_LEN256] = {""};
13335 	gmt_M_unused(GMT);
13336 
13337 	if (rgb[0] < -0.5)
13338 		strcpy (text, "-");
13339 	else
13340 		snprintf (text, GMT_LEN256, "%.5g/%.5g/%.5g", gmt_M_t255(rgb,0), gmt_M_t255(rgb,1), gmt_M_t255(rgb,2));
13341 	gmtinit_append_trans (text, rgb[3]);
13342 	return (text);
13343 }
13344 
13345 /*! Creates the string c/m/y/k corresponding to the CMYK quadruplet */
gmtlib_putcmyk(struct GMT_CTRL * GMT,double * cmyk)13346 char *gmtlib_putcmyk (struct GMT_CTRL *GMT, double *cmyk) {
13347 
13348 	static char text[GMT_LEN256] = {""};
13349 	gmt_M_unused(GMT);
13350 
13351 	if (cmyk[0] < -0.5)
13352 		strcpy (text, "-");
13353 	else
13354 		snprintf (text, GMT_LEN256, "%.5g/%.5g/%.5g/%.5g", gmt_M_q(cmyk[0]), gmt_M_q(cmyk[1]), gmt_M_q(cmyk[2]), gmt_M_q(cmyk[3]));
13355 	gmtinit_append_trans (text, cmyk[4]);
13356 	return (text);
13357 }
13358 
13359 /*! Creates the string h/s/v corresponding to the HSV triplet */
gmtlib_puthsv(struct GMT_CTRL * GMT,double * hsv)13360 char *gmtlib_puthsv (struct GMT_CTRL *GMT, double *hsv) {
13361 
13362 	static char text[GMT_LEN256] = {""};
13363 	gmt_M_unused(GMT);
13364 
13365 	if (hsv[0] < -0.5)
13366 		strcpy (text, "-");
13367 	else
13368 		snprintf (text, GMT_LEN256, "%.5g-%.5g-%.5g", gmt_M_q(hsv[0]), gmt_M_q(hsv[1]), gmt_M_q(hsv[2]));
13369 	gmtinit_append_trans (text, hsv[3]);
13370 	return (text);
13371 }
13372 
13373 /*! . */
gmt_convert_units(struct GMT_CTRL * GMT,char * string,unsigned int default_unit,unsigned int target_unit)13374 double gmt_convert_units (struct GMT_CTRL *GMT, char *string, unsigned int default_unit, unsigned int target_unit) {
13375 	/* Converts the input string "value" to a float in units indicated by target_unit
13376 	 * If value does not contain a unit (''c', 'i', or p') then the units indicated
13377 	 * by default_unit will be used.
13378 	 * Both target_unit and default_unit are either GMT_PT, GMT_CM, GMT_INCH or GMT_M.
13379 	 */
13380 
13381 	int c = 0, len, given_unit;
13382 	bool have_unit = false;
13383 	double value;
13384 
13385 	if (string && strncmp (string, "auto", 4U) == 0) return GMT->session.d_NaN;	/* Auto in gmt.conf settings means undefined = NaN */
13386 
13387 	if ((len = (int)strlen(string))) {
13388 		c = string[len-1];
13389 		if ((have_unit = isalpha ((int)c))) string[len-1] = '\0';	/* Temporarily remove unit */
13390 	}
13391 
13392 	/* So c is either 0 (meaning default unit) or any letter (even junk like z) */
13393 
13394 	given_unit = gmtlib_unit_lookup (GMT, c, default_unit);	/* Will warn if c is not 0, 'c', 'i', 'p' */
13395 
13396 	if (!gmtinit_is_valid_number (string))
13397 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s not a valid number and may not be decoded properly.\n", string);
13398 
13399 	value = atof (string) * GMT->session.u2u[given_unit][target_unit];
13400 	if (have_unit) string[len-1] = (char)GMT->session.unit_name[given_unit][0];	/* Put back the (implied) given unit */
13401 
13402 	return (value);
13403 }
13404 
13405 /*! . */
gmtlib_unit_lookup(struct GMT_CTRL * GMT,int c,unsigned int unit)13406 unsigned int gmtlib_unit_lookup (struct GMT_CTRL *GMT, int c, unsigned int unit) {
13407 	if (!isalpha ((int)c))	/* Not a unit modifier - just return the current default unit */
13408 		return (unit);
13409 
13410 	/* Now we check for the c-i-p units and barf otherwise */
13411 
13412 	switch (c) {
13413 		case 'c': case 'C':	/* Centimeters */
13414 			unit = GMT_CM;
13415 			break;
13416 		case 'i': case 'I':	/* Inches */
13417 			unit = GMT_INCH;
13418 			break;
13419 		case 'p': case 'P':	/* Points */
13420 			unit = GMT_PT;
13421 			break;
13422 		default:
13423 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Length <unit> %c not supported - revert to default unit [%s]\n", (int)c, GMT->session.unit_name[unit]);
13424 			break;
13425 	}
13426 
13427 	return (unit);
13428 }
13429 
13430 /*! . */
gmt_hash_lookup(struct GMT_CTRL * GMT,const char * key,struct GMT_HASH * hashnode,unsigned int n,unsigned int n_hash)13431 int gmt_hash_lookup (struct GMT_CTRL *GMT, const char *key, struct GMT_HASH *hashnode, unsigned int n, unsigned int n_hash) {
13432 	int i;
13433 	unsigned int ui, k;
13434 
13435 	i = gmtinit_hash (GMT, key, n_hash);			/* Get initial hash key */
13436 
13437 	if (i < 0 || (ui = i) >= n) return (GMT_NOTSET);	/* Bad key */
13438 	if (hashnode[ui].n_id == 0) return (GMT_NOTSET);	/* No entry for this hash value */
13439 	/* Must search among the entries with identical hash value ui, starting at item k = 0 */
13440 	k = 0;
13441 	while (k < hashnode[ui].n_id && strcmp (hashnode[ui].key[k], key)) k++;
13442 	if (k == hashnode[ui].n_id) return (GMT_NOTSET);	/* Bad key; no match found */
13443 	return (hashnode[ui].id[k]);			/* Return array index that goes with this key */
13444 }
13445 
13446 /*! Set up hash table */
gmt_hash_init(struct GMT_CTRL * GMT,struct GMT_HASH * hashnode,char ** keys,unsigned int n_hash,unsigned int n_keys)13447 int gmt_hash_init (struct GMT_CTRL *GMT, struct GMT_HASH *hashnode, char **keys, unsigned int n_hash, unsigned int n_keys) {
13448 	unsigned int i, next;
13449 	int entry;
13450 
13451 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Enter: gmt_hash_init\n");
13452 	gmt_M_memset (hashnode, n_hash, struct GMT_HASH);	/* Start with NULL everywhere */
13453 	for (i = 0; i < n_keys; i++) {
13454 		entry = gmtinit_hash (GMT, keys[i], n_hash);
13455 		next = hashnode[entry].n_id;
13456 		if (next == GMT_HASH_MAXDEPTH) {
13457 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "%s makes hash-depth exceed hard-wired limit of %d - increment GMT_HASH_MAXDEPTH in gmt_hash.h and recompile GMT\n", keys[i], GMT_HASH_MAXDEPTH);
13458 			return GMT_DIM_TOO_SMALL;
13459 		}
13460 		hashnode[entry].key[next] = keys[i];
13461 		hashnode[entry].id[next]  = i;
13462 		hashnode[entry].n_id++;
13463 	}
13464 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Exit:  gmt_hash_init\n");
13465 	return GMT_OK;
13466 }
13467 
13468 /*! Return ID of requested ellipsoid, or -1 if not found */
gmt_get_ellipsoid(struct GMT_CTRL * GMT,char * name)13469 int gmt_get_ellipsoid (struct GMT_CTRL *GMT, char *name) {
13470 	int i, n;
13471 	char line[GMT_LEN128], ename[GMT_LEN64];
13472 	double pol_radius;
13473 
13474 	/* Try to get ellipsoid from the default list; use case-insensitive checking */
13475 
13476 	strncpy (ename, name, GMT_LEN64-1);		/* Make a copy of name */
13477 	gmt_str_tolower (ename);	/* Convert to lower case */
13478 	for (i = 0; i < GMT_N_ELLIPSOIDS; i++) {
13479 		strcpy (line, GMT->current.setting.ref_ellipsoid[i].name);
13480 		gmt_str_tolower (line);	/* Convert to lower case */
13481 		if (!strcmp (ename, line)) return (i);
13482 	}
13483 
13484 	i = GMT_N_ELLIPSOIDS - 1;	/* Place any custom ellipsoid in this position in array */
13485 
13486 	/* Read ellipsoid information as <a>,<finv> */
13487 	n = sscanf (name, "%lf,%s", &GMT->current.setting.ref_ellipsoid[i].eq_radius, line);
13488 	if (n < 1) {}	/* Failed to read arguments */
13489 	else if (n == 1)
13490 		GMT->current.setting.ref_ellipsoid[i].flattening = 0.0; /* Read equatorial radius only ... spherical */
13491 	else if (line[0] == 'b') {	/* Read semi-minor axis */
13492 		n = sscanf (&line[2], "%lf", &pol_radius);
13493 		GMT->current.setting.ref_ellipsoid[i].flattening = 1.0 - (pol_radius / GMT->current.setting.ref_ellipsoid[i].eq_radius);
13494 	}
13495 	else if (line[0] == 'f') {	/* Read flattening */
13496 		n = sscanf (&line[2], "%lf", &GMT->current.setting.ref_ellipsoid[i].flattening);
13497 	}
13498 	else {				/* Read inverse flattening */
13499 		n = sscanf (line, "%lf", &GMT->current.setting.ref_ellipsoid[i].flattening);
13500 		if (!gmt_M_is_spherical (GMT)) GMT->current.setting.ref_ellipsoid[i].flattening = 1.0 / GMT->current.setting.ref_ellipsoid[i].flattening;
13501 	}
13502 	if (n == 1) return (i);
13503 
13504 	if (gmt_M_compat_check (GMT, 4)) {
13505 		FILE *fp = NULL;
13506 		char path[PATH_MAX];
13507 		double slop;
13508 		/* Try to open as file first in (1) current dir, then in (2) $GMT->session.SHAREDIR */
13509 
13510 		GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Assigning PROJ_ELLIPSOID a file name is deprecated, use <a>,<inv_f> instead\n");
13511 		gmt_getsharepath (GMT, NULL, name, "", path, R_OK);
13512 
13513 		if ((fp = fopen (name, "r")) != NULL || (fp = fopen (path, "r")) != NULL) {
13514 			/* Found file, now get parameters */
13515 			while (fgets (line, GMT_LEN128, fp) && (line[0] == '#' || line[0] == '\n'));
13516 			fclose (fp);
13517 			n = sscanf (line, "%s %d %lf %lf %lf", GMT->current.setting.ref_ellipsoid[i].name,
13518 				&GMT->current.setting.ref_ellipsoid[i].date, &GMT->current.setting.ref_ellipsoid[i].eq_radius,
13519 				&pol_radius, &GMT->current.setting.ref_ellipsoid[i].flattening);
13520 			if (n != 5) {
13521 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while decoding user ellipsoid parameters (%s)\n", line);
13522 				return GMT_NOTSET;
13523 			}
13524 
13525 			if (pol_radius == 0.0) {} /* Ignore semi-minor axis */
13526 			else if (gmt_M_is_spherical (GMT)) {
13527 				/* zero flattening means we must compute flattening from the polar and equatorial radii: */
13528 
13529 				GMT->current.setting.ref_ellipsoid[i].flattening = 1.0 - (pol_radius / GMT->current.setting.ref_ellipsoid[i].eq_radius);
13530 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "user-supplied ellipsoid has implicit flattening of %.8f\n", GMT->current.setting.ref_ellipsoid[i].flattening);
13531 			}
13532 			/* else check consistency: */
13533 			else if ((slop = fabs (GMT->current.setting.ref_ellipsoid[i].flattening - 1.0 +
13534 			         (pol_radius/GMT->current.setting.ref_ellipsoid[i].eq_radius))) > 1.0e-8) {
13535 				GMT_Report (GMT->parent, GMT_MSG_WARNING,
13536 				            "Possible inconsistency in user ellipsoid parameters (%s) [off by %g]\n", line, slop);
13537 			}
13538 			return (i);
13539 		}
13540 	}
13541 
13542 	return (GMT_NOTSET);
13543 }
13544 
13545 /*! . */
gmt_get_time_system(struct GMT_CTRL * GMT,char * name,struct GMT_TIME_SYSTEM * time_system)13546 bool gmt_get_time_system (struct GMT_CTRL *GMT, char *name, struct GMT_TIME_SYSTEM *time_system) {
13547 	/* Convert TIME_SYSTEM into TIME_EPOCH and TIME_UNIT.
13548 	   TIME_SYSTEM can be one of the following: j2000, jd, mjd, s1985, unix, dr0001, rata
13549 	   or any string in the form "TIME_UNIT since TIME_EPOCH", like "seconds since 1985-01-01".
13550 	   This function only splits the strings, no validation or analysis is done.
13551 	   See gmt_init_time_system_structure for that.
13552 	   TIME_SYSTEM = other is completely ignored.
13553 	*/
13554 	char *epoch = NULL;
13555 	gmt_M_unused(GMT);
13556 
13557 	if (!strcmp (name, "j2000")) {
13558 		strcpy (time_system->epoch, "2000-01-01T12:00:00");
13559 		time_system->unit = 'd';
13560 	}
13561 	else if (!strcmp (name, "jd")) {
13562 		strcpy (time_system->epoch, "-4713-11-24T12:00:00");
13563 		time_system->unit = 'd';
13564 	}
13565 	else if (!strcmp (name, "mjd")) {
13566 		strcpy (time_system->epoch, "1858-11-17T00:00:00");
13567 		time_system->unit = 'd';
13568 	}
13569 	else if (!strcmp (name, "s1985")) {
13570 		strcpy (time_system->epoch, "1985-01-01T00:00:00");
13571 		time_system->unit = 's';
13572 	}
13573 	else if (!strcmp (name, "unix")) {
13574 		strcpy (time_system->epoch, "1970-01-01T00:00:00");
13575 		time_system->unit = 's';
13576 	}
13577 	else if (!strcmp (name, "dr0001") || !strcmp (name, "rd0001")) {	/* rd & dr because gmt.conf man says RD0001 */
13578 		strcpy (time_system->epoch, "0001-01-01T00:00:00");
13579 		time_system->unit = 's';
13580 	}
13581 	else if (!strcmp (name, "rata")) {
13582 		strcpy (time_system->epoch, "0000-12-31T00:00:00");
13583 		time_system->unit = 'd';
13584 	}
13585 	else if (!strcmp (name, "other")) {
13586 		/* Ignore completely */
13587 	}
13588 	else if ((epoch = strstr (name, "since"))) {
13589 		epoch += 6;
13590 		strncpy (time_system->epoch, epoch, GMT_LEN64-1);
13591 		time_system->unit = name[0];
13592 		if (!strncmp (name, "mon", 3U)) time_system->unit = 'o';
13593 	}
13594 	else
13595 		return (true);
13596 	return (false);
13597 }
13598 
13599 /*! . */
gmt_end(struct GMT_CTRL * GMT)13600 void gmt_end (struct GMT_CTRL *GMT) {
13601 	/* gmt_end will clean up after us. */
13602 
13603 	unsigned int i;
13604 
13605 	gmtinit_put_history (GMT);
13606 
13607 	/* Remove font structures */
13608 	gmt_M_free (GMT, GMT->session.font);
13609 #ifdef __FreeBSD__
13610 #ifdef _i386_
13611 	fpresetsticky (FP_X_DZ | FP_X_INV);
13612 	fpsetmask (FP_X_DZ | FP_X_INV);
13613 #endif
13614 #endif
13615 
13616 	gmt_M_str_free (GMT->init.runtime_bindir);
13617 	gmt_M_str_free (GMT->init.runtime_libdir);
13618 	gmt_M_str_free (GMT->init.runtime_library);
13619 	gmt_M_str_free (GMT->init.runtime_plugindir);
13620 	gmtinit_free_dirnames (GMT);
13621 	for (i = 0; i < GMT_N_PROJ4; i++)
13622 		gmt_M_str_free (GMT->current.proj.proj4[i].name);
13623 	gmt_M_free (GMT, GMT->current.proj.proj4);
13624 	for (i = 0; i < GMT_N_UNIQUE; i++)
13625 		gmt_M_str_free (GMT->init.history[i]);
13626 	gmtinit_reset_colformats (GMT);	/* Wipe settings */
13627 	for (i = 0; i < GMT->common.a.n_aspatial; i++)
13628 		gmt_M_str_free (GMT->common.a.name[i]);
13629 	gmt_M_str_free (GMT->common.h.title);
13630 	gmt_M_str_free (GMT->common.h.remark);
13631 	gmt_M_str_free (GMT->common.h.colnames);
13632 
13633 	if (GMT->current.setting.io_gridfile_shorthand) gmtinit_freeshorthand (GMT);
13634 
13635 	fflush (GMT->session.std[GMT_OUT]);	/* Make sure output buffer is flushed */
13636 
13637 	gmtlib_free_ogr (GMT, &(GMT->current.io.OGR), 1);	/* Free up the GMT/OGR structure, if used */
13638 	gmtlib_free_tmp_arrays (GMT);			/* Free emp memory for vector io or processing */
13639 	gmtinit_free_user_media (GMT);
13640 	/* Terminate PSL machinery (if used) */
13641 	PSL_endsession (GMT->PSL);
13642 	/* Free remote file information structure */
13643 	gmt_M_free (GMT, GMT->parent->remote_info);
13644 	/* Free snapshot of GMT common option structure */
13645 	gmt_M_free (GMT, GMT->parent->common_snapshot);	/* Free snapshot */
13646 
13647 #ifdef MEMDEBUG
13648 	gmt_memtrack_report (GMT);
13649 	gmt_M_str_free (GMT->hidden.mem_keeper);
13650 #endif
13651 
13652 	gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
13653 }
13654 
13655 /*! . */
gmtinit_begin_module_sub(struct GMTAPI_CTRL * API,const char * lib_name,const char * mod_name,struct GMT_CTRL ** Ccopy)13656 GMT_LOCAL struct GMT_CTRL *gmtinit_begin_module_sub (struct GMTAPI_CTRL *API, const char *lib_name, const char *mod_name, struct GMT_CTRL **Ccopy) {
13657 	/* All GMT modules (i.e. GMT_psxy, GMT_blockmean, ...) must call gmt_init_module
13658 	 * as their first call and call gmt_end_module as their last call.  This
13659 	 * allows us to capture the GMT control structure so we can reset all
13660 	 * parameters to what they were before exiting the module. Note:
13661 	 * 1. Session items that remain unchanged are not replicated if allocated separately.
13662 	 * 2. Items that may grow through session are not replicated if allocated separately.
13663 	 */
13664 
13665 	unsigned int i;
13666 	struct GMT_CTRL *GMT = API->GMT, *Csave = NULL;
13667 
13668 	Csave = calloc (1U, sizeof (struct GMT_CTRL));
13669 
13670 	gmtlib_free_tmp_arrays (GMT);			/* Free temp memory for vector io or processing */
13671 
13672 	/* First memcpy over everything; this will include pointer addresses we will have to fix below */
13673 
13674 	gmt_M_memcpy (Csave, GMT, 1, struct GMT_CTRL);
13675 
13676 	/* Increment level uint64_t */
13677 	GMT->hidden.func_level++;		/* This lets us know how deeply we are nested when a GMT module is called */
13678 
13679 	/* Now fix things that were allocated separately from the main GMT structure.  These are usually text strings
13680 	 * that were allocated via strdup since the structure only have a pointer allocated. */
13681 
13682 	/* GMT_INIT */
13683 	if (GMT->session.n_user_media) {
13684 		Csave->session.n_user_media = GMT->session.n_user_media;
13685 		Csave->session.user_media = gmt_M_memory (GMT, NULL, GMT->session.n_user_media, struct GMT_MEDIA);
13686 		Csave->session.user_media_name = gmt_M_memory (GMT, NULL, GMT->session.n_user_media, char *);
13687 		for (i = 0; i < GMT->session.n_user_media; i++) Csave->session.user_media_name[i] = strdup (GMT->session.user_media_name[i]);
13688 	}
13689 
13690 	/* GMT_PLOT */
13691 	if (GMT->current.plot.n_alloc) {
13692 		Csave->current.plot.n_alloc = GMT->current.plot.n_alloc;
13693 		Csave->current.plot.x = gmt_M_memory (GMT, NULL, GMT->current.plot.n_alloc, double);
13694 		Csave->current.plot.y = gmt_M_memory (GMT, NULL, GMT->current.plot.n_alloc, double);
13695 		Csave->current.plot.pen = gmt_M_memory (GMT, NULL, GMT->current.plot.n_alloc, unsigned int);
13696 		gmt_M_memcpy (Csave->current.plot.x, GMT->current.plot.x, GMT->current.plot.n_alloc, double);
13697 		gmt_M_memcpy (Csave->current.plot.y, GMT->current.plot.y, GMT->current.plot.n_alloc, double);
13698 		gmt_M_memcpy (Csave->current.plot.pen, GMT->current.plot.pen, GMT->current.plot.n_alloc, unsigned int);
13699 	}
13700 
13701 	/* GMT_IO */
13702 	Csave->current.io.OGR = gmtlib_duplicate_ogr (GMT, GMT->current.io.OGR);	/* Duplicate OGR struct, if set */
13703 	gmtlib_free_ogr (GMT, &(GMT->current.io.OGR), 1);		/* Free up the GMT/OGR structure, if used */
13704 	GMT->current.setting.io_lonlat_toggle[GMT_IN] = GMT->current.setting.io_lonlat_toggle[GMT_OUT] = false;
13705 
13706 	gmt_M_memset (Csave->current.io.o_format, GMT_MAX_COLUMNS, char *);
13707 	for (i = 0; i < GMT_MAX_COLUMNS; i++)
13708 		if (GMT->current.io.o_format[i]) Csave->current.io.o_format[i] = strdup (GMT->current.io.o_format[i]);
13709 
13710 	/* GMT_COMMON */
13711 	if (GMT->common.U.label) Csave->common.U.label = strdup (GMT->common.U.label);
13712 	for (i = 0; i < GMT->common.a.n_aspatial; i++)
13713 		if (GMT->common.a.name[i]) Csave->common.a.name[i] = strdup (GMT->common.a.name[i]);
13714 	if (GMT->common.h.title) Csave->common.h.title = strdup (GMT->common.h.title);
13715 	if (GMT->common.h.remark) Csave->common.h.remark = strdup (GMT->common.h.remark);
13716 	if (GMT->common.h.colnames) Csave->common.h.colnames = strdup (GMT->common.h.colnames);
13717 
13718 	/* DIR NAMES */
13719 
13720 	Csave->session.GSHHGDIR = (GMT->session.GSHHGDIR) ? strdup (GMT->session.GSHHGDIR) : NULL;
13721 	Csave->session.DCWDIR = (GMT->session.DCWDIR) ? strdup (GMT->session.DCWDIR) : NULL;
13722 	Csave->session.SHAREDIR = (GMT->session.SHAREDIR) ? strdup (GMT->session.SHAREDIR) : NULL;
13723 	Csave->session.HOMEDIR = (GMT->session.HOMEDIR) ? strdup (GMT->session.HOMEDIR) : NULL;
13724 	Csave->session.USERDIR = (GMT->session.USERDIR) ? strdup (GMT->session.USERDIR) : NULL;
13725 	Csave->session.CACHEDIR = (GMT->session.CACHEDIR) ? strdup (GMT->session.CACHEDIR) : NULL;
13726 	Csave->session.DATADIR = (GMT->session.DATADIR) ? strdup (GMT->session.DATADIR) : NULL;
13727 	Csave->session.TMPDIR = (GMT->session.TMPDIR) ? strdup (GMT->session.TMPDIR) : NULL;
13728 	Csave->session.CUSTOM_LIBS = (GMT->session.CUSTOM_LIBS) ? strdup (GMT->session.CUSTOM_LIBS) : NULL;
13729 	Csave->session.DATASERVER = (GMT->session.DATASERVER) ? strdup (GMT->session.DATASERVER) : NULL;
13730 
13731 	/* Reset all the common.?.active settings to false */
13732 
13733 	GMT->common.B.active[GMT_PRIMARY] = GMT->common.B.active[GMT_SECONDARY] = GMT->common.K.active = GMT->common.O.active = false;
13734 	GMT->common.P.active = GMT->common.U.active = GMT->common.V.active = false;	GMT->current.setting.map_logo = false;
13735 	GMT->common.X.active = GMT->common.Y.active = false;
13736 	GMT->common.R.active[RSET] = GMT->common.R.active[ISET] = GMT->common.R.active[GSET] = GMT->common.R.active[FSET] = GMT->common.J.active = false;
13737 	GMT->common.a.active = GMT->common.b.active[GMT_IN] = GMT->common.b.active[GMT_OUT] = false;
13738 	GMT->common.f.active[GMT_IN] = GMT->common.f.active[GMT_OUT] = GMT->common.g.active = GMT->common.h.active = false;
13739 	GMT->common.p.active = GMT->common.s.active = GMT->common.t.active = GMT->common.colon.active = false;
13740 	gmt_M_memset (GMT->common.b.ncol, 2, int);
13741 
13742 	/* Initialize bg fill to white although we don't use it until GMT->current.map.frame.paint[GMT_Z] = true;
13743 	   But needed when using images with an transparency layer.
13744 	*/
13745 
13746 	GMT->current.map.frame.fill[GMT_Z].rgb[0] = GMT->current.map.frame.fill[GMT_Z].rgb[1] = GMT->current.map.frame.fill[GMT_Z].rgb[2] = 1.0;
13747 
13748 	*Ccopy = Csave; /* Pass back out for safe-keeping by the module until gmt_end_module is called */
13749 
13750 	GMT->init.module_name = mod_name;
13751 	GMT->init.module_lib  = lib_name;
13752 
13753 	if (gmt_M_is_dnan (GMT->current.setting.map_vector_shape))	/* Do it here since independent on map size */
13754 		GMT->current.setting.map_vector_shape = 0.5;
13755 
13756 	return (GMT);
13757 }
13758 
13759 /* Subplot functions */
13760 
gmtinit_get_current_panel(struct GMTAPI_CTRL * API,int fig,int * row,int * col,double gap[],char * tag,unsigned int * first)13761 GMT_LOCAL int gmtinit_get_current_panel (struct GMTAPI_CTRL *API, int fig, int *row, int *col, double gap[], char *tag, unsigned int *first) {
13762 	/* Gets the current subplot panel, returns 1 if found and 0 otherwise.
13763 	 * If gap == NULL that means that not finding a panel file is OK since it may be the first; we are just trying to get row,col. */
13764 	char file[PATH_MAX] = {""};
13765 	FILE *fp = NULL;
13766 	int ios;
13767 	snprintf (file, PATH_MAX, "%s/gmt.panel.%d", API->gwf_dir, fig);
13768 	if (access (file, F_OK))	{	/* Panel selection file not available so we are not doing subplots */
13769 		if (gap == NULL) {	/* By default we do the first panel */
13770 			*row = *col = INT_MAX;
13771 			return GMT_NOERROR;
13772 		}
13773 		GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_current_panel: No current panel selected so not in subplot mode\n");
13774 		API->error = GMT_NOERROR;
13775 		return GMT_RUNTIME_ERROR;	/* It is an "error" in the sense we don't have a panel situation */
13776 	}
13777 	/* Here there is a current panel, get it */
13778 	if ((fp = fopen (file, "r")) == NULL) {
13779 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s!\n", file);
13780 		API->error = GMT_RUNTIME_ERROR;
13781 		return GMT_RUNTIME_ERROR;
13782 	}
13783 	if (gap == NULL) {
13784 		if ((ios = fscanf (fp, "%d %d %*g %*g %*g %*g %*d", row, col)) != 2) {
13785 			GMT_Report (API, GMT_MSG_ERROR, "Failed to decode record from %s!\n", file);
13786 			API->error = GMT_RUNTIME_ERROR;
13787 			fclose (fp);
13788 			return GMT_RUNTIME_ERROR;
13789 		}
13790 	}
13791 	else if ((ios = fscanf (fp, "%d %d %lg %lg %lg %lg %d %[^\n]", row, col, &gap[XLO], &gap[XHI], &gap[YLO], &gap[YHI], first, tag)) != 8) {
13792 		GMT_Report (API, GMT_MSG_ERROR, "Failed to decode record from %s!\n", file);
13793 		API->error = GMT_RUNTIME_ERROR;
13794 		fclose (fp);
13795 		return GMT_RUNTIME_ERROR;
13796 	}
13797 	fclose (fp);
13798 	if (*row < 0 || *col < 0) {
13799 		GMT_Report (API, GMT_MSG_ERROR, "Current panel has row or column outside range!\n");
13800 		API->error = GMT_RUNTIME_ERROR;
13801 		return GMT_RUNTIME_ERROR;
13802 	}
13803 	GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_current_panel: Current panel is (%d, %d)\n", *row, *col);
13804 	return GMT_NOERROR;
13805 }
13806 
gmt_set_current_panel(struct GMTAPI_CTRL * API,int fig,int row,int col,double gap[],char * label,unsigned first)13807 int gmt_set_current_panel (struct GMTAPI_CTRL *API, int fig, int row, int col, double gap[], char *label, unsigned first) {
13808 	/* Update gmt.panel with current pane's (row,col) and write first as 0 or 1.
13809 	 * first should be 1 the first time we visit this panel so that the automatic -B
13810 	 * and panel tag stuff only happens once. */
13811 	char file[PATH_MAX] = {""}, *L = NULL;
13812 	static char *dummy = "@";	/* Signify "use the the auto label" */
13813 	FILE *fp = NULL;
13814 	L = (label && label[0]) ? label : dummy;
13815 	snprintf (file, PATH_MAX, "%s/gmt.panel.%d", API->gwf_dir, fig);
13816 	if ((fp = fopen (file, "w")) == NULL) {
13817 		GMT_Report (API, GMT_MSG_ERROR, "Unable to create file %s!\n", file);
13818 		API->error = GMT_RUNTIME_ERROR;
13819 		return GMT_RUNTIME_ERROR;
13820 	}
13821 	if (gap == NULL)
13822 		fprintf (fp, "%d %d 0 0 0 0 %d %s\n", row, col, first, L);
13823 	else
13824 		fprintf (fp, "%d %d %g %g %g %g %d %s\n", row, col, gap[XLO], gap[XHI], gap[YLO], gap[YHI], first, L);
13825 	fclose (fp);
13826 	if (first) API->GMT->current.plot.color_seq_id[0] = API->GMT->current.plot.color_seq_id[1] = 0;	/* Reset for new panel */
13827 	API->error = GMT_NOERROR;
13828 	return GMT_NOERROR;
13829 }
13830 
gmt_get_next_panel(struct GMTAPI_CTRL * API,int fig,int * row,int * col)13831 int gmt_get_next_panel (struct GMTAPI_CTRL *API, int fig, int *row, int *col) {
13832 	/* Auto-advance to next panel, with initialization at first panel.  The order of advancement
13833 	 * was set by -A's +v modifier and is found by reading the subplotorder file */
13834 
13835 	int n_rows, n_cols, order;
13836 	char file[PATH_MAX] = {""};
13837 	FILE *fp = NULL;
13838 
13839 	snprintf (file, PATH_MAX, "%s/gmt.subplotorder.%d", API->gwf_dir, fig);
13840 	if ((fp = fopen (file, "r")) == NULL) {
13841 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s!\n", file);
13842 		API->error = GMT_ERROR_ON_FOPEN;
13843 		return GMT_ERROR_ON_FOPEN;
13844 	}
13845 	/* Read the matrix dimensions and the marching order */
13846 	if (fscanf (fp, "%d %d %d", &n_rows, &n_cols, &order) != 3) {
13847 		GMT_Report (API, GMT_MSG_ERROR, "Unable to read file %s!\n", file);
13848 		API->error = GMT_DATA_READ_ERROR;
13849 		fclose (fp);
13850 		return GMT_DATA_READ_ERROR;
13851 	}
13852 	fclose (fp);
13853 
13854 	/* If the panel file does not exist we initialize to row = col = 0 */
13855 	if (*col != INT_MAX && gmtinit_get_current_panel (API, fig, row, col, NULL, NULL, NULL)) {	/* Not good */
13856 		API->error = GMT_RUNTIME_ERROR;
13857 		return GMT_RUNTIME_ERROR;
13858 	}
13859 
13860 	if (*row == INT_MAX && *col == INT_MAX)	/* First panel */
13861 		*row = *col = 0;
13862 	else if (*col == INT_MAX) {	/* row has index which gives (row,col) depending on order */
13863 		unsigned int index = *row;
13864 		if (order == GMT_IS_COL_FORMAT) {	/* March down columns */
13865 			*col = index / n_rows;
13866 			*row = index % n_rows;
13867 		}
13868 		else {
13869 			*col = index % n_cols;
13870 			*row = index / n_cols;
13871 		}
13872 		GMT_Report (API, GMT_MSG_DEBUG, "Index %u goes to (%u, %u)\n", index, *row, *col);
13873 	}
13874 	else {	/* Auto-advance to next panel */
13875 		if (order == GMT_IS_COL_FORMAT) {	/* Going down columns */
13876 			if (*row == (n_rows-1)) /* Top of next column */
13877 				*row = 0, (*col)++;
13878 			else	/* Down current column */
13879 				(*row)++;
13880 		}
13881 		else {	/* Going across rows */
13882 			if (*col == (n_cols-1)) /* Start of next row */
13883 				*col = 0, (*row)++;
13884 			else	/* Across current row */
13885 				(*col)++;
13886 		}
13887 	}
13888 
13889 	API->error = GMT_NOERROR;
13890 	return GMT_NOERROR;
13891 }
13892 
gmt_subplot_gaps(struct GMTAPI_CTRL * API,int fig,double * gap)13893 void gmt_subplot_gaps (struct GMTAPI_CTRL *API, int fig, double *gap) {
13894 	/* Need to determine any subplot-wide gaps in gmt subplot set before we even start plotting */
13895 	char file[PATH_MAX] = {""}, line[PATH_MAX] = {""};
13896 	bool found = false;
13897 	FILE *fp = NULL;
13898 
13899 	gmt_M_memset (gap, 4, double);
13900 	/* Now read subplot information file */
13901 	snprintf (file, PATH_MAX, "%s/gmt.subplot.%d", API->gwf_dir, fig);
13902 	if (access (file, F_OK)) {	/* Subplot information file not available */
13903 		GMT_Report (API, GMT_MSG_ERROR, "No subplot information file found!\n");
13904 		return;
13905 	}
13906 	/* Here there is an information file, get it */
13907 	if ((fp = fopen (file, "r")) == NULL) {
13908 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s!\n", file);
13909 		return;
13910 	}
13911 
13912 	/* Now read it */
13913 	while (!found && fgets (line, PATH_MAX, fp)) {
13914 		if (line[0] == '\n')	/* Blank line */
13915 			continue;
13916 		if (!strncmp (line, "# GAPS:", 7U)) {
13917 			sscanf (&line[8], "%lg %lg %lg %lg", &gap[XLO], &gap[XHI], &gap[YLO], &gap[YHI]);
13918 			found = true;
13919 		}
13920 		else if (line[0] != '#')
13921 			found = true;	/* Done reading */
13922 	}
13923 	fclose (fp);
13924 }
13925 
13926 /*! Return information about current panel */
gmt_subplot_info(struct GMTAPI_CTRL * API,int fig)13927 struct GMT_SUBPLOT *gmt_subplot_info (struct GMTAPI_CTRL *API, int fig) {
13928 	/* Only called under modern mode */
13929 	char file[PATH_MAX] = {""}, line[PATH_MAX] = {""}, tmp[GMT_LEN128] = {""}, *c = NULL;
13930 	bool found = false;
13931 	int row, col;
13932 	unsigned int first, k;
13933 	int n;
13934 	double gap[4] = {0.0, 0.0, 0.0, 0.0};
13935 	struct GMT_SUBPLOT *P = NULL;
13936 	FILE *fp = NULL;
13937 
13938 	API->error = GMT_RUNTIME_ERROR;
13939 	if (gmtinit_get_current_panel (API, fig, &row, &col, gap, tmp, &first))	/* No panel or there was an error */
13940 		return NULL;
13941 
13942 	/* Now read subplot information file */
13943 	snprintf (file, PATH_MAX, "%s/gmt.subplot.%d", API->gwf_dir, fig);
13944 	if (access (file, F_OK)) {	/* Subplot information file not available */
13945 		GMT_Report (API, GMT_MSG_ERROR, "No subplot information file found!\n");
13946 		return NULL;
13947 	}
13948 	/* Here there is an information file, get it */
13949 	if ((fp = fopen (file, "r")) == NULL) {
13950 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s!\n", file);
13951 		return NULL;
13952 	}
13953 
13954 	P = &(API->GMT->current.plot.panel);	/* Lazy shorthand only */
13955 	P->dir[GMT_X] = P->dir[GMT_Y] = +1;	/* Default direction of Cartesian axis if -JX */
13956 
13957 	/* Now read it */
13958 	while (!found && fgets (line, PATH_MAX, fp)) {
13959 		if (line[0] == '\n')	/* Blank line */
13960 			continue;
13961 		if (line[0] == '#') {	/* Comment line */
13962 			if (!strncmp (line, "# ORIGIN:", 9U))
13963 				sscanf (&line[10], "%lg %lg", &P->origin[GMT_X], &P->origin[GMT_Y]);
13964 			else if (!strncmp (line, "# DIMENSION:", 12U))
13965 				sscanf (&line[13], "%lg %lg", &P->dim[GMT_X], &P->dim[GMT_Y]);
13966 			else if (!strncmp (line, "# PARALLEL:", 11U))
13967 				P->parallel = atoi (&line[12]);
13968 			else if (!strncmp (line, "# INSIDE:", 9U))
13969 				P->inside = atoi (&line[10]);
13970 			else if (!strncmp (line, "# DIRECTION:", 12U))
13971 				sscanf (&line[13], "%d %d", &P->dir[GMT_X], &P->dir[GMT_Y]);
13972 			else if (!strncmp (line, "# GAPS:", 7U))
13973 				sscanf (&line[8], "%lg %lg %lg %lg", &P->gap[XLO], &P->gap[XHI], &P->gap[YLO], &P->gap[YHI]);
13974 			continue;
13975 		}
13976  		if ((n = sscanf (line, "%*d %d %d %d %d", &P->row, &P->col, &P->nrows, &P->ncolumns)) != 4) {
13977 			GMT_Report (API, GMT_MSG_ERROR, "Failure while decoding row/col from subplot information file %s.  Bad format? [%s] (n=%d)\n", file, line, n);
13978 			fclose (fp);
13979 			return NULL;
13980 		}
13981 		if (row >= P->nrows || col >= P->ncolumns) {
13982 			GMT_Report (API, GMT_MSG_ERROR, "Selected current panel (%d,%d) exceeds dimension of current subplot (%dx%d)\n", row, col, P->nrows, P->ncolumns);
13983 			GMT_Report (API, GMT_MSG_ERROR, "Note: Subplot panel first index starts at 0 or 0,0\n");
13984 			fclose (fp);
13985 			return NULL;
13986 		}
13987 		if (P->row == row && P->col == col) {	/* Found it */
13988 			if ((n = sscanf (line, "%*d %*d %*d %*d %*d %lg %lg %lg %lg %s %lg %lg %lg %lg %s %s %s %s %lg %lg %s",
13989 				&P->x, &P->y, &P->w, &P->h, P->tag, &P->off[GMT_X], &P->off[GMT_Y], &P->clearance[GMT_X], &P->clearance[GMT_Y], P->refpoint, P->justify, P->fill, P->pen, &P->soff[GMT_X], &P->soff[GMT_Y], P->shade)) != 16) {
13990 				GMT_Report (API, GMT_MSG_ERROR, "Failure while decoding subplot information file %s.  Bad format? [%s] (n=%d)\n", file, line, n);
13991 				fclose (fp);
13992 				return NULL;
13993 			}
13994 			if (P->fill[0] == '-') P->fill[0] = '\0';	/* - means no fill */
13995 			if (P->shade[0] == '-') P->shade[0] = '\0';	/* - means no fill */
13996 			if (P->pen[0] == '-') P->pen[0] = '\0';		/* - means no pen */
13997 			P->first = first;
13998 			gmt_M_memcpy (P->gap, gap, 4, double);
13999 			if (strcmp (tmp, "@")) strncpy (P->tag, tmp, GMT_LEN128-1);	/* Replace auto-tag with manually added tag */
14000 			if ((c = strchr (line, GMT_ASCII_GS)) == NULL) {	/* Get the position before frame setting */
14001 				GMT_Report (API, GMT_MSG_ERROR, "Failure while decoding subplot information file %s.  Bad format? [%s] (n=%d)\n", file, line, n);
14002 				fclose (fp);
14003 				return NULL;
14004 			}
14005 			c++;	k = 0;	/* Now at start of axes */
14006 			while (*c != GMT_ASCII_GS) P->Baxes[k++] = *(c++);	/* Copy it over until end */
14007 			P->Baxes[k] = '\0'; c++;	k = 0;	/* Now at start of xaxis */
14008 			while (*c != GMT_ASCII_GS) P->Bxlabel[k++] = *(c++);	/* Copy it over until end */
14009 			P->Bxlabel[k] = '\0'; c++;	k = 0;	/* Now at start of yaxis */
14010 			while (*c != GMT_ASCII_GS) P->Bylabel[k++] = *(c++);	/* Copy it over until end */
14011 			P->Bylabel[k] = '\0'; c++;	k = 0;	/* Now at start of xannot */
14012 			while (*c != GMT_ASCII_GS) P->Bxannot[k++] = *(c++);	/* Copy it over until end */
14013 			P->Bxannot[k] = '\0'; c++;	k = 0;	/* Now at start of yannot */
14014 			while (*c != GMT_ASCII_GS) P->Byannot[k++] = *(c++);	/* Copy it over until end */
14015 			P->Byannot[k] = '\0';
14016 			found = true;	/* We are done */
14017 		}
14018 	}
14019 	fclose (fp);
14020 	if (!found) {
14021 		GMT_Report (API, GMT_MSG_ERROR, "Unable to match specified row,col with subplot information in %s\n", file);
14022 		return NULL;
14023 	}
14024 	API->error = GMT_NOERROR;
14025 	P->active = 1;
14026 	return (P);
14027 }
14028 
14029 /*! Determine if the current module is a PostScript-producing module that will be writing PostScript */
gmtinit_is_PS_module(struct GMTAPI_CTRL * API,const char * name,const char * keys,struct GMT_OPTION ** in_options)14030 GMT_LOCAL bool gmtinit_is_PS_module (struct GMTAPI_CTRL *API, const char *name, const char *keys, struct GMT_OPTION **in_options) {
14031 	struct GMT_OPTION *opt = NULL, *options = NULL;
14032 
14033 	if (keys == NULL || in_options == NULL) return false;	/* Definitively classic code, possibly MB system */
14034 	if (keys[0] == '\0' || (strstr (keys, ">X}") == NULL && strstr (keys, ">?}") == NULL)) return false;	/* Can never produce PostScript */
14035 
14036 	options = *in_options;
14037 
14038 	if (!strncmp (name, "gmtinfo", 7U)) return false;	/* Does not ever produce PS */
14039 	if (!strncmp (name, "gmtread", 7U)) return false;	/* Does not ever produce PS */
14040 	if (!strncmp (name, "gmtwrite", 8U)) return false;	/* Does not ever produce PS */
14041 	if (!strncmp (name, "gmtbinstats", 11U)) return false;	/* Does not ever return PS */
14042 
14043 	/* Must do more specific checking since some of the PS producers take options that turns them into other things... */
14044 	if (!strncmp (name, "psbasemap", 9U)) {	/* Check for -A option */
14045 		if ((opt = GMT_Find_Option (API, 'A', options))) return false;	/* -A writes dataset */
14046 	}
14047 	else if (!strncmp (name, "pscoast", 7U)) {	/* Check for -M -E options */
14048 		if ((opt = GMT_Find_Option (API, 'M', options))) return false;	/* -M writes dataset */
14049 		if ((opt = GMT_Find_Option (API, 'J', options))) return true;	/* -J writes PS regardless of -E */
14050 		if ((opt = GMT_Find_Option (API, 'E', options)) == NULL) return true;	/* Without -E writes PS */
14051 		if (gmt_found_modifier (API->GMT, opt->arg, "cCgp")) return true;	/* -E...+g|p|c|C writes PS */
14052 		if (gmt_found_modifier (API->GMT, opt->arg, "rR")) return false;	/* -E...+r|R writes dataset */
14053 	}
14054 	else if (!strncmp (name, "grdimage", 8U)) {	/* Check for -A option */
14055 		if ((opt = GMT_Find_Option (API, 'A', options))) return false;	/* -A writes image */
14056 	}
14057 	else if (!strncmp (name, "grdcontour", 10U)) {	/* Check for -D option */
14058 		if ((opt = GMT_Find_Option (API, 'D', options))) return false;	/* -D writes dataset */
14059 	}
14060 	else if (!strncmp (name, "pscontour", 9U)) {	/* Check for -D option */
14061 		if ((opt = GMT_Find_Option (API, 'D', options))) return false;	/* -D writes dataset */
14062 	}
14063 	else if (!strncmp (name, "psevents", 8U)) {	/* Check for -D option */
14064 		if ((opt = GMT_Find_Option (API, 'A', options)) == NULL) return true;	/* All but -A is guaranteed to write PS */
14065 		if (opt->arg[0] == 'r' && opt->arg[1] && isdigit (opt->arg[1])) return false;	/* This is just preparing an densely sampled file */
14066 		return true;	/* Any other case gets here and makes PS */
14067 	}
14068 	else if (!strncmp (name, "pshistogram", 11U)) {	/* Check for -I option */
14069 		if ((opt = GMT_Find_Option (API, 'I', options))) return false;	/* -I writes dataset */
14070 	}
14071 	else if (!strncmp (name, "pssolar", 7U)) {	/* Check for -M -I options */
14072 		if ((opt = GMT_Find_Option (API, 'M', options))) return false;	/* -M writes dataset */
14073 		if ((opt = GMT_Find_Option (API, 'I', options))) return false;	/* -I writes dataset */
14074 	}
14075 	return true;	/* Remaining PostScript producing modules always write PostScript */
14076 }
14077 
gmt_round_wesn(double wesn[],bool geo)14078 void gmt_round_wesn (double wesn[], bool geo) {	/* Use data range to round to nearest reasonable multiples */
14079 	bool set[2] = {false, false};
14080 	unsigned int side, item;
14081 	double mag, inc, range[2] = {0.0, 0.0};
14082 	range[GMT_X] = wesn[XHI] - wesn[XLO];
14083 	range[GMT_Y] = wesn[YHI] - wesn[YLO];
14084 	if (geo) {	/* Special checks due to periodicity */
14085 		if (range[GMT_X] > 306.0) {	/* If within 15% of a full 360 we promote to 360 */
14086 			wesn[XLO] = 0.0;	wesn[XHI] = 360.0;
14087 			set[GMT_X] = true;
14088 		}
14089 		if (range[GMT_Y] > 153.0) {	/* If within 15% of a full 180 we promote to 180 */
14090 			wesn[YLO] = -90.0;	wesn[YHI] = 90.0;
14091 			set[GMT_Y] = true;
14092 		}
14093 	}
14094 	else {			/* Add a tinny pad so that the rounding algorithm always round to next fifth of decade */
14095 		double dx, dy;
14096 		dx = range[GMT_X] * 0.001;		dy = range[GMT_Y] * 0.001;
14097 		wesn[0] -= dx;	wesn[1] += dx;	wesn[2] -= dy;	wesn[3] += dy;
14098 	}
14099 	for (side = GMT_X, item = XLO; side <= GMT_Y; side++) {
14100 		if (set[side]) continue;	/* Done above */
14101 		mag = rint (log10 (range[side])) - 1.0;
14102 		inc = pow (10.0, mag);
14103 		if ((range[side] / inc) > 10.0) inc *= 2.0;	/* Factor of 2 in the rounding */
14104 		if ((range[side] / inc) > 10.0) inc *= 2.5;	/* Factor of 5 in the rounding */
14105 		if (geo) {	/* Use arc integer minutes or seconds if possible */
14106 			double s = 1;
14107 			if (inc < 1.0 && inc > 0.05) {	/* Nearest arc minute */
14108 				s = 60.0; inc = 1.0;
14109 				if ((s * range[side] / inc) > 10.0) inc *= 2.0;	/* 2 arcmin */
14110 				if ((s * range[side] / inc) > 10.0) inc *= 2.5;	/* 5 arcmin */
14111 			}
14112 			else if (inc < 0.1 && inc > 0.005) {	/* Nearest arc second */
14113 				s = 3600.0; inc = 1.0;
14114 				if ((s * range[side] / inc) > 10.0) inc *= 2.0;	/* 2 arcsec */
14115 				if ((s * range[side] / inc) > 10.0) inc *= 2.5;	/* 5 arcsec */
14116 			}
14117 			wesn[item] = (floor (s * wesn[item] / inc) * inc) / s;	item++;
14118 			wesn[item] = (ceil  (s * wesn[item] / inc) * inc) / s;	item++;
14119 		}
14120 		else {
14121 			/* Round BB to the next fifth of a decade. */
14122 			double x, one_fifth_dec = inc / 5;					/* One fifth of a decade */
14123 			x = (floor(wesn[item] / inc) * inc);
14124 			wesn[item] = x - ceil((x - wesn[item]) / one_fifth_dec) * one_fifth_dec;	item++;
14125 			x = (ceil(wesn[item] / inc) * inc);
14126 			wesn[item] = x - floor((x - wesn[item]) / one_fifth_dec) * one_fifth_dec;	item++;
14127 		}
14128 	}
14129 }
14130 
gmtinit_get_region_from_data(struct GMTAPI_CTRL * API,int family,bool exact,struct GMT_OPTION ** options,double wesn[])14131 GMT_LOCAL int gmtinit_get_region_from_data (struct GMTAPI_CTRL *API, int family, bool exact, struct GMT_OPTION **options, double wesn[]) {
14132 	/* Determines the data region by examining the input data.  This could be a grid or datasets.  The
14133 	 * latter may be one or many files, or none, meaning we must capture stdin.  If we do, then we must
14134 	 * add that temporary file to the module's options, otherwise we cannot read the data a 2nd time.
14135 	 * We return the wesn array with the exact or rounded region, depending on the setting of exact.
14136 	 */
14137 	unsigned int item;
14138 	int k_data;
14139 	bool geo;
14140 	bool is_PS, is_oneliner;
14141 	struct GMT_GRID *G = NULL;
14142 	struct GMT_OPTION *opt = NULL, *head = NULL, *tmp = NULL;
14143 	struct GMT_DATASET *Out = NULL;
14144 	char virt_file[GMT_VF_LEN] = {""}, tmpfile[PATH_MAX] = {""}, *list = "bfi:", *file = NULL;
14145 	struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
14146 
14147 	switch (family) {
14148 		case GMT_IS_GRID:
14149 			if ((opt = GMT_Find_Option (API, GMT_OPT_INFILE, *options)) == NULL) return GMT_NO_INPUT;	/* Got no input argument*/
14150 			if ((k_data = gmt_remote_dataset_id (API, opt->arg)) != GMT_NOTSET) {	/* This is a remote grid so -Rd */
14151 				wesn[XLO] = -180.0;	wesn[XHI] = +180.0;	wesn[YLO] = -90.0;	wesn[YHI] = +90.0;
14152 			}
14153 			else {	/* Must read the grid header */
14154 				file = opt->arg;
14155 				if (gmt_access (API->GMT, file, R_OK)) return GMT_FILE_NOT_FOUND;	/* No such file found */
14156 				if ((G = GMT_Read_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY|GMT_IO_RESET, NULL, file, NULL)) == NULL)
14157 					return API->error;	/* Failure to read grid header */
14158 				gmt_M_memcpy (wesn, G->header->wesn, 4, double);	/* Copy over the grid region */
14159 				HH = gmt_get_H_hidden (G->header);
14160 				if (!exact) gmt_round_wesn (wesn, HH->grdtype > 0);	/* Use grid w/e/s/n to round to nearest reasonable multiples */
14161 				if (GMT_Destroy_Data (API, &G) != GMT_NOERROR) return API->error;	/* Failure to destroy the temporary grid structure */
14162 			}
14163 			break;
14164 
14165 		case GMT_IS_DATASET:
14166 			for (opt = *options; opt; opt = opt->next) {	/* Loop over all options */
14167 				if (opt->option != GMT_OPT_INFILE) continue;	/* Look for input files we can append to new list */
14168 				if ((tmp = GMT_Make_Option (API, GMT_OPT_INFILE, opt->arg)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14169 					return API->error;	/* Failure to make new option and append to list */
14170 				gmt_filename_set (opt->arg);	/* Replace any spaces with ASCII 29, will be undone by GMT_Get_FilePath */
14171 			}
14172 			if (head == NULL) {	/* User gave no input so we must process stdin */
14173 				/* Make name for a temporary file */
14174 				FILE *fp = NULL;
14175 				char *file = NULL;
14176 				void *content = NULL;
14177 				size_t n_read = 0;
14178 
14179 				GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_region_from_data: Must save stdin to a temporary file.\n");
14180 
14181 				if ((fp = gmt_create_tempfile (API, "gmt_saved_stdin", NULL, tmpfile)) == NULL) {	/* Not good... */
14182 					GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_region_from_data: Could not create and open temporary file name %s.\n", tmpfile);
14183 					return GMT_RUNTIME_ERROR;
14184 				}
14185 				file = tmpfile;
14186 
14187 				/* Dump stdin to that temp file */
14188 				GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_region_from_data: Send stdin to %s.\n", file);
14189 				if ((content = malloc (GMT_BUFSIZ)) == NULL) {
14190 					GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_region_from_data: Unable to allocate %d bytes for buffer.\n", GMT_BUFSIZ);
14191 				    fclose (fp);
14192 					return GMT_RUNTIME_ERROR;
14193 				}
14194 				while ((n_read = fread (content, 1, GMT_BUFSIZ, API->GMT->session.std[GMT_IN]))) {
14195 			        if (fwrite (content, 1, n_read, fp) != n_read) {
14196 						GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_region_from_data: fwrite failure.\n");
14197 						free (content);
14198 				    	fclose (fp);
14199 						return GMT_RUNTIME_ERROR;
14200 					}
14201 			    }
14202 			    fclose (fp);
14203 				free (content);
14204 				if ((tmp = GMT_Make_Option (API, GMT_OPT_INFILE, file)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14205 					return API->error;	/* Failure to make new option or append to list */
14206 				if ((tmp = GMT_Make_Option (API, GMT_OPT_INFILE, file)) == NULL || (*options = GMT_Append_Option (API, tmp, *options)) == NULL)
14207 					return API->error;	/* Failure to append option to calling module option list */
14208 				GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_region_from_data: Replace stdin input with -<%s.\n", file);
14209 			}
14210 
14211 			/* Here we have all the input options OR a single tempfile input option.  Now look for special modifiers and add those too */
14212 			for (item = 0; item < strlen (list); item++) {
14213 				if ((opt = GMT_Find_Option (API, list[item], *options)) == NULL) continue;
14214 				if ((tmp = GMT_Make_Option (API, list[item], opt->arg)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14215 					return API->error;	/* Failure to make new option or append to list */
14216 			}
14217 			opt = GMT_Find_Option (API, 'f', head);	/* See if we have -f */
14218 			geo = (opt && (opt->arg[0] == 'g' || strchr (opt->arg, 'x')));	/* Geographic data (could still fail with some odd -f I guess) */
14219 			if (!geo && (opt = GMT_Find_Option (API, 'J', *options))) {	/* Passed -J but no geo via -fg */
14220 				if (strchr ("xXpP", opt->arg[0]) == NULL || (toupper (opt->arg[0]) == 'X' && opt->arg[strlen(opt->arg)-1] == 'd')) {	/* Geographic projection of some sort */
14221 					if ((tmp = GMT_Make_Option (API, 'f', "g")) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14222 						return API->error;	/* Failure to make new option or append to list */
14223 				}
14224 			}
14225 			if ((tmp = GMT_Make_Option (API, 'C', NULL)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14226 				return API->error;	/* Failure to make new option -C or append to list */
14227 			if ((tmp = GMT_Make_Option (API, '0', NULL)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14228 				return API->error;	/* Failure to make new option -0 for requesting column feedback */
14229 			if ((tmp = GMT_Make_Option (API, '-', "GMT_HISTORY=readonly")) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14230 				return API->error;	/* Failure to make new option -- or append to list */
14231 
14232 			/* Set up virtual file to hold the result of gmt info */
14233 			if (GMT_Open_VirtualFile (API, GMT_IS_DATASET, GMT_IS_POINT, GMT_OUT|GMT_IS_REFERENCE, NULL, virt_file) == GMT_NOTSET)
14234 				return (API->error);
14235 			if ((tmp = GMT_Make_Option (API, GMT_OPT_OUTFILE, virt_file)) == NULL || (head = GMT_Append_Option (API, tmp, head)) == NULL)
14236 		        return API->error;	/* Failure to make new output option or append to the list */
14237 
14238 			/* Since we will be calling another module (gmtinfo), we must prevent it from ending the session prematurely.
14239 			 * Thus we temporarily unset any PS and oneliner values before calling it, set allow_reuse, then reset afterwards */
14240 			is_PS = API->GMT->current.ps.active;
14241 			is_oneliner = API->GMT->current.ps.oneliner;
14242 			API->GMT->current.ps.active = API->GMT->current.ps.oneliner = false;	/* To avoid gmtinfo from ending things */
14243 			API->allow_reuse = true;	/* So that we do not flag the input file as used after reading it in gmtinfo */
14244 			if (GMT_Call_Module (API, "gmtinfo", GMT_MODULE_OPT, head) != GMT_OK)	/* Get the data domain via gmtinfo */
14245 				return (API->error);
14246 			/* Restore these settings to what they were before */
14247 			API->GMT->current.ps.active = is_PS;
14248 			API->GMT->current.ps.oneliner = is_oneliner;
14249 			API->allow_reuse = false;	/* Rest to normal behavior */
14250 			if (GMT_Destroy_Options (API, &head))	/* Free the temporary option list */
14251 				return (API->error);
14252 
14253 			if ((Out = GMT_Read_VirtualFile (API, virt_file)) == NULL)
14254 				return (API->error);
14255 			/* Close the virtual files */
14256 			if (GMT_Close_VirtualFile (API, virt_file) != GMT_NOERROR)
14257 				return (API->error);
14258 			if (Out->n_columns < 4) {	/* No can do */
14259 				GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_region_from_data: Not enough data columns (%d) to determine region %s.\n", (unsigned int)Out->n_columns);
14260 				return (GMT_RUNTIME_ERROR);
14261 			}
14262 			/* Get the four values from the first and only output record */
14263 			wesn[XLO] = Out->table[0]->segment[0]->data[0][0];
14264 			wesn[XHI] = Out->table[0]->segment[0]->data[1][0];
14265 			wesn[YLO] = Out->table[0]->segment[0]->data[2][0];
14266 			wesn[YHI] = Out->table[0]->segment[0]->data[3][0];
14267 			gmt_set_column_type (API->GMT, GMT_IN, GMT_X, irint (Out->table[0]->segment[0]->data[4][0]));
14268 			gmt_set_column_type (API->GMT, GMT_IN, GMT_Y, irint (Out->table[0]->segment[0]->data[5][0]));
14269 			if (GMT_Destroy_Data (API, &Out) != GMT_OK)
14270 				return (API->error);
14271 			geo = gmt_M_is_geographic (API->GMT, GMT_IN);
14272 			if (!exact) gmt_round_wesn (wesn, geo);	/* Use data range to round to nearest reasonable multiples */
14273 			/* Safety valve if w == e or s == n */
14274 			if (doubleAlmostEqualZero (wesn[XLO], wesn[XHI])) {
14275 				if (gmt_M_is_zero (wesn[XLO]))	/* No info to do anything other than this */
14276 					wesn[XLO] = -1.0, wesn[XHI] = +1.0;
14277 				else
14278 					wesn[XLO] *= 0.9, wesn[XHI] *= 1.1;	/* +/- 10% of values */
14279 			}
14280 			if (doubleAlmostEqualZero (wesn[YLO], wesn[YHI])) {
14281 				if (gmt_M_is_zero (wesn[YLO]))	/* No info to do anything other than this */
14282 					wesn[YLO] = -1.0, wesn[YHI] = +1.0;
14283 				else
14284 					wesn[YLO] *= 0.9, wesn[YHI] *= 1.1;	/* +/- 10% of values */
14285 			}
14286 			break;
14287 		default:
14288 			GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_get_region_from_data: Family %d not supported", family);
14289 			return GMT_NOT_A_VALID_FAMILY;
14290 			break;
14291 	}
14292 	return GMT_NOERROR;
14293 }
14294 
14295 /*! Add -Rw/e/s/s for those modules that may implicitly obtain the region via a grid */
gmtinit_set_missing_R_from_grid(struct GMTAPI_CTRL * API,const char * args,bool exact,struct GMT_OPTION ** options)14296 GMT_LOCAL int gmtinit_set_missing_R_from_grid (struct GMTAPI_CTRL *API, const char *args, bool exact, struct GMT_OPTION **options) {
14297 	/* When a module uses -R indirectly via a grid then we need to set that explicitly in the options.
14298 	 * Modules with this issue have "g" in their THIS_MODULE_NEEDS string.
14299 	 */
14300 	struct GMT_OPTION *opt = NULL;
14301 	double wesn[4] = {0.0, 0.0, 0.0, 0.0};
14302 	char region[GMT_LEN256] = {""};
14303 	int err = GMT_NOERROR;
14304 	gmt_M_unused(args);
14305 
14306 	/* Here we know the module is using a grid to get -R implicitly */
14307 	if ((err = gmtinit_get_region_from_data (API, GMT_IS_GRID, exact, options, wesn)))
14308 		return err;
14309 
14310 	snprintf (region, GMT_LEN256, "%.16g/%.16g/%.16g/%.16g", wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI]);
14311 	if ((opt = GMT_Make_Option (API, 'R', region)) == NULL)
14312 		return API->error;	/* Failure to make option */
14313 	if ((*options = GMT_Append_Option (API, opt, *options)) == NULL)
14314 		return API->error;	/* Failure to append option */
14315 
14316 	return GMT_NOERROR;
14317 }
14318 
14319 /*! Add -Rw/e/s/n for those modules that may implicitly obtain the region via input dataset(s) */
gmtinit_set_missing_R_from_datasets(struct GMTAPI_CTRL * API,const char * args,bool exact,struct GMT_OPTION ** options)14320 GMT_LOCAL int gmtinit_set_missing_R_from_datasets (struct GMTAPI_CTRL *API, const char *args, bool exact, struct GMT_OPTION **options) {
14321 	/* When a module uses -R indirectly via input data then we need to set that explicitly in the options.
14322 	 * Modules with this issue have "d" in their THIS_MODULE_NEEDS string.
14323 	 * If exact is true then we return exact -R; otherwise we round outwards.
14324 	 */
14325 	char region[GMT_LEN256] = {""};
14326 	int err = GMT_NOERROR;
14327 	double wesn[4] = {0.0, 0.0, 0.0, 0.0};
14328 	struct GMT_OPTION *opt = NULL;
14329 	gmt_M_unused(args);
14330 
14331 	/* Here we know the module is using datasets to get -R implicitly */
14332 	if ((err = gmtinit_get_region_from_data (API, GMT_IS_DATASET, exact, options, wesn)))
14333 		return err;
14334 	snprintf (region, GMT_LEN256, "%.16g/%.16g/%.16g/%.16g", wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI]);
14335 	if ((opt = GMT_Make_Option (API, 'R', region)) == NULL)
14336 		return API->error;	/* Failure to make option */
14337 	if ((*options = GMT_Append_Option (API, opt, *options)) == NULL)
14338 		return API->error;	/* Failure to append option */
14339 	GMT_Report (API, GMT_MSG_DEBUG, "Modern: Adding -R%s to options.\n", opt->arg);
14340 
14341 	return GMT_NOERROR;
14342 }
14343 
14344 /*! Add -Rw/e/s/n for those modules that may implicitly obtain the region via a grid or datasets */
gmtinit_determine_R_option_from_data(struct GMTAPI_CTRL * API,const char * args,bool exact,struct GMT_OPTION ** options)14345 GMT_LOCAL int gmtinit_determine_R_option_from_data (struct GMTAPI_CTRL *API, const char *args, bool exact, struct GMT_OPTION **options) {
14346 	/* When a module uses -R indirectly via a grid or datasets then we need to set that explicitly in the options.
14347 	 * Modules with this issue have "g" in their THIS_MODULE_NEEDS string.
14348 	 * Modules that read datasets may have "d" in their THIS_MODULE_NEEDS which forces us to determine the region.
14349 	 */
14350 
14351 	if (GMT_Find_Option (API, 'R', *options)) return GMT_NOERROR;	/* Set explicitly, so do nothing */
14352 	if (strchr (args, 'g'))	/* Use the input grid to add a valid -R into the options */
14353 		return (gmtinit_set_missing_R_from_grid (API, args, true, options));
14354 	else if (strchr (args, 'd'))	/* Use input dataset(s) to find and add -R in this module */
14355 		return (gmtinit_set_missing_R_from_datasets (API, args, exact, options));
14356 	/* Nothing could be done */
14357 	return GMT_NOERROR;
14358 }
14359 
14360 /*! Search the list for the -J? option (? != 'z|Z) and return the pointer to the item. */
gmtinit_find_J_option(void * V_API,struct GMT_OPTION * head)14361 GMT_LOCAL struct GMT_OPTION * gmtinit_find_J_option (void *V_API, struct GMT_OPTION *head) {
14362 	struct GMT_OPTION *current = NULL, *ptr = NULL;
14363 	gmt_M_unused(V_API);
14364 
14365 	if (head == NULL) return (NULL);	/* Hard to find something in a non-existent list */
14366 
14367 	for (current = head; ptr == NULL && current; current = current->next) {	/* Linearly search for the specified option */
14368 		if (current->option == 'J' && !(current->arg[0] == 'z' || current->arg[0] == 'Z'))
14369 			ptr = current;
14370 	}
14371 	return (ptr);	/* NULL if not found */
14372 }
14373 
gmtinit_strip_R_from_E_in_pscoast(struct GMT_CTRL * GMT,struct GMT_OPTION * options,char r_code[])14374 GMT_LOCAL unsigned int gmtinit_strip_R_from_E_in_pscoast (struct GMT_CTRL *GMT, struct GMT_OPTION *options, char r_code[]) {
14375 	/* Separate out any region-specific parts from one or more -E arguments and
14376 	 * pass those separately to a new -R instead (if -R not given).
14377 	 * Return code is bitflags:
14378 	 *	0 : No _r|R or +l|L given, most likely just setting countries and implicitly -R
14379 	 * 	1 : Found a region-modifying modifier +r or +R
14380 	 * 	2 : Found a list-request +l or +L.  Not plotting or region desired.
14381 	 */
14382 	char p[GMT_LEN256] = {""}, *c = NULL;
14383 	char e_code[GMT_LEN256] = {""}, r_opt[GMT_LEN128] = {""};
14384 	unsigned int pos, n_errors = 0, answer = 0;
14385 	struct GMT_OPTION *E = options;
14386 
14387 	while ((E = GMT_Find_Option (GMT->parent, 'E', E))) {	/* For all -E options */
14388 		c = NULL;
14389 		if ((c = strchr (E->arg, '+')))
14390 			c[0] = '\0';	/* Temporarily hide the modifiers */
14391 		if (r_code[0]) strcat (r_code, ",");	/* Accumulate all codes across multiple -E options */
14392 		strcat (r_code, E->arg);	/* Append country codes only */
14393 		strncpy (e_code, E->arg, GMT_LEN256-1);	/* Duplicate country codes only */
14394 		if (c) {	/* Now process the modifiers */
14395 			c[0] = '+';	/* Unhide the modifiers */
14396 			pos = 0;	/* Initialize position counter for this string */
14397 			while (gmt_getmodopt (GMT, 'E', c, "lLcCgprRwz", &pos, p, &n_errors) && n_errors == 0) {
14398 				switch (p[0]) {
14399 					case 'r': case 'R':
14400 						if (r_opt[0] == 0) {	/* Only set this once */
14401 							r_opt[0] = '+';	strncat (r_opt, p, GMT_LEN128-2);
14402 						}
14403 						break;
14404 					case 'w': break;	/* Do nothing with defunct +w that was never documented anyway */
14405 					case 'l': case 'L':
14406 						answer |= 2;
14407 						/* Intentionally fall through - set this flag then fall through on purpose to default */
14408 					default: strcat (e_code, "+"); strcat (e_code, p); break;	/* Append as is */
14409 				}
14410 			}
14411 		}
14412 		gmt_M_str_free (E->arg);	E->arg = strdup (e_code);	/* Update -E argument */
14413 		E = E->next;	/* Go to next option so that GMT_Find_Option will get the next -E or NULL */
14414 	}
14415 	if (r_opt[0]) strcat (r_code, r_opt);	/* This string is returned back for possible use by -R */
14416 	if (r_opt[0]) answer |= 1;	/* answer & 1 set if +r or +R was used */
14417 	return (answer);
14418 }
14419 
gmtinit_is_region_geographic(struct GMT_CTRL * GMT,struct GMT_OPTION * options,const char * module)14420 GMT_LOCAL bool gmtinit_is_region_geographic (struct GMT_CTRL *GMT, struct GMT_OPTION *options, const char *module) {
14421 	/* Determine if -R<args> imply geographic or Cartesian domain */
14422 	struct GMT_OPTION *opt = NULL;
14423 	unsigned int n_slashes;
14424 	size_t len;
14425 	/* If geographic is already set we just return true */
14426 
14427 	if (gmt_M_is_geographic (GMT, GMT_IN)) return true;
14428 	/* First deal with all the modules that only involve geographic data */
14429 	if (!strncmp (module, "grdlandmask", 11U)) return true;
14430 	if (!strncmp (module, "pscoast", 7U)) return true;
14431 	if (!strncmp (module, "pssolar", 7U)) return true;
14432 	if (!strncmp (module, "sph2grd", 7U)) return true;
14433 	if (!strncmp (module, "sphdistance", 11U)) return true;
14434 	if (!strncmp (module, "sphinterpolate", 14U)) return true;
14435 	if (!strncmp (module, "img2grd", 7U)) return true;
14436 	if (!strncmp (module, "pscoupe", 7U)) return true;
14437 	if (!strncmp (module, "psmeca", 6U)) return true;
14438 	if (!strncmp (module, "pspolar", 7U)) return true;
14439 	/* pssac: Cartesion by default, unless -S option is used */
14440 	if (!strncmp (module, "pssac", 5U) && (opt = GMT_Find_Option (GMT->parent, 'S', options)) != NULL) return true;
14441 	if (!strncmp (module, "psvelo", 6U)) return true;
14442 	if (!strncmp (module, "mgd77track", 10U)) return true;
14443 	if (!strncmp (module, "grdpmodeler", 11U)) return true;
14444 	if (!strncmp (module, "grdrotater", 10U)) return true;
14445 	if (!strncmp (module, "grdspotter", 10U)) return true;
14446 	if (!strncmp (module, "polespotter", 11U)) return true;
14447 	if ((opt = GMT_Find_Option (GMT->parent, 'R', options)) == NULL) return false;	/* Should not happen but lets just say Cartesian for now */
14448 	n_slashes = gmt_count_char (GMT, opt->arg, '/');	/* Distinguishes -Rw/e/s/n from other things */
14449 	/* Check if -R[=]<code>[,<code>,...][+r|R] which means use country name etc to set region; clearly geographical */
14450 	if (n_slashes == 0 && ((isupper ((int)opt->arg[0]) && isupper ((int)opt->arg[1])) || opt->arg[0] == '=' || strchr (opt->arg, ',') || strstr (opt->arg, "+r") || strstr (opt->arg, "+R"))) return true;
14451 	if (!gmt_access (GMT, opt->arg, F_OK)) {	/* Gave a grid file */
14452 		struct GMT_GRID *G = NULL;
14453 		if ((G = GMT_Read_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, NULL, opt->arg, NULL)) == NULL)	/* Read header */
14454 			return (false);
14455 		if (GMT_Destroy_Data (GMT->parent, &G) != GMT_OK)
14456 			return (false);
14457 		return (gmt_M_is_geographic (GMT, GMT_IN));
14458 	}
14459 	len = strlen (opt->arg);
14460 	if (n_slashes == 0) {	/* Giving continent or country code(s) or -Rg|d */
14461 		if (len == 1 && strchr ("dg", opt->arg[0])) return true;	/* Gave -Rg or -Rd */
14462 		else if (len == 2 || opt->arg[0] == '=' || strchr (opt->arg, ',')) return true;	/* Giving continent or country code(s) or a list of them */
14463 		else if (len == 5 && opt->arg[2] == '.') return true;	/* Gave a single state, e.g., US.TX */
14464 	}
14465 	else if (n_slashes == 3) {
14466 		if (strchr (opt->arg, 'T')) return false;	/* Giving dateTclock or similar */
14467 		else if (strchr (opt->arg, ':')) return true;	/* Giving ddd:mm or similar */
14468 		else if (strchr (opt->arg, 'W') || strchr (opt->arg, 'E') || strchr (opt->arg, 'S')|| strchr (opt->arg, 'N')) return true;	/* Giving ddd:mm or similar */
14469 		else if (strstr (opt->arg, "+r")) return true;	/* Gave oblique region so geographic */
14470 	}
14471 	return false;	/* Default to Cartesian */
14472 }
14473 
gmtinit_set_modern_mode_if_oneliner(struct GMTAPI_CTRL * API,struct GMT_OPTION ** options)14474 GMT_LOCAL int gmtinit_set_modern_mode_if_oneliner (struct GMTAPI_CTRL *API, struct GMT_OPTION **options) {
14475 	/* Determine if user is attempting a modern mode one-liner plot */
14476 	unsigned int pos;
14477 	int error, k;
14478 	char figure[GMT_LEN128] = {""}, session[GMT_LEN128] = {""}, p[GMT_LEN16] = {""}, *c = NULL;
14479 	struct GMT_OPTION *opt = NULL;
14480 	if (options == NULL) return GMT_NOERROR;
14481 	for (opt = *options; opt; opt = opt->next)	/* Loop over all options */
14482 		if (opt->option == 'O' || opt->option == 'K') return GMT_NOERROR;	/* Cannot be a one-liner if -O or -K are involved */
14483 	/* No -O -K present, so go ahead and check */
14484 	for (opt = *options; opt; opt = opt->next) {	/* Loop over all options */
14485 		if (strlen (opt->arg) < 1) continue;	/* ps is the shortest format extension */
14486 		snprintf (figure, GMT_LEN128, "%c%s", opt->option, opt->arg);	/* So -png,jpg, which would parse as -p with arg ng,jpg, are reunited to png,jpg */
14487 		if ((c = strchr (figure, ','))) c[0] = 0;	/* Chop off other format for the initial id test */
14488 		if ((k = gmt_get_graphics_id (API->GMT, figure)) == GMT_NOTSET) continue;	/* Not a quicky one-liner option */
14489 		/* Make sure all formats are valid */
14490 		if (c) c[0] = ',';	/* Restore any comma we found */
14491 		pos = 0;
14492 		while (gmt_strtok (figure, ",", &pos, p)) {	/* Check each format to make sure each is OK */
14493 			if ((k = gmt_get_graphics_id (API->GMT, p)) == GMT_NOTSET) {
14494 				GMT_Report (API, GMT_MSG_ERROR, "Unrecognized graphics format %s\n", p);
14495 				return GMT_NOTSET;
14496 			}
14497 		}
14498 		if (opt->next == NULL || (opt->next && opt->next->option == GMT_OPT_INFILE)) {	/* Found a -ext[,ext,ext,...] <prefix> pair */
14499 			if (opt->next == NULL) {	/* Forgot to give prefix, we supply gmtsession */
14500 				GMT_Report (API, GMT_MSG_WARNING, "Modern mode oneliner syntax given but no file prefix provided - using %s\n", GMT_SESSION_NAME);
14501 				snprintf (session, GMT_LEN128, "%s %s", GMT_SESSION_NAME, figure);
14502 				if (GMT_Delete_Option (API, opt, options)) {
14503 					GMT_Report (API, GMT_MSG_ERROR, "Unable to remove -ext option in gmtinit_set_modern_mode_if_oneliner.\n");
14504 					return GMT_NOTSET;
14505 				}
14506 			}
14507 			else {	/* Gave the file prefix */
14508 				if (strchr (opt->next->arg, ' '))	/* File name has spaces, must surround it in single quotes */
14509 					snprintf (session, GMT_LEN128, "\'%s\' %s", opt->next->arg, figure);
14510 				else
14511 					snprintf (session, GMT_LEN128, "%s %s", opt->next->arg, figure);
14512 				/* Remove the one-liner options before the parser chokes on them */
14513 				if (GMT_Delete_Option (API, opt->next, options) || GMT_Delete_Option (API, opt, options)) {
14514 					GMT_Report (API, GMT_MSG_ERROR, "Unable to remove -ext <prefix> options in gmtinit_set_modern_mode_if_oneliner.\n");
14515 					return GMT_NOTSET;
14516 				}
14517 			}
14518 			API->GMT->hidden.func_level++;	/* Must do this here since it has not yet been increased by gmtinit_begin_module_sub ! */
14519 			gmt_reset_history (API->GMT);	/* A one-liner should have no history */
14520 			gmtinit_conf_modern_override (API->GMT);
14521 
14522 			if ((error = GMT_Call_Module (API, "begin", GMT_MODULE_CMD, session))) {
14523 				GMT_Report (API, GMT_MSG_ERROR, "Unable to call module begin from gmtinit_set_modern_mode_if_oneliner.\n");
14524 				return GMT_NOTSET;
14525 			}
14526 			API->GMT->current.setting.run_mode = GMT_MODERN;
14527 			gmtinit_setautopagesize (API->GMT);
14528 			API->GMT->current.ps.oneliner = true;	/* Special flag */
14529 			API->GMT->hidden.func_level--;	/* Restore to what we had */
14530 			return GMT_NOERROR;	/* All set */
14531 		}
14532 		else {
14533 			GMT_Report (API, GMT_MSG_ERROR, "Modern mode oneliner syntax given but no file prefix provided?\n");
14534 			return GMT_NOTSET;
14535 		}
14536 	}
14537 	return GMT_NOERROR;
14538 }
14539 
gmtinit_get_last_dimensions(struct GMTAPI_CTRL * API,int fig)14540 GMT_LOCAL int gmtinit_get_last_dimensions (struct GMTAPI_CTRL *API, int fig) {
14541 	/* Get dimensions of previous plot, if any */
14542 	FILE *fp = NULL;
14543 	char file[PATH_MAX] = {""};
14544 	if (API->gwf_dir == NULL) {
14545 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_last_dimensions: No workflow directory set\n");
14546 		return GMT_NOT_A_VALID_DIRECTORY;
14547 	}
14548 	snprintf (file, PATH_MAX, "%s/gmt.canvas.%d", API->gwf_dir, fig);
14549 	/* See if there is a gmt.canvas file to read for this figure */
14550 	if (access (file, R_OK))	/* No gmt.canvas file available for current figure so return 0 */
14551 		return GMT_NOERROR;
14552 	/* Get previous dimensions */
14553 	if ((fp = fopen (file, "r")) == NULL) {
14554 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_last_dimensions: Could not open file %s for figure %d\n", file, fig);
14555 		return GMT_ERROR_ON_FOPEN;
14556 	}
14557 	if (fscanf (fp, "%lg %lg", &API->GMT->current.map.last_width, &API->GMT->current.map.last_height) != 2) {
14558 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_get_last_dimensions: Could not read dimensions from file %s for figure %d\n", file, fig);
14559 		fclose (fp);
14560 		return GMT_DATA_READ_ERROR;
14561 	}
14562 	fclose (fp);
14563 	return (GMT_NOERROR);
14564 }
14565 
gmtinit_set_last_dimensions(struct GMTAPI_CTRL * API)14566 GMT_LOCAL int gmtinit_set_last_dimensions (struct GMTAPI_CTRL *API) {
14567 	/* Save dimensions of current plot */
14568 	int fig;
14569 	FILE *fp = NULL;
14570 	char file[PATH_MAX] = {""};
14571 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC)  return GMT_NOERROR;	/* Not in modern mode */
14572 	if (API->GMT->current.map.width == 0.0) return GMT_NOERROR;	/* No dimensions set yet */
14573 	if (API->gwf_dir == NULL) {
14574 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_set_last_dimensions: No workflow directory set\n");
14575 		return GMT_NOT_A_VALID_DIRECTORY;
14576 	}
14577 	fig = gmt_get_current_figure (API);
14578 	snprintf (file, PATH_MAX, "%s/gmt.canvas.%d", API->gwf_dir, fig);
14579 	/* Write current dimensions */
14580 	if ((fp = fopen (file, "w")) == NULL) {
14581 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_set_last_dimensions: Could not create file %s for figure %d\n", file, fig);
14582 		return GMT_ERROR_ON_FOPEN;
14583 	}
14584 	fprintf (fp, "%lg %lg\n", API->GMT->current.map.width, API->GMT->current.map.height);
14585 	fclose (fp);
14586 	return (GMT_NOERROR);
14587 }
14588 
gmtinit_replace_missing_with_questionmark(struct GMTAPI_CTRL * API,char * arg,char * newarg)14589 GMT_LOCAL bool gmtinit_replace_missing_with_questionmark (struct GMTAPI_CTRL *API, char *arg, char *newarg) {
14590 	/* If this -J argument is like the '?' args (but with the '?' missing) and having trailing /, or missing an only arg, then
14591 	 * we append or insert the missing ? so that the gmtinit_build_new_J_option can work as is.
14592 	 * We assume newarg is completely blank.  We only get here if there is no ? in arg. */
14593 	size_t o = 0, i = 0, L = strlen (arg) - 1;	/* Index of last character in arg (we know arg has at least length 1) */
14594 
14595 	/* Category 1 projections: Always has slashes and need to end in /? */
14596 	if ((strchr ("cC", arg[0]) && !strncmp (&arg[1], "yl_stere", 8U)) || strchr ("aAbBcCdDeEfFgGlLoOsStT", arg[0])) {	/* These projection all must end in / and if no ? then append it */
14597 		if (arg[L] == '/')	/* User followed instructions and left a trailing / */
14598 			sprintf (newarg, "%s?", arg);
14599 		else {
14600 			GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_replace_missing_with_questionmark: -J%s has no trailing slash. Assumed to be a complete geographic projection\n", arg);
14601 			return false;
14602 		}
14603 	}
14604 	else if ((strchr ("pP", arg[0]) && !strncmp (&arg[1], "oly", 3U)) || strchr ("hHiIjJkKmMnNqQrRvVwWyYuU", arg[0])) {	/* These may or may not have a trailing slash */
14605 		if (arg[L] == '/') {	/* Multiple argument so left a trailing / */
14606 			sprintf (newarg, "%s?", arg);
14607 		}
14608 		else {	/* Used defaults so here we have things like -JM, -Jkf, or -Jpoly; otherwise it is -JM15c etc (i.e., with argument) */
14609 			if (L == 0 || (L == 1 && strchr ("kK", arg[0]) && strchr ("fs", arg[1])))	/* No argument, just append ? */
14610 				sprintf (newarg, "%s?", arg);
14611 			else {
14612 				GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_replace_missing_with_questionmark: -J%s has no trailing slash. Assumed to be a complete geographic projection\n", arg);
14613 				return false;
14614 			}
14615 		}
14616 	}
14617 	else if (strchr ("xX", arg[0])) {	/* Cartesian projection, must worry about separate x and y settings if a slash is found */
14618 		/* Look for Cartesian -Jx|X[-][d|l|p<pow>][/[-][d|l|p<pow>]] which needs one or two ?-marks to be inserted for the two dummy Cartesian scales.
14619 		 * But don't touch things like -Jx|X[+|-]<number> */
14620 		newarg[o++] = arg[i++];	/* This is x or X */
14621 		if (arg[i] && strchr ("-+", arg[i])) newarg[o++] = arg[i++];	/* Placing an optional sign (-Jx has no modifiers) */
14622 		if (isdigit (arg[i]) || arg[i] == '.') { /* Got -Jx-.5, -JX3c, -JX-2 or similar, do nothing */
14623 			GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_replace_missing_with_questionmark: -J%s assumed to be a complete Cartesian projection\n", arg);
14624 			return false;
14625 		}
14626 		/* Here we must insert or append one or two ? */
14627 		newarg[o++] = '?';	/* Insert the first ?-mark */
14628 		if (strchr (arg, '/')) {	/* slash[0] == '/' means we got separate x and y scale args for linear[d]/log/power axes */
14629 			while (arg[i] != '/') newarg[o++] = arg[i++];	/* Copying any linear[d]/log etc args for x-axis until the slash */
14630 			newarg[o++] = arg[i++];	/* This is the / */
14631 			if (arg[i] && strchr ("-+", arg[i])) newarg[o++] = arg[i++];	/* Placing the optional second sign */
14632 			newarg[o++] = '?';	/* Insert the second ?-mark */
14633 			while (arg[i]) newarg[o++] = arg[i++];	/* Copying any linear[d]/log etc args for y-axis until the end */
14634 		}
14635 		else {	/* Just a single scale/width. With ? already appended, check for the log,power,degree args */
14636 			while (arg[i]) newarg[o++] = arg[i++];	/* Copying any linear[d]/log etc args until the end */
14637 		}
14638 	}
14639 	else if (strchr ("pP", arg[0])) {	/* Polar (cylindrical) projection, must insert missing ?-mark if no scale/width given */
14640 		newarg[o++] = arg[i++];	/* This is p or P */
14641 		if (isdigit (arg[i]) || arg[i] == '.') { /* Got -Jp.5, -JP15c or similar (there are no signs here */
14642 			GMT_Report (API, GMT_MSG_DEBUG, "gmtinit_replace_missing_with_questionmark: -J%s assumed to be a complete Polar projection\n", arg);
14643 			return false;
14644 		}
14645 		newarg[o++] = '?';	/* Insert the ?-mark */
14646 		while (arg[i]) newarg[o++] = arg[i++];	/* Copying any polar modifiers until the end */
14647 	}
14648 	GMT_Report (API, GMT_MSG_DEBUG, "Modern mode: First converted -J%s to -J%s.\n", arg, newarg);
14649 
14650 	return true;	/* yes, we made changes */
14651 }
14652 
gmtinit_build_new_J_option(struct GMTAPI_CTRL * API,struct GMT_OPTION * opt_J,struct GMT_SUBPLOT * P,struct GMT_INSET * I,bool is_psrose)14653 GMT_LOCAL bool gmtinit_build_new_J_option (struct GMTAPI_CTRL *API, struct GMT_OPTION *opt_J, struct GMT_SUBPLOT *P, struct GMT_INSET *I, bool is_psrose) {
14654 	/* Look for Cartesian -Jx|X[-]?[d|l|p<pow>][/[-]?[d|l|p<pow>]] which needs one or two ?-marks to be replaced with dummy Cartesian scales.
14655 	 * Otherwise, -J<code>? or -J<code><arg>/<arg>/.../? which needs only one ?-mark to be replaced with dummy map scale. */
14656 
14657 	char sclX[GMT_LEN64] = {""}, sclY[GMT_LEN64] = {""}, arg[GMT_LEN128] = {""}, oldarg[GMT_LEN128] = {""};
14658 	char *slash = NULL, *c = NULL, *c2 = NULL;
14659 	int Iyscl = 1;
14660 
14661 	if (opt_J == NULL) return false;	/* No -J option to update */
14662 	if (opt_J->arg == NULL || opt_J->arg[0] == '\0') return false;	/* No argument to update */
14663 	if (strchr (opt_J->arg, '?'))	/* Found ?, we must go to work, make a copy of oldargs */
14664 		strncpy (oldarg, opt_J->arg, GMT_LEN128-1);
14665 	else if (!gmtinit_replace_missing_with_questionmark (API, opt_J->arg, oldarg))	/* Not an argument we should update */
14666 		/* If an argument with nothing instead of ? then we insert ? so the rest of the function can work */
14667 		return false;
14668 
14669 	c = strchr (oldarg, '?');	/* Pointer to questionmark in the argument */
14670 	/* Here, c[0] is the first question mark (there may be one or two) */
14671 	if (strchr ("xX", oldarg[0])) {	/* Cartesian projection, must worry about separate x and y settings if a slash is found */
14672 		slash = strchr (oldarg, '/');	/* slash[0] == '/' means we got separate x and y scale args for linear[d]/log/power axes */
14673 		if (slash && slash[1] == '-') {	/* While any negative x-scale will automatically be included, for y we just make sure we scale by -1 if a hyphen is found after the slash */
14674 			if (P) P->dir[GMT_Y] = -1; else if (I) Iyscl = -1;	/* Only use P or I if defined */
14675 		}
14676 	}
14677 	if (P) {	/* Subplot mode */
14678 		if (P->dir[GMT_X] == -1 || P->dir[GMT_Y] == -1) {	/* Nonstandard Cartesian directions set via subplot */
14679 			snprintf (sclX, GMT_LEN64, "%gi",  P->dir[GMT_X] * P->w);
14680 			snprintf (sclY, GMT_LEN64, "%gi",  P->dir[GMT_Y] * P->h);
14681 		}
14682 		else if (slash) {	/* Found separate x and y scales */
14683 			snprintf (sclX, GMT_LEN64, "%gi", P->w);
14684 			snprintf (sclY, GMT_LEN64, "%gi", P->h);
14685 		}
14686 		else if (is_psrose)	/* Just append the minimum dimension as the diameter */
14687 			snprintf (sclX, GMT_LEN64, "%gi", MIN(P->w, P->h));
14688 		else	/* Just append a dummy width */
14689 			snprintf (sclX, GMT_LEN64, "%gi", P->w);
14690 	}
14691 	else if (I) {	/* Inset mode */
14692 		if (slash) {	/* Found separate x and y scales */
14693 			snprintf (sclX, GMT_LEN64, "%gi", I->w);
14694 			snprintf (sclY, GMT_LEN64, "%gi", Iyscl * I->h);
14695 		}
14696 		else if (is_psrose)	/* Just append the minimum dimension as the diameter */
14697 			snprintf (sclX, GMT_LEN64, "%gi", MIN(I->w, I->h));
14698 		else	/* Just append a dummy width */
14699 			snprintf (sclX, GMT_LEN64, "%gi", I->w);
14700 	}
14701 	arg[0] = c[0] = '\0';	/* Chop off everything from first ? to end */
14702 	snprintf (arg, GMT_LEN128, "%s%s", oldarg, sclX);	/* Build new -J<arg> from initial J arg, then replace first ? with sclX */
14703 	c[0] = '?';	/* Put back the ? we removed */
14704 	if (c[1] == 'l')	/* Must add the log character after the scale */
14705 		strcat (arg, "l");
14706 	else if (c[1] == 'd')	/* Must add the d (degree) character after the scale */
14707 		strcat (arg, "d");
14708 	else if (c[1] == 'p') {	/* Must add p<power> after the scale */
14709 		size_t len = strlen (arg), k = 1;
14710 		while (c[k] && c[k] != '/')	/* Copy letters until we hit the slash or run out */
14711 			arg[len++] = c[k++];
14712 	}
14713 	else if (!slash && c[1])	/* More arguments after initial scale, probably -JPa?/angle */
14714 		strncat (arg, &c[1], GMT_LEN128-1);
14715 	if (slash && (c2 = strchr (&c[1], '?'))) {	/* Must place a Y-scale instead of the 2nd ? mark */
14716 		strcat (arg, "/");	/* Add the slash divider */
14717 		strcat (arg, sclY);	/* Append the y scale/height */
14718 		if (c2[1] == 'l')	/* Must add the log character after the y-scale */
14719 			strcat (arg, "l");
14720 		else if (c2[1] == 'd')	/* Must add the d (degree) character after the y-scale */
14721 			strcat (arg, "d");
14722 		else if (c2[1] == 'p') {	/* Must add p<power> after the y-scale */
14723 			size_t len = strlen (arg), k = 1;
14724 			while (c2[k])	/* Keep copying until we run out */
14725 				arg[len++] = c2[k++];
14726 		}
14727 	}
14728 	GMT_Update_Option (API, opt_J, arg);	/* Failure to append option */
14729 	GMT_Report (API, GMT_MSG_DEBUG, "Modern mode: Func level %d, Updated -J%s to -J%s.\n", API->GMT->hidden.func_level, oldarg, opt_J->arg);
14730 	return true;
14731 }
14732 
14733 /* The way we avoid applying -B settings more than once per subplot panel is to write
14734  * an empty file called gmt.B.<fig>.<row>.<col> after applying -B, and once that file
14735  * exist we do not apply -B again. */
14736 
gmtlib_panel_B_file(struct GMTAPI_CTRL * API,int fig,int row,int col,char * file)14737 void gmtlib_panel_B_file (struct GMTAPI_CTRL *API, int fig, int row, int col, char *file) {
14738 	/* Create the B_setting file name for this subplot panel */
14739 	sprintf (file, "%s/gmt.B.%d.%d.%d", API->gwf_dir, fig, row, col);
14740 }
14741 
gmtinit_panel_B_set(struct GMTAPI_CTRL * API,int fig,int row,int col)14742 GMT_LOCAL void gmtinit_panel_B_set (struct GMTAPI_CTRL *API, int fig, int row, int col) {
14743 	/* Mark that -B options have been applied for this subplot panel */
14744 	char Bfile[PATH_MAX] = {""};
14745 	FILE *fp = NULL;
14746 	gmtlib_panel_B_file (API, fig, row, col, Bfile);
14747 	sprintf (Bfile, "%s/gmt.B.%d.%d.%d", API->gwf_dir, fig, row, col);
14748 	if ((fp = fopen (Bfile, "w"))) fclose (fp);
14749 }
14750 
gmtinit_panel_B_get(struct GMTAPI_CTRL * API,int fig,int row,int col)14751 GMT_LOCAL bool gmtinit_panel_B_get (struct GMTAPI_CTRL *API, int fig, int row, int col) {
14752 	/* Determine if -B options have been applied to this panel before */
14753 	char Bfile[PATH_MAX] = {""};
14754 	gmtlib_panel_B_file (API, fig, row, col, Bfile);
14755 	if (access (Bfile, F_OK) == 0) {	/* Return true if file is found */
14756 		GMT_Report (API, GMT_MSG_DEBUG, "B already set for fig %d subplot panel (%d, %d)\n", fig, row, col);
14757 		return true;
14758 	}
14759 	return false;
14760 }
14761 
gmtlib_module_may_get_R_from_RP(struct GMT_CTRL * GMT,const char * mod_name)14762 bool gmtlib_module_may_get_R_from_RP (struct GMT_CTRL *GMT, const char *mod_name) {
14763 	/* The cases where a module can consult the plot region because a projection or grid domain is not set is
14764 	 * limited to these cases:
14765 	 * 	  pscoast -M:  We wish to dump data and often as part of a plot situation
14766 	 *    psbasemap -A: Writing out the bounds of the region may need the plot region
14767 	 *    mapproject -W: OFten done in computing positions in a plot, hence plot region is reasonable.
14768 	 */
14769 	return (GMT->current.ps.active || (!strncmp (mod_name, "subplot", 7U) || !strncmp (mod_name, "pscoast", 7U) || !strncmp (mod_name, "psbasemap", 9U) || !strncmp (mod_name, "mapproject", 10U)));
14770 }
14771 
14772 /*! Classic mode: Discover if a certain option was set in the history and re-set it */
gmtinit_complete_RJ(struct GMT_CTRL * GMT,char * codes,struct GMT_OPTION * options)14773 void gmtinit_complete_RJ (struct GMT_CTRL *GMT, char *codes, struct GMT_OPTION *options) {
14774 	/* When a module discovers it needs -R or -J and it maybe was not given
14775 	 * see if we can tease out the answer from the history and parse it.
14776 	 * Currently used in gmt_get_refpoint where we may learn that -R -J will
14777 	 * indeed be required.  We then check if they have been given.  If not,
14778 	 * then under classic mode we abort, while under modern mode we add them,
14779 	 * if possible.
14780 	 */
14781 	int id = 0, j;
14782 	char str[3] = {""};
14783 	struct GMT_OPTION *opt;
14784 
14785 	assert (codes);	/* Should never be NULL */
14786 
14787 	for (j = 0; codes[j]; j++) {	/* Do this for all required options listed */
14788 		assert (strchr ("JR", codes[j]));	/* Only J and/or R should be present in options */
14789 		if ((opt = GMT_Find_Option (GMT->parent, codes[j], options)) == NULL) continue;	/* Not found */
14790 		if (opt->arg[0]) continue;	/* Set already */
14791 		/* Must dig around in the history array */
14792 		gmt_M_memset (str, 3, char);
14793 		str[0] = codes[j];
14794 		if ((id = gmt_get_option_id (0, str)) == GMT_NOTSET) continue;	/* Not an option we have history for yet */
14795 		if (codes[j] == 'R' && !GMT->current.ps.active) id++;		/* Examine -RG history if not a plotter */
14796 		if (GMT->init.history[id] == NULL) continue;	/* No history for this option */
14797 		if (codes[j] == 'J') {	/* Must now search for actual option since -J only has the code (e.g., -JM) */
14798 			/* Continue looking for -J<code> */
14799 			str[1] = GMT->init.history[id][0];
14800 			if ((id = gmt_get_option_id (id + 1, str)) == GMT_NOTSET) continue;	/* Not an option we have history for yet */
14801 			if (GMT->init.history[id] == NULL) continue;	/* No history for this option */
14802 		}
14803 		GMT_Update_Option (GMT->parent, opt, GMT->init.history[id]);	/* Failure to update option */
14804 	}
14805 }
14806 
gmtinit_mapproject_needs_RJ(struct GMTAPI_CTRL * API,struct GMT_OPTION * options)14807 GMT_LOCAL bool gmtinit_mapproject_needs_RJ (struct GMTAPI_CTRL *API, struct GMT_OPTION *options) {
14808 	struct GMT_OPTION *opt = NULL;
14809 	if ((opt = GMT_Find_Option (API, 'E', options))) return false;	/* The -E option means conversion to/from Earth Centered Earth Fixed so no projection */
14810 	if ((opt = GMT_Find_Option (API, 'N', options))) return false;	/* The -N option means conversion of auxiliary latitudes so no projection */
14811 	if ((opt = GMT_Find_Option (API, 'Q', options))) return false;	/* The -Q option just dumps information about datums and ellipsoids and then exits */
14812 	if ((opt = GMT_Find_Option (API, 'T', options))) return false;	/* The -T option means we want to change datums which uses no projection */
14813 	if ((opt = GMT_Find_Option (API, 'W', options))) return true;	/* The -W option means we must project to plot coordinates so -R -J are required */
14814 	if ((opt = GMT_Find_Option (API, 'I', options))) return true;	/* The -I option (with no -E or -N) means we must inversely project so -R -J are required */
14815 	if ((opt = GMT_Find_Option (API, 'C', options))) return true;	/* The -C option means we want to change projection offsets so -R -J are required */
14816 	/* The above are straightforward, the next set may or may not use -R -J so hence not required by themselves */
14817 	if ((opt = GMT_Find_Option (API, 'A', options))) return false;	/* The -A option computes azimuths and does not require -R -J */
14818 	if ((opt = GMT_Find_Option (API, 'G', options))) return false;	/* The -G option computes distances between points and does not require -R -J */
14819 	if ((opt = GMT_Find_Option (API, 'L', options))) return false;	/* The -L option computes distances to lines and does not require -R -J */
14820 	if ((opt = GMT_Find_Option (API, 'Z', options))) return false;	/* The -Z option computes distances and times = speeds and does not require -R -J */
14821 
14822 	return (true);	/* We get here when a classic command like "gmt mapproject -R -J file" in modern mode looks like "gmt mapproject file" and thus -R -J is required */
14823 }
14824 
gmtinit_might_be_remotefile(char * file)14825 GMT_LOCAL bool gmtinit_might_be_remotefile (char *file) {
14826 	bool quote = false;	/* We are outside any quoted text */
14827 	size_t k;
14828 	if (strchr (file, '@') == NULL) return false;	/* No @ anywhere */
14829 	if (gmt_M_file_is_memory (file)) return false;	/* Not a remote file but a memory reference */
14830 	if (file[0] == '@') return true;	/* Definitively a remote file */
14831 	/* Get here when a @ is not in the first position. Return true unless @ is inside quotes */
14832 	for (k = 0; k < strlen (file); k++) {
14833 		if (file[k] == '\"' || file[k] == '\'') quote = !quote;
14834 		if (file[k] == '@' && !quote) return true;	/* Found an unquoted at-symbol */
14835 	}
14836 	return false;	/* Nothing */
14837 }
14838 
14839 /*! . */
gmtinit_compare_resolutions(const void * point_1,const void * point_2)14840 GMT_LOCAL int gmtinit_compare_resolutions (const void *point_1, const void *point_2) {
14841 	/* Sorts differences from desired nodes-per-degree from small to big  */
14842 	double delta_1 = fabs(((struct GMT_RESOLUTION *)point_1)->resolution);
14843 	double delta_2 = fabs(((struct GMT_RESOLUTION *)point_2)->resolution);
14844 	if (delta_1 < delta_2) return (-1);
14845 	if (delta_1 > delta_2) return (+1);
14846 	/* If a tie then we place the pixel registration first */
14847 	if (((struct GMT_RESOLUTION *)point_1)->reg > ((struct GMT_RESOLUTION *)point_2)->reg) return (-1);	/* p > g so go -1 */
14848 	if (((struct GMT_RESOLUTION *)point_1)->reg < ((struct GMT_RESOLUTION *)point_2)->reg) return (+1);	/* p > g so go +1 */
14849 	return (0);
14850 }
14851 
gmtinit_map_vertical_degree(struct GMT_CTRL * GMT)14852 GMT_LOCAL double gmtinit_map_vertical_degree (struct GMT_CTRL *GMT) {
14853 	/* Return the max latitude separation in degrees across the map */
14854 	double Y = GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO];
14855 	return (Y);
14856 }
14857 
gmtinit_map_vertical_inches(struct GMT_CTRL * GMT)14858 GMT_LOCAL double gmtinit_map_vertical_inches (struct GMT_CTRL *GMT) {
14859 	/* Return the max map height in inches */
14860 	double L = GMT->current.map.height;
14861 	return (L);
14862 }
14863 
gmtinit_dryrun_report(struct GMTAPI_CTRL * API,double * wesn,double inc,char * incstring,struct GMT_OPTION ** options)14864 GMT_LOCAL int gmtinit_dryrun_report (struct GMTAPI_CTRL *API, double *wesn, double inc, char *incstring, struct GMT_OPTION **options) {
14865 	double out[6];	/* To hold w e s n dx dy */
14866 	char record[GMT_LEN256] = {""};	/* To hold -Rw/e/s/n -Idx/dy */
14867 	struct GMT_RECORD *Out = NULL;
14868 	struct GMT_OPTION *opt_D = GMT_Find_Option (API, 'D', *options);
14869 	bool text = (strstr (opt_D->arg, "+t"));
14870 	GMT_Report (API, GMT_MSG_INFORMATION, "Extracted grid region will be %g/%g/%g/%g for increments %s/%s\n", wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI], incstring, incstring);
14871 	if (GMT_Set_Columns (API, GMT_OUT, text ? 0 : 6, text ? GMT_COL_FIX : GMT_COL_FIX_NO_TEXT) != GMT_NOERROR)
14872 		return API->error;
14873 	if (GMT_Init_IO (API, GMT_IS_DATASET, GMT_IS_NONE, GMT_OUT, GMT_ADD_DEFAULT, 0, *options) != GMT_NOERROR) {	/* Registers default output destination, unless already set */
14874 		return API->error;
14875 	}
14876 	if (GMT_Begin_IO (API, GMT_IS_DATASET, GMT_OUT, GMT_HEADER_OFF) != GMT_NOERROR) {	/* Enables data output and sets access mode */
14877 		return API->error;
14878 	}
14879 
14880 	if (text) {
14881 		Out = gmt_new_record (API->GMT, NULL, record);	/* The trailing text output record */
14882 		gmt_format_region (API->GMT, record, wesn);	/* Typeset the -Rw/e/s/n part */
14883 		strcat (record, " -I");	strcat (record, incstring);
14884 		strcat (record, "/");	strcat (record, incstring);
14885 	}
14886 	else {
14887 		Out = gmt_new_record (API->GMT, out, NULL);	/* The numerical output record */
14888 		gmt_M_memcpy (out, wesn, 4U, double);	/* Place the grid boundaries */
14889 		out[4] = out[5] = inc;
14890 	}
14891 	GMT_Put_Record (API, GMT_WRITE_DATA, Out);
14892 	if (GMT_End_IO (API, GMT_OUT, 0) != GMT_NOERROR)	/* Disables further data output */
14893 		return API->error;
14894 	gmt_M_free (API->GMT, Out);
14895 	gmt_M_str_free (opt_D->arg);	/* Free any previous arguments */
14896 	strcpy (record, "done-in-gmt_init_module");
14897 	if (text) strcat (record, "+t");
14898 	opt_D->arg = strdup (record);	/* Flag so that grdcut can deal with the mixed cases later */
14899 	return (GMT_NOERROR);
14900 }
14901 
14902 /*! Prepare options if missing and initialize module */
gmt_init_module(struct GMTAPI_CTRL * API,const char * lib_name,const char * mod_name,const char * keys,const char * in_required,struct GMT_KEYWORD_DICTIONARY * this_module_kw,struct GMT_OPTION ** options,struct GMT_CTRL ** Ccopy)14903 struct GMT_CTRL *gmt_init_module (struct GMTAPI_CTRL *API, const char *lib_name, const char *mod_name, const char *keys, const char *in_required, struct GMT_KEYWORD_DICTIONARY *this_module_kw, struct GMT_OPTION **options, struct GMT_CTRL **Ccopy) {
14904 	/* For modern runmode only - otherwise we simply call gmtinit_begin_module_sub.
14905 	 * We must consult the required string.  It may contain options that we need to set implicitly.
14906 	 * Possible letters in the required string are:
14907 	 *	R  The -R option is required for this program and if missing we will hunt for the last region in the history.
14908 	 *	r  The -R option may be required depending on other settings.  If it becomes required and no -R<region> was
14909 	 *     given then we hunt for one in the history.  This happens after GMT_Parse_Common has been called.
14910 	 *  g  The region is required but if none is given the we simply use -R<grid>.  This applies to those modules that
14911 	 *     have a required grid for input (e.g., modules like grdimage).
14912 	 *  d  If no -R<region> is given AND there are no region in the history, then determine a suitable region
14913 	 *     from the input dataset.  This enables automatic region determination via gmtinfo.
14914 	 *  J  The -J option is required and if missing we will hunt for the last region in the history.
14915 	 *  j  The -J option may be required depending on other settings.  If it becomes required and no -J<info> was
14916 	 *     given then we hunt for one in the history. This happens after GMT_Parse_Common has been called.
14917 	 *
14918 	 * Note: 1. If no -J can be found in the history we provide either -JQ15c (geographic data) or -JX15c (Cartesian).
14919 	 *
14920 	 * Modules like pslegend has "rj" since -R -J are not required if -Dx is used but required for other settings.
14921 	 * Modules like blockmean, surface has "R" since it is never cool to autodetermine grid domains as this also
14922 	 *  depends on grid spacing, for instance.
14923 	 * Modules like grdview has "g" since they always have a grid domain to fall back on in the absence of -R.
14924 	 * Modules like psxy has "d" so we can make a quick map without specifying -R.
14925 	 */
14926 
14927 	bool is_PS, is_psrose = false, is_D_module = false, remote_first = true;
14928 	char *required = (char *)in_required;
14929 	unsigned int k;
14930 	static char *D_module[4] = {"gmtlogo", "psimage", "pslegend", "psscale"};	/* These all may take -Dx and thus may not need -R -J */
14931 	struct GMT_OPTION *E = NULL, *opt = NULL, *opt_R = NULL;
14932 	struct GMT_CTRL *GMT = API->GMT;
14933 	API->error = GMT_NOERROR;
14934 
14935 	#if defined(USE_COMMON_LONG_OPTIONS)
14936 	gmtinit_translate_to_short_options (API, this_module_kw, options);	/* Replace --long-option syntax with equivalent -onechar options */
14937 	#else
14938 	gmt_M_unused(this_module_kw);
14939 	#endif
14940 
14941 	/* First handle any half-hearted naming of remote datasets where _g or _p should be appended */
14942 
14943 	if (options) {
14944 	  for (opt = *options; opt; opt = opt->next) {	/* Loop over all options */
14945 		  if (!gmtinit_might_be_remotefile (opt->arg)) continue;
14946 		  if (remote_first) {
14947 			  gmt_refresh_server (API);	/* Refresh hash and info tables as needed */
14948 			  remote_first = false;
14949 		  }
14950 		  gmt_set_unspecified_remote_registration (API, &(opt->arg));	/* If argument is a remote file name then this handles any missing registration _p|_g */
14951 		}
14952 	}
14953 
14954 	/* Making -R<country-codes> globally available means it must affect history, etc.  The simplest fix here is to
14955 	 * make sure pscoast -E, if passing old +r|R area settings via -E, is split into -R before GMT_Parse_Common is called */
14956 
14957 	if (options && !strcmp (mod_name, "pscoast") && (E = GMT_Find_Option (API, 'E', *options))) { /* Determine if need for RJ */
14958 		if (strstr (E->arg, "+c") || strstr (E->arg, "+C")) {
14959 			/* Will need RJ */
14960 		}
14961 		else if (strstr (E->arg, "+g") == NULL && strstr (E->arg, "+p") == NULL) { /* Determine if need for RJ */
14962 			if (!((opt = GMT_Find_Option (API, 'G', *options)) || (opt = GMT_Find_Option (API, 'M', *options)) || (opt = GMT_Find_Option (API, 'W', *options)))) {
14963 				required = "";
14964 				GMT_Report (API, GMT_MSG_DEBUG, "Given -E, -R -J not required for pscoast.\n");
14965 			}
14966 		}
14967 	}
14968 	/* Determine if module is one of the 4 horsemen of the apocalypse that potentially uses -Dx and thus may have no -R -J, but needed in subplots */
14969 	for (k = 0; !is_D_module && k < 4; k++)
14970 		if (!strcmp (mod_name, D_module[k]))
14971 			is_D_module = true;
14972 
14973 	if (options && is_D_module && (opt = GMT_Find_Option (API, 'D', *options))) { /* Worry about the -D option */
14974 		if (strchr ("jJg", opt->arg[0])) /* Must turn jr into JR, but will revisit this case later when we know if we have subplots or not */
14975 			required = "JR";
14976 	}
14977 	if (options && !strcmp (mod_name, "mapproject") && gmtinit_mapproject_needs_RJ (API, *options))	/* mapproject is a complicated beast that needs some help here */
14978 		required = "JR";
14979 	if (options && !strcmp (mod_name, "psxy") && (opt = GMT_Find_Option (API, 'T', *options)) && (opt = GMT_Find_Option (API, 'B', *options)) == NULL) { /* Can turn off JR if -T and no -B as long as -X -Y do not contain c */
14980 		if (!(((opt = GMT_Find_Option (API, 'X', *options)) && opt->arg[0] == 'c') || ((opt = GMT_Find_Option (API, 'Y', *options)) && opt->arg[0] == 'c')))
14981 			required = "";
14982 	}
14983 	if (options && strstr (mod_name, "grdflexure") && (opt = GMT_Find_Option (API, 'Q', *options))) /* Must turn off g */
14984 		required = "";
14985 	if (options && strstr (mod_name, "psclip") && (opt = GMT_Find_Option (API, 'C', *options)) && (opt = GMT_Find_Option (API, 'B', *options))) /* psclip -C with -B requires -R -J */
14986 		required = "JR";
14987 
14988 	if (options && !strcmp (mod_name, "pscoast") && (E = GMT_Find_Option (API, 'E', *options)) && (opt = GMT_Find_Option (API, 'R', *options)) == NULL) {
14989 		/* Running pscoast -E without -R: Must make sure any the region-information in -E is added as args to new -R.
14990 		 * If there are no +r|R in the -E then we consult the history to see if there is an -R in effect. */
14991 		char r_code[GMT_LEN512] = {""};
14992 		bool add_R = true;
14993 		unsigned int E_flags;
14994 		GMT_Report (API, GMT_MSG_DEBUG, "Given -E, determine if -R is needed for pscoast.\n");
14995 		E_flags = gmtinit_strip_R_from_E_in_pscoast (GMT, *options, r_code);
14996 		if (GMT->current.setting.run_mode == GMT_MODERN && !(E_flags & 1)) {	/* Just country codes and plot settings, no region specs */
14997 			int id = gmt_get_option_id (0, "R");		/* The -RP history item */
14998 			GMT_Report (API, GMT_MSG_DEBUG, "Given -E, explore if there is a grid or plot region already.\n");
14999 			if (!GMT->init.history[id]) id++;		/* No history for -RP, increment to -RG as fallback */
15000 			if (GMT->init.history[id]) {	/* There is history for -R so -R will be added below */
15001 				GMT_Report (API, GMT_MSG_DEBUG, "Given -E, found there is a grid or plot region already.\n");
15002 				if ((opt = GMT_Make_Option (API, 'R', GMT->init.history[id])) == NULL) return NULL;	/* Failure to make -R option */
15003 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append -R option */
15004 				GMT_Report (API, GMT_MSG_DEBUG, "Added -R%s for pscoast.\n", opt->arg);
15005 				add_R = false;
15006 			}
15007 		}
15008 		if (E_flags & 2) {	/* No -R should be set since we want a country code listing only */
15009 			add_R = false;
15010 			GMT_Report (API, GMT_MSG_DEBUG, "Given -E, no -R needed for country code listing request\n");
15011 		}
15012 		if (add_R) {	/* Need to add a specific -R option that carries the information set via -E */
15013 			if ((opt = GMT_Make_Option (API, 'R', r_code)) == NULL) return NULL;	/* Failure to make -R option */
15014 			if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append -R option */
15015 			GMT_Report (API, GMT_MSG_DEBUG, "Given -E, add equivalent -R%s for pscoast.\n", opt->arg);
15016 		}
15017 	}
15018 	else if (options && gmt_M_compat_check (GMT, 6) && !strncmp (mod_name, "psrose", 6U)) {
15019 		if ((opt = GMT_Find_Option (API, 'J', *options)) == NULL) {
15020 			/* Running psrose with old -S[n]<radius syntax and no -J.  Need to replace with new -J [-S] syntax */
15021 			struct GMT_OPTION *S = GMT_Find_Option (API, 'S', *options);
15022 			if (S && S->arg[0] && strstr (S->arg, "+a") == NULL) {	/* Gave -S option with some arguments but not the new -S+a */
15023 				char j_code[GMT_LEN256] = {""};
15024 				unsigned int k, norm = (S->arg[0] == 'n') ? 1 : 0;
15025 				double radius;
15026 				k = norm;
15027 				if (norm == 0 && S->arg[strlen(S->arg)-1] == 'n') {	/* Old-style -S<radius>n syntax */
15028 					norm = 2;
15029 					S->arg[strlen(S->arg)-1] = '\0';
15030 				}
15031 				radius = (S->arg[k]) ? gmt_M_to_inch (GMT, &S->arg[k]) : 0.5 * 15 / 2.54;	/* Get the radius or default to (7.5 = 15/2 cm), now in inches */
15032 				snprintf (j_code, GMT_LEN256, "X%gi", 2 * radius);
15033 				if ((opt = GMT_Make_Option (API, 'J', j_code)) == NULL) return NULL;		/* Failed to make -J option */
15034 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failed to append -J option */
15035 				if (norm) {	/* Need a plain -S for normalization */
15036 					if (GMT_Update_Option (API, S, "")) return NULL;		/* Failed to update -S */
15037 				}
15038 				else {	/* Remove the S option */
15039 					if (GMT_Delete_Option (API, S, options)) return NULL;		/* Failed to remove -S */
15040 				}
15041 			}
15042 			else {	/* No old-style -S option given either, so user expects default diameter via -J and no normalization */
15043 				if (GMT->current.setting.run_mode == GMT_MODERN) {
15044 					if ((opt = GMT_Make_Option (API, 'J', "X?")) == NULL) return NULL;	/* Failure to make -J option */
15045 					is_psrose = true;
15046 				}
15047 				else {
15048 					if ((opt = GMT_Make_Option (API, 'J', "X15c")) == NULL) return NULL;	/* Failure to make -J option */
15049 				}
15050 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append -J option */
15051 			}
15052 		}
15053 		else {	/* Gave -J, flag if with ? for subplots or insets */
15054 			is_psrose = (strchr (opt->arg, '?') && GMT->current.setting.run_mode == GMT_MODERN);
15055 		}
15056 	}
15057 
15058 	is_PS = gmtinit_is_PS_module (API, mod_name, keys, options);	/* true if module will produce PS */
15059 	if (is_PS) {
15060 		if (gmtinit_set_modern_mode_if_oneliner (API, options))	/* Look out for modern -png mymap and similar specs */
15061 			return NULL;
15062 	}
15063 
15064 	GMT->current.ps.active = is_PS;		/* true if module will produce PS */
15065 
15066 	/* Check if there is an input remote grid or memory file so we may set geographic to be true now before we must decide on auto-J */
15067 	if (options && (opt = GMT_Find_Option (API, GMT_OPT_INFILE, *options))) {
15068 		if (gmt_remote_dataset_id (API, opt->arg) != GMT_NOTSET)	/* All remote data grids/images are geographic */
15069 			gmt_set_geographic (GMT, GMT_IN);
15070 		else if (gmtlib_data_is_geographic (API, opt->arg))	/* This dataset, grid, image, matrix, or vector is geographic */
15071 			gmt_set_geographic (GMT, GMT_IN);
15072 	}
15073 
15074 	if (options && GMT->current.setting.run_mode == GMT_MODERN) {	/* Make sure options conform to this mode's harsh rules: */
15075 		unsigned int n_errors = 0, subplot_status = 0, inset_status = 0, n_slashes = 0;
15076 		int id, fig;
15077 		bool got_R = false, got_J = false, exceptionb, exceptionp;
15078 		char arg[GMT_LEN256] = {""}, scl[GMT_LEN64] = {""};
15079 		struct GMT_OPTION *opt_J = NULL;
15080 		struct GMT_SUBPLOT *P = NULL;
15081 
15082 		fig = gmt_get_current_figure (API);	/* Get current figure number */
15083 
15084 		gmtinit_get_last_dimensions (API, fig);	/* Get dimensions of previous plot, if any */
15085 
15086 		GMT->current.ps.initialize = false;	/* Start from scratch */
15087 
15088 		opt_R = GMT_Find_Option (API, 'R', *options);
15089 		opt_J = gmtinit_find_J_option (API, *options);
15090 		if (GMT->hidden.func_level == GMT_CONTROLLER) {	/* The -R -J -O -K prohibition only applies to top-level module call */
15091 			/* 1. No -O allowed */
15092 			if ((opt = GMT_Find_Option (API, 'O', *options))) {
15093 				GMT_Report (API, GMT_MSG_ERROR, "Option -O not allowed for modern GMT mode.\n");
15094 				n_errors++;
15095 			}
15096 			/* 2. No -K allowed */
15097 			if ((opt = GMT_Find_Option (API, 'K', *options))) {
15098 				GMT_Report (API, GMT_MSG_ERROR, "Option -K not allowed for modern GMT mode.\n");
15099 				n_errors++;
15100 			}
15101 			/* 3. No -R option without arguments is allowed at top module to reach here (we may add -R later if history is available) */
15102 			if (opt_R) {	/* Gave -R option */
15103 				if (opt_R->arg[0] == '\0') {
15104 					GMT_Report (API, GMT_MSG_ERROR, "Shorthand -R not allowed for modern GMT mode.\n");
15105 					n_errors++;
15106 				}
15107 				else if (!strncmp (opt_R->arg, "auto", 4U) || (opt_R->arg[0] == 'a' && opt_R->arg[1] == '\0'))	{	/* -Ra[uto] determines smart -R from data */
15108 					if (GMT->current.ps.active) {
15109 						if (GMT_Delete_Option (API, opt_R, options)) n_errors++;	/* Must remove old -R so next function can add a complete -R */
15110 						n_errors += gmtinit_determine_R_option_from_data (API, required, false, options);
15111 					}
15112 					else {
15113 						GMT_Report (API, GMT_MSG_ERROR, "Shorthand -Ra[uto] not allowed for non-plotting modules.\n");
15114 						n_errors++;
15115 					}
15116 				}
15117 				else if (!strncmp (opt_R->arg, "exact", 5U) || (opt_R->arg[0] == 'e' && opt_R->arg[1] == '\0'))	{	/* -Re[xact] determines exact -R from data */
15118 					if (GMT->current.ps.active) {
15119 						if (GMT_Delete_Option (API, opt_R, options)) n_errors++;	/* Must remove old -R so next function can add a complete -R */
15120 						n_errors += gmtinit_determine_R_option_from_data (API, required, true, options);
15121 					}
15122 					else {
15123 						GMT_Report (API, GMT_MSG_ERROR, "Shorthand -Re[xact] not allowed for non-plotting modules.\n");
15124 						n_errors++;
15125 					}
15126 				}
15127 				got_R = true;
15128 			}
15129 			/* 4. No -J without arguments are allowed at top module to reach here (we may add -J later if history is available) */
15130 			if (opt_J) {
15131 				if (opt_J->arg[0] == '\0') {
15132 					GMT_Report (API, GMT_MSG_ERROR, "Shorthand -J not allowed for modern GMT mode.\n");
15133 					n_errors++;
15134 				}
15135 				got_J = true;
15136 			}
15137 			if (n_errors) {	/* Oh, well, live and learn */
15138 				API->error = GMT_OPTION_NOT_ALLOWED;
15139 				return NULL;
15140 			}
15141 
15142 			if (GMT->current.ps.active)	/* true if module will produce PS */
15143 				(void)gmt_set_psfilename (GMT);	/* Sets GMT->current.ps.initialize=true if the expected (and hidden) PS plot file cannot be found */
15144 		}
15145 		else {	/* Not top-level, meaning these are modules called by other modules and they better have set -R -J if required */
15146 			if (opt_R) got_R = true;
15147 			if (opt_J) got_J = true;
15148 		}
15149 
15150 		if (GMT->current.ps.active) {	/* Only explore -c settings, etc for plot modules */
15151 			/* Check if a subplot operation is in effect and if there is a current panel already */
15152 			subplot_status = gmt_subplot_status (API, fig);
15153 			if ((inset_status = gmtinit_get_inset_dimensions (API, fig, &GMT->current.plot.inset))) {
15154 				return (NULL);
15155 			}
15156 
15157 			if (GMT->hidden.func_level == GMT_CONTROLLER && subplot_status & GMT_SUBPLOT_ACTIVE) {	/* Explore -c setting */
15158 				int row = 0, col = 0;
15159 				double gap[4], legend_width = 0.0, legend_scale = 1.0;
15160 				char legend_justification[4] = {""}, pen[GMT_LEN32] = {""}, fill[GMT_LEN32] = {""}, off[GMT_LEN32] = {""};
15161 				if ((opt = GMT_Find_Option (API, 'c', *options))) {	/* Got -c<row,col> for subplot so must update current gmt.panel */
15162 					if (gmt_get_legend_info (API, &legend_width, &legend_scale, legend_justification, pen, fill, off)) {	/* Unplaced legend file */
15163 						char cmd[GMT_LEN64] = {""};
15164 						int error;
15165 						/* Default to white legend with 1p frame offset 0.2 cm from selected justification point [TR] */
15166 						snprintf (cmd, GMT_LEN64, "-Dj%s+w%gi+o%s -F+p%s+g%s -S%g", legend_justification, legend_width, off, pen, fill, legend_scale);
15167 						if ((error = GMT_Call_Module (API, "legend", GMT_MODULE_CMD, cmd))) {
15168 							GMT_Report (API, GMT_MSG_ERROR, "Failed to place legend on current subplot figure\n");
15169 							return NULL;
15170 						}
15171 					}
15172 					gmt_subplot_gaps (API, fig, gap);	/* First see if there were subplot-wide -Cgaps settings in effect */
15173 					if (opt->arg[0] && strchr (opt->arg,',')) {	/* Gave a comma-separate argument so presumably this is our row,col */
15174 						sscanf (opt->arg, "%d,%d", &row, &col);
15175 						if (row < 0 || col < 0) {
15176 							GMT_Report (API, GMT_MSG_ERROR, "Negative row and/or column given to -c!\n");
15177 							return NULL;
15178 						}
15179 					}
15180 					else if (opt->arg[0] && (strchr ("-+", opt->arg[0]) || isdigit (opt->arg[0]))) {	/* Probably gave index */
15181 						row = atoi (opt->arg);
15182 						if (row < 0) {
15183 							GMT_Report (API, GMT_MSG_ERROR, "Negative index given to -c!\n");
15184 							return NULL;
15185 						}
15186 						col = INT_MAX;
15187 						if (gmt_get_next_panel (API, fig, &row, &col)) return NULL;	/* Bad */
15188 					}
15189 					else {	/* If there is a previously set panel, we move to the next panel, otherwise set to first */
15190 						if (gmt_get_next_panel (API, fig, &row, &col)) return NULL;	/* Bad */
15191 					}
15192 					if (gmt_set_current_panel (API, fig, row, col, gap, NULL, 1)) return NULL;	/* Make this the current panel */
15193 					if (GMT_Delete_Option (API, opt, options)) n_errors++;	/* Remove -c option here so not causing trouble downstream */
15194 					gmt_reload_history (GMT);	/* Start fresh in this panel */
15195 					gmt_reload_settings (GMT);	/* Start fresh in this panel */
15196 				}
15197 				else if (subplot_status & GMT_PANEL_NOTSET) {	/* Did NOT do -c the first time, which we will declare to mean -c as well */
15198 					gmt_subplot_gaps (API, fig, gap);	/* First see if there were subplot-wide -Cgaps settings in effect */
15199 					if (gmt_get_next_panel (API, fig, &row, &col)) return NULL;	/* Bad */
15200 					if (gmt_set_current_panel (API, fig, row, col, gap, NULL, 1)) return NULL;
15201 				}
15202 			}
15203 			if (strncmp (mod_name, "inset", 5U) && GMT->current.plot.inset.active && got_J) {	/* Map inset and gave -J */
15204 				if (gmtinit_build_new_J_option (API, opt_J, NULL, &GMT->current.plot.inset, is_psrose))	/* Found ?-mark(s) and replaced them */
15205 					GMT->current.plot.panel.no_scaling = 0;
15206 				else
15207 					GMT->current.plot.panel.no_scaling = 1;
15208 			}
15209 		}
15210 		/* Need to check for an active subplot, but NOT if the current call is "gmt subplot end" or psscale */
15211 		exceptionb = (!strncmp (mod_name, "psscale", 7U));
15212 		exceptionp = ((!strncmp (mod_name, "subplot", 7U) && *options && !strncmp ((*options)->arg, "end", 3U)));
15213 		if (GMT->current.ps.active && !exceptionp && (P = gmt_subplot_info (API, fig))) {	/* Yes, so set up current panel settings */
15214 			bool frame_set = false, x_set = false, y_set = false, B_set;
15215 			char *c = NULL;
15216 			int row = 0, col = 0;
15217 			gmtinit_get_current_panel (API, fig, &row, &col, NULL, NULL, NULL);
15218 			B_set = gmtinit_panel_B_get (API, fig, row, col);
15219 			if (exceptionb == 0 && P->first == 1 && !B_set) {
15220 				/* Examine all -B settings and add/merge the panel settings */
15221 				for (opt = *options; opt; opt = opt->next) {	/* Loop over all options */
15222 					if (opt->option != 'B') continue;	/* Just interested in -B here */
15223 					/* Deal with the frame option check first */
15224 					if (strchr ("WESNwesnlrbt", opt->arg[0]))	/* User is overriding the frame settings - that is their choice */
15225 						frame_set = true;
15226 					else if (gmt_found_modifier (API->GMT, opt->arg, "gt")) {	/* No axis specs means we have to add default */
15227 						/* Frame but no sides specified.  Insert the required sides */
15228 						snprintf (arg, GMT_LEN256, "%s", P->Baxes);
15229 						strncat (arg, opt->arg, GMT_LEN256-1);
15230 						GMT_Update_Option (API, opt, arg);
15231 						frame_set = true;
15232 						GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker revised -B frame option arg to %s\n", arg);
15233 					}
15234 					else if (opt->arg[0] == 'x') {	/* Gave specific x-setting */
15235 						if (opt->arg[1] == '+')	{	/* No x-axis annot/tick given, prepend default determined in subplot  */
15236 							snprintf (arg, GMT_LEN256, "x%s%s", P->Bxannot, &opt->arg[1]);
15237 							GMT_Update_Option (API, opt, arg);
15238 							GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker revised -Bx axis option arg to %s\n", arg);
15239 						}
15240 						if (P->Bxlabel[0]) {	/* Provided a label during subplot initialization */
15241 							strcpy (arg, opt->arg);	/* Start with what we were given */
15242 						 	if ((c = strstr (arg, "+l"))) {	/* See if we must append x label set during subplot call */
15243 								c += 2;
15244 								if (c[0] == '\0') strcat (arg, P->Bxlabel);	/* Yes, +l was empty so add preset label */
15245 							}
15246 							else {	/* No panel-speicific label override, use the preset label */
15247 								strcat (arg, "+l");
15248 								strcat (arg, P->Bxlabel);
15249 							}
15250 							GMT_Update_Option (API, opt, arg);
15251 							GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker revised -Bx axis label option arg to %s\n", arg);
15252 						}
15253 						x_set = true;
15254 					}
15255 					else if (opt->arg[0] == 'y') {	/* Gave specific y-setting */
15256 						if (opt->arg[1] == '+')	{	/* No x-axis annot/tick set, prepend default af */
15257 							snprintf (arg, GMT_LEN256, "y%s%s", P->Byannot, &opt->arg[1]);
15258 							GMT_Update_Option (API, opt, arg);
15259 							GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker revised -By axis option arg to %s\n", arg);
15260 						}
15261 						if (P->Bylabel[0]) {
15262 							strcpy (arg, opt->arg);	/* Start with what we were given */
15263 						 	if ((c = strstr (arg, "+l"))) {	/* See if we must append y label set during subplot call */
15264 								c += 2;
15265 								if (c[0] == '\0') strcat (arg, P->Bylabel);	/* Yes, +l was empty so add preset label */
15266 							}
15267 							else {	/* No panel-specific label override, use the preset label */
15268 								strcat (arg, "+l");
15269 								strcat (arg, P->Bylabel);
15270 							}
15271 							GMT_Update_Option (API, opt, arg);
15272 							GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker revised -By axis label option arg to %s\n", arg);
15273 						}
15274 						y_set = true;
15275 					}
15276 					else /* Gave a common x and y setting; keep as given */
15277 						x_set = y_set = true;
15278 				}
15279 				if (!frame_set) {	/* Did not specify frame setting so impose the subplot choices */
15280 					if (P->Baxes[0]) {	/* Gave frame settings */
15281 						if ((opt = GMT_Make_Option (API, 'B', P->Baxes)) == NULL) return NULL;
15282 					}
15283 					else if ((opt = GMT_Make_Option (API, 'B', "0")) == NULL) return NULL;	/* Add -B0 to just draw frame */
15284 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15285 					GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker added -B frame option with arg %s\n", arg);
15286 				}
15287 				if (P->inside) {	/* Ensure we get inside ticks/annots */
15288 					GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker added --MAP_FRAME_TYPE=inside\n");
15289 					if ((opt = GMT_Make_Option (API, '-', "MAP_FRAME_TYPE=inside")) == NULL) return NULL;
15290 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15291 				}
15292 				if (!x_set) {	/* Did not specify x-axis setting either via -Bx or -B so do that now */
15293 					snprintf (arg, GMT_LEN256, "x%s", P->Bxannot);	/* Start with the x tick,annot,grid choices */
15294 					if (P->Bxlabel[0]) {strcat (arg, "+l"); strcat (arg, P->Bxlabel);}	/* Add label, if active */
15295 					if ((opt = GMT_Make_Option (API, 'B', arg)) == NULL) return NULL;	/* Failure to make option */
15296 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15297 					GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker added -Bx axis option with arg %s\n", arg);
15298 				}
15299 				if (!y_set) {	/* Did not specify y-axis setting so do that now */
15300 					snprintf (arg, GMT_LEN256, "y%s", P->Byannot);	/* Start with the x tick,annot,grid choices */
15301 					if (P->Bylabel[0]) {strcat (arg, "+l"); strcat (arg, P->Bylabel);}	/* Add label, if active */
15302 					if ((opt = GMT_Make_Option (API, 'B', arg)) == NULL) return NULL;	/* Failure to make option */
15303 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15304 					GMT_Report (API, GMT_MSG_DEBUG, "Subplot-checker added -By axis option with arg %s\n", arg);
15305 				}
15306 				gmtinit_panel_B_set (API, fig, row, col);
15307 			}
15308 			if (GMT->hidden.func_level == GMT_CONTROLLER) {	/* Top-level function called by subplot needs to handle positioning and possibly set -J */
15309 				/* Set -X -Y for absolute positioning */
15310 				snprintf (arg, GMT_LEN256, "a%gi", P->origin[GMT_X] + P->x);
15311 				if ((opt = GMT_Make_Option (API, 'X', arg)) == NULL) return NULL;	/* Failure to make option */
15312 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15313 				snprintf (arg, GMT_LEN256, "a%gi", P->origin[GMT_Y] + P->y);
15314 				if ((opt = GMT_Make_Option (API, 'Y', arg)) == NULL) return NULL;	/* Failure to make option */
15315 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15316 				if (gmtinit_build_new_J_option (API, opt_J, P, NULL, is_psrose))	/* Found ?-mark(s) and replaced them */
15317 					GMT->current.plot.panel.no_scaling = 0;
15318 				else if (opt_J && strchr (opt_J->arg, '?') == NULL) /* Do not auto-scale but use the given dimensions */
15319 					GMT->current.plot.panel.no_scaling = 1;
15320 			}
15321 		}
15322 
15323 		if (is_psrose && P == NULL && !GMT->current.plot.inset.active && strchr (opt_J->arg, '?')) {	/* If we still have -JX? and not inset or panel, replace by default */
15324 			if (GMT_Update_Option (API, opt_J, "X15c")) return NULL;		/* Failed to update -J */
15325 		}
15326 		if (is_D_module && !got_R && !got_J && P)	/* Module call with -Dx in a subplot, turn on JR since we know both must exist */
15327 			required = "JR";
15328 		if (got_R == false && (strchr (required, 'R') || strchr (required, 'g') || strchr (required, 'd'))) {	/* Need a region but no -R was set */
15329 			/* First consult the history */
15330 			id = gmt_get_option_id (0, "R");	/* The -RP history item */
15331 			if (gmtlib_module_may_get_R_from_RP (API->GMT, mod_name)) {	/* First check -RP history */
15332 				if (!GMT->init.history[id]) id++;	/* No history for -RP, increment to -RG as fall-back */
15333 			}
15334 			else id++;	/* Only examine -RG history if not a plotter */
15335 			if (GMT->init.history[id]) {	/* There is history for -R */
15336 				if ((opt = GMT_Make_Option (API, 'R', "")) == NULL) return NULL;	/* Failure to make option */
15337 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15338 				n_slashes = gmt_count_char (GMT, GMT->init.history[id], '/');	/* May need to know if it is 3 (2-D) or 5 (3-D) later regarding -p -JZ */
15339 				GMT_Report (API, GMT_MSG_DEBUG, "Modern mode: Added -R to options since history is available.\n");
15340 			}
15341 			else if (strchr (required, 'g') || strchr (required, 'd')) {	/* No history but can examine input data sets */
15342 				if (gmtinit_determine_R_option_from_data (API, required, false, options)) {
15343 					GMT_Report (API, GMT_MSG_DEBUG, "Modern mode: Failure while determining the region from input data.\n");
15344 				}
15345 			}
15346 		}
15347 		if (got_J == false && strchr (required, 'J')) {	/* Need a projection but no -J was set */
15348 			if ((id = gmt_get_option_id (0, "J")) >= 0 && GMT->init.history[id]) {	/* There is history for -J */
15349 				/* Must now search for actual option since -J only has the code (e.g., -JM) */
15350 				/* Continue looking for -J<code> */
15351 				char str[3] = {"J"};
15352 				str[1] = GMT->init.history[id][0];
15353 				if ((id = gmt_get_option_id (id + 1, str)) >= 0 && GMT->init.history[id]) {	/* There is history for this -J */
15354 					if ((opt = GMT_Make_Option (API, 'J', "")) == NULL) return NULL;	/* Failure to make option */
15355 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15356 					GMT_Report (API, GMT_MSG_DEBUG, "Modern: Adding -J to options since there is history available.\n");
15357 					got_J = true;
15358 				}
15359 			}
15360 			if (got_J == false) {	/* No history, apply default projection, but watch out for subplots and time-axis */
15361 				unsigned int geo = gmtinit_is_region_geographic (GMT, *options, mod_name);
15362 				if (geo) 	/* Max dimension lon/lat plot of 15 cm */
15363 					snprintf (scl, GMT_LEN64, "Q15c+");
15364 				else {	/* Use 15cm square but watch out for panels and time-axes */
15365 					char *Tcode[2] = {"", "T"};
15366 					unsigned int xy[2];
15367 					xy[GMT_X] = (gmt_get_column_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME);
15368 					xy[GMT_Y] = (gmt_get_column_type (GMT, GMT_IN, GMT_Y) == GMT_IS_ABSTIME);
15369 					if (P && (P->dir[GMT_X] == -1 || P->dir[GMT_Y] == -1))	/* Nonstandard Cartesian axes directions */
15370 						snprintf (scl, GMT_LEN64, "X%gi%s/%gi%s",  P->dir[GMT_X]*P->w, Tcode[xy[GMT_X]], P->dir[GMT_Y]*P->h, Tcode[xy[GMT_Y]]);
15371 					else
15372 						snprintf (scl, GMT_LEN64, "X15c%s/15c%s",  Tcode[xy[GMT_X]], Tcode[xy[GMT_Y]]);
15373 				}
15374 				if ((opt = GMT_Make_Option (API, 'J', scl)) == NULL) return NULL;	/* Failure to make option */
15375 				if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15376 				GMT_Report (API, GMT_MSG_DEBUG, "Modern: Adding -J%s to options since there is no history available.\n", scl);
15377 			}
15378 		}
15379 		/* Check if -p was given and if we need to add -Jz|Z from history */
15380 		if (options && (opt = GMT_Find_Option (API, 'p', *options)) && (opt_R = GMT_Find_Option (API, 'R', *options)) && (n_slashes == 5 || gmt_count_char (GMT, opt_R->arg, '/') == 5)) {	/* 3-D perspective plotting module with -Rx0/x1/y0/y1/z0/z1 */
15381 			bool got_JZ = false;
15382 			char str[3] = {"J"};
15383 			/* First check if -Jz|Z was not given */
15384 			for (opt = *options; !got_JZ && opt; opt = opt->next) {
15385 				if (opt->option == 'J' && opt->arg[0] && strchr ("zZ", opt->arg[0]))
15386 					got_JZ = true;
15387 			}
15388 
15389 			if (!got_JZ && (id = gmt_get_option_id (0, "Z")) >= 0 && GMT->init.history[id]) {	/* Did not specify vertical projection but there is history for Z */
15390 				str[1] = GMT->init.history[id][0];
15391 				if ((id = gmt_get_option_id (0, str)) >= 0 && GMT->init.history[id]) {	/* There is history for this -Jz|Z */
15392 					if ((opt = GMT_Make_Option (API, 'J', &str[1])) == NULL) return NULL;	/* Failure to make option -Jz|Z */
15393 					if ((*options = GMT_Append_Option (API, opt, *options)) == NULL) return NULL;	/* Failure to append option */
15394 					GMT_Report (API, GMT_MSG_DEBUG, "Modern: Adding -J%c to options since there is history available and -p and -R implies a vertical projection.\n", str[1]);
15395 				}
15396 			}
15397 		}
15398 	}
15399 
15400 	if (options) {	/* Check if any filename argument is a remote tiled dataset */
15401 		bool first_time = true, parsed_R = false, dry_run = false, got_grdcut = false, got_grdimage = false, inc_set = false;
15402 		int k_data, registration;
15403 		unsigned int srtm_flag;
15404 		char *list = NULL, *c = NULL, s_inc[GMT_LEN8] = {""};
15405 		double wesn[4], d_inc;
15406 		struct GMT_OPTION *opt_J = NULL, *opt_S = NULL, *opt_D = NULL, *opt_dash = NULL;
15407 		struct GMT_DATA_INFO *I = NULL;
15408 
15409 		opt_R = GMT_Find_Option (API, 'R', *options);
15410 		opt_J = GMT_Find_Option (API, 'J', *options);
15411 		got_grdcut = !strncmp (mod_name, "grdcut", 6U);
15412 		got_grdimage = !strncmp (mod_name, "grdimage", 8U);	/* To ensure grdimage -A is allowed */
15413 		dry_run = (got_grdcut && (opt_D = GMT_Find_Option (API, 'D', *options)));	/* Do not want the grid, just the information */
15414 		if (dry_run && GMT_Find_Option (API, 'G', *options)) {	/* Check here since parse is in the future */
15415 			GMT_Report (API, GMT_MSG_ERROR, "Option -D: Cannot specify -G since no grid will be returned\n");
15416 			return NULL;
15417 		}
15418 
15419 		for (opt = *options; opt; opt = opt->next) {	/* Loop over all options */
15420 			if (gmt_M_file_is_memory (opt->arg) || opt->arg[0] != '@') continue;	/* No remote file argument given */
15421 			if ((k_data = gmt_remote_dataset_id (API, opt->arg)) != GMT_NOTSET) {	/* Got a remote file to work on */
15422 				API->tile_inc = API->remote_info[k_data].d_inc;	/* In case rounding is needed elsewhere */
15423 				API->tile_reg = API->remote_info[k_data].reg;	/* So we can create a grid header without reading the tile data */
15424 			}
15425 			if ((k_data = gmt_remote_no_extension (API, opt->arg)) != GMT_NOTSET) {	/* Remote file without file extension */
15426 				char *file = malloc (strlen(opt->arg)+1+strlen (API->remote_info[k_data].ext));
15427 				sprintf (file, "%s", opt->arg);
15428 				strcat (file, API->remote_info[k_data].ext);
15429 				gmt_M_str_free (opt->arg);
15430 				opt->arg = file;
15431 				continue;
15432 			}
15433 			if ((k_data = gmt_remote_no_resolution_given (API, opt->arg, &registration)) != GMT_NOTSET) {	/* Gave no resolution, e.g., just "eart_relief" */
15434 				bool at_least_one = false;
15435 				char codes[3] = {""}, reg[2] = {'g', 'p'}, dir[GMT_LEN64] = {""}, file[GMT_LEN128] = {""}, *p_dir = NULL;
15436 				unsigned int n_to_set = 0, level = GMT->hidden.func_level;	/* Since we will need to increment prematurely since gmtinit_begin_module_sub has not been reached yet */
15437 				unsigned int k, n_R = 0;
15438 				int k_data2 = GMT_NOTSET;
15439 				double D, L, this_n_per_degree, dpi;
15440 				struct GMT_RESOLUTION *R = NULL;	/* Sorted list of number of nodes per degree for possible grids */
15441 
15442 				if (opt_R == NULL || opt_J == NULL) {
15443 					GMT_Report (API, GMT_MSG_ERROR, "Cannot request automatic remote grid resolution without -R -J settings [%s]\n", opt->arg);
15444 					return NULL;
15445 				}
15446 				if (!(GMT->current.ps.active || got_grdcut || got_grdimage)) {
15447 					GMT_Report (API, GMT_MSG_ERROR, "Except for grdcut, cannot request automatic remote grid resolution if not plotting [%s]\n", opt->arg);
15448 					return NULL;
15449 				}
15450 				if (!opt_R->arg[0]) codes[n_to_set++] = 'R';	/* Must have -R parsed */
15451 				if (!opt_J->arg[0]) codes[n_to_set++] = 'J';	/* Must have -J parsed */
15452 				if (n_to_set) gmtinit_complete_RJ (GMT, codes, *options);	/* Fill in what -R actually is (and possibly fill in -J) */
15453 				if (opt_J) gmtinit_parse_J_option (GMT, opt_J->arg);
15454 				API->GMT->hidden.func_level++;	/* Must do this here in case gmt_parse_R_option calls mapproject */
15455 				gmt_parse_R_option (GMT, opt_R->arg);
15456 				parsed_R = true;
15457 				API->GMT->hidden.func_level = level;	/* Reset to what it should be */
15458 				GMT->common.J.active = GMT->common.R.active[RSET] = true;	/* Since we have set those here */
15459 				if (gmt_map_setup (GMT, GMT->common.R.wesn))
15460 					return NULL;
15461 				GMT->common.R.active[RSET] = false;	/* Since we will need to parse it again officially in GMT_Parse_Common */
15462 				/* PW: The simplification here is that we try to equate the long diagonal distance L on the map (in inches) times the dpi as a rough
15463 				 * estimates of pixels we would like.  To retain the highest resolution of the data here we need approximately that many nodes
15464 				 * in the grid of the same distance, which we compute as a great circle distance D. The balance is L * dpi = n * D, where we solve
15465 				 * for n and find the closest match of possible grid resolutions (in the struct GMT_RESOLUTION array R). If the user actually
15466 				 * specified a specific registration then we only look for that one, else we look for either, with preference for pixel registration. */
15467 
15468 				/* Because GMT_Parse_Common has not yet been called, we would not have parsed any --GMT_GRAPHICS_DPU=xxxx which it may be reasonable
15469 				 * to give in this context.  Thus we check for this setting here first before using it in the code below */
15470 				if ((opt_dash = GMT_Find_Option (API, GMT_OPT_PARAMETER, *options)) && !strncmp (opt_dash->arg, "GMT_GRAPHICS_DPU", 16U)) {
15471 					char *value = strchr (opt_dash->arg, '='), unit;
15472 		 			if (value == NULL || value[1] == '\0') {
15473 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "No value given to GMT_GRAPHICS_DPU\n");
15474 						return NULL;
15475 					}
15476 					else {	/* May parse the argument */
15477 						value++;	/* Skip past the equal sign */
15478 						GMT->current.setting.graphics_dpu = atof (value);
15479 						unit = value[strlen(value)-1];
15480 						GMT->current.setting.graphics_dpu_unit = (strchr ("ci", unit)) ? unit : GMT_IMAGE_DPU_UNIT;
15481 					}
15482 				}
15483 				dpi = GMT->current.setting.graphics_dpu; if (GMT->current.setting.graphics_dpu_unit == 'c') dpi *= 2.54;	/* Convert dpc to dpi */
15484 				D = gmtinit_map_vertical_degree (GMT);	/* Map latitudinal extent in degrees */
15485 				L = gmtinit_map_vertical_inches (GMT);	/* Map height in inches */
15486 				this_n_per_degree = L * dpi / D;	/* Number of equivalent nodes per degree needed */
15487 				R = gmt_remote_resolutions (API, opt->arg, &n_R);	/* List of available resolutions for this family */
15488 				for (k = 0; k < n_R; k++) {	/* Compute deviation from desired resolution */
15489 					R[k].resolution = R[k].resolution - this_n_per_degree;
15490 					if (registration != GMT_NOTSET && reg[registration] != R[k].reg) continue;	/* Skip this registration */
15491 					if (R[k].resolution >= 0.0) at_least_one = true;	/* At least one resolution will be adequate */
15492 				}
15493 				mergesort (R, n_R, sizeof (struct GMT_RESOLUTION), gmtinit_compare_resolutions);	/* Sort so entry 0 is the best */
15494 				/* Find the closest fit to what we need that has positive deviation and actually has a file and registration that matches */
15495 				strncpy (dir, API->remote_info[k_data].dir, strlen (API->remote_info[k_data].dir)-1);	/* Make a copy without the trailing slash */
15496 				p_dir = strrchr (dir, '/');	/* Start of final subdirectory */
15497 				p_dir++;	/* Skip past the slash */
15498 				for (k = 0; k_data2 == GMT_NOTSET && k < n_R; k++) {
15499 					if (R[k].resolution < 0.0 && at_least_one) continue;	/* Must at least satisfy the dpi criterion */
15500 					if (registration != GMT_NOTSET && reg[registration] != R[k].reg) continue;	/* Skip this registration */
15501 					sprintf (file, "@%s_%s_%c", p_dir, R[k].inc, R[k].reg);	/* Candidate name */
15502 					k_data2 = gmt_remote_dataset_id (API, file);
15503 				}
15504 				gmt_M_free (GMT, R);	/* Free the list */
15505 				if (k_data2 == GMT_NOTSET) {	/* Replace entry with correct version */
15506 					if (registration != GMT_NOTSET)
15507 						GMT_Report (API, GMT_MSG_ERROR, "No matching data with suitable resolution for this registration was found [%s]\n", opt->arg);
15508 					else
15509 						GMT_Report (API, GMT_MSG_ERROR, "No matching data with suitable resolution and any registration was found [%s]\n", opt->arg);
15510 					return NULL;
15511 				}
15512 				else {	/* Replace entry with correct version */
15513 					GMT_Report (API, GMT_MSG_INFORMATION, "Given -R%s -J%s, representative distances D_y = %g degrees, D_h = %g %s and dpu = %g%c (this_n_per_degree = %lg) we replace %s by %s\n",
15514 						GMT->common.R.string, GMT->common.J.string, D, L * GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit],
15515 						GMT->session.unit_name[GMT->current.setting.proj_length_unit], GMT->current.setting.graphics_dpu, GMT->current.setting.graphics_dpu_unit, this_n_per_degree, opt->arg, file);
15516 					gmt_M_str_free (opt->arg);
15517 					opt->arg = strdup (file);
15518 					API->tile_inc = d_inc = API->remote_info[k_data2].d_inc;
15519 					API->tile_reg = API->remote_info[k_data2].reg;
15520 					strncpy (s_inc, API->remote_info[k_data2].inc, GMT_LEN8);
15521 					inc_set = true;
15522 					gmt_M_memcpy (wesn, GMT->common.R.wesn, 4, double);
15523 					wesn[XLO] = floor ((wesn[XLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15524 					wesn[XHI] = ceil  ((wesn[XHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15525 					wesn[YLO] = floor ((wesn[YLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15526 					wesn[YHI] = ceil  ((wesn[YHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15527 					gmt_M_memcpy (API->tile_wesn, wesn, 4, double);	/* Retain this knowledge in case it was obtained via map_setup for an oblique area */
15528 					API->got_remote_wesn = true;	/* In case we need to use this subset when reading a grid or image */
15529 				}
15530 				if (dry_run)
15531 					goto dry_run;
15532 			}
15533 			if ((c = strchr (opt->arg, '+'))) {	/* Maybe have things like -I@earth_relief_30m+d given to grdimage */
15534 				c[0] = '\0';
15535 				if ((k_data = gmtlib_remote_file_is_tiled (API, opt->arg, &srtm_flag)) == GMT_NOTSET) {
15536 					c[0] = '+';
15537 					continue;	/* Argument is not a remote tiled dataset */
15538 				}
15539 			}
15540 			else if ((k_data = gmtlib_remote_file_is_tiled (API, opt->arg, &srtm_flag)) == GMT_NOTSET)
15541 				continue;	/* Argument is not a remote tiled dataset */
15542 			/* Here, the argument IS a tiled remote dataset */
15543 			if (first_time) {
15544 				/* Replace the magic reference to a tiled remote dataset with a file list of the required tiles.
15545 				 * Because GMT_Parse_Common has not been called yet, no -R -J have been processed yet.
15546 				 * Since -J may be needed if -R does oblique or give a region in projected units
15547 				 * we must also parse -J here first before parsing -R. If just -R -J then we must update from history now */
15548 
15549 dry_run:		if (opt_R == NULL) {	/* In this context we imply -Rd unless grdcut -S is what we are running */
15550 					if (!strncmp (mod_name, "grdcut", 6U) && (opt_S = GMT_Find_Option (API, 'S', *options))) {
15551 						char Sunit, za[GMT_LEN64] = {""}, zb[GMT_LEN64] = {""}, zc[GMT_LEN64] = {""}, *s = NULL;
15552 						int k = 0, Smode, n_errors = 0;
15553 						double Slon, Slat, Sradius;
15554 						if ((s = strstr (opt_S->arg, "+n")))
15555 							s[0] = '\0';	/* Chop off modifier */
15556 						else if (opt_S->arg[k] == 'n' && gmt_M_compat_check (API->GMT, 5))	/* Old-style -Sn<lon>/<lat>/<radius> syntax */
15557 							k = 1;	/* Skip leading n */
15558 						gmt_set_geographic (API->GMT, GMT_IN);	/* Ensure we expect geographic coordinates */
15559 						if (sscanf (&opt_S->arg[k], "%[^/]/%[^/]/%s", za, zb, zc) == 3) {
15560 							n_errors += gmt_verify_expectations (API->GMT, GMT_IS_LON, gmt_scanf_arg (API->GMT, za, GMT_IS_LON, false, &Slon), za);
15561 							n_errors += gmt_verify_expectations (API->GMT, GMT_IS_LAT, gmt_scanf_arg (API->GMT, zb, GMT_IS_LAT, false, &Slat), zb);
15562 							if (n_errors) {
15563 								GMT_Report (API, GMT_MSG_DEBUG, "Cannot parse lon/lat given grdcut -S%s\n", opt_S->arg);
15564 								return NULL;
15565 							}
15566 							Smode = gmt_get_distance (API->GMT, zc, &Sradius, &Sunit);
15567 						}
15568 						else {
15569 							GMT_Report (API, GMT_MSG_DEBUG, "Cannot parse arguments given grdcut -S%s\n", opt_S->arg);
15570 							return NULL;
15571 						}
15572 						if (s) s[0] = '+';	/* Restore modifier */
15573 						if (gmt_init_distaz (API->GMT, Sunit, Smode, GMT_MAP_DIST) == GMT_NOT_A_VALID_TYPE) {
15574 							GMT_Report (API, GMT_MSG_DEBUG, "Cannot initialize distances given grdcut -S%s\n", opt_S->arg);
15575 							return NULL;
15576 						}
15577 						Sradius = R2D * (Sradius / API->GMT->current.map.dist[GMT_MAP_DIST].scale) / GMT->current.proj.mean_radius;	/* Approximate radius in degrees */
15578 						gmt_circle_to_region (API->GMT, Slon, Slat, Sradius, wesn);	/* Get equivalent region */
15579 					}
15580 					else {	/* Must assume a global region */
15581 						wesn[XLO] = -180.0;	wesn[XHI] = +180.0;	wesn[YLO] = -90.0;	wesn[YHI] = +90.0;
15582 						GMT_Report (API, GMT_MSG_DEBUG, "Modern: Assuming -Rd since %s was given and no -R specified\n", opt->arg);
15583 					}
15584 				}
15585 				else {
15586 					char codes[3] = {""};
15587 					unsigned int n_to_set = 0, level = GMT->hidden.func_level;	/* Since we will need to increment prematurely since gmtinit_begin_module_sub has not been reached yet */
15588 					if (!opt_R->arg[0]) codes[n_to_set++] = 'R';	/* Must have -R */
15589 					if (opt_J && !opt_J->arg[0]) codes[n_to_set++] = 'J';	/* May or may not have -J */
15590 					if (n_to_set) gmtinit_complete_RJ (GMT, codes, *options);	/* Fill in what -R actually is (and possibly fill in -J) */
15591 					if (opt_J) gmtinit_parse_J_option (GMT, opt_J->arg);
15592 					API->GMT->hidden.func_level++;	/* Must do this here in case gmt_parse_R_option calls mapproject */
15593 					if (!parsed_R) gmt_parse_R_option (GMT, opt_R->arg);
15594 					API->GMT->hidden.func_level = level;	/* Reset to what it should be */
15595 					if (GMT->common.R.oblique || GMT->current.proj.projection == GMT_OBLIQUE_MERC || GMT->current.proj.projection == GMT_GENPER) {
15596 						/* Must do gmt_map_setup here to get correct region for building tiles */
15597 						if (!opt_J) {
15598 							GMT_Report (API, GMT_MSG_ERROR, "Cannot select %s and an oblique region without -J!\n", opt->arg);
15599 							return NULL;
15600 						}
15601 						GMT->common.J.active = GMT->common.R.active[RSET] = true;	/* Since we have set those here */
15602 
15603 						if (gmt_map_setup (GMT, GMT->common.R.wesn))
15604 							return NULL;
15605 						if (gmt_map_perimeter_search (GMT, GMT->common.R.wesn, false))	/* Refine without 0.1 degree padding */
15606 							return NULL;
15607 					}
15608 					GMT->common.R.active[RSET] = false;	/* Since we will need to parse it again officially in GMT_Parse_Common */
15609 					gmt_M_memcpy (wesn, GMT->common.R.wesn, 4, double);
15610 				}
15611 				first_time = false;	/* Done with getting valid region */
15612 			}
15613 			if (!inc_set) {
15614 				I = &API->remote_info[k_data];
15615 				d_inc = I->d_inc;
15616 				strncpy (s_inc, I->inc, GMT_LEN8);
15617 			}
15618 
15619 			/* Enforce multiple of tile grid resolution in wesn so requested region is in phase with tiles and at least covers the given region. */
15620 			wesn[XLO] = floor ((wesn[XLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15621 			wesn[XHI] = ceil  ((wesn[XHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15622 			wesn[YLO] = floor ((wesn[YLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15623 			wesn[YHI] = ceil  ((wesn[YHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15624 			if (dry_run) {	/* Only want to learn about the grid domain and resolution */
15625 				if (gmtinit_dryrun_report (API, wesn, d_inc, s_inc, options))
15626 					return NULL;
15627 			}
15628 			else {
15629 				/* Get a file with a list of all needed tiles */
15630 				list = gmtlib_get_tile_list (API, wesn, k_data, GMT->current.ps.active, srtm_flag);
15631 				/* Replace the remote file name with this local list */
15632 				gmt_M_str_free (opt->arg);
15633 				if (c) {	/* Must append the modifiers */
15634 					char args[GMT_LEN128] = {""};
15635 					c[0] = '+';
15636 					sprintf (args, "%s%s", list, c);
15637 					opt->arg = strdup (args);
15638 					gmt_M_str_free (list);
15639 				}
15640 				else
15641 					opt->arg = list;
15642 				API->got_remote_wesn = true;	/* Since we are making a grdblend job of tiles */
15643 			}
15644 		}
15645 	}
15646 
15647 	if (gmt_M_is_verbose (GMT, GMT_MSG_DEBUG) && options) {
15648 		char *string = GMT_Create_Cmd (API, *options);
15649 		GMT_Report (API, GMT_MSG_DEBUG, "Revised options: %s\n", string);
15650 		GMT_Destroy_Cmd (API, &string);
15651 	}
15652 	/* Here we can call the rest of the initialization */
15653 
15654 	return (gmtinit_begin_module_sub (API, lib_name, mod_name, Ccopy));
15655 }
15656 
gmt_detect_oblique_region(struct GMT_CTRL * GMT,char * file)15657 void gmt_detect_oblique_region (struct GMT_CTRL *GMT, char *file) {
15658 	 /* Special check to catch two types of situations:
15659 	  * 1. We do an oblique projection or some sorts and would like to know the rectangular w/e/s/n
15660 	  * 2. Same, but in connection with a remote grid or image so the w/e/sn should be rounded outwards by the increment.
15661 	  * Knowing this w/e/s/n can save a lot of work as we otherwise might be reading a large file due to -Rg|d
15662 	  *
15663 	  * We wish to "do no harm" as well, so only some situations will get through this function.
15664 	  */
15665 	int k_data;
15666 	double d_inc, wesn[4];
15667 	struct GMTAPI_CTRL *API = GMT->parent;	/* Shorthand */
15668 
15669 	if (gmt_M_is_cartesian (GMT, GMT_IN)) return;	/* This check only applies to geographic data */
15670 	if (API->got_remote_wesn) return;	/* Already set, probably in gmt_init_module */
15671 	if (gmt_M_is_linear (GMT) || GMT->current.proj.projection == GMT_POLAR) return;	/* Not a geographic projection */
15672 	if (!GMT->common.R.active[RSET]) return;	/* No -R given, presumably use whole grid or image */
15673 	if (!(gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]) && gmt_M_180_range (GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]))) return;	/* Gave -Rd or -Rg so need to probe more*/
15674 	if (gmt_M_is_azimuthal (GMT) && doubleAlmostEqual (fabs (GMT->current.proj.lat0), 90.0) && !GMT->common.R.oblique) return;	/* Nothing to do */
15675 	gmt_M_memcpy (wesn, GMT->common.R.wesn, 4, double);	/* Save the region we were given */
15676 
15677  	if (gmt_map_setup (GMT, GMT->common.R.wesn))	/* Set up projection */
15678 		return;	/* Something went wrong */
15679 	if (gmt_map_perimeter_search (GMT, GMT->common.R.wesn, false))	/* Refine without 0.1 degree padding */
15680 		return;	/* Something went wrong */
15681 	if (GMT->common.R.wesn[XHI] < GMT->common.R.wesn[XLO] || GMT->common.R.wesn[YHI] < GMT->common.R.wesn[YLO])
15682 		gmt_M_memcpy (GMT->common.R.wesn, wesn, 4, double);	/* Reset to give region if junk resulted */
15683 	else if (gmt_M_360_range (wesn[XLO], wesn[XHI]) && gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]))
15684 		gmt_M_memcpy (GMT->common.R.wesn, wesn, 2, double);	/* Reset to given global w/e in the same format */
15685 	gmt_M_memcpy (API->tile_wesn, GMT->common.R.wesn, 4, double);	/* Save the region we found */
15686 	d_inc = API->tile_inc;	/* Increment in degrees, if set */
15687 	if (d_inc == 0.0 && file && file[0] == '@' && (k_data = gmt_remote_dataset_id (API, file)) != GMT_NOTSET) {	/* Got a remote file to work on */
15688 		d_inc = API->remote_info[k_data].d_inc;	/* Increment in degrees */
15689 	}
15690 	if (d_inc > 0.0) {
15691 		API->tile_wesn[XLO] = floor ((API->tile_wesn[XLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15692 		API->tile_wesn[XHI] = ceil  ((API->tile_wesn[XHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15693 		API->tile_wesn[YLO] = floor ((API->tile_wesn[YLO] / d_inc) + GMT_CONV8_LIMIT) * d_inc;
15694 		API->tile_wesn[YHI] = ceil  ((API->tile_wesn[YHI] / d_inc) - GMT_CONV8_LIMIT) * d_inc;
15695 	}
15696 	API->got_remote_wesn = true;	/* In case we need to use this subset when reading a grid or image */
15697 }
15698 
15699 /*! Backwards compatible gmt_begin_module function for external modules built with GMT 5.3 or earlier */
gmt_begin_module(struct GMTAPI_CTRL * API,const char * lib_name,const char * mod_name,struct GMT_CTRL ** Ccopy)15700 struct GMT_CTRL * gmt_begin_module (struct GMTAPI_CTRL *API, const char *lib_name, const char *mod_name, struct GMT_CTRL **Ccopy) {
15701 	API->GMT->current.setting.run_mode = GMT_CLASSIC;	/* Since gmt_begin_module is 5.3 or earlier */
15702 	return (gmt_init_module (API, lib_name, mod_name, "", "", NULL, NULL, Ccopy));
15703 }
15704 
15705 /*! . */
gmt_end_module(struct GMT_CTRL * GMT,struct GMT_CTRL * Ccopy)15706 void gmt_end_module (struct GMT_CTRL *GMT, struct GMT_CTRL *Ccopy) {
15707 	unsigned int i, V_level = GMT->current.setting.verbose;	/* Keep copy of currently selected level */
15708 	bool pass_changes_back;
15709 	struct GMT_DEFAULTS saved_settings;
15710 	double spacing[2];
15711 
15712 	gmt_M_memcpy (spacing, GMT->current.plot.gridline_spacing, 2U, double);	/* Remember these so they can survive the end of the module */
15713 
15714 #ifdef HAVE_GDAL
15715 	/* If that's the case, clean up anu GDAL CT object */
15716 	if (GMT->current.gdal_read_in.hCT_fwd)
15717 		OCTDestroyCoordinateTransformation(GMT->current.gdal_read_in.hCT_fwd);
15718 	if (GMT->current.gdal_read_in.hCT_inv)
15719 		OCTDestroyCoordinateTransformation(GMT->current.gdal_read_in.hCT_inv);
15720 	GMT->current.gdal_read_in.hCT_fwd = GMT->current.gdal_read_in.hCT_inv = NULL;
15721 #endif
15722 
15723 	if (GMT->hidden.func_level == GMT_TOP_MODULE && GMT->current.ps.oneliner && GMT->current.ps.active) {
15724 		char *setting = getenv ("GMT_END_SHOW");
15725 		char *show = (setting && !strcmp (setting, "off")) ? "" : "show";
15726 		GMT->current.ps.oneliner = false;
15727 		if ((i = GMT_Call_Module (GMT->parent, "end", GMT_MODULE_CMD, show))) {
15728 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to call module end for a one-liner plot.\n");
15729 			return;
15730 		}
15731 		GMT->current.setting.run_mode = GMT_CLASSIC;	/* all pau with modern session */
15732 	}
15733 
15734 	if (GMT->hidden.func_level == GMT_TOP_MODULE && GMT->parent->log_level == GMT_LOG_ONCE) {	/* Reset logging to default at the end of a top-level module */
15735 		fclose (GMT->session.std[GMT_ERR]);
15736 		GMT->session.std[GMT_ERR] = stderr;
15737 		GMT->parent->log_level = GMT_LOG_OFF;
15738 	}
15739 
15740 	gmtinit_set_last_dimensions (GMT->parent);	/* Save canvas size */
15741 
15742 	if (GMT->current.proj.n_geodesic_approx) {
15743 		GMT_Report(GMT->parent, GMT_MSG_DEBUG, "Of % " PRIu64 " geodesic calls, % " PRIu64 " exceeded the iteration limit of 50.\n",
15744 		           GMT->current.proj.n_geodesic_calls, GMT->current.proj.n_geodesic_approx);
15745 	}
15746 
15747 	gmtlib_garbage_collection (GMT->parent, GMT->hidden.func_level);	/* Free up all registered memory for this module level */
15748 
15749 	/* At the end of the module we restore all GMT settings as we found them (in Ccopy) */
15750 
15751 	pass_changes_back = (!strncmp (GMT->init.module_name, "gmtset", 6U));	/* gmtset is special as we want it to affect the session */
15752 	if (pass_changes_back) gmt_M_memcpy (&saved_settings, &(GMT->current.setting), 1U, struct GMT_DEFAULTS);
15753 
15754 	/* GMT_INIT */
15755 
15756 	if (GMT->current.setting.run_mode == GMT_MODERN && !GMT->current.ps.active && GMT->common.R.active[RSET]) {	/* Modern mode: Add enhanced RG history, if possible */
15757 		/* For grid regions we add history under RG in the form <region>[+I<incs>][+GP|G] */
15758 		char RG[GMT_LEN256] = {""}, tmp[GMT_LEN64] = {""};
15759 		int id = gmt_get_option_id (0, "R") + 1;	/* The RG history slot */
15760 		if (GMT->init.history[id]) gmt_M_str_free (GMT->init.history[id]);
15761 		snprintf (RG, GMT_LEN256, "%.16g/%.16g/%.16g/%.16g", GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
15762 		if (GMT->common.R.active[ISET]) {	/* Also want grid increment saved */
15763 			snprintf (tmp, GMT_LEN64, "+I%.16g/%.16g", GMT->common.R.inc[GMT_X], GMT->common.R.inc[GMT_Y]);
15764 			strcat (RG, tmp);
15765 		}
15766 		if (GMT->common.R.active[GSET]) {	/* Also want grid registration saved */
15767 			snprintf (tmp, GMT_LEN64, "+G%c%c", GMT->common.R.registration == GMT_GRID_PIXEL_REG ? 'P' : 'G', GMT->common.R.row_order == k_nc_start_north ? 'T' : 'B');
15768 			strcat (RG, tmp);
15769 		}
15770 		GMT->init.history[id] = strdup (RG);
15771 	}
15772 
15773 	/* We treat the history explicitly since we accumulate the history regardless of nested level */
15774 
15775 	for (i = 0; i < GMT_N_UNIQUE; i++)
15776 		Ccopy->init.history[i] = GMT->init.history[i];
15777 
15778 	/* GMT_CURRENT */
15779 
15780 	Ccopy->current.ps.clip_level = GMT->current.ps.clip_level;
15781 	Ccopy->current.ps.layer = GMT->current.ps.layer;
15782 	Ccopy->current.ps.active = GMT->current.ps.active;
15783 	Ccopy->current.ps.initialize = GMT->current.ps.initialize;
15784 	Ccopy->current.plot.color_seq_id[0] = GMT->current.plot.color_seq_id[0];
15785 	Ccopy->current.plot.color_seq_id[1] = GMT->current.plot.color_seq_id[1];
15786 
15787 	/* GMT_COMMON */
15788 
15789 	if (Ccopy->common.U.label != GMT->common.U.label) gmt_M_str_free (Ccopy->common.U.label);
15790 	Ccopy->common.U.label = GMT->common.U.label;
15791 	for (i = 0; i < GMT->common.a.n_aspatial; i++) gmt_M_str_free (GMT->common.a.name[i]);
15792 	gmt_M_str_free (GMT->common.h.title);
15793 	gmt_M_str_free (GMT->common.h.remark);
15794 	gmt_M_str_free (GMT->common.h.colnames);
15795 	if (GMT->common.e.active) gmt_free_text_selection (GMT, &GMT->common.e.select);
15796 
15797 	/* GMT_PLOT */
15798 
15799 	gmtinit_free_plot_array (GMT);	/* Free plot arrays and reset n_alloc, n */
15800 	gmtlib_free_custom_symbols (GMT);	/* Free linked list of custom psxy[z] symbols, if any, since only valid in this module */
15801 	gmtinit_free_user_media (GMT);	/* Free user-specified media formats */
15802 
15803 	/* Reset frame fill painting */
15804 	Ccopy->current.map.frame.paint[GMT_Z] = GMT->current.map.frame.paint[GMT_Z];
15805 	gmt_M_memcpy (Ccopy->current.map.frame.fill[GMT_Z].rgb, GMT->current.map.frame.fill[GMT_Z].rgb, 3, double);
15806 
15807 	/* GMT_IO */
15808 
15809 	gmtlib_free_ogr (GMT, &(GMT->current.io.OGR), 1);	/* Free up the GMT/OGR structure, if used */
15810 	gmtlib_free_tmp_arrays (GMT);		/* Free emp memory for vector io or processing */
15811 	gmtinit_reset_colformats (GMT);		/* Wipe previous settings */
15812 	gmtinit_free_dirnames (GMT);		/* Wipe previous dir names */
15813 
15814 	gmtlib_fft_cleanup (GMT); /* Clean FFT resources */
15815 
15816 	gmt_M_memcpy (GMT->parent->common_snapshot, &GMT->common, 1, struct GMT_COMMON);	/* Get a common option snapshot */
15817 
15818 	/* Overwrite GMT with what we saved in gmt_init_module */
15819 	gmt_M_memcpy (GMT, Ccopy, 1, struct GMT_CTRL);	/* Overwrite struct with things from Ccopy */
15820 	/* ALL POINTERS IN GMT ARE NOW JUNK AGAIN */
15821 	if (pass_changes_back) gmt_M_memcpy (&(GMT->current.setting), &saved_settings, 1U, struct GMT_DEFAULTS);
15822 	GMT->current.setting.verbose = V_level;	/* Pass the currently selected level back up */
15823 	GMT->init.n_custom_symbols = 0;	GMT->init.custom_symbol = NULL;	/* Reset this to 0/NULL since just junk (already freed) */
15824 
15825 	/* Try this to fix valgrind leaks */
15826 
15827 	GMT->session.GSHHGDIR = (Ccopy->session.GSHHGDIR) ? strdup (Ccopy->session.GSHHGDIR) : NULL;
15828 	GMT->session.DCWDIR = (Ccopy->session.DCWDIR) ? strdup (Ccopy->session.DCWDIR) : NULL;
15829 	GMT->session.SHAREDIR = (Ccopy->session.SHAREDIR) ? strdup (Ccopy->session.SHAREDIR) : NULL;
15830 	GMT->session.HOMEDIR = (Ccopy->session.HOMEDIR) ? strdup (Ccopy->session.HOMEDIR) : NULL;
15831 	GMT->session.USERDIR = (Ccopy->session.USERDIR) ? strdup (Ccopy->session.USERDIR) : NULL;
15832 	GMT->session.CACHEDIR = (Ccopy->session.CACHEDIR) ? strdup (Ccopy->session.CACHEDIR) : NULL;
15833 	GMT->session.DATADIR = (Ccopy->session.DATADIR) ? strdup (Ccopy->session.DATADIR) : NULL;
15834 	GMT->session.TMPDIR = (Ccopy->session.TMPDIR) ? strdup (Ccopy->session.TMPDIR) : NULL;
15835 	GMT->session.CUSTOM_LIBS = (Ccopy->session.CUSTOM_LIBS) ? strdup (Ccopy->session.CUSTOM_LIBS) : NULL;
15836 	GMT->session.DATASERVER = (Ccopy->session.DATASERVER) ? strdup (Ccopy->session.DATASERVER) : NULL;
15837 
15838 	/* Now fix things that were allocated separately */
15839 	if (Ccopy->session.n_user_media) {
15840 		GMT->session.n_user_media = Ccopy->session.n_user_media;
15841 		GMT->session.user_media = gmt_M_memory (GMT, NULL, Ccopy->session.n_user_media, struct GMT_MEDIA);
15842 		GMT->session.user_media_name = gmt_M_memory (GMT, NULL, Ccopy->session.n_user_media, char *);
15843 		for (i = 0; i < Ccopy->session.n_user_media; i++) GMT->session.user_media_name[i] = strdup (Ccopy->session.user_media_name[i]);
15844 	}
15845 
15846 	gmtinit_free_user_media (Ccopy);		/* Free user-specified media formats */
15847 
15848 	/* These are because they are kept between module calls in a same session. So we need to reset them
15849 	 * except for Symbol and ZapfDingbats, etc. */
15850 	if (GMT->hidden.func_level == GMT_CONTROLLER) {	/* Only when top-level module ends */
15851 		GMT->current.setting.verbose = GMT_MSG_WARNING;
15852 		for (i = 0; i < (unsigned int)GMT->PSL->internal.N_FONTS; i++)
15853 			GMT->PSL->internal.font[i].encoded = GMT->PSL->internal.font[i].encoded_orig;
15854 		GMT->PSL->current.fontsize = 0;
15855 	}
15856 
15857 	gmt_M_memcpy (GMT->current.plot.gridline_spacing, spacing, 2U, double);	/* Put these back so they can go into gmt.history (if nonzero) */
15858 
15859 	/*
15860 	if (GMT->parent->external) {
15861 		gmt_reload_settings (GMT);	// Re-read local GMT default settings (if any) and override with user settings
15862 	}
15863 	*/
15864 	GMT->current.setting.io_lonlat_toggle[GMT_IN] = GMT->current.setting.io_lonlat_toggle[GMT_OUT] = false;
15865 
15866 #ifdef HAVE_GDAL
15867 	/* Reset these GDAL in/out stuff */
15868 	gmt_M_memset (&GMT->current.gdal_read_in,  1, struct GMT_GDALREAD_IN_CTRL);
15869 	gmt_M_memset (&GMT->current.gdal_read_out, 1, struct GMT_GDALREAD_OUT_CTRL);
15870 	gmt_M_memset (&GMT->current.gdal_write,    1, struct GMT_GDALWRITE_CTRL);
15871 #endif
15872 
15873 	GMT->parent->cache = false;		/* Otherwise gdalread from externals on Windows would mingle CACHEDIR in fnames */
15874 
15875 	gmt_M_str_free (Ccopy);	/* Good riddance */
15876 }
15877 
15878 /*! Update vector head length and width parameters based on size_z and v_angle, and deal with pen/fill settings */
gmt_init_vector_param(struct GMT_CTRL * GMT,struct GMT_SYMBOL * S,bool set,bool outline,struct GMT_PEN * pen,bool do_fill,struct GMT_FILL * fill)15879 int gmt_init_vector_param (struct GMT_CTRL *GMT, struct GMT_SYMBOL *S, bool set, bool outline, struct GMT_PEN *pen, bool do_fill, struct GMT_FILL *fill) {
15880 	bool no_outline = false, no_fill = false;
15881 	if (S == NULL) return 0;	/* Nothing to do */
15882 	if (set) {	/* Determine proper settings for head fill or outline */
15883 		if (outline && (S->v.status & PSL_VEC_OUTLINE2) == 0 && pen) S->v.pen = *pen;	/* If no +p<pen> but -W<pen> was used, use same pen for vector heads */
15884 		else if (!outline && S->v.status & PSL_VEC_OUTLINE2 && pen) *pen = S->v.pen;	/* If no -W<pen> was set but +p<pen> given, use same pen for vector tails */
15885 		else if (!outline && (S->v.status & PSL_VEC_OUTLINE2) == 0) no_outline = true;
15886 		if (do_fill && (S->v.status & PSL_VEC_FILL2) == 0 && fill) S->v.fill = *fill;	/* If no +g<fill> but -G<fill> was used, use same fill for vector heads */
15887 		else if (!do_fill && S->v.status & PSL_VEC_FILL2) no_fill = false;		/* If no -G<fill> was set but +g<fill> given, we do nothing here */
15888 		else if (!do_fill && (S->v.status & PSL_VEC_FILL2) == 0) no_fill = true;	/* Neither -G<fill> nor +g<fill> were set */
15889 		if (no_outline && no_fill && (S->v.status & PSL_VEC_HEADS)) {
15890 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot draw vector heads without specifying at least one of head outline or head fill.\n");
15891 			return 1;
15892 		}
15893 	}
15894 	if (gmt_M_is_zero (S->size_x)) return 0;	/* Not set yet */
15895 	if (!(S->symbol == GMT_SYMBOL_VECTOR_V4 || S->v.parsed_v4)) {
15896 		S->v.h_length = (float)S->size_x;
15897 		S->v.h_width = (float)(2.0 * S->v.h_length * tand (0.5 * S->v.v_angle));
15898 	}
15899 	return 0;
15900 }
15901 
15902 /*! Parser for -Sv|V, -S=, and -Sm */
gmt_parse_vector(struct GMT_CTRL * GMT,char symbol,char * text,struct GMT_SYMBOL * S)15903 int gmt_parse_vector (struct GMT_CTRL *GMT, char symbol, char *text, struct GMT_SYMBOL *S) {
15904 
15905 	unsigned int pos = 0, k, f = 0, end, error = 0;
15906 	size_t len;
15907 	bool p_opt = false, g_opt = false;
15908 	int j;
15909 	char p[GMT_BUFSIZ];
15910 	double value[2];
15911 
15912 	S->v.pen = GMT->current.setting.map_default_pen;
15913 	gmt_init_fill (GMT, &S->v.fill, -1.0, -1.0, -1.0);	/* Default is no fill */
15914 	S->v.status = 0;	/* Start with no flags turned on */
15915 	S->v.v_angle = 30.0f;	S->v.v_norm = -1.0f;	S->v.v_stem = 0.1f;
15916 	S->v.v_kind[0] = S->v.v_kind[1] = PSL_VEC_ARROW;
15917 	S->v.v_shape = (float)GMT->current.setting.map_vector_shape;	/* Can be overridden with +h<shape> */
15918 	for (k = 0; text[k] && text[k] != '+'; k++);	/* Either find the first plus or run out or chars */
15919 	strncpy (p, text, k); p[k] = 0;
15920 
15921 	while ((gmt_strtok (&text[k], "+", &pos, p))) {	/* Parse any +<modifier> statements */
15922 		switch (p[0]) {
15923 			case 'a': 	/* Vector head opening angle [30] */
15924 				S->v.v_angle = (float)atof (&p[1]);
15925 				if (S->v.v_angle >= 180) {	/* Bad apex angle */
15926 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Vector head angle cannot be >= 180 degrees\n");
15927 					error++;
15928 				}
15929 				break;
15930 			case 'b':	/* Vector head at beginning point */
15931 				S->v.status |= PSL_VEC_BEGIN;
15932 				switch (p[1]) {
15933 					case 'a': S->v.v_kind[0] = PSL_VEC_ARROW;	break; /* Explicitly selected arrow head */
15934 					case 'A': S->v.v_kind[0] = PSL_VEC_ARROW_PLAIN;	break;
15935 					case 'i': S->v.v_kind[0] = PSL_VEC_TAIL;	break;
15936 					case 'I': S->v.v_kind[0] = PSL_VEC_TAIL_PLAIN;	break;
15937 					case 'c': S->v.v_kind[0] = PSL_VEC_CIRCLE;	break;
15938 					case 's': S->v.v_kind[0] = PSL_VEC_SQUARE;	break;
15939 					case 't': S->v.v_kind[0] = PSL_VEC_TERMINAL;	break;
15940 			  	 	case 'l': S->v.v_kind[0] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_BEGIN_L;	break;	/* Only left  half of head requested */
15941 			  	  	case 'r': S->v.v_kind[0] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_BEGIN_R;	break;	/* Only right half of head requested */
15942 					default:  S->v.v_kind[0] = PSL_VEC_ARROW;	break;
15943 				}
15944 				if (p[1] && p[2]) {
15945 					if (p[2] == 'l') S->v.status |= PSL_VEC_BEGIN_L;	/* Only left  half of head requested */
15946 	  	  			else if (p[2] == 'r') S->v.status |= PSL_VEC_BEGIN_R;	/* Only right half of head requested */
15947 				}
15948 				break;
15949 			case 'e':	/* Vector head at end point */
15950 				S->v.status |= PSL_VEC_END;
15951 				switch (p[1]) {
15952 					case 'a': S->v.v_kind[1] = PSL_VEC_ARROW;	break;	/* Explicitly selected arrow head */
15953 					case 'A': S->v.v_kind[1] = PSL_VEC_ARROW_PLAIN;	break;
15954 					case 'i': S->v.v_kind[1] = PSL_VEC_TAIL;	break;
15955 					case 'I': S->v.v_kind[1] = PSL_VEC_TAIL_PLAIN;	break;
15956 					case 'c': S->v.v_kind[1] = PSL_VEC_CIRCLE;	break;
15957 					case 's': S->v.v_kind[1] = PSL_VEC_SQUARE;	break;
15958 					case 't': S->v.v_kind[1] = PSL_VEC_TERMINAL;	break;
15959 			  	 	case 'l': S->v.v_kind[1] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_END_L;	break;	/* Only left  half of head requested */
15960 			  	  	case 'r': S->v.v_kind[1] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_END_R;	break;	/* Only right half of head requested */
15961 					default:  S->v.v_kind[1] = PSL_VEC_ARROW;	break;
15962 				}
15963 				if (p[1] && p[2]) {
15964 					if (p[2] == 'l') S->v.status |= PSL_VEC_END_L;	/* Only left half of head requested */
15965 	  	  			else if (p[2] == 'r') S->v.status |= PSL_VEC_END_R;	/* Only right half of head requested */
15966 				}
15967 				break;
15968 			case 'g':	/* Vector head fill +g[<fill>]*/
15969 				g_opt = true;	/* Marks that +g was used */
15970 				if (p[1] == '-' || p[1] == '\0') break; /* Do NOT turn on fill [- is deprecated] */
15971 				S->v.status |= PSL_VEC_FILL;
15972 				if (p[1]) {
15973 					if (gmt_getfill (GMT, &p[1], &S->v.fill)) {
15974 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +g<fill> argument %s\n", &p[1]);
15975 						error++;
15976 					}
15977 					S->v.status |= PSL_VEC_FILL2;
15978 				}
15979 				else
15980 					S->v.status |= PSL_VEC_FILL;
15981 				break;
15982 			case 'h':	/* Vector shape [MAP_VECTOR_SHAPE] */
15983 				S->v.v_shape = (float)atof (&p[1]);
15984 				if (S->v.v_shape < -2.0 || S->v.v_shape > 2.0) {
15985 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +h<shape> modifier value: Must be in -2/+2 range\n");
15986 					error++;
15987 				}
15988 				break;
15989 			case 'j':	/* Vector justification */
15990 				if (symbol == 'm') {
15991 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "-Sm does not accept +j<just> modifiers.\n");
15992 					error++;
15993 				}
15994 				else {
15995 					switch (p[1]) {
15996 						case 'b': S->v.status |= PSL_VEC_JUST_B;	break;	/* Input (x,y) refers to vector beginning point */
15997 						case 'c': S->v.status |= PSL_VEC_JUST_C;	break;	/* Input (x,y) refers to vector center point */
15998 						case 'e': S->v.status |= PSL_VEC_JUST_E;	break;	/* Input (x,y) refers to vector end point */
15999 						default:  /* Bad justifier code */
16000 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +j<just> modifier %c\n", p[1]);
16001 							error++;
16002 							break;
16003 					}
16004 				}
16005 				break;
16006 			case 'l': S->v.status |= (PSL_VEC_BEGIN_L + PSL_VEC_END_L);	break;	/* Obsolete modifier for left halves at active heads */
16007 			case 'm':	/* Vector head at midpoint of segment */
16008 				switch (p[1]) {
16009 					case '\0':	f = 1;	S->v.status |= PSL_VEC_MID_FWD;	break;	/* Place forward-pointing arrow head at center of segment */
16010 					case 'f':	f = 2;	S->v.status |= PSL_VEC_MID_FWD;	break;	/* Place forward-pointing arrow head at center of segment */
16011 					case 'r':	f = 2;	S->v.status |= PSL_VEC_MID_BWD;	break;	/* Place backward-pointing arrow head at center of segment */
16012 					case 'a': case 'c': case 's': case 't':	f = 1;	S->v.status |= PSL_VEC_MID_FWD;	break;	/* Handle these below */
16013 					default:  /* Bad direction code */
16014 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +m<dir> modifier %c\n", p[1]);
16015 						error++;
16016 						break;
16017 				}
16018 				end = (S->v.status & PSL_VEC_MID_FWD) ? PSL_END : PSL_BEGIN;	/* Which head-type to use at center */
16019 				switch (p[f]) {	/* Optional types */
16020 					case 'a': S->v.v_kind[end] = PSL_VEC_ARROW;	break;	/* Explicitly selected arrow head */
16021 					case 'A': S->v.v_kind[end] = PSL_VEC_ARROW_PLAIN;	break;
16022 					case 'i': S->v.v_kind[end] = PSL_VEC_TAIL;	break;
16023 					case 'I': S->v.v_kind[end] = PSL_VEC_TAIL_PLAIN;	break;
16024 					case 'c': S->v.v_kind[end] = PSL_VEC_CIRCLE;	break;
16025 					case 's': S->v.v_kind[end] = PSL_VEC_SQUARE;	break;
16026 					case 't': S->v.v_kind[end] = PSL_VEC_TERMINAL;	break;
16027 			  	 	case 'l': S->v.v_kind[end] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_END_L;	break;	/* Only left  half of head requested */
16028 			  	  	case 'r': S->v.v_kind[end] = PSL_VEC_ARROW;	S->v.status |= PSL_VEC_END_R;	break;	/* Only right half of head requested */
16029 					default:  S->v.v_kind[end] = PSL_VEC_ARROW;	break;	/* Default is arrow */
16030 				}
16031 				if (p[f] && p[f+1]) {
16032 	  	  			if (p[f+1] == 'l') S->v.status |= PSL_VEC_END_L;		/* Only left half of head requested */
16033 	  	  			else if (p[f+1] == 'r') S->v.status |= PSL_VEC_END_R;	/* Only right half of head requested */
16034 				}
16035 				break;
16036 			case 'n':	/* Vector shrinking head */
16037 				len = strlen (p);
16038 				j = (symbol == 'v' || symbol == 'V') ? gmt_get_dim_unit (GMT, p[len-1]) : -1;	/* Only -Sv|V takes unit */
16039 				S->v.v_norm = (float)atof (&p[1]);	/* This is normalizing length in given units, not (yet) converted to inches or degrees (but see next lines) */
16040 				if (symbol == '=') {	/* Since norm distance is in km we convert to spherical degrees */
16041 					if (strchr (GMT_DIM_UNITS GMT_LEN_UNITS2, p[len-1]) && p[len-1] != 'k') {
16042 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Vector shrink length limit for geovectors must be given in km!\n");
16043 						error++;
16044 					}
16045 					S->v.v_norm /= (float)GMT->current.proj.DIST_KM_PR_DEG;
16046 				}
16047 				else if (j >= GMT_CM)	/* Convert length from given unit to inches */
16048 					S->v.v_norm *= GMT->session.u2u[j][GMT_INCH];
16049 				else	/* Convert length from default unit to inches */
16050 					S->v.v_norm *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
16051 				/* Here, v_norm is either in inches (if Cartesian vector) or spherical degrees (if geovector) */
16052 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Vector shrink scale v_norm = %g\n", S->v.v_norm);
16053 				break;
16054 			case 'o':	/* Sets oblique pole for small or great circles */
16055 				S->v.status |= PSL_VEC_POLE;
16056 				if (!p[1]) {	/* Gave no pole, use North pole */
16057 					S->v.pole[GMT_X] = 0.0f;	S->v.pole[GMT_Y] = 90.0f;
16058 				}
16059 				else if ((j = GMT_Get_Values (GMT->parent, &p[1], value, 2)) != 2) {
16060 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +o[<plon>/<plat>] argument %s\n", &p[1]);
16061 					error++;
16062 				}
16063 				else {	/* Successful parsing of pole */
16064 					S->v.pole[GMT_X] = (float)value[GMT_X];	S->v.pole[GMT_Y] = (float)value[GMT_Y];}
16065 				break;
16066 			case 'p':	/* Vector pen and head outline +p[<pen>] */
16067 				p_opt = true;	/* Marks that +p was used */
16068 				if (p[1] == '\0')	/* Do NOT turn on head outlines */
16069 					j = 1;
16070 				else if (p[1] == '-')	/* Do NOT turn on head outlines [deprecated] */
16071 					j = 2;
16072 				else {	/* Do turn on head outlines */
16073 					j = 1;
16074 					S->v.status |= PSL_VEC_OUTLINE;
16075 				}
16076 				if (p[j]) {	/* Change default vector pen */
16077 					if (gmt_getpen (GMT, &p[j], &S->v.pen)) {
16078 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +p<pen> argument %s\n", &p[1]);
16079 						error++;
16080 					}
16081 					S->v.status |= PSL_VEC_OUTLINE2;	/* Flag that a pen specification was given */
16082 				}
16083 				/* So status may here be both PSL_VEC_OUTLINE (draw head outline) and PSL_VEC_OUTLINE2 (do it with this pen) */
16084 				break;
16085 			case 'q': S->v.status |= PSL_VEC_ANGLES;	break;	/* Expect start,stop angle rather than length in input */
16086 			case 'r': S->v.status |= (PSL_VEC_BEGIN_R + PSL_VEC_END_R);	break;	/* Obsolete modifier for right halves at active heads */
16087 			case 's': S->v.status |= PSL_VEC_JUST_S;	break;	/* Input (angle,length) are vector end point (x,y) instead */
16088 			case 't':	/* Get endpoint trim(s) */
16089 				switch (p[1]) {
16090 					case 'b':	f = 2;	S->v.status |= PSL_VEC_OFF_BEGIN;	break;	/* Shift begin point by some trim amount */
16091 					case 'e':	f = 2;	S->v.status |= PSL_VEC_OFF_END;		break;	/* Shift end point by some trim amount */
16092 					default:  	f = 1;	S->v.status |= (PSL_VEC_OFF_BEGIN+PSL_VEC_OFF_END);	break;	/* Do both */
16093 				}
16094 				if ((j = gmt_get_pair (GMT, &p[f], GMT_PAIR_DIM_DUP, value)) < 1)  {
16095 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +t <trim> values %s\n", &p[f]);
16096 					error++;
16097 				}
16098 				else {	/* Successful parsing of trim(s) */
16099 					if (S->v.status & PSL_VEC_OFF_BEGIN) {
16100 						if (value[PSL_BEGIN] < 0.0) {
16101 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "The +tb <trim> values cannot be negative\n");
16102 							error++;
16103 						}
16104 						else
16105 							S->v.v_trim[PSL_BEGIN] = (float)value[PSL_BEGIN];
16106 					}
16107 					if (S->v.status & PSL_VEC_OFF_END) {
16108 						if (value[PSL_END] < 0.0) {
16109 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "The +te <trim> values cannot be negative\n");
16110 							error++;
16111 						}
16112 						else
16113 							S->v.v_trim[PSL_END] = (float)value[PSL_END];
16114 					}
16115 				}
16116 				break;
16117 			case 'z':	/* Input (angle,length) are vector components (dx,dy) instead */
16118 				S->v.status |= PSL_VEC_COMPONENTS;
16119 				S->v.comp_scale = (float)gmt_convert_units (GMT, &p[1], GMT->current.setting.proj_length_unit, GMT_INCH);
16120 				break;
16121 			default:
16122 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad modifier +%c\n", p[0]);
16123 				error++;
16124 				break;
16125 		}
16126 	}
16127 	if ((S->v.status & PSL_VEC_MID_FWD || S->v.status & PSL_VEC_MID_BWD) && (S->v.status & PSL_VEC_BEGIN || S->v.status & PSL_VEC_END)) {
16128 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot combine mid-point vector head (+m) with end-point heads (+b | +e)\n");
16129 		error++;
16130 	}
16131 	if (!g_opt) S->v.status |= PSL_VEC_FILL;	/* Default is to fill vector head with current fill unless (a) no fill given or (b) turned off with +g- */
16132 	if (!p_opt) S->v.status |= PSL_VEC_OUTLINE;	/* Default is to draw vector head outline with current pen unless explicitly turned off with +p- */
16133 
16134 	/* Set head parameters */
16135 	gmt_init_vector_param (GMT, S, false, false, NULL, false, NULL);
16136 
16137 	return (error);
16138 }
16139 
gmtinit_get_diameter(struct GMT_CTRL * GMT,char code,char * text,bool * geo)16140 double gmtinit_get_diameter (struct GMT_CTRL *GMT, char code, char *text, bool *geo) {
16141 	bool is_geo;
16142 	size_t len = strlen (text);
16143 	double d;
16144 	if (code == 'w' || strchr (GMT_DIM_UNITS, text[len-1])) {	/* Got diameter in plot units */
16145 		is_geo = false;
16146 		d = gmt_M_to_inch (GMT, text);
16147 	}
16148 	else {	/* Geo-wedge with radius given in a distance unit */
16149 		(void)gmtlib_scanf_geodim (GMT, text, &d);
16150 		is_geo = true;
16151 	}
16152 	if (geo) *geo = is_geo;	/* Since geo may be passed as NULL */
16153 	return (d);
16154 }
16155 
16156 #define GMT_VECTOR_CODES "mMvV="	/* The vector symbol codes */
16157 
16158 /*! . */
gmt_parse_symbol_option(struct GMT_CTRL * GMT,char * text,struct GMT_SYMBOL * p,unsigned int mode,bool cmd)16159 int gmt_parse_symbol_option (struct GMT_CTRL *GMT, char *text, struct GMT_SYMBOL *p, unsigned int mode, bool cmd) {
16160 	/* mode = 0 for 2-D (psxy) and = 1 for 3-D (psxyz); cmd = true when called to process command line options */
16161 	int decode_error = 0, bset = 0, j, n = 0, k, slash = 0, colon, col_off = mode, len, n_z = 0;
16162 	bool check = true, degenerate = false, add_to_base = false;
16163 	unsigned int ju, col;
16164 	char symbol_type, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, txt_d[GMT_LEN256] = {""};
16165 	char text_cp[GMT_LEN256] = {""}, diameter[GMT_LEN32] = {""}, *c = NULL;
16166 	static char *allowed_symbols[2] = {"~=-+AaBbCcDdEefGgHhIiJjMmNnpqRrSsTtVvWwxy", "=-+AabCcDdEefGgHhIiJjMmNnOopqRrSsTtUuVvWwxy"};
16167 	static char *bar_symbols[2] = {"Bb", "-BbOoUu"};
16168 	if (cmd) {
16169 		p->base = GMT->session.d_NaN;
16170 		p->u = GMT->current.setting.proj_length_unit;
16171 	}
16172 	else {
16173 		p->read_size = p->read_size_cmd;
16174 	}
16175 	p->n_required = p->convert_angles = p->n_nondim = p->base_set = 0;
16176 	p->user_unit[GMT_X] = p->user_unit[GMT_Y] = p->u_set = false;
16177 	p->font = GMT->current.setting.font_annot[GMT_PRIMARY];
16178 	if (p->read_size)  p->given_size_x = p->given_size_y = p->size_x = p->size_y = 0.0;
16179 	p->factor = 1.0;
16180 
16181 	/* col_off is the col number of first parameter after (x,y) [or (x,y,z) if mode == 1)].
16182 	   However, if size is not given then that is required too so col_off++ */
16183 
16184 	if (!strncmp (text, "E-", 2U) || !strncmp (text, "J-", 2U)) {
16185 		/* Special degenerate geographic ellipse and rectangle symbols, remove the - to avoid parsing issues */
16186 		degenerate = true;
16187 		if (text[2]) strncpy (diameter, &text[2], GMT_LEN32-1);	/* Gave circle diameter on command line */
16188 		text[1] = 0;
16189 	}
16190 	if (!text[0]) {	/* No symbol or size given */
16191 		p->size_x = p->size_y = 0.0;
16192 		symbol_type = '*';
16193 		col_off++;
16194 		if (cmd) p->read_size_cmd = true;
16195 		if (cmd) p->read_symbol_cmd = 1;
16196 	}
16197 	else if (isdigit ((int)text[0]) || text[0] == '.') {	/* Size, but no symbol given */
16198 		n = sscanf (text, "%[^/]/%s", txt_a, txt_b);
16199 		p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_a);
16200 		if (n == 2)
16201 			p->size_y = p->given_size_y = gmt_M_to_inch (GMT, txt_b);
16202 		else if (n == 1)
16203 			p->size_y = p->given_size_y = p->size_x;
16204 		else
16205 			decode_error++;
16206 		symbol_type = '*';
16207 		if (cmd) p->read_symbol_cmd = 1;
16208 	}
16209 	else if (text[0] == 'l') {	/* Letter symbol is special case */
16210 		strncpy (text_cp, text, GMT_LEN256-1);	/* Copy for processing later */
16211 		symbol_type = 'l';
16212 		if (!text[1]) {	/* No size or text given */
16213 			if (p->size_x == 0.0) p->size_x = p->given_size_x;
16214 			if (p->size_y == 0.0) p->size_y = p->given_size_y;
16215 			if (cmd) p->read_size_cmd = true;
16216 			col_off++;
16217 		}
16218 		else if (text[1] == '+' || (text[1] == '/' && gmt_M_compat_check (GMT, 4))) {	/* No size given */
16219 			/* Any deprecate message comes below so no need here */
16220 			if (p->size_x == 0.0) p->size_x = p->given_size_x;
16221 			if (p->size_y == 0.0) p->size_y = p->given_size_y;
16222 			col_off++;
16223 		}
16224 		else {
16225 			n = sscanf (&text_cp[1], "%[^+]+%*s", txt_a);
16226 			p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_a);
16227 			decode_error += (n != 1);
16228 		}
16229 	}
16230 	else if (text[0] == 'k' || text[0] == 'K') {	/* Custom symbol spec -Sk|K<path_to_custom_symbol>[/<size>]*/
16231 		/* <path_to_custom_symbol> may contains slashes (UNIX) or backslashes (Windows) so we search from the end of the string: */
16232 		for (j = (int)strlen (text)-1; j > 0 && text[j] != '/'; --j);	/* Determine last slash */
16233 		if (j == 0) {	/* No slash, i.e., no symbol size given (and no UNIX path either) */
16234 			if (p->size_x == 0.0) p->size_x = p->given_size_x;
16235 			sscanf (text, "%c%s", &symbol_type, text_cp);
16236 			col_off++;
16237 		}
16238 		else {	/* Found a slash, is it separating the size or part of UNIX path? */
16239 			text[j] = ' ';	/* Temporary remove the slash we found */
16240 			sscanf (text, "%c%s %s", &symbol_type, text_cp, txt_a);
16241 			text[j] = '/';	/* Restore the bugger */
16242 			if (strchr("CcIiPp", txt_a[strlen(txt_a) - 1])) {	/* If last char equals a unit char... */
16243 				char t[GMT_LEN64] = {""};
16244 				strncpy (t, txt_a, strlen(txt_a) - 1);	/* Make a copy of what we found minus the unit char */
16245 				if (gmtinit_is_valid_number(t))         /* txt_a without the unit char is a size string. Decode it. */
16246 					p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_a);
16247 				else                                /* txt_a is just the symbol name and text_cp has the directory */
16248 					{strcat(text_cp, "/");	strcat(text_cp, txt_a);col_off++;}
16249 			}
16250 			else {                                  /* It still can be a size even though no unit was given */
16251 				if (gmtinit_is_valid_number (txt_a))     /* Yes, it is */
16252 					p->size_x = p->given_size_x = gmt_M_to_inch(GMT, txt_a);
16253 				else {                              /* No, txt_a is the symbol name. */
16254 					strcat(text_cp, "/");	strcat(text_cp, txt_a);
16255 					if (p->size_x == 0.0) p->size_x = p->given_size_x;
16256 					col_off++;
16257 				}
16258 			}
16259 		}
16260 	}
16261 	else if (strchr (GMT_VECTOR_CODES, text[0])) {
16262 		/* Vectors gets separate treatment because of optional modifiers [+j<just>+b+e+s+l+r+a<angle>+n<norm>] */
16263 		int one;
16264 		char arg[GMT_LEN64] = {""};
16265 		n = sscanf (text, "%c%[^+]", &symbol_type, arg);	/* arg should be symbols size with no +<modifiers> at the end */
16266 		if (n == 1) strncpy (arg, &text[1], GMT_LEN64-1);	/* No modifiers present, set arg to text following symbol code */
16267 		k = 1;
16268 		p->v.parsed_v4 = false;
16269 		if (strchr (text, '/') && !strchr (text, '+')) {
16270 			/* Gave old-style arrow dimensions; cannot exactly reproduce GMT 4 arrows since those were polygons */
16271 			p->v.status |= PSL_VEC_END;		/* Default is head at end */
16272 			p->size_y = p->given_size_y = 0.0;
16273 			one = (strchr ("bhstBHST", text[1])) ? 2 : 1;
16274 			sscanf (&text[one], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
16275 			p->v.v_width  = (float)gmt_M_to_inch (GMT, txt_a);
16276 			p->v.h_length = (float)gmt_M_to_inch (GMT, txt_b);
16277 			p->v.h_width  = (float)(gmt_M_to_inch (GMT, txt_c) * 2.0);
16278 			p->v.v_angle  = (float)atand ((0.5 * p->v.h_width / p->v.h_length) * 2.0);
16279 			p->v.parsed_v4 = true;
16280 			p->size_x = p->given_size_x = p->v.h_length;
16281 		}
16282 		else if (strchr ("vV", symbol_type) && text[1] && strchr ("bhstBHST", text[1])) {	/* Old style */
16283 			//GMT_Report (GMT->parent, GMT_MSG_COMPAT, "bhstBHST vector modifiers is deprecated GMT3/4 syntax; see -S%c for current syntax.\n", text[0]);
16284 			p->v.status |= PSL_VEC_END;		/* Default is head at end */
16285 			k = 2;
16286 			strncpy (arg, &text[2], GMT_LEN64-1);
16287 		}
16288 		if (text[k] && strchr (GMT_DIM_UNITS, (int) text[k])) {	/* No size given, only unit information */
16289 			if (p->size_x == 0.0) p->size_x = p->given_size_x;
16290 			if (p->size_y == 0.0) p->size_y = p->given_size_y;
16291 			if ((j = gmt_get_dim_unit (GMT, text[k])) < 0) decode_error++; else { p->u = j; p->u_set = true;}
16292 			col_off++;
16293 			if (cmd) p->read_size_cmd = true;
16294 		}
16295 		else if (!text[k] || text[k] == '+') {	/* No size nor unit, just possible attributes */
16296 			if (p->size_x == 0.0) p->size_x = p->given_size_x;
16297 			if (p->size_y == 0.0) p->size_y = p->given_size_y;
16298 			if (p->size_x == 0.0)	/* If symbol size was given on command line then we don't want to read it again */
16299 				col_off++;
16300 			if (cmd) p->read_size_cmd = true;
16301 		}
16302 		else if (!p->v.parsed_v4) {	/* Need to get size */
16303 			if (cmd) p->read_size_cmd = false;
16304 			p->size_x = p->given_size_x = gmt_M_to_inch (GMT, arg), check = false;
16305 		}
16306 	}
16307 	else if (strchr (allowed_symbols[mode], (int) text[0]) && text[1] && strchr (GMT_DIM_UNITS, (int) text[1])) {
16308 		/* Symbol, but no size given (size assumed given on command line), only unit information */
16309 		sscanf (text, "%c", &symbol_type);
16310 		if (p->size_x == 0.0) p->size_x = p->given_size_x;
16311 		if (p->size_y == 0.0) p->size_y = p->given_size_y;
16312 		if (text[1]) {	/* Gave unit information */
16313 			if ((j = gmt_get_dim_unit (GMT, text[1])) < 0)
16314 				decode_error++;
16315 			else {
16316 				p->u = j; p->u_set = true;
16317 			}
16318 		}
16319 		col_off++;
16320 	}
16321 	else if (strchr (allowed_symbols[mode], (int) text[0]) && (text[1] == '\n' || !text[1]) && !strchr ("EJWw", text[0])) {
16322 		/* Symbol, but no size given (size assumed given on command line) */
16323 		sscanf (text, "%c", &symbol_type);
16324 		if (p->size_x == 0.0) p->size_x = p->given_size_x;
16325 		if (p->size_y == 0.0) p->size_y = p->given_size_y;
16326 		col_off++;
16327 	}
16328 	else if (strchr (bar_symbols[mode], (int) text[0])) {	/* Bar, column, cube with size */
16329 
16330 		/* Bar:		-Sb|B[<size_x|size_y>[c|i|p|u]][+b|B[<base>]][+v|i<nz>][+s[<gap>]]				*/
16331 		/* Column:	-So|O[<size_x>[c|i|p|u][/<ysize>[c|i|p|u]]][+b|B[<base>]][+v|i<nz>]	*/
16332 		/* Cube:	-Su|U[<size_x>[c|i|p|u]]	*/
16333 
16334 		/* Also worry about backwards handling of +z|Z, now +v|i */
16335 		if ((c = strstr (text, "+v")) || (c = strstr (text, "+i"))|| (c = strstr (text, "+z")) || (c = strstr (text, "+Z"))) {	/* Got +z|Z<nz> */
16336 			char *s = strstr (text, "+s");
16337 			if (strchr ("uU", text[0])) {
16338 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Symbol u|U does not support the +%c<nz> modifier\n", c[1]);
16339 				decode_error++;
16340 			}
16341 			else {	/* Only bars and columns have this feature */
16342 				char *b = NULL;
16343 				if ((n_z = atoi (&c[2])) <= 0) {
16344 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Modifier +%c<nz> given bad value for <nz> (%d)\n", c[1], n_z);
16345 					decode_error++;
16346 				}
16347 				if (c[1]== 'i' || c[1] == 'Z') p->accumulate = true;	/* Getting dv1 dv2 ... etc and not v1 v1 ... */
16348 				/* Must deal with situations where +b|B is given before +v|i|h|z|Z or vice versa */
16349 				if (s && s < c) c = s;	/* +s was given before the +i|v|z|Z section */
16350 				c[0] = '\0';	/* Temporarily chop off the +i+v+z|Z modifier... (possibly starting with +s) */
16351 				strncpy (text_cp, text, GMT_LEN256-1);	/* Copy over everything up to [+s]+v|i|z|Z[+s] */
16352 				c[0] = '+';	/* Restore modifier */
16353 				c++;	/* Move past this plus sign */
16354 				if ((b = strstr (c, "+b")) || (b = strstr (c, "+B")) || (b = strchr (c, 'b')) || (b = strchr (c, 'B'))) {	/* Check for both current and deprecated bar settings */
16355 					strncat (text_cp, b, GMT_LEN256-1);	/* Append this modifier to text_cp */
16356 				}
16357 				if (s) {
16358 					p->sidebyside = true;
16359 					if (s[2]) {	/* Gave a bar gap setting; we will return this in percent even if we got a fraction */
16360 						if (((p->gap = atof (&s[2])) <= 0.0 || p->gap > 100.0)) {
16361 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Modifier +s<gap> given bad value for <gap> (%g)\n", p->gap);
16362 							decode_error++;
16363 						}
16364 						else if (p->gap < 1.0)	/* Clearly a gap given as fraction, convert to percent */
16365 							p->gap *= 100.0;
16366 					}	/* Else the p->gap is zero for no gap between bars */
16367 				}
16368 			}
16369 		}
16370 		else	/* Copy as is */
16371 			strncpy (text_cp, text, GMT_LEN256-1);
16372 		for (j = 1; text_cp[j]; j++) {	/* Look at chars following the symbol code */
16373 			if (text_cp[j] == '/') slash = j;
16374 			if (text_cp[j] == 'b' || text_cp[j] == 'B') bset = j;	/* Basically not worry about +b|B vs b|B by just checking for b|B */
16375 		}
16376 		if (bset) {	/* Chop off the b|B<base> from copy to avoid confusion when parsing.  <base> is always in user units */
16377 			if (text_cp[bset] == 'B') add_to_base = true;
16378 			if (text_cp[bset-1] == '+')	/* Gave +b|B */
16379 				text_cp[bset-1] = 0;
16380 			else	/* Handle backwards compatible case of just b[<base>] */
16381 				text_cp[bset] = 0;
16382 		}
16383 		if (slash) {	/* Separate x/y sizes */
16384 			n = sscanf (text_cp, "%c%[^/]/%s", &symbol_type, txt_a, txt_b);
16385 			decode_error += (n != 3);
16386 			if (((len = (int)strlen (txt_a)) > 0) && txt_a[len-1] == 'u') {
16387 				p->user_unit[GMT_X] = true;	/* Specified xwidth in user units */
16388 				txt_a[len-1] = '\0';	/* Chop off the 'u' */
16389 			}
16390 			if (((len = (int)strlen (txt_b)) > 0) && txt_b[len-1] == 'u') {
16391 				p->user_unit[GMT_Y] = true;	/* Specified ywidth in user units */
16392 				txt_b[len-1] = '\0';	/* Chop off the 'u' */
16393 			}
16394 			if (p->user_unit[GMT_X]) {
16395 				if (gmtinit_get_uservalue (GMT, txt_a, gmt_M_type (GMT, GMT_IN, GMT_X), &p->given_size_x, "-Sb|B|o|O|u|u x-size value")) return GMT_PARSE_ERROR;
16396 				p->size_x = p->given_size_x;
16397 			}
16398 			else
16399 				p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_a);
16400 			if (p->user_unit[GMT_Y]) {
16401 				if (gmtinit_get_uservalue (GMT, txt_b, gmt_M_type (GMT, GMT_IN, GMT_Y), &p->given_size_y, "-Sb|B|o|O|u|u y-size value")) return GMT_PARSE_ERROR;
16402 				p->size_y = p->given_size_y;
16403 			}
16404 			else
16405 				p->size_y = p->given_size_y = gmt_M_to_inch (GMT, txt_b);
16406 		}
16407 		else {	/* Only a single x = y size */
16408 			n = sscanf (text_cp, "%c%s", &symbol_type, txt_a);
16409 			if ((len = (int)strlen (txt_a)) && txt_a[len-1] == 'u') {
16410 				p->user_unit[GMT_X] = p->user_unit[GMT_Y] = true;	/* Specified xwidth [=ywidth] in user units */
16411 				txt_a[len-1] = '\0';	/* Chop off the 'u' */
16412 			}
16413 			if (n == 2) {	/* Gave size */
16414 				if (p->user_unit[GMT_X]) {
16415 					if (gmtinit_get_uservalue (GMT, txt_a, gmt_M_type (GMT, GMT_IN, GMT_X), &p->given_size_x, "-Sb|B|o|O|u|u x-size value")) return GMT_PARSE_ERROR;
16416 					p->size_x = p->size_y = p->given_size_y = p->given_size_x;
16417 				}
16418 				else
16419 					p->size_x = p->size_y = p->given_size_x = p->given_size_y = gmt_M_to_inch (GMT, txt_a);
16420 			}
16421 			else {
16422 				if (p->size_x == 0.0) p->size_x = p->given_size_x;
16423 				if (p->size_y == 0.0) p->size_y = p->given_size_y;
16424 			}
16425 		}
16426 	}
16427 	else {	/* Everything else */
16428 		char s_upper;
16429 		n = sscanf (text, "%c%[^/]/%[^/]/%s", &symbol_type, txt_a, txt_b, txt_c);
16430 		s_upper = (char)toupper ((int)symbol_type);
16431 		if (strchr ("FVQM~", s_upper))	/* "Symbols" that do not take a normal symbol size */
16432 			p->size_y = p->given_size_y = 0.0;
16433 		else if (s_upper == 'W') {	/* Wedges and spiders: -Sw|W[<diameter>[/<angle1>/<angle2>]][+i<inner_diameter>][+a[<dr>][+r[<da>]]] */
16434 			/* Watch for deprecated syntax -Sw|W<diameter>[/<inner_diameter>][+a[<dr>][+r[<da>]]]  */
16435 			unsigned int type = 0, n_slash;
16436 			char *c = NULL;
16437 			p->w_active = (symbol_type == 'W');
16438 			if (gmt_get_modifier (text, 'i', txt_d)) {	/* Want nonzero inner diameter */
16439 				if (txt_d[0] == '\0')	/* Must read from file */
16440 					p->w_get_di = true;
16441 				else	/* Get that value now */
16442 					p->w_radius_i = gmtinit_get_diameter (GMT, symbol_type, txt_d, &p->w_active);
16443 			}
16444 			if ((c = gmt_first_modifier (GMT, text, "aipr"))) c[0] = '\0';	/* Chop off all modifiers so we can parse the info */
16445 			n_slash = gmt_count_char (GMT, text, '/');
16446 			switch (n_slash) {
16447 				case 0:	/* Gave -SW|w[<diameter>]; n will become 0 or 1 */
16448 					if ((n = sscanf (&text[1], "%s", txt_a)) != 1)	/* No diameter, read from file */
16449 						p->w_get_do = true;
16450 					p->w_get_a = true;	/* Get two angles */
16451 					break;
16452 				case 1:	/* Gave -SW|w<diameter>/<inner> [deprecated] */
16453 					n = sscanf (&text[1], "%[^/]/%s", txt_a, txt_d);
16454 					p->w_get_a = true;	/* Get two angles */
16455 					break;
16456 				case 2:	/* Gave -SW|w<diameter>/<angle1>/<angle2>[+i[<inner>]] */
16457 					n = sscanf (&text[1], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
16458 					break;
16459 				default:
16460 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad argument -S%s\n", text);
16461 					decode_error++;
16462 			}
16463 			if (p->w_get_do && p->read_symbol_cmd && p->size_x > 0.0) {	/* Got a fixed size via -S<size> and must honor it throughout as diameter */
16464 				p->w_radius = p->size_x;
16465 				p->w_get_do = false;
16466 			}
16467 			check = false;
16468 			if (n >= 1) {	/* Got at least the diameter */
16469 				p->w_radius = gmtinit_get_diameter (GMT, symbol_type, txt_a, &p->w_active);
16470 			}
16471 			if (n_slash == 1)	/* Deprecated syntax */
16472 				p->w_radius_i = gmtinit_get_diameter (GMT, symbol_type, txt_d, &p->w_active);
16473 			else if (n_slash == 2) {	/* New syntax for angles */
16474 				p->size_x = p->given_size_x = atof (txt_b);
16475 				p->size_y = p->given_size_y = atof (txt_c);
16476 			}
16477 			if (c) {	/* Now process any modifiers (other than +i) */
16478 				char q[GMT_LEN256] = {""};
16479 				unsigned int pos = 0, error = 0;
16480 				c[0] = '+';	/* Restore that character */
16481 				while (gmt_getmodopt (GMT, 'S', c, "apr", &pos, q, &error) && error == 0) {
16482 					switch (q[0]) {
16483 						case 'a':	/* Arc(s) */
16484 							type |= GMT_WEDGE_ARCS;	/* Arc only */
16485 							if (q[1])	/* Got delta_r */
16486 								p->w_dr = gmtinit_get_diameter (GMT, symbol_type, &q[1], NULL);
16487 							break;
16488 						case 'p':	/* Spider pen, stored in the vector structure */
16489 							if (gmt_getpen (GMT, &q[1], &p->v.pen)) {
16490 								GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +p<pen> argument %s\n", &q[1]);
16491 								error++;
16492 							}
16493 							p->v.status = PSL_VEC_OUTLINE2;	/* Flag that a pen specification was given */
16494 							break;
16495 						case 'r':	/* Radial lines */
16496 							type |= GMT_WEDGE_RADII;	/* Radial lines */
16497 							if (q[1])	/* Got delta_az */
16498 								p->w_da = atof (&q[1]);
16499 							break;
16500 						default:	/* These are caught in gmt_getmodopt so break is just for Coverity */
16501 							break;
16502 					}
16503 				}
16504 			}
16505 			p->w_type = type;
16506 		}
16507 		else if (strchr ("ej", symbol_type) && n >= 2) {	/* Don't want to interpret angle as dimension */
16508 			if (n == 4) {
16509 				p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_b);
16510 				p->size_y = p->given_size_y = gmt_M_to_inch (GMT, txt_c);
16511 				p->factor = atof (txt_a);
16512 			}
16513 			else if (n == 2) {	/* Degenerate */
16514 				p->size_x = p->given_size_x = p->size_y = p->given_size_y = gmt_M_to_inch (GMT, txt_a);
16515 				p->factor = 0.0;
16516 			}
16517 			else
16518 				decode_error++;
16519 			p->par_set = (n == 2 || n == 4);
16520 		}
16521 		else if (!strchr ("EJ", symbol_type) && !(symbol_type == 'r' && strstr (text, "+s"))) {	/* Don't want to interpret -Sr+s a dimension */
16522 			p->size_x = p->given_size_x = gmt_M_to_inch (GMT, txt_a);
16523 			if (n == 4) p->factor = gmt_M_to_inch (GMT, txt_c);	/* Place the radius of rounded rectangles here */
16524 			if (n >= 3)
16525 				p->size_y = p->given_size_y = gmt_M_to_inch (GMT, txt_b);
16526 			else if (n == 2)
16527 				p->size_y = p->given_size_y = p->size_x;
16528 			else
16529 				decode_error++;
16530 		}
16531 	}
16532 	switch (symbol_type) {
16533 		case '*':
16534 			p->symbol = GMT_SYMBOL_NOT_SET;
16535 			break;
16536 		case '-':
16537 			p->symbol = PSL_XDASH;
16538 			break;
16539 		case 'A':
16540 			p->factor = 1.67289326141;	/* To equal area of circle with same diameter */
16541 			p->size_x *= p->factor;
16542 			/* Intentionally fall through - to 'a' */
16543 		case 'a':
16544 			p->symbol = PSL_STAR;
16545 			break;
16546 		case 'B':
16547 			p->symbol = GMT_SYMBOL_BARX;
16548 			if (n_z) {
16549 				p->n_required = n_z;	/* Need more than one z value from file */
16550 				for (k = 0; k < n_z; k++) p->nondim_col[p->n_nondim++] = 2 + k;	/* all band z in user units */
16551 			}
16552 			if (bset) {
16553 				if (text[bset+1] == '\0') {	/* Read it from data file (+ means probably +z<col>) */
16554 					p->base_set = GMT_BASE_READ;
16555 					p->n_required++;
16556 					p->nondim_col[p->n_nondim++] = 2 + col_off;	/* base in user units */
16557 				}
16558 				else {
16559 					if (gmtinit_get_uservalue (GMT, &text_cp[bset+1], gmt_M_type (GMT, GMT_IN, GMT_X), &p->base, "-SB base value")) return GMT_PARSE_ERROR;
16560 					p->base_set = GMT_BASE_ARG;
16561 				}
16562 			}
16563 			break;
16564 		case 'b':
16565 			p->symbol = GMT_SYMBOL_BARY;
16566 			if (n_z) {
16567 				p->n_required = n_z;	/* Need more than one z value from file */
16568 				for (k = 0; k < n_z; k++) p->nondim_col[p->n_nondim++] = 2 + k;	/* all band z in user units */
16569 			}
16570 			if (bset) {
16571 				if (p->user_unit[GMT_Y]) text_cp[strlen(text_cp)-1] = '\0';	/* Chop off u */
16572 				if (text_cp[bset+1] == '\0') {	/* Read it from data file */
16573 					p->base_set = GMT_BASE_READ;
16574 					p->n_required++;
16575 					p->nondim_col[p->n_nondim++] = 2 + col_off;	/* base in user units */
16576 				}
16577 				else {
16578 					if (gmtinit_get_uservalue (GMT, &text_cp[bset+1], gmt_M_type (GMT, GMT_IN, GMT_Y), &p->base, "-Sb base value")) return GMT_PARSE_ERROR;
16579 					p->base_set = GMT_BASE_ARG;
16580 				}
16581 			}
16582 			break;
16583 		case 'C':
16584 		case 'c':
16585 			p->symbol = PSL_CIRCLE;
16586 			break;
16587 		case 'D':
16588 			p->factor = 1.25331413732;	/* To equal area of circle with same diameter */
16589 			p->size_x *= p->factor;
16590 			/* Intentionally fall through - to 'd' */
16591 		case 'd':
16592 			p->symbol = PSL_DIAMOND;
16593 			break;
16594 		case 'E':	/* Expect axis in km to be scaled based on -J */
16595 			p->symbol = PSL_ELLIPSE;
16596 			p->convert_angles = 1;
16597 			if (n == 2) {
16598 				degenerate = true;
16599 				strcpy (diameter, txt_a);
16600 			}
16601 			if (degenerate) {	/* Degenerate ellipse = circle */
16602 				if (diameter[0]) {	/* Gave a fixed diameter as symbol size */
16603 					(void)gmtlib_scanf_geodim (GMT, diameter, &p->size_y);
16604 					p->size_x = p->size_y;
16605 					p->factor = 0.0;
16606 					p->par_set = true;
16607 				}
16608 				else {	/* Must read from data file */
16609 					p->n_required = 1;	/* Only expect diameter */
16610 					p->nondim_col[p->n_nondim++] = 2 + mode;	/* Since diameter is in geo-units, not inches or cm etc */
16611 				}
16612 			}
16613 			else if (n == 4) {	/* Gave three parameters on the command line */
16614 				(void)gmtlib_scanf_geodim (GMT, txt_b, &p->size_x);
16615 				(void)gmtlib_scanf_geodim (GMT, txt_c, &p->size_y);
16616 				p->factor = atof (txt_a);
16617 				p->par_set = true;
16618 				p->n_required = 0;	/* All set */
16619 			}
16620 			else {
16621 				p->n_required = 3;
16622 				p->nondim_col[p->n_nondim++] = 2 + mode;	/* Angle */
16623 				p->nondim_col[p->n_nondim++] = 3 + mode;	/* Since they are in geo-units, not inches or cm etc */
16624 				p->nondim_col[p->n_nondim++] = 4 + mode;
16625 			}
16626 			check = false;
16627 			break;
16628 		case 'e':
16629 			p->symbol = PSL_ELLIPSE;
16630 			/* Expect angle in degrees, then major and major axes in plot units */
16631 			if (!p->par_set) {	/* Must read parameters from file */
16632 				p->n_required = 3;
16633 				p->nondim_col[p->n_nondim++] = 2 + mode;	/* Angle */
16634 			}
16635 			else
16636 				p->n_required = 0;	/* All set */
16637 			check = false;
16638 			break;
16639 
16640 		case 'f':	/* Fronts: -Sf<spacing>[/<size>][+r+l][+f+t+s+c+b][+o<offset>][+p<pen>]	[WAS: -Sf<spacing>/<size>[dir][type][:<offset>]	*/
16641 			p->symbol = GMT_SYMBOL_FRONT;
16642 			p->f.f_off = 0.0;	p->f.f_symbol = GMT_FRONT_FAULT;	p->f.f_sense = GMT_FRONT_CENTERED;
16643 			p->f.f_angle = 20.0;			/* Default slip arrow angle */
16644 			check = false;
16645 			if (!text[1]) {	/* No args given, must parse segment header later */
16646 				p->fq_parse = true;	/* This will be set to false once at least one header has been parsed */
16647 				break;
16648 			}
16649 			strncpy (text_cp, text, GMT_LEN256-1);
16650 			if (gmt_M_compat_check (GMT, 4)) {
16651 				len = (int)strlen (text_cp) - 1;
16652 				if (strchr (text_cp, ':') || (!strchr (text_cp, '+') && len > 0 && strchr ("bcflrst", text_cp[len]))) {	/* Old style */
16653 					GMT_Report (GMT->parent, GMT_MSG_COMPAT,
16654 					            "Option -Sf: Sf<spacing>/<size>[dir][type][:<offset>] is deprecated syntax\n");
16655 					if ((c = strchr (text_cp, ':'))) {	/* Gave :<offset>, set it and strip it off */
16656 						c++;	/* Skip over the colon */
16657 						p->f.f_off = gmt_M_to_inch (GMT, c);
16658 						c--;	/* Go back to colon */
16659 						*c = 0;	/* Effectively chops off the offset modifier */
16660 					}
16661 					len = (int)strlen (text_cp) - 1;
16662 
16663 					switch (text_cp[len]) {
16664 						case 'f':	/* Fault front */
16665 							p->f.f_symbol = GMT_FRONT_FAULT;	len--;	break;
16666 						case 't':	/* Triangle front */
16667 							p->f.f_symbol = GMT_FRONT_TRIANGLE;	len--;	break;
16668 						case 's':	/* Strike-slip front */
16669 							p->f.f_symbol = GMT_FRONT_SLIP;		len--;	break;
16670 						case 'c':	/* [half-]circle front */
16671 							p->f.f_symbol = GMT_FRONT_CIRCLE;	len--;	break;
16672 						case 'b':	/* [half-]square front */
16673 							p->f.f_symbol = GMT_FRONT_BOX;		len--;	break;
16674 						default:
16675 							p->f.f_sense = GMT_FRONT_CENTERED;	break;
16676 					}
16677 
16678 					switch (text_cp[len]) {	/* Get sense - default is centered */
16679 						case 'l':
16680 							p->f.f_sense = GMT_FRONT_LEFT;			break;
16681 						case 'r':
16682 							p->f.f_sense = GMT_FRONT_RIGHT;			break;
16683 						default:
16684 							p->f.f_sense = GMT_FRONT_CENTERED;	len++;	break;
16685 					}
16686 
16687 					text_cp[len] = 0;	/* Gets rid of the [dir][type] flags, if present */
16688 
16689 					/* Pull out and get spacing and size */
16690 
16691 					sscanf (&text_cp[1], "%[^/]/%s", txt_a, txt_b);
16692 					p->f.f_gap = (txt_a[0] == '-') ? atof (txt_a) : gmt_M_to_inch (GMT, txt_a);
16693 					p->f.f_len = gmt_M_to_inch (GMT, txt_b);
16694 				}
16695 				else
16696 					gmtinit_parse_front (GMT, text_cp, p);	/* Parse new -Sf syntax */
16697 			}
16698 			else
16699 				gmtinit_parse_front (GMT, text_cp, p);	/* Parse new -Sf syntax */
16700 			if (p->f.f_sense == GMT_FRONT_CENTERED && p->f.f_symbol == GMT_FRONT_SLIP) {
16701 				GMT_Report (GMT->parent, GMT_MSG_ERROR,
16702 				            "Option -Sf: Must specify (l)eft-lateral or (r)ight-lateral slip\n");
16703 				return GMT_PARSE_ERROR;
16704 			}
16705 			if (gmt_M_is_zero (p->f.f_gap) || gmt_M_is_zero (p->f.f_len)) {
16706 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sf: Neither <gap> nor <ticklength> can be zero!\n");
16707 				return GMT_PARSE_ERROR;
16708 			}
16709 			if (p->f.f_gap < 0.0) {	/* Gave -# of ticks desired */
16710 				k = irint (fabs (p->f.f_gap));
16711 				if (k == 0) {
16712 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sf: Number of front ticks cannot be zero!\n");
16713 					return GMT_PARSE_ERROR;
16714 				}
16715 				if (!gmt_M_is_zero (p->f.f_off)) {
16716 					GMT_Report (GMT->parent, GMT_MSG_ERROR,
16717 					            "Option -Sf: +<offset> cannot be used when number of ticks is specified!\n");
16718 					return GMT_PARSE_ERROR;
16719 				}
16720 			}
16721 			p->fq_parse = false;	/* No need to parse more later */
16722 			break;
16723 		case 'G':
16724 			p->factor = 1.05390736526;	/* To equal area of circle with same diameter */
16725 			p->size_x *= p->factor;
16726 			/* Intentionally fall through - to 'g' */
16727 		case 'g':
16728 			p->symbol = PSL_OCTAGON;
16729 			break;
16730 		case 'H':
16731 			p->factor = 1.09963611079;	/* To equal area of circle with same diameter */
16732 			p->size_x *= p->factor;
16733 			/* Intentionally fall through - to 'h' */
16734 		case 'h':
16735 			p->symbol = PSL_HEXAGON;
16736 			break;
16737 		case 'I':
16738 			p->factor = 1.55512030156;	/* To equal area of circle with same diameter */
16739 			p->size_x *= p->factor;
16740 			/* Intentionally fall through - to 'i' */
16741 		case 'i':
16742 			p->symbol = PSL_INVTRIANGLE;
16743 			break;
16744 		case 'J':	/* Expect dimensions in km to be scaled based on -J */
16745 			p->symbol = PSL_ROTRECT;
16746 			p->convert_angles = 1;
16747 			if (n == 2) {
16748 				degenerate = true;
16749 				strcpy (diameter, txt_a);
16750 			}
16751 			if (degenerate) {	/* Degenerate rectangle = square with zero angle */
16752 				if (diameter[0]) {	/* Gave a fixed diameter as symbol size */
16753 					(void)gmtlib_scanf_geodim (GMT, diameter, &p->size_y);
16754 					p->size_x = p->size_y;
16755 					p->factor = 0.0;
16756 					p->par_set = true;
16757 				}
16758 				else {	/* Must read diameter from data file */
16759 					p->n_required = 1;	/* Only expect diameter */
16760 					p->nondim_col[p->n_nondim++] = 2 + mode;	/* Since diameter is in km, not inches or cm etc */
16761 				}
16762 			}
16763 			else if (n == 4) {	/* Gave three parameters on the command line */
16764 				(void)gmtlib_scanf_geodim (GMT, txt_b, &p->size_x);
16765 				(void)gmtlib_scanf_geodim (GMT, txt_c, &p->size_y);
16766 				p->factor = atof (txt_a);
16767 				p->par_set = true;
16768 				p->n_required = 0;	/* All set */
16769 			}
16770 			else {	/* Get all three from file */
16771 				p->n_required = 3;
16772 				p->nondim_col[p->n_nondim++] = 2 + mode;	/* Angle */
16773 				p->nondim_col[p->n_nondim++] = 3 + mode;	/* Since they are in km, not inches or cm etc */
16774 				p->nondim_col[p->n_nondim++] = 4 + mode;
16775 			}
16776 			check = false;
16777 			break;
16778 		case 'j':
16779 			p->symbol = PSL_ROTRECT;
16780 			if (!p->par_set) {	/* Must read parameters from file */
16781 				p->n_required = 3;
16782 				p->nondim_col[p->n_nondim++] = 2 + mode;	/* Angle */
16783 			}
16784 			else
16785 				p->n_required = 0;	/* All set */
16786 			check = false;
16787 			break;
16788 		case 'l':
16789 			p->symbol = GMT_SYMBOL_TEXT;
16790 			if (gmtinit_parse_text (GMT, text_cp, p)) decode_error++;
16791 			break;
16792 		case 'M':
16793 		case 'm':
16794 			p->symbol = PSL_MARC;
16795 			p->n_required = 3;	/* Need radius, angle1 and angle2 */
16796 			if (gmt_parse_vector (GMT, symbol_type, text, p)) {
16797 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S%c parsing failure\n", symbol_type);
16798 				decode_error++;
16799 			}
16800 			if (symbol_type == 'M') p->v.status |= PSL_VEC_MARC90;	/* Flag means we will plot right angle symbol if angles extend 90 exactly */
16801 			p->nondim_col[p->n_nondim++] = 3 + col_off;	/* Angle */
16802 			p->nondim_col[p->n_nondim++] = 4 + col_off;	/* Angle */
16803 			break;
16804 		case 'N':
16805 			p->factor = 1.14948092619;	/* To equal area of circle with same diameter */
16806 			p->size_x *= p->factor;
16807 			/* Intentionally fall through - to 'n' */
16808 		case 'n':
16809 			p->symbol = PSL_PENTAGON;
16810 			break;
16811 		case 'o':	/*3-D symbol */
16812 			p->shade3D = true;
16813 			/* Intentionally fall through - to 'O' */
16814 		case 'O':	/* Same but now enabling shading */
16815 			p->symbol = GMT_SYMBOL_COLUMN;
16816 			if (n_z) {
16817 				p->n_required = n_z;	/* Need more than one z value from file */
16818 				for (k = 0; k < n_z; k++) p->nondim_col[p->n_nondim++] = 2 + k;	/* all band z in user units */
16819 			}
16820 			if (bset) {
16821 				if (text[bset+1] == '\0' || text[bset+1] == '+') {	/* Read it from data file */
16822 					p->base_set = GMT_BASE_READ;
16823 					p->n_required++;
16824 					p->nondim_col[p->n_nondim++] = 2 + col_off;	/* base in user units */
16825 				}
16826 				else {
16827 					if (gmtinit_get_uservalue (GMT, &text[bset+1], gmt_M_type (GMT, GMT_IN, GMT_Z), &p->base, "-So|O base value")) return GMT_PARSE_ERROR;
16828 					p->base_set = GMT_BASE_ARG;
16829 				}
16830 			}
16831 			if (mode == 0) {
16832 				decode_error++;
16833 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S: Symbol type %c is 3-D only\n", symbol_type);
16834 			}
16835 			break;
16836 		case 'P':
16837 		case 'p':
16838 			p->symbol = PSL_DOT;
16839 			if (p->size_x == 0.0 && !p->read_size) {	/* User forgot to set size */
16840 				p->size_x = GMT_DOT_SIZE;
16841 				check = false;
16842 			}
16843 			break;
16844 		case 'q':	/* Quoted lines: -Sq[d|n|l|s|x]<info>[:<labelinfo>] */
16845 			p->symbol = GMT_SYMBOL_QUOTED_LINE;
16846 			check = false;
16847 			if (!text[1]) {	/* No args given, must parse segment header later */
16848 				p->fq_parse = true;	/* This will be set to false once at least one header has been parsed */
16849 				break;
16850 			}
16851 			/* Determine the first colon as a separator between info and specs */
16852 			for (j = 1, colon = GMT_NOTSET; colon == GMT_NOTSET && text[j]; j++) if (text[j] == ':') colon = j;
16853 			if (colon != GMT_NOTSET) {	/* Gave :<labelinfo> */
16854 				text[colon] = 0;
16855 				gmt_contlabel_init (GMT, &p->G, 0);
16856 				decode_error += gmt_contlabel_info (GMT, 'S', &text[1], &p->G);
16857 				decode_error += gmt_contlabel_specs (GMT, &text[colon+1], &p->G);
16858 				if (!cmd && gmt_contlabel_prep (GMT, &p->G, NULL)) decode_error++;
16859 			}
16860 			else	/* No <labelinfo> given */
16861 				decode_error += gmt_contlabel_info (GMT, 'S', &text[1], &p->G);
16862 			p->fq_parse = false;	/* No need to parse more later */
16863 			break;
16864 		case 'r':
16865 			p->symbol = PSL_RECT;
16866 			if (strstr (text, "+s")) {	/* Make a rectangle from two corners of a diagonal */
16867 				p->diagonal = true;
16868 				p->n_required = 2;	/* If we did not give width/height then we must read them from input */
16869 			}
16870 			else
16871 				p->n_required = (n >= 2) ? 0 : 2;	/* If we did not give width/height then we must read them from input */
16872 			check = false;
16873 			break;
16874 		case 'R':
16875 			p->symbol = PSL_RNDRECT;
16876 			p->n_required = (n == 4) ? 0 : 3;	/* If we did not give width/height/radius then we must read them from input */
16877 			check = false;
16878 			break;
16879 		case 'S':
16880 			p->factor = 1.25331413732;	/* To equal area of circle with same diameter */
16881 			p->size_x *= p->factor;
16882 			/* Intentionally fall through - to 's' */
16883 		case 's':
16884 			p->symbol = PSL_SQUARE;
16885 			break;
16886 		case 'T':
16887 			p->factor = 1.55512030156;	/* To equal area of circle with same diameter */
16888 			p->size_x *= p->factor;
16889 			/* Intentionally fall through - to 't' */
16890 		case 't':
16891 			p->symbol = PSL_TRIANGLE;
16892 			break;
16893 		case 'u':	/*3-D symbol */
16894 			p->shade3D = true;
16895 			/* Intentionally fall through - to 'U' */
16896 		case 'U':	/* Same but disable shading */
16897 			p->symbol = GMT_SYMBOL_CUBE;
16898 			if (mode == 0) {
16899 				decode_error++;
16900 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S: Symbol type %c is 3-D only\n", symbol_type);
16901 			}
16902 			break;
16903 		case 'V':
16904 			p->convert_angles = 1;
16905 			/* Intentionally fall through - to 'v' */
16906 		case 'v':
16907 			p->symbol = PSL_VECTOR;
16908 			if (!gmt_M_compat_check (GMT, 4) || (strchr (text, '+') || !p->v.parsed_v4)) {	/* Check if new syntax before decoding */
16909 				if (gmt_parse_vector (GMT, symbol_type, text, p)) {	/* Error decoding new vector syntax */
16910 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S%c parsing failure\n", symbol_type);
16911 					decode_error++;
16912 				}
16913 				if (!(p->v.status & PSL_VEC_JUST_S)) p->nondim_col[p->n_nondim++] = 2 + col_off;
16914 			}
16915 			else {	/* Parse old-style vector specs */
16916 				int one = 2;
16917 				if (p->v.status & PSL_VEC_BEGIN) p->v.status -= PSL_VEC_BEGIN;	/* Remove previous setting */
16918 				switch (text[1]) {	/* Check if s(egment), h(ead), b(alance center), or t(ail) have been specified */
16919 					case 'S':	/* Input (x,y) refers to vector head (the tip), double heads */
16920 						p->v.status |= PSL_VEC_BEGIN;
16921 						/* Intentionally fall through - to 's' */
16922 					case 's':	/* Input (x,y) refers to vector head (the tip), head  at end */
16923 						p->v.status |= (PSL_VEC_JUST_S + PSL_VEC_END);
16924 						break;
16925 					case 'H':	/* Input (x,y) refers to vector head (the tip), double heads */
16926 						p->v.status |= PSL_VEC_BEGIN;
16927 						/* Intentionally fall through - to 'sh' */
16928 					case 'h':	/* Input (x,y) refers to vector head (the tip), single head */
16929 						p->v.status |= (PSL_VEC_JUST_E + PSL_VEC_END);
16930 						p->nondim_col[p->n_nondim++] = 2 + mode;
16931 						break;
16932 					case 'B':	/* Input (x,y) refers to balance point of vector, double heads */
16933 						p->v.status |= PSL_VEC_BEGIN;
16934 						/* Intentionally fall through - to 'b' */
16935 					case 'b':	/* Input (x,y) refers to balance point of vector, single head */
16936 						p->v.status |= (PSL_VEC_JUST_C + PSL_VEC_END);
16937 						p->nondim_col[p->n_nondim++] = 2 + mode;
16938 						break;
16939 					case 'T':	/* Input (x,y) refers to tail of vector, double heads */
16940 						p->v.status |= PSL_VEC_BEGIN;
16941 						/* Intentionally fall through - to 't' */
16942 					case 't':	/* Input (x,y) refers to tail of vector [Default], single head */
16943 						p->v.status |= (PSL_VEC_JUST_B + PSL_VEC_END);
16944 						p->nondim_col[p->n_nondim++] = 2 + mode;
16945 						break;
16946 					default:	/* No modifier given, default to tail, single head */
16947 						p->v.status |= (PSL_VEC_JUST_B + PSL_VEC_END);
16948 						one = 1;
16949 						p->nondim_col[p->n_nondim++] = 2 + mode;
16950 						break;
16951 				}
16952 				for (j = one; text[j] && text[j] != 'n'; j++);
16953 				len = (int)strlen(text) - 1;
16954 				if (text[j] == 'n') {	/* Normalize option used */
16955 					k = gmt_get_dim_unit (GMT, text[len]);
16956 					p->v.v_norm = (float)atof (&text[j+1]);
16957 					if (k >= GMT_CM)	/* Convert length from given units to inch */
16958 						p->v.v_norm *= GMT->session.u2u[k][GMT_INCH];
16959 					else	/* Convert from default units to inch */
16960 						p->v.v_norm *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
16961 					text[j] = 0;	/* Chop off the shrink part */
16962 					/* Here, p->v.v_norm will be in inches */
16963 				}
16964 				if (text[one]) {
16965 					char txt_c[GMT_LEN256] = {""};
16966 					/* It is possible that the user have appended a unit modifier after
16967 					 * the <size> argument (which here are vector attributes).  We use that
16968 					 * to set the unit, but only if the vector attributes themselves have
16969 					 * units. (If not we would override MEASURE_UNIT without cause).
16970 					 * So, -SV0.1i/0.2i/0.3ic will expect 4th column to have length in cm
16971 					 * while SV0.1i/0.2i/0.3i expects data units in MEASURE_UNIT
16972 					 */
16973 
16974 					if (isalpha ((int)text[len]) && isalpha ((int)text[len-1])) {
16975 						k = gmt_get_dim_unit (GMT, text[len]);
16976 						if (k >= 0) { p->u = k; p->u_set = true;}
16977 						text[len] = 0;
16978 					}
16979 					if (!p->v.parsed_v4) {
16980 						sscanf (&text[one], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
16981 						p->v.v_width  = (float)gmt_M_to_inch (GMT, txt_a);
16982 						p->v.h_length = (float)gmt_M_to_inch (GMT, txt_b);
16983 						p->v.h_width  = (float)(gmt_M_to_inch (GMT, txt_c) * 2.0);
16984 						p->v.v_angle = (float)(atand (0.5 * p->v.h_width / p->v.h_length) * 2.0);
16985 					}
16986 				}
16987 				if (p->v.v_norm >= 0.0) text[j] = 'n';	/* Put back the n<shrink> part */
16988 			}
16989 			p->n_required = 2;
16990 			break;
16991 		case 'W':
16992 			p->convert_angles = 1;
16993 			/* Intentionally fall through - to 'w' */
16994 		case 'w':
16995 			p->symbol = PSL_WEDGE;
16996 			p->n_required = 0;
16997 			col = 2 + col_off;
16998 			if (p->w_get_do) {	/* Need outer diameter */
16999 				if (p->w_active) p->nondim_col[p->n_nondim++] = col;
17000 				p->n_required++;	col++;
17001 			}
17002 			if (p->w_get_a) { /* Need both angles */
17003 				p->nondim_col[p->n_nondim++] = col++, p->n_required++;
17004 				p->nondim_col[p->n_nondim++] = col++, p->n_required++;
17005 			}
17006 			if (p->w_get_di) {	/* Need inner diameter */
17007 				if (p->w_active) p->nondim_col[p->n_nondim++] = col;
17008 				p->n_required++;
17009 			}
17010 			if (p->w_active) p->convert_angles = 0;	/* Expect azimuths directly */
17011 			break;
17012 		case '+':
17013 			p->symbol =  PSL_PLUS;
17014 			break;
17015 		case 'x':
17016 			p->symbol = PSL_CROSS;
17017 			break;
17018 		case 'y':
17019 			p->symbol = PSL_YDASH;
17020 			break;
17021 		case 'z':
17022 			p->symbol = GMT_SYMBOL_ZDASH;
17023 			break;
17024 		case 'K':
17025 			if (cmd) p->read_symbol_cmd = 2;	/* Kustom symbol given as -SK implies we must read text data records */
17026 			if (cmd) p->read_size_cmd = true;
17027 			/* Intentionally fall through - to 'k' */
17028 		case 'k':
17029 			p->custom = gmtlib_get_custom_symbol (GMT, text_cp);
17030 			if (!p->custom) {
17031 				GMT->init.n_custom_symbols = 0;
17032 				decode_error++;
17033 				break;
17034 			}
17035 			p->symbol = GMT_SYMBOL_CUSTOM;
17036 			p->n_required = p->custom->n_required;
17037 			for (ju = p->n_nondim = 0; ju < p->n_required; ju++) {	/* Flag input columns that are NOT lengths */
17038 				if (p->custom->type[ju] != GMT_IS_DIMENSION) p->nondim_col[p->n_nondim++] = 2 + col_off + ju;
17039 			}
17040 			break;
17041 		case '=':
17042 			p->symbol = GMT_SYMBOL_GEOVECTOR;
17043 			p->convert_angles = 1;
17044 			p->n_required = 2;
17045 			if (gmt_parse_vector (GMT, symbol_type, text, p)) {
17046 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S= parsing failure\n");
17047 				decode_error++;
17048 			}
17049 			if (p->v.status & PSL_VEC_POLE) {	/* Small circle vector */
17050 				if (p->v.status & PSL_VEC_ANGLES) {
17051 					p->nondim_col[p->n_nondim++] = 2 + col_off;	/* Start angle */
17052 					p->nondim_col[p->n_nondim++] = 3 + col_off;	/* Stop angle */
17053 				}
17054 				else {
17055 					p->nondim_col[p->n_nondim++] = 2 + col_off;	/* Arc length */
17056 					p->n_required = 1;
17057 				}
17058 			}
17059 			else {	/* Great circle vector */
17060 				p->nondim_col[p->n_nondim++] = 2 + col_off;	/* Angle [or longitude] */
17061 				p->nondim_col[p->n_nondim++] = 3 + col_off;	/* Arc length [or latitude] */
17062 			}
17063 			break;
17064 		case '~':	/* Decorated lines: -S~[d|n|l|s|x]<info>[:<symbolinfo>] */
17065 			p->symbol = GMT_SYMBOL_DECORATED_LINE;
17066 			check = false;
17067 			if (!text[1]) {	/* No args given, must parse segment header later */
17068 				p->fq_parse = true;	/* This will be set to false once at least one header has been parsed */
17069 				break;
17070 			}
17071 			for (j = 1, colon = 0; text[j]; j++) if (text[j] == ':') colon = j;
17072 			if (colon) {	/* Gave :<symbolinfo> */
17073 				text[colon] = 0;
17074 				gmtlib_decorate_init (GMT, &p->D, 0);
17075 				decode_error += gmtlib_decorate_info (GMT, 'S', &text[1], &p->D);
17076 				decode_error += gmtlib_decorate_specs (GMT, &text[colon+1], &p->D);
17077 				if (!cmd && gmt_decorate_prep (GMT, &p->D, NULL)) decode_error++;
17078 			}
17079 			else {
17080 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S~: No symbol information given\n");
17081 				decode_error++;
17082 			}
17083 			p->fq_parse = false;	/* No need to parse more later */
17084 			break;
17085 		default:
17086 			decode_error++;
17087 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S: Unrecognized symbol type %c\n", symbol_type);
17088 			break;
17089 	}
17090 	if (p->n_nondim > GMT_MAX_SYMBOL_COLS) {
17091 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Internal limitation:  Must change GMT_MAX_SYMBOL_COLS\n");
17092 	}
17093 	if (p->given_size_x == 0.0 && check) {
17094 		p->read_size = true;
17095 		p->n_required++;
17096 		if (p->symbol == GMT_SYMBOL_COLUMN) p->n_required++;
17097 	}
17098 	else
17099 		p->read_size = false;
17100 	if (bset || cmd) { /* Since we may not know if we have logarithmic projection at this point, skip the next checks. */ }
17101 	else if (p->symbol == GMT_SYMBOL_BARX)
17102 		p->base = (GMT->current.proj.xyz_projection[GMT_X] == GMT_LOG10) ? 1.0 : 0.0;
17103 	else if (p->symbol == GMT_SYMBOL_BARY)
17104 		p->base = (GMT->current.proj.xyz_projection[GMT_Y] == GMT_LOG10) ? 1.0 : 0.0;
17105 	else if (p->symbol == GMT_SYMBOL_COLUMN)
17106 		p->base = (GMT->current.proj.xyz_projection[GMT_Z] == GMT_LOG10) ? 1.0 : 0.0;
17107 	if (add_to_base) p->base_set |= GMT_BASE_ORIGIN;	/* Means to add base value to height offset to get actual z at top */
17108 	return (decode_error);
17109 }
17110 
17111 /*! . */
gmt_init_scales(struct GMT_CTRL * GMT,unsigned int unit,double * fwd_scale,double * inv_scale,double * inch_to_unit,double * unit_to_inch,char * unit_name)17112 int gmt_init_scales (struct GMT_CTRL *GMT, unsigned int unit, double *fwd_scale, double *inv_scale, double *inch_to_unit, double *unit_to_inch, char *unit_name) {
17113 	/* unit is 0-8 (see gmt_project.h for enums) and stands for m, km, mile, nautical mile, inch, cm, point, foot, or (US) survey foot */
17114 	/* fwd_scale is used to convert user distance units to meter */
17115 	/* inv_scale is used to convert meters to user distance units */
17116 	/* inch_to_unit is used to convert internal inches to users units (c, i, p) */
17117 	/* unit_to_inch is used to convert users units (c, i, p) to internal inches */
17118 	/* unit_name (unless NULL) is set to the name of the user's map measure unit (cm/inch/point) */
17119 
17120 	if (unit >= GMT_N_UNITS) {
17121 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT Unit id must be 0-%d\n", GMT_N_UNITS-1);
17122 		return GMT_DIM_TOO_LARGE;
17123 	}
17124 
17125 	/* These scales are used when 1:1 is not set to ensure that the
17126 	 * output (or input with -I) is given (taken) in the units set
17127 	 * by PROJ_LENGTH_UNIT */
17128 
17129 	switch (GMT->current.setting.proj_length_unit) {
17130 		case GMT_CM:
17131 			*inch_to_unit = 2.54;
17132 			if (unit_name) strcpy (unit_name, "cm");
17133 			break;
17134 		case GMT_INCH:
17135 			*inch_to_unit = 1.0;
17136 			if (unit_name) strcpy (unit_name, "inch");
17137 			break;
17138 		case GMT_PT:
17139 			*inch_to_unit = 72.0;
17140 			if (unit_name) strcpy (unit_name, "point");
17141 			break;
17142 		case GMT_M:
17143 			if (gmt_M_compat_check (GMT, 4)) {
17144 				*inch_to_unit = 0.0254;
17145 				if (unit_name) strcpy (unit_name, "m");
17146 			}
17147 			break;
17148 	}
17149 	*unit_to_inch = 1.0 / (*inch_to_unit);
17150 	*fwd_scale = 1.0 / GMT->current.proj.m_per_unit[unit];
17151 	*inv_scale = GMT->current.proj.m_per_unit[unit];
17152 	return GMT_OK;
17153 }
17154 
17155 /*! Converts character unit (e.g., 'k') to unit number (e.g., GMT_IS_KM) */
gmtlib_get_unit_number(struct GMT_CTRL * GMT,char unit)17156 enum gmt_enum_units gmtlib_get_unit_number (struct GMT_CTRL *GMT, char unit) {
17157 	enum gmt_enum_units mode;
17158 	gmt_M_unused(GMT);
17159 
17160 	switch (unit) {
17161 		case '\0':
17162 		case 'e':
17163 			mode = GMT_IS_METER;
17164 			break;
17165 		case 'k':
17166 			mode = GMT_IS_KM;
17167 			break;
17168 		case 'M':
17169 			mode = GMT_IS_MILE;
17170 			break;
17171 		case 'n':
17172 			mode = GMT_IS_NAUTICAL_MILE;
17173 			break;
17174 		case 'i':
17175 			mode = GMT_IS_INCH;
17176 			break;
17177 		case 'c':
17178 			mode = GMT_IS_CM;
17179 			break;
17180 		case 'p':
17181 			mode = GMT_IS_PT;
17182 			break;
17183 		case 'f':
17184 			mode = GMT_IS_FOOT;
17185 			break;
17186 		case 'u':
17187 			mode = GMT_IS_SURVEY_FOOT;
17188 			break;
17189 		default:
17190 			mode = GMT_IS_NOUNIT;
17191 	}
17192 
17193 	return (mode);
17194 }
17195 
17196 /*! . */
gmt_check_scalingopt(struct GMT_CTRL * GMT,char option,char unit,char * unit_name)17197 unsigned int gmt_check_scalingopt (struct GMT_CTRL *GMT, char option, char unit, char *unit_name) {
17198 	int smode;
17199 	unsigned int mode;
17200 
17201 	if ((smode = gmtlib_get_unit_number (GMT, unit)) == GMT_IS_NOUNIT) {
17202 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT ERROR Option -%c: Only append one of %s|%s\n",
17203 		            option, GMT_DIM_UNITS_DISPLAY, GMT_LEN_UNITS2_DISPLAY);
17204 		return GMT_NOTSET;
17205 	}
17206 	mode = (unsigned int)smode;
17207 	switch (mode) {
17208 		case GMT_IS_METER:		strcpy (unit_name, "m");		break;
17209 		case GMT_IS_KM:			strcpy (unit_name, "km");		break;
17210 		case GMT_IS_MILE:		strcpy (unit_name, "mile");		break;
17211 		case GMT_IS_NAUTICAL_MILE:	strcpy (unit_name, "nautical mile");	break;
17212 		case GMT_IS_INCH:		strcpy (unit_name, "inch");		break;
17213 		case GMT_IS_CM:			strcpy (unit_name, "cm");		break;
17214 		case GMT_IS_PT:			strcpy (unit_name, "point");		break;
17215 		case GMT_IS_FOOT:		strcpy (unit_name, "foot");		break;
17216 		case GMT_IS_SURVEY_FOOT:	strcpy (unit_name, "survey foot");	break;
17217 	}
17218 
17219 	return (mode);
17220 }
17221 
17222 /*! Option to override the GMT measure unit default */
gmt_set_measure_unit(struct GMT_CTRL * GMT,char unit)17223 int gmt_set_measure_unit (struct GMT_CTRL *GMT, char unit) {
17224 	int k;
17225 
17226 	if ((k = gmt_get_dim_unit (GMT, unit)) < 0) {
17227 		if (unit)
17228 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad plot measure selected (%c); use c, i, or p.\n", unit);
17229 		else
17230 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "No plot measure selected; use c, i, or p.\n");
17231 		return (GMT_MAP_BAD_MEASURE_UNIT);
17232 	}
17233 	GMT->current.setting.proj_length_unit = k;
17234 	return (GMT_NOERROR);
17235 }
17236 
17237 /*! Use to parse various -S -Q options when backwardsness has been enabled */
gmtinit_backwards_SQ_parsing(struct GMT_CTRL * GMT,char option,char * item)17238 int gmtinit_backwards_SQ_parsing (struct GMT_CTRL *GMT, char option, char *item) {
17239 	int j;
17240 
17241 	GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -%c[-]<mode>[/<threshold>] is deprecated. Use -n<mode>[+a][+t<threshold>] instead.\n", option);
17242 
17243 	for (j = 0; j < 3 && item[j]; j++) {
17244 		switch (item[j]) {
17245 			case '-':
17246 				GMT->common.n.antialias = false; break;
17247 			case 'n':
17248 				GMT->common.n.interpolant = BCR_NEARNEIGHBOR; break;
17249 			case 'l':
17250 				GMT->common.n.interpolant = BCR_BILINEAR; break;
17251 			case 'b':
17252 				GMT->common.n.interpolant = BCR_BSPLINE; break;
17253 			case 'c':
17254 				GMT->common.n.interpolant = BCR_BICUBIC; break;
17255 			case '/':
17256 				GMT->common.n.threshold = atof (&item[j+1]);
17257 				if (GMT->common.n.threshold < 0.0 || GMT->common.n.threshold > 1.0) {
17258 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Interpolation threshold must be in [0,1] range\n");
17259 					return (1);
17260 				}
17261 				break;
17262 			default:
17263 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Specify -%c[-]b|c|l|n[/threshold] to set grid interpolation mode.\n", option);
17264 				return (1);
17265 				break;
17266 		}
17267 	}
17268 	return (GMT_NOERROR);
17269 }
17270 
gmt_parse_inc_option(struct GMT_CTRL * GMT,char option,char * item)17271 unsigned int gmt_parse_inc_option (struct GMT_CTRL *GMT, char option, char *item) {
17272 	/* Closest thing to parsing a global -I option we can get */
17273 	if (gmt_getinc (GMT, item, GMT->common.R.inc)) {
17274 		gmt_inc_syntax (GMT, option, 1);
17275 		return 1U;
17276 	}
17277 	GMT->common.R.active[ISET] = true;
17278 	return GMT_NOERROR;
17279 }
17280 
17281 #ifdef HAVE_GDAL
gmtinit_parse_proj4(struct GMT_CTRL * GMT,char * item,char * dest)17282 GMT_LOCAL int gmtinit_parse_proj4 (struct GMT_CTRL *GMT, char *item, char *dest) {
17283 	/* Deal with proj.4 or EPSGs passed in -J option */
17284 	char  *item_t1 = NULL, *item_t2 = NULL, wktext[32] = {""}, *pch;
17285 	bool   do_free = false;
17286 	int    error = 0, scale_pos;
17287 	size_t k, len;
17288 	double sc;
17289 
17290 	if (item[0] == '+') {
17291 		bool found = false;
17292 		len = strlen (item);
17293 		for (k = 1; k < len; k++) {			/* Search for glued tokens */
17294 			if (item[k] == '+' && item[k-1] != ' ') {
17295 				found = true;
17296 				break;
17297 			}
17298 		}
17299 		if (found) {			/* OK, need to break up things like +x=0+y=0 into "+x=0 +y=0" */
17300 			item_t1 = gmt_strrep (item, "+", " +");
17301 			do_free = true;		/* Signal that we must free item_t1 */
17302 		}
17303 		else
17304 			item_t1 = item;
17305 
17306 		snprintf(GMT->common.J.proj4string, GMT_LEN256, "%s", item_t1);		/* Copy the proj.4 string */
17307 	}
17308 	else if (!strncmp(item, "EPSG:", 5) || !strncmp(item, "epsg:", 5))
17309 		item_t1 = &item[5];		/* Drop the EPSG: part because gmt_impotproj4 is not expecting it */
17310 	else
17311 		item_t1 = item;
17312 
17313 	/* Don't remember anymore if we still need to call gmt_importproj4(). We don't for simple
17314 	   mapproject usage but maybe we still need for mapping purposes. To-be-rediscovered.
17315 	*/
17316 	item_t2 = gmt_importproj4 (GMT, item_t1, &scale_pos);		/* This is GMT -J proj string */
17317 	if (item_t2 && !GMT->current.ps.active && !strcmp(item_t2, "/1:1")) {
17318 		/* Even though it failed to do the mapping we can still use it in mapproject */
17319 		GMT->current.proj.projection_GMT = GMT_NO_PROJ;
17320 		GMT->current.proj.is_proj4 = true;
17321 		GMT->current.proj.pars[14] = 1;
17322 	}
17323 	else if (item_t2) {
17324 		char *pch2;
17325 		len = strlen(item_t2);
17326 		if (item_t2[len-1] == 'W') {				/* See if scale is in fact a width */
17327 			item_t2[0] = (char)toupper(item_t2[0]);		/* and let the GMT machinery detect this fact */
17328 			item_t2[len-1] = '\0';
17329 		}
17330 		if (scale_pos != 0) {	/* Because if == 0 than it means we have a scale only string (i.e. no GMT mapped proj) */
17331 			error += (gmt_M_check_condition (GMT, GMT->common.J.active, "Option -J given more than once\n") ||
17332 			                                 gmtinit_parse_J_option (GMT, item_t2));
17333 		}
17334 		else		/* Not particularly useful yet because mapproject will fail anyway when scale != 1:1 */
17335 			GMT->current.proj.projection_GMT = GMT_NO_PROJ;
17336 
17337 		/* Check if the scale is 1 or 1:1, and don't get fooled with, for example, 1:10 */
17338 		pch = &item_t2[scale_pos];
17339 		if ((pch2 = strchr(pch, ':')) != NULL) {
17340 			if ((sc = atof(&pch2[1])) == 1)
17341 				GMT->current.proj.pars[14] = 1;
17342 		}
17343 		else if ((sc = atof(pch)) == 1)
17344 			GMT->current.proj.pars[14] = 1;
17345 
17346 		free (item_t2);			/* Cannot be freed before */
17347 		item_t2 = NULL;
17348 	}
17349 	else {
17350 		if (do_free) free (item_t1);
17351 		return 1;
17352 	}
17353 
17354 	if (isdigit(item[0]))
17355 		sprintf (dest, "EPSG:%s", item);
17356 	else if (strstr(item, "EPSG:") || strstr(item, "epsg:"))
17357 		sprintf (dest, "%s", item);
17358 	else
17359 		sprintf (dest, "%s", item_t1);
17360 
17361 	/* For the proj.4 string detect if this projection is supported by GDAL. If not will append a +wktext later */
17362 	if (!strncmp(item, "+proj=", 6) && GDAL_VERSION_NUM < 2050000) {	/* Almost for sure GDAL 2.5 doesn't need this */
17363 		char prjcode[8] = {""};
17364 		k = 6;
17365 		while (item[k] && (item[k] != '+' && item[k] != ' ' && item[k] != '\t' && item[k] != '/')) k++;
17366 		strncpy(prjcode, &item[6], k - 6);
17367 
17368 		/* List taken from https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogr_srs_proj4.cpp#L616  */
17369 		if (strcmp(prjcode, "longlat") &&
17370 			strcmp(prjcode, "latlong") &&
17371 			strcmp(prjcode, "geocent") &&
17372 			strcmp(prjcode, "bonne") &&
17373 			strcmp(prjcode, "cass") &&
17374 			strcmp(prjcode, "nzmg") &&
17375 			strcmp(prjcode, "cea") &&
17376 			strcmp(prjcode, "tmerc") &&
17377 			strcmp(prjcode, "etmerc") &&
17378 			strcmp(prjcode, "utm") &&
17379 			strcmp(prjcode, "merc") &&
17380 			strcmp(prjcode, "stere") &&
17381 			strcmp(prjcode, "sterea") &&
17382 			strcmp(prjcode, "eqc") &&
17383 			strcmp(prjcode, "gstmerc") &&
17384 			strcmp(prjcode, "gnom") &&
17385 			strcmp(prjcode, "ortho") &&
17386 			strcmp(prjcode, "laea") &&
17387 			strcmp(prjcode, "aeqd") &&
17388 			strcmp(prjcode, "eqdc") &&
17389 			strcmp(prjcode, "mill") &&
17390 			strcmp(prjcode, "moll") &&
17391 			strcmp(prjcode, "eck1") &&
17392 			strcmp(prjcode, "eck2") &&
17393 			strcmp(prjcode, "eck3") &&
17394 			strcmp(prjcode, "eck4") &&
17395 			strcmp(prjcode, "eck5") &&
17396 			strcmp(prjcode, "eck6") &&
17397 			strcmp(prjcode, "poly") &&
17398 			strcmp(prjcode, "aea") &&
17399 			strcmp(prjcode, "robin") &&
17400 			strcmp(prjcode, "vandg") &&
17401 			strcmp(prjcode, "sinu") &&
17402 			strcmp(prjcode, "gall") &&
17403 			strcmp(prjcode, "goode") &&
17404 			strcmp(prjcode, "igh") &&
17405 			strcmp(prjcode, "geos") &&
17406 			strcmp(prjcode, "lcc") &&
17407 			strcmp(prjcode, "omerc") &&
17408 			strcmp(prjcode, "somerc") &&
17409 			strcmp(prjcode, "krovak") &&
17410 			strcmp(prjcode, "iwm_p") &&
17411 			strcmp(prjcode, "wag1") &&
17412 			strcmp(prjcode, "wag2") &&
17413 			strcmp(prjcode, "wag3") &&
17414 			strcmp(prjcode, "wag4") &&
17415 			strcmp(prjcode, "wag5") &&
17416 			strcmp(prjcode, "wag6") &&
17417 			strcmp(prjcode, "wag7") &&
17418 			strcmp(prjcode, "qsc") &&
17419 			strcmp(prjcode, "sch") &&
17420 			strcmp(prjcode, "tpeqd")) {
17421 
17422 			sprintf(wktext, " +wktext");	/* Projection NOT internally supported by GDAL */
17423 			if (!strstr(item, "+ellps") && !strstr(item, "+a=") && !strstr(item, "+R="))
17424 				strcat(dest, " +ellps=WGS84");
17425 		}
17426 	}
17427 
17428 	if (do_free) free (item_t1);			/* When we got a glued +proj=... and had to insert spaces */
17429 
17430 	if ((pch = strchr(dest, '/')) != NULL || (pch = strstr(dest, "+width=")) != NULL || (pch = strstr(dest, "+scale=")) != NULL)
17431 		/* If we have a scale, drop it before passing the string to GDAL */
17432 		pch[0] = '\0';
17433 
17434 	if (wktext[0]) strcat(dest, wktext);	/* Append a +wktext to make this projection recognized by GDAL */
17435 
17436 	if (item_t2) free (item_t2);
17437 
17438 	return error;
17439 }
17440 #endif
17441 
17442 /*! gmt_parse_common_options interprets the command line for the common, unique options
17443  * -B, -J, -K, -O, -P, -R, -U, -V, -X, -Y, -a, -b, -c, -d, -e, -f, -g, -h, -i, -j, -l, -n, -o, -p, -q, -r, -s, -t, -w, -:, -- and -^.
17444  * The list passes all of these that we should consider.
17445  * The API will also consider -I for grid increments.
17446  */
gmt_parse_common_options(struct GMT_CTRL * GMT,char * list,char option,char * item)17447 int gmt_parse_common_options (struct GMT_CTRL *GMT, char *list, char option, char *item) {
17448 
17449 	int error = 0, i = 0, q = 0;	/* The i and i+= gmt_M_more_than_once are there to avoid compiler warnings... */
17450 
17451 	if (!list || !strchr (list, option)) return (0);	/* Not a common option we accept */
17452 
17453 	if (gmt_M_compat_check (GMT, 4)) {
17454 		/* Translate some GMT4 options */
17455 		switch (option) {
17456 			case 'E': gmt_M_compat_opt ('p'); break;
17457 			case 'F': gmt_M_compat_opt ('r'); break;
17458 			case 'H': gmt_M_compat_opt ('h'); break;
17459 		}
17460 	}
17461 
17462 	switch (option) {	/* Handle parsing of this option, if allowed here */
17463 		case 'B':
17464 			switch (item[0]) {	/* Check for -B[p] and -Bs */
17465 				case 'p': GMT->common.B.active[GMT_PRIMARY] = true; q = 1; break;
17466 				case 's': GMT->common.B.active[GMT_SECONDARY] = true; q = 1; break;
17467 				default:  GMT->common.B.active[GMT_PRIMARY] = true; break;
17468 			}
17469 			if (!error) {
17470 				if (GMT->current.setting.run_mode == GMT_MODERN && (!GMT->current.map.frame.set[GMT_X] || !GMT->current.map.frame.set[GMT_Y] || (GMT->common.J.zactive && !GMT->current.map.frame.set[GMT_Z]))) {
17471 					char code[2], args[GMT_LEN256] = {""}, *c = strchr (item, '+');	/* Start of modifiers, if any */
17472 					if (item[q] && strstr (item, "+f")) GMT->current.plot.calclock.geo.wesn = 1;	/* Got +f, so enable W|E|S|N suffices */
17473 					if (c && strchr (GMT_AXIS_MODIFIERS, c[1]))	/* We got the ones suitable for axes that we can chop off */
17474 						c[0] = '\0';	/* Temporarily chop off these modifiers only */
17475 					code[0] = item[q]; code[1] = (item[q]) ? item[q+1] : '\0';
17476 					if (c) c[0] = '+';	/* Restore modifiers */
17477 					if (code[0] == '\0') {	/* Default is -Baf if nothing given */
17478 						if (q) args[0] = item[0]; strcat (args, "af");	if (c) strncat (args, c, GMT_LEN256-1);
17479 					}
17480 					else if (code[0] == 'x' && code[1] == '\0') {	/* If indicating x we do -Bxaf */
17481 						if (q) args[0] = item[0]; strcat (args, "xaf");	if (c) strncat (args, c, GMT_LEN256-1);
17482 					}
17483 					else if (code[0] == 'y' && code[1] == '\0') {	/* If indicating y we do -Byaf */
17484 						if (q) args[0] = item[0]; strcat (args, "yaf");	if (c) strncat (args, c, GMT_LEN256-1);
17485 					}
17486 					else if (code[0] == 'z' && code[1] == '\0') {	/* If indicating z we do -Bzaf */
17487 						if (q) args[0] = item[0]; strcat (args, "zaf");	if (c) strncat (args, c, GMT_LEN256-1);
17488 					}
17489 					else	/* Keep what we got */
17490 						strncpy (args, item, GMT_LEN256-1);
17491 					error = gmtlib_parse_B_option (GMT, args);
17492 				}
17493 				else
17494 					error = gmtlib_parse_B_option (GMT, item);
17495 			}
17496 			break;
17497 
17498 		case 'I':
17499 			if (GMT->hidden.func_level > GMT_CONTROLLER) return (0);	/* Just skip if we are inside a GMT module. -I is an API common option only */
17500 			error = gmt_parse_inc_option (GMT, 'I', item);
17501 			GMT->common.R.active[ISET] = true;
17502 			break;
17503 
17504 		case 'J':
17505 			if (item && (item[0] == 'Z' || item[0] == 'z')) {	/* -JZ or -Jz */
17506 				error += (gmt_M_check_condition (GMT, GMT->common.J.zactive, "Option -JZ|z given more than once\n") ||
17507 				                                 gmtinit_parse_J_option (GMT, item));
17508 				GMT->common.J.zactive = true;
17509 			}
17510 			else if (item && (item[0] == '+' || isdigit(item[0]) || !strncmp(item, "EPSG:", 5) || !strncmp(item, "epsg:", 5))) {
17511 #ifdef HAVE_GDAL
17512 				char   source[GMT_LEN1024] = {""}, dest[GMT_LEN1024] = {""}, *pch;
17513 				bool two = false;
17514 
17515 				/* When reading from gmt.history, those are prefixed with a '+' but we must remove it */
17516 				if (!strncmp(item, "+EPSG", 5) || !strncmp(item, "+epsg", 5) || (item[0] == '+' && isdigit(item[1])))
17517 					item++;
17518 
17519 				/* Distinguish between the +to key extension and the +towgs84 key */
17520 				if ((pch = strstr(item, "+to")) != NULL) {
17521 					if (pch[3] == ' ' || pch[3] == '\t' || pch[3] == '+' || pch[3] == 'E' || pch[3] == 'e' || isdigit(pch[3]))
17522 						two = true;
17523 					else if (pch[3] == '\0') {
17524 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "There is no destiny referencing system after the '+to' keyword.\n");
17525 						error = 1;
17526 						break;
17527 					}
17528 				}
17529 
17530 				if (!two) {		/* A single CRS */
17531 					sprintf(source, "+proj=latlong +datum=WGS84");
17532 					error = gmtinit_parse_proj4 (GMT, item, dest);
17533 				}
17534 				else {
17535 					size_t len, k = 3;
17536 
17537 					if (!strcmp(GMT->init.module_name, "grdproject")) {
17538 						GMT_Report(GMT->parent, GMT_MSG_ERROR, "Cannot use the PROJ +to construct here. Not implemented, try grdgdal instead.\n\n");
17539 						error = 1;
17540 						break;
17541 					}
17542 
17543 					len = strlen(pch);
17544 					pch[0] = '\0';
17545 					error  = gmtinit_parse_proj4 (GMT, item, source);
17546 					if (pch[3] == ' ' || pch[3] == '\t')
17547 						while (pch[k] && isspace(pch[k])) k++;
17548 					if (k == len) {
17549 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "There is no destiny referencing system after the '+to' keyword.\n");
17550 						error = 1;
17551 						break;
17552 					}
17553 					if (GMT->current.proj.projection_GMT != GMT_LINEAR)
17554 						GMT->current.proj.proj4_is_cart[0] = true;		/* Need to know this in mapproject*/
17555 
17556 					error += gmtinit_parse_proj4 (GMT, &pch[k], dest);
17557 					if (GMT->current.proj.projection_GMT != GMT_LINEAR)
17558 						GMT->current.proj.proj4_is_cart[1] = true;
17559 				}
17560 				GMT->current.gdal_read_in.hCT_fwd = gmt_OGRCoordinateTransformation (GMT, source, dest);
17561 				GMT->current.gdal_read_in.hCT_inv = gmt_OGRCoordinateTransformation (GMT, dest, source);
17562 				GMT->current.proj.projection      = GMT_PROJ4_PROJS;		/* This now make it use the proj4 lib */
17563 				GMT->common.J.active = true;
17564 				if (GMT->current.gdal_read_in.hCT_fwd == NULL || GMT->current.gdal_read_in.hCT_inv == NULL)
17565 					error = 1;
17566 			}
17567 #else
17568 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "PROJ.4 can only be used with GDAL linked GMT.\n");
17569 				error = 1;
17570 			}
17571 #endif
17572 			else {	/* Horizontal map projection */
17573 				error += (gmt_M_check_condition (GMT, GMT->common.J.active, "Option -J given more than once\n") ||
17574 				                                 gmtinit_parse_J_option (GMT, item));
17575 				GMT->common.J.active = true;
17576 			}
17577 			break;
17578 
17579 		case 'K':
17580 			i += gmt_M_more_than_once (GMT, GMT->common.K.active);
17581 			GMT->common.K.active = true;
17582 			break;
17583 
17584 		case 'O':
17585 			i += gmt_M_more_than_once (GMT, GMT->common.O.active);
17586 			GMT->common.O.active = true;
17587 			break;
17588 
17589 		case 'P':
17590 			if (GMT->current.setting.run_mode == GMT_CLASSIC) {
17591 				i += gmt_M_more_than_once (GMT, GMT->common.P.active);
17592 				GMT->common.P.active = true;
17593 			}
17594 			else {
17595 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a recognized common option in GMT modern mode\n", option);
17596 				return (1);
17597 			}
17598 			break;
17599 
17600 		case 'Q':
17601 		case 'S':
17602 			if (gmt_M_compat_check (GMT, 4)) {
17603 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -%c is deprecated. Use -n instead.\n" GMT_COMPAT_INFO, option);
17604 				error += gmtinit_backwards_SQ_parsing (GMT, option, item);
17605 			}
17606 			else {
17607 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a recognized common option\n", option);
17608 				return (1);
17609 			}
17610 			break;
17611 
17612 		case 'R':
17613 			error += (gmt_M_more_than_once (GMT, GMT->common.R.active[RSET]) || gmt_parse_R_option (GMT, item));
17614 			GMT->common.R.active[RSET] = true;
17615 			break;
17616 
17617 		case 'U':
17618 			error += (gmt_M_more_than_once (GMT, GMT->common.U.active) || gmtinit_parse_U_option (GMT, item));
17619 			GMT->common.U.active = true;
17620 			break;
17621 
17622 		case 'V':
17623 			i += gmt_M_more_than_once (GMT, GMT->common.V.active);
17624 			GMT->common.V.active = true;
17625 			if (item && item[0]) {	/* Specified a verbosity level */
17626 				if (gmtinit_parse_V_option (GMT, item[0])) {
17627 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unknown argument to -V option, -V%c\n", item[0]);
17628 					error++;
17629 				}
17630 			}
17631 			else
17632 				GMT->current.setting.verbose = GMT_MSG_INFORMATION;
17633 			break;
17634 
17635 		case 'X':
17636 			error += (gmt_M_more_than_once (GMT, GMT->common.X.active) || gmtinit_parse_X_option (GMT, item));
17637 			GMT->common.X.active = true;
17638 			break;
17639 
17640 		case 'Y':
17641 			error += (gmt_M_more_than_once (GMT, GMT->common.Y.active) || gmtinit_parse_Y_option (GMT, item));
17642 			GMT->common.Y.active = true;
17643 			break;
17644 
17645 		case 'Z':	/* GMT4 Backwards compatibility */
17646 			if (gmt_M_compat_check (GMT, 4)) {
17647 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -Z[<zlevel>] is deprecated. Use -p<azim>/<elev>[/<zlevel>] instead.\n"
17648 				            GMT_COMPAT_INFO);
17649 				if (item && item[0]) {
17650 					if (gmtinit_get_uservalue (GMT, item, gmt_M_type (GMT, GMT_IN, GMT_Z), &GMT->current.proj.z_level, "-Z zlevel value"))
17651 						return 1;
17652 				}
17653 			}
17654 			else {
17655 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a recognized common option\n", option);
17656 				return (1);
17657 			}
17658 			break;
17659 
17660 		case 'a':
17661 			error += (gmt_M_more_than_once (GMT, GMT->common.a.active) || gmtinit_parse_a_option (GMT, item));
17662 			GMT->common.a.active = true;
17663 			break;
17664 
17665 		case 'b':
17666 			switch (item[0]) {
17667 				case 'i':
17668 					error += gmt_M_check_condition (GMT, GMT->common.b.active[GMT_IN], "Warning Option -bi given more than once\n");
17669 					GMT->common.b.active[GMT_IN] = true;
17670 					break;
17671 				case 'o':
17672 					error += gmt_M_check_condition (GMT, GMT->common.b.active[GMT_OUT], "Warning Option -bo given more than once\n");
17673 					GMT->common.b.active[GMT_OUT] = true;
17674 					break;
17675 				default:
17676 					error += gmt_M_check_condition (GMT, GMT->common.b.active[GMT_IN] + GMT->common.b.active[GMT_OUT],
17677 					                                "Warning Option -b given more than once\n");
17678 					GMT->common.b.active[GMT_IN] = GMT->common.b.active[GMT_OUT] = true;
17679 					break;
17680 			}
17681 			error += gmtinit_parse_b_option (GMT, item);
17682 			break;
17683 
17684 		case 'd':
17685 			switch (item[0]) {
17686 				case 'i':
17687 					error += gmt_M_check_condition (GMT, GMT->common.d.active[GMT_IN], "Warning Option -di given more than once\n");
17688 					break;
17689 				case 'o':
17690 					error += gmt_M_check_condition (GMT, GMT->common.d.active[GMT_OUT], "Warning Option -do given more than once\n");
17691 					break;
17692 				default:
17693 					error += gmt_M_check_condition (GMT, GMT->common.d.active[GMT_IN] + GMT->common.d.active[GMT_OUT],
17694 					                                "Warning Option -d given more than once\n");
17695 					break;
17696 			}
17697 			error += gmt_parse_d_option (GMT, item);
17698 			break;
17699 
17700 		case 'e':
17701 			error += (gmt_M_more_than_once (GMT, GMT->common.e.active) || gmtinit_parse_e_option (GMT, item));
17702 			GMT->common.e.active = true;
17703 			break;
17704 
17705 		case 'f':
17706 			switch (item[0]) {
17707 				case 'i':
17708 #if 0
17709 					error += gmt_M_check_condition (GMT, GMT->common.f.active[GMT_IN], "Warning Option -fi given more than once\n");
17710 #endif
17711 					GMT->common.f.active[GMT_IN] = true;
17712 					break;
17713 				case 'o':
17714 #if 0
17715 					error += gmt_M_check_condition (GMT, GMT->common.f.active[GMT_OUT], "Warning Option -fo given more than once\n");
17716 #endif
17717 					GMT->common.f.active[GMT_OUT] = true;
17718 					break;
17719 				default:
17720 #if 0
17721 					error += gmt_M_check_condition (GMT, GMT->common.f.active[GMT_IN] | GMT->common.f.active[GMT_OUT], "Warning Option -f given more than once\n");
17722 #endif
17723 					GMT->common.f.active[GMT_IN] = GMT->common.f.active[GMT_OUT] = true;
17724 					break;
17725 			}
17726 			error += gmtinit_parse_f_option (GMT, item);
17727 			break;
17728 
17729 		case 'g':
17730 			error += gmt_parse_g_option (GMT, item);
17731 			GMT->common.g.active = true;
17732 			break;
17733 
17734 		case 'h':
17735 			error += (gmt_M_more_than_once (GMT, GMT->common.h.active) || gmtinit_parse_h_option (GMT, item));
17736 			GMT->common.h.active = true;
17737 			break;
17738 
17739 		case 'i':
17740 			error += (gmt_M_more_than_once (GMT, GMT->common.i.active) || gmt_parse_i_option (GMT, item));
17741 			GMT->common.i.active = true;
17742 			break;
17743 
17744 		case 'j':
17745 			error += (gmt_M_more_than_once (GMT, GMT->common.j.active) || gmt_parse_j_option (GMT, item));
17746 			GMT->common.j.active = true;
17747 			break;
17748 
17749 		case 'l':
17750 			error += (gmt_M_more_than_once (GMT, GMT->common.l.active) || gmtinit_parse_l_option (GMT, item));
17751 			GMT->common.l.active = true;
17752 			break;
17753 
17754 		case 'M':	/* Backwards compatibility */
17755 		case 'm':
17756 			if (gmt_M_compat_check (GMT, 4)) {
17757 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Option -%c is deprecated. Segment headers are automatically identified.\n", option);
17758 			}
17759 			else {
17760 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a recognized common option\n", option);
17761 				return (1);
17762 			}
17763 			break;
17764 
17765 		case 'n':
17766 			error += (gmt_M_more_than_once (GMT, GMT->common.n.active) || gmtinit_parse_n_option (GMT, item));
17767 			GMT->common.n.active = true;
17768 			break;
17769 
17770 		case 'o':
17771 			error += (gmt_M_more_than_once (GMT, GMT->common.o.active) || gmt_parse_o_option (GMT, item));
17772 			GMT->common.o.active = true;
17773 			break;
17774 
17775 		case 'p':
17776 			error += (gmt_M_more_than_once (GMT, GMT->common.p.active) || gmtinit_parse_p_option (GMT, item));
17777 			GMT->common.p.active = true;
17778 			break;
17779 
17780 		case 'q':
17781 			switch (item[0]) {
17782 				case 'o':
17783 					error += (gmt_M_more_than_once (GMT, GMT->common.q.active[GMT_OUT]) || gmtinit_parse_q_option (GMT, item));
17784 					GMT->common.q.active[GMT_OUT] = true;
17785 					break;
17786 				default:
17787 					error += (gmt_M_more_than_once (GMT, GMT->common.q.active[GMT_IN]) || gmtinit_parse_q_option (GMT, item));
17788 					GMT->common.q.active[GMT_IN] = true;
17789 					break;
17790 			}
17791 			break;
17792 
17793 		case 'r':
17794 			error += gmt_M_more_than_once (GMT, GMT->common.R.active[GSET]);
17795 			GMT->common.R.active[GSET] = true;
17796 			if (item[0]) {	/* Gave argument for specific registration */
17797 				switch (item[0]) {
17798 					case 'p': GMT->common.R.registration = GMT_GRID_PIXEL_REG; break;
17799 					case 'g': GMT->common.R.registration = GMT_GRID_NODE_REG; break;
17800 					default:
17801 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -r: Syntax is -r[g|p]\n");
17802 						error++;
17803 						break;
17804 				}
17805 			}
17806 			else	/* By default, -r means pixel registration */
17807 				GMT->common.R.registration = GMT_GRID_PIXEL_REG;
17808 			break;
17809 
17810 		case 's':
17811 			error += (gmt_M_more_than_once (GMT, GMT->common.s.active) || gmt_parse_s_option (GMT, item));
17812 			GMT->common.s.active = true;
17813 			break;
17814 
17815 		case 't':
17816 			error += gmt_M_more_than_once (GMT, GMT->common.t.active) || gmtinit_parse_t_option (GMT, item);
17817 			break;
17818 
17819 		case 'w':
17820 			error += gmt_M_more_than_once (GMT, GMT->common.w.active) || gmtinit_parse_w_option (GMT, item);
17821 			break;
17822 
17823 #ifdef GMT_MP_ENABLED
17824 		case 'x':
17825 			error += (gmt_M_more_than_once (GMT, GMT->common.x.active) || gmtinit_parse_x_option (GMT, item));
17826 			GMT->common.x.active = true;
17827 			break;
17828 #endif
17829 
17830 		case ':':
17831 			error += (gmt_M_more_than_once (GMT, GMT->common.colon.active) || gmtinit_parse_colon_option (GMT, item));
17832 			GMT->common.colon.active = true;
17833 			break;
17834 
17835 		case '^':
17836 			if (GMT->common.synopsis.active) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option - given more than once\n");
17837 			GMT->common.synopsis.active = true;
17838 			break;
17839 
17840 		case '-':
17841 			error += gmtinit_parse_dash_option (GMT, item);
17842 			break;
17843 
17844 		case '>':	/* Registered output file; nothing to do here */
17845 			break;
17846 
17847 		case '=':	/* List of input files? */
17848 			if (item[0] == '\0') {
17849 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c requires a file argument\n", option);
17850 				error++;
17851 			}
17852 			else if (!gmt_M_file_is_memory (item) && gmt_access (GMT, item, F_OK)) {	/* File does not exist */
17853 				error++;
17854 			}
17855 			break;
17856 
17857 		default:	/* Here we end up if an unrecognized option is passed (should not happen, though) */
17858 			if (GMT->current.ps.oneliner)
17859 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a valid common option for one-liner mode\n", option);
17860 			else
17861 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c is not a recognized common option\n", option);
17862 			return (1);
17863 			break;
17864 	}
17865 
17866 	/* On error, give syntax message */
17867 
17868 	if (error) {
17869 		gmt_syntax (GMT, option);
17870 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Offending option -%c%s\n", option, item);
17871 	}
17872 
17873 	return (error);
17874 }
17875 
17876 /*! . */
gmt_init_time_system_structure(struct GMT_CTRL * GMT,struct GMT_TIME_SYSTEM * time_system)17877 int gmt_init_time_system_structure (struct GMT_CTRL *GMT, struct GMT_TIME_SYSTEM *time_system) {
17878 	/* Processes strings time_system.unit and time_system.epoch to produce a time system scale
17879 	   (units in seconds), inverse scale, and rata die number and fraction of the epoch (days).
17880 	   Return values: 0 = no error, 1 = unit error, 2 = epoch error, 3 = unit and epoch error.
17881 	*/
17882 	int error = GMT_NOERROR;
17883 
17884 	/* Check the unit sanity */
17885 	switch (time_system->unit) {
17886 		case 'y':	case 'Y':
17887 			/* This is a kludge: we assume all years are the same length, thinking that a user
17888 			with decimal years doesn't care about precise time.  To do this right would
17889 			take an entirely different scheme, not a simple unit conversion. */
17890 			time_system->scale = GMT_YR2SEC_F;
17891 			break;
17892 		case 'o':	case 'O':
17893 			/* This is also a kludge: we assume all months are the same length, thinking that a user
17894 			with decimal years doesn't care about precise time.  To do this right would
17895 			take an entirely different scheme, not a simple unit conversion. */
17896 			time_system->scale = GMT_MON2SEC_F;
17897 			break;
17898 		case 'w':	case 'W':
17899 			time_system->scale = GMT_WEEK2SEC_F;
17900 			break;
17901 		case 'd':	case 'D':
17902 			time_system->scale = GMT_DAY2SEC_F;
17903 			break;
17904 		case 'h':	case 'H':
17905 			time_system->scale = GMT_HR2SEC_F;
17906 			break;
17907 		case 'm':	case 'M':
17908 			time_system->scale = GMT_MIN2SEC_F;
17909 			break;
17910 		case 's':	case 'S':
17911 			time_system->scale = 1.0;
17912 			break;
17913 		case 'c':	case 'C':
17914 			if (gmt_M_compat_check (GMT, 4)) {
17915 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Unit c (seconds) is deprecated; use s instead.\n");
17916 				time_system->scale = 1.0;
17917 			}
17918 			else
17919 				error ++;
17920 			break;
17921 		default:
17922 			error ++;
17923 			break;
17924 	}
17925 
17926 	/* Set inverse scale and store it to avoid divisions later */
17927 	time_system->i_scale = 1.0 / time_system->scale;
17928 
17929 	/* Now convert epoch into rata die number and fraction */
17930 	if (gmtinit_scanf_epoch (GMT, time_system->epoch, &time_system->rata_die, &time_system->epoch_t0)) error += 2;
17931 
17932 	if (error & 1) {
17933 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "TIME_UNIT is invalid.  Default second is assumed.\n");
17934 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Choose one only from %s\n", GMT_TIME_UNITS_DISPLAY);
17935 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Corresponding to year month week day hour minute second\n");
17936 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Note year and month are simply defined (365.2425 days and 1/12 of a year)\n");
17937 	}
17938 	if (error & 2) {
17939 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "TIME_EPOCH format is invalid.  Default assumed.\n");
17940 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "    A correct format has the form [-]yyyy-mm-ddThh:mm:ss[.xxx]\n");
17941 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "    or (using ISO weekly calendar)   yyyy-Www-dThh:mm:ss[.xxx]\n");
17942 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "    An example of a correct format is:  2000-01-01T12:00:00\n");
17943 	}
17944 	return (error);
17945 }
17946 
gmt_get_option_id(int start,char * this_option)17947 int gmt_get_option_id (int start, char *this_option) {
17948 	/* Search the GMT_unique_option list starting at given position for this_option */
17949 	int k, id = GMT_NOTSET;
17950 	for (k = start; k < GMT_N_UNIQUE && id == GMT_NOTSET; k++)
17951 		if (!strcmp (GMT_unique_option[k], this_option)) id = k;	/* Got entry index into history array for requested option */
17952 	return (id);
17953 }
17954 
17955 /*! Discover if a certain option was set in the history and re-set it */
gmt_set_missing_options(struct GMT_CTRL * GMT,char * options)17956 int gmt_set_missing_options (struct GMT_CTRL *GMT, char *options) {
17957 	/* When a module discovers it needs -R or -J and it maybe was not given
17958 	 * see if we can tease out the answer from the history and parse it.
17959 	 * Currently used in gmt_get_refpoint where we may learn that -R -J will
17960 	 * indeed be required.  We then check if they have been given.  If not,
17961 	 * then under classic mode we abort, while under modern mode we add them,
17962 	 * if possible.
17963 	 */
17964 	int id = 0, j, err = 0;
17965 	char str[3] = {""};
17966 
17967 	if (GMT->current.setting.run_mode == GMT_CLASSIC) return GMT_NOERROR;	/* Do nothing */
17968 	if (GMT->current.ps.active && GMT->current.ps.initialize) return GMT_NOERROR;	/* Cannot use history unless overlay */
17969 
17970 	assert (options);	/* Should never be NULL */
17971 
17972 	for (j = 0; options[j]; j++) {	/* Do this for all required options listed */
17973 		assert (strchr ("JR", options[j]));	/* Only J and/or R should be present in options */
17974 		if (options[j] == 'R' && GMT->common.R.active[RSET]) continue;	/* Set already */
17975 		if (options[j] == 'J' && GMT->common.J.active) continue;	/* Set already */
17976 		/* Must dig around in the history array */
17977 		gmt_M_memset (str, 3, char);
17978 		str[0] = options[j];
17979 		if ((id = gmt_get_option_id (0, str)) == GMT_NOTSET) continue;	/* Not an option we have history for yet */
17980 		if (options[j] == 'R' && !GMT->current.ps.active) id++;		/* Examine -RG history if not a plotter */
17981 		if (GMT->init.history[id] == NULL) continue;	/* No history for this option */
17982 		if (options[j] == 'J') {	/* Must now search for actual option since -J only has the code (e.g., -JM) */
17983 			/* Continue looking for -J<code> */
17984 			str[1] = GMT->init.history[id][0];
17985 			if ((id = gmt_get_option_id (id + 1, str)) == GMT_NOTSET) continue;	/* Not an option we have history for yet */
17986 			if (GMT->init.history[id] == NULL) continue;	/* No history for this option */
17987 		}
17988 		/* Here we should have a parsable command option */
17989 		err += gmt_parse_common_options (GMT, str, options[j], GMT->init.history[id]);
17990 	}
17991 	return ((err) ? GMT_PARSE_ERROR : GMT_NOERROR);
17992 }
17993 
gmt_add_R_if_modern_and_true(struct GMT_CTRL * GMT,const char * needs,bool do_it)17994 unsigned int gmt_add_R_if_modern_and_true (struct GMT_CTRL *GMT, const char *needs, bool do_it) {
17995 	if (strchr (needs, 'r') == NULL) return GMT_NOERROR;	/* -R is not a conditional option */
17996 	if (do_it)
17997 		return (gmt_set_missing_options (GMT, "R"));
17998 	return GMT_NOERROR;
17999 }
18000 
18001 /*! Changes the 4 GMT default pad values to given isotropic pad */
gmt_set_pad(struct GMT_CTRL * GMT,unsigned int pad)18002 void gmt_set_pad (struct GMT_CTRL *GMT, unsigned int pad) {
18003 	GMT->current.io.pad[XLO] = GMT->current.io.pad[XHI] = GMT->current.io.pad[YLO] = GMT->current.io.pad[YHI] = pad;
18004 }
18005 
18006 /*! . */
gmt_begin(struct GMTAPI_CTRL * API,const char * session,unsigned int pad)18007 struct GMT_CTRL *gmt_begin (struct GMTAPI_CTRL *API, const char *session, unsigned int pad) {
18008 	/* gmt_begin is called once by GMT_Create_Session and does basic
18009 	 * one-time initialization of GMT before the GMT modules take over.
18010 	 * It will load in the gmt.conf settings from the share dir and
18011 	 * reset them with the user's gmt.conf settings (if any).
18012 	 * It then does final processing of defaults so that all internal
18013 	 * GMT parameters are properly initialized and ready to go. This
18014 	 * means it is possible to write a functioning GMT application that
18015 	 * does not require the use of any GMT modules.  However,
18016 	 * most GMT applications will call various GMT modules and these
18017 	 * may need to process additional --PAR=value arguments. This will
18018 	 * require renewed processing of defaults and takes place in gmt_init_module
18019 	 * which is called at the start of all GMT modules.  This basically
18020 	 * performs a save/restore operation so that when the GMT module
18021 	 * returns the GMT structure is restored to its original values.
18022 	 */
18023 
18024 	struct GMT_CTRL *GMT = NULL;
18025 	char version[GMT_LEN8] = {""};
18026 #if defined(HAVE_GDAL) && (GDAL_VERSION_MAJOR >= 3)
18027 	char *path1 = NULL, *path2 = NULL, *paths[2] = {NULL, NULL};
18028 	int  local_count = 0;
18029 #endif
18030 
18031 	global_API = NULL;	/* Initialize global variable to NULL once in GMT_Create_Session.  Only used by gmtlib_terminate_session and assigned in gmt_manage_workflow */
18032 
18033 	gmt_M_memset (GMT_keyword_updated, GMT_N_KEYS, bool); /* Need to start with all set as false */
18034 
18035 #ifdef __FreeBSD__
18036 #ifdef _i386_
18037 	/* allow divide by zero -- Inf */
18038 	fpsetmask (fpgetmask () & ~(FP_X_DZ | FP_X_INV));
18039 #endif
18040 #endif
18041 
18042 #ifdef WIN32
18043 	/* Set all I/O to binary mode */
18044 	if ( _setmode(_fileno(stdin), _O_BINARY) == -1 ) {
18045 		if (API->external)
18046 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stdin. This may no be a fatal error but...\n");
18047 		else {
18048 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stdin.\n");
18049 			return NULL;
18050 		}
18051 	}
18052 	if ( _setmode(_fileno(stdout), _O_BINARY) == -1 ) {
18053 		if (API->external)
18054 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stdout. This may no be a fatal error but...\n");
18055 		else {
18056 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stdout.\n");
18057 			return NULL;
18058 		}
18059 	}
18060 	if ( _setmode(_fileno(stderr), _O_BINARY) == -1 ) {
18061 		if (API->external)
18062 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stderr. This may no be a fatal error but...\n");
18063 		else {
18064 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for stderr.\n");
18065 			return NULL;
18066 		}
18067 	}
18068 	if ( _set_fmode(_O_BINARY) != 0 ) {
18069 		if (API->external)
18070 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for file I/O. This may no be a fatal error but...\n");
18071 		else {
18072 			GMT_Report (API, GMT_MSG_WARNING, "Could not set binary mode for file I/O.\n");
18073 			return NULL;
18074 		}
18075 	}
18076 #endif
18077 
18078 	if ((GMT = gmtinit_new_GMT_ctrl (API, session, pad)) == NULL)	/* Allocate and initialize a new common control structure */
18079 		return NULL;
18080 
18081 	API->GMT = GMT;
18082 
18083 #if defined(HAVE_GDAL) && (GDAL_VERSION_MAJOR >= 3)
18084 	/* Here we allow users to declare a different location for the GDAL's GDAL_DATA and
18085 	   PROJ4 PROJ_LIB directories. The later may be particularly useful in these days of
18086 	   transition to GDAL3.0, which imply PROJ4 V6 but many (different) PROJ_LIB from PROJ4 < 6
18087 	   are still trailing around.
18088 	   If neither of those ENV var is provided, we default to GMT share/GDAL_DATA, if that
18089 	   dir exists. In case it does, then it's expected to contain both the files from GDAL's
18090 	   GDAL_DATA (data) and PROJ4 PRPJ_LIB (share). This will be the case on Windows where
18091 	   the installer will create and fill that directory.
18092 	*/
18093 	if ((path1 = getenv ("LOCAL_GDAL_DATA")) != NULL) paths[local_count++] = path1;
18094 	if ((path2 = getenv ("LOCAL_PROJ_LIB")) != NULL)  paths[local_count++] = path2;
18095 	if (!local_count) {			/* If none of the above was provided, default to share/GDAL_DATA */
18096 		char dir[PATH_MAX];
18097 		snprintf (dir, PATH_MAX, "%s/GDAL_DATA/n%s/proj", API->GMT->session.SHAREDIR, API->GMT->session.SHAREDIR);
18098 		if (access (dir, F_OK) == 0) {		/* ... if it exists */
18099 			paths[0] = strdup(dir);
18100 			local_count = -1;
18101 		}
18102 	}
18103 	if (local_count) {		/* Means we have a request to use custom GDAL/PROJ4 data dirs */
18104 		OSRSetPROJSearchPaths ((const char* const *)paths);
18105 		if (local_count < 0)  free (paths[0]);		/* This case was strdup allocated, so it can be freed */
18106 	}
18107 #endif
18108 
18109 	snprintf (version, GMT_LEN8, "GMT%d", GMT_MAJOR_VERSION);
18110 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: New_PSL_Ctrl\n");
18111 	GMT->PSL = New_PSL_Ctrl (version);		/* Allocate a PSL control structure */
18112 	GMT_Report (API, GMT_MSG_DEBUG, "Exit:  New_PSL_Ctrl\n");
18113 	if (!GMT->PSL) {
18114 		GMT_Report (API, GMT_MSG_ERROR, "Could not initialize PSL - Aborting.\n");
18115 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18116 		return NULL;
18117 	}
18118 
18119 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmt_manage_workflow\n");
18120 	if (gmt_manage_workflow (API, GMT_USE_WORKFLOW, NULL)) {
18121 		GMT_Report (API, GMT_MSG_ERROR, "Could not initialize the GMT workflow - Aborting.\n");
18122 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18123 		return NULL;
18124 	}
18125 	GMT_Report (API, GMT_MSG_DEBUG, "Exit : gmt_manage_workflow\n");
18126 
18127 	GMT->PSL->init.unit = PSL_INCH;		/* We use inches internally in PSL */
18128 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: PSL_beginsession\n");
18129 	if (PSL_beginsession (GMT->PSL, API->external, GMT->session.SHAREDIR, GMT->session.USERDIR)) {	/* Initializes the session and sets a few defaults */
18130 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18131 		return NULL;
18132 	}
18133 	GMT_Report (API, GMT_MSG_DEBUG, "Exit : PSL_beginsession\n");
18134 	/* Reset session defaults to the chosen GMT settings; these are fixed for the entire PSL session */
18135 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: PSL_setdefaults\n");
18136 	PSL_setdefaults (GMT->PSL, GMT->current.setting.ps_magnify, GMT->current.setting.ps_page_rgb, GMT->current.setting.ps_encoding.name);
18137 	GMT_Report (API, GMT_MSG_DEBUG, "Exit : PSL_setdefaults\n");
18138 
18139 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmtlib_io_init\n");
18140 	gmtlib_io_init (GMT);		/* Init the table i/o structure before parsing GMT defaults */
18141 	GMT_Report (API, GMT_MSG_DEBUG, "Exit : gmtlib_io_init\n");
18142 
18143 	gmtinit_init_unit_conversion (GMT);	/* Set conversion factors from various units to meters */
18144 
18145 	if (gmt_hash_init (GMT, keys_hashnode, GMT_keyword, GMT_N_KEYS, GMT_N_KEYS)) {	/* Initialize hash table for GMT defaults */
18146 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18147 		return NULL;
18148 	}
18149 
18150 	/* Set up hash table for colornames (used to convert <colorname> to <r/g/b>) */
18151 
18152 	if (gmt_hash_init (GMT, GMT->session.rgb_hashnode, gmt_M_color_name, GMT_N_COLOR_NAMES, GMT_N_COLOR_NAMES)) {
18153 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18154 		return NULL;
18155 	}
18156 
18157 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmt_reload_settings\n");
18158 	gmt_reload_settings (GMT);	/* Initialize the standard GMT system default settings and overload with user's settings */
18159 	GMT_Report (API, GMT_MSG_DEBUG, "Exit:  gmt_reload_settings\n");
18160 
18161 	if (API->runmode) GMT->current.setting.run_mode = GMT_MODERN;	/* Enforced at API Creation */
18162 
18163 	/* There is no longer a -m option in GMT so multi segments are now always true.
18164 	   However, in GMT_COMPAT mode the -mi and -mo options WILL turn off multi in the other direction. */
18165 	gmt_set_segmentheader (GMT, GMT_IN, true);
18166 	gmt_set_segmentheader (GMT, GMT_OUT, false);	/* Will be turned true when either of two situation arises: */
18167 	/* 1. We read a multisegment header
18168 	   2. The -g option is set which will create gaps and thus multiple segments
18169 	 */
18170 
18171 	/* Initialize the output and plot format machinery for ddd:mm:ss[.xxx] strings from the default format strings.
18172 	 * While this is also done in the default parameter loop it is possible that when a decimal plain format has been selected
18173 	 * the format_float_out string has not yet been processed.  We clear that up by processing again here. */
18174 
18175 	GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmtlib_plot_C_format\n");
18176 	gmtlib_geo_C_format (GMT);
18177 	gmtlib_plot_C_format (GMT);
18178 	GMT_Report (API, GMT_MSG_DEBUG, "Exit:  gmtlib_plot_C_format\n");
18179 
18180 	/* Set default for -n parameters */
18181 	GMT->common.n.antialias = true; GMT->common.n.interpolant = BCR_BICUBIC; GMT->common.n.threshold = 0.5;
18182 
18183 	gmtinit_get_history (GMT);	/* Process and store command shorthands passed to the application */
18184 
18185 	if (GMT->current.setting.io_gridfile_shorthand && gmtinit_setshorthand (GMT)) {	/* Load the short hand mechanism from gmt.io */
18186 		gmtinit_free_GMT_ctrl (GMT);	/* Deallocate control structure */
18187 		return NULL;
18188 	}
18189 
18190 	gmtlib_fft_initialization (GMT);	/* Determine which FFT algos are available and set pointers */
18191 
18192 	gmtinit_set_today (GMT);	/* Determine today's rata die value */
18193 
18194 	API->common_snapshot = gmt_M_memory (GMT, NULL, 1U, struct GMT_COMMON);	/* For holding snapshots of common options */
18195 
18196 	return (GMT);
18197 }
18198 
gmtlib_get_pos_of_filename(const char * url)18199 unsigned int gmtlib_get_pos_of_filename (const char *url) {
18200 	/* Takes an URL and finds start of the filename. If given just a filename it returns 0 */
18201 	size_t pos = strlen (url);
18202 	assert (pos > 0);
18203 	pos--;	/* Last character in name */
18204 	while (url[pos] && pos > 0 && url[pos] != '/') pos--;	/* Wind to first slash */
18205 	if (url[pos] == '/') pos++;	/* First letter after last slash */
18206 	if (url[pos] == '@') pos++;	/* Step over leading @ for cache files */
18207 	return (unsigned int)pos;
18208 }
18209 
18210 /*! . */
gmt_check_filearg(struct GMT_CTRL * GMT,char option,char * file,unsigned int direction,unsigned int family)18211 bool gmt_check_filearg (struct GMT_CTRL *GMT, char option, char *file, unsigned int direction, unsigned int family) {
18212 	/* Return true if a file arg was given and, if direction is GMT_IN, check that the file
18213 	 * exists and is readable. If remote we try to download the file first. Otherwise we return false. */
18214 	char message[GMT_LEN16] = {""};
18215 	if (option == GMT_OPT_INFILE)
18216 		sprintf (message, "for input file");
18217 	else if (option == GMT_OPT_OUTFILE)
18218 		sprintf (message, "for output file");
18219 	else
18220 		snprintf (message, GMT_LEN16, "by option -%c", option);
18221 
18222 	if (!file || file[0] == '\0') {
18223 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No filename provided %s\n", message);
18224 		return false;	/* No file given */
18225 	}
18226 	if (direction == GMT_OUT) return true;		/* Cannot check any further */
18227 
18228 	if (GMT_Get_FilePath (GMT->parent, family, direction, GMT_FILE_REMOTE|GMT_FILE_CHECK, &file))
18229 		return false;	/* No file given */
18230 
18231 	return true;	/* Seems OK */
18232 }
18233 
18234 #ifdef SET_IO_MODE
18235 
18236 /* Under non-Unix operating systems running on the PC, such as
18237  * Windows, files are opened in either TEXT or BINARY mode.
18238  * This difference does not exist under UNIX, but is important
18239  * on the PC.  Specifically, it causes a problem when a program
18240  * that writes/reads standard i/o wants to use binary data.
18241  * In those situations we must change the default (TEXT) mode of
18242  * the file handle to BINARY via a call to "setmode".
18243  *
18244  * This can also be done under Win32 with the Microsoft VC++
18245  * compiler which supports ANSI-C (P. Wessel).  This may be true
18246  * of other Win32 compilers as well.
18247  */
18248 
18249 /*! Changes the stream to deal with BINARY rather than TEXT data */
gmt_setmode(struct GMT_CTRL * GMT,int direction)18250 void gmt_setmode (struct GMT_CTRL *GMT, int direction) {
18251 
18252 	FILE *fp = NULL;
18253 	static const char *IO_direction[2] = {"Input", "Output"};
18254 
18255 	if (GMT->common.b.active[direction]) {	/* User wants native binary i/o */
18256 
18257 		fp = (direction == 0) ? GMT->session.std[GMT_IN] : GMT->session.std[GMT_OUT];
18258 		fflush (fp);	/* Should be untouched but anyway... */
18259 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Set binary mode for %s\n", IO_direction[direction]);
18260 		setmode (fileno (fp), _O_BINARY);
18261 	}
18262 }
18263 
18264 #endif	/* SET_IO_MODE */
18265 
18266 /*! . */
gmt_message(struct GMT_CTRL * GMT,char * format,...)18267 int gmt_message (struct GMT_CTRL *GMT, char *format, ...) {
18268 	char line[GMT_BUFSIZ];
18269 	va_list args;
18270 	if (GMT->current.setting.verbose == GMT_MSG_QUIET) return 0;	/* Nothing should be printed if -Vq is used */
18271 	va_start (args, format);
18272 	vsnprintf (line, GMT_BUFSIZ, format, args);
18273 	GMT->parent->print_func (GMT->session.std[GMT_ERR], line);
18274 	va_end (args);
18275 	return (0);
18276 }
18277 
18278 /*! . */
gmtlib_report_func(struct GMT_CTRL * GMT,unsigned int level,const char * source_line,const char * format,...)18279 int gmtlib_report_func (struct GMT_CTRL *GMT, unsigned int level, const char *source_line, const char *format, ...) {
18280 	char message[GMT_BUFSIZ];
18281 	const char *module_name;
18282 	size_t source_info_len;
18283 	va_list args;
18284 	if (level > GMT->current.setting.verbose)
18285 		return 0;
18286 	module_name = ((GMT->current.setting.run_mode == GMT_MODERN)) ? gmtlib_get_active_name (GMT->parent, GMT->init.module_name) : GMT->init.module_name;
18287 	snprintf (message, GMT_BUFSIZ, "%s (%s): ", module_name, source_line);
18288 	source_info_len = strlen (message);
18289 	va_start (args, format);
18290 	/* append format to the message: */
18291 	vsnprintf (message + source_info_len, GMT_BUFSIZ - source_info_len, format, args);
18292 	va_end (args);
18293 	GMT->parent->print_func (GMT->session.std[GMT_ERR], message);
18294 	return 1;
18295 }
18296 
gmt_remove_dir(struct GMTAPI_CTRL * API,char * dir,bool recreate)18297 int gmt_remove_dir (struct GMTAPI_CTRL *API, char *dir, bool recreate) {
18298 	/* Delete all files in a directory, then the directory itself.
18299 	 * It is assumed that we created the directory so that there is only
18300 	 * one level, i.e., there are no sub-directories inside the directory. */
18301 	unsigned int n_files, k;
18302 	int error = GMT_NOERROR;
18303 	char **filelist = NULL, *here = NULL;
18304 	struct GMT_CTRL *GMT = API->GMT;	/* Shorthand */
18305 	if (access (dir, F_OK)) {
18306 		GMT_Report (API, GMT_MSG_ERROR, "No directory named %s\n", dir);
18307 		return GMT_FILE_NOT_FOUND;
18308 	}
18309 	if ((here = getcwd (NULL, 0)) == NULL) {	/* Get the current directory */
18310 		GMT_Report (API, GMT_MSG_ERROR, "Cannot determine current directory!\n");
18311 		return GMT_RUNTIME_ERROR;
18312 	}
18313 	if (chdir (dir)) {
18314 		perror (dir);
18315 		return GMT_RUNTIME_ERROR;
18316 	}
18317 	if ((n_files = (unsigned int)gmtlib_glob_list (GMT, "*", &filelist))) {
18318 		for (k = 0; k < n_files; k++) {
18319 			if (gmt_remove_file (GMT, filelist[k]))
18320 				GMT_Report (API, GMT_MSG_WARNING, "Unable to remove %s [permissions?]\n", filelist[k]);
18321 		}
18322 		gmt_free_list (GMT, filelist, n_files);	/* Free the file list */
18323 	}
18324 	if (chdir (here)) {		/* Get back to where we were */
18325 		perror (here);
18326 		gmt_M_str_free (here);
18327 		return GMT_RUNTIME_ERROR;
18328 	}
18329 	gmt_M_str_free (here);
18330 	if (!recreate && rmdir (dir)) {	/* Unable to delete the directory */
18331 		perror (dir);
18332 		error = GMT_RUNTIME_ERROR;
18333 	}
18334 	return error;
18335 }
18336 
gmtinit_open_figure_file(struct GMTAPI_CTRL * API,unsigned int mode,int * err)18337 GMT_LOCAL FILE *gmtinit_open_figure_file (struct GMTAPI_CTRL *API, unsigned int mode, int *err) {
18338 	char line[PATH_MAX] = {""}, *opt[2] = {"r", "a"};
18339 	FILE *fp = NULL;
18340 	snprintf (line, PATH_MAX, "%s/gmt.figures", API->gwf_dir);	/* Path to gmt.figures */
18341 	*err = 0;	/* No error yet */
18342 	if (mode == 0 && access (line, F_OK)) {
18343 		GMT_Report (API, GMT_MSG_DEBUG, "No figure file %s - nothing to do\n", line);
18344 		return NULL;
18345 	}
18346 	/* Here the file gmt.figures exists and we must read in the entries */
18347 	if ((fp = fopen (line, opt[mode])) == NULL) {
18348 		if (mode == 1) {	/* Failure to open existing or create new gmt.figures file */
18349 			GMT_Report (API, GMT_MSG_ERROR, "Unable to open/create %s with mode %s\n", line, opt[mode]);
18350 			*err = 1;	/* No error yet */
18351 		}
18352 	}
18353 	return fp;
18354 }
18355 
gmt_get_graphics_id(struct GMT_CTRL * GMT,const char * format)18356 int gmt_get_graphics_id (struct GMT_CTRL *GMT, const char *format) {
18357 	int code = 0;
18358 	gmt_M_unused(GMT);
18359 	while (gmt_session_format[code] && strncmp (format, gmt_session_format[code], strlen (gmt_session_format[code])))
18360 		code++;
18361 	return (gmt_session_format[code]) ? code : GMT_NOTSET;
18362 }
18363 
18364 /*! . */
gmtinit_get_session_name_format(struct GMTAPI_CTRL * API,char prefix[GMT_LEN256],char formats[GMT_LEN256])18365 GMT_LOCAL void gmtinit_get_session_name_format (struct GMTAPI_CTRL *API, char prefix[GMT_LEN256], char formats[GMT_LEN256]) {
18366 	/* Read the session name [and graphics format] from file GMT_SESSION_FILE */
18367 	int n;
18368 	char file[PATH_MAX] = {""};
18369 	FILE *fp = NULL;
18370 	snprintf (file, PATH_MAX, "%s/%s", API->gwf_dir, GMT_SESSION_FILE);
18371 	if (access (file, F_OK)) {	/* Use default session name and format */
18372 		strcpy (prefix, GMT_SESSION_NAME);
18373 		strcpy (formats, gmt_session_format[API->GMT->current.setting.graphics_format]);
18374 		return;
18375 	}
18376 	if ((fp = fopen (file, "r")) == NULL) {
18377 		GMT_Report (API, GMT_MSG_ERROR, "Failed to open session file %s\n", file);
18378 		return;
18379 	}
18380 	/* Recycle file as line record */
18381 	gmt_fgets (API->GMT, file, PATH_MAX, fp);
18382 	gmt_chop (file);	/* Strip off trailing return */
18383 	if ((n = sscanf (file, "%s %s\n", prefix, formats)) < 1) {
18384 		GMT_Report (API, GMT_MSG_ERROR, "Failed to read from session file %s\n", file);
18385 		fclose (fp);
18386 		return;
18387 	}
18388 	if (n == 1)	/* Assign default format */
18389 		strcpy (formats, gmt_session_format[API->GMT->current.setting.graphics_format]);
18390 	gmt_filename_get (prefix);
18391 	GMT_Report (API, GMT_MSG_DEBUG, "Got session name as %s and default graphics formats as %s\n", prefix, formats);
18392 	fclose (fp);
18393 }
18394 
18395 /*! . */
gmtinit_read_figures(struct GMT_CTRL * GMT,unsigned int mode,struct GMT_FIGURE ** figs)18396 GMT_LOCAL int gmtinit_read_figures (struct GMT_CTRL *GMT, unsigned int mode, struct GMT_FIGURE **figs) {
18397 	/* Load or count any figures stored in the queue file gmt.figures.
18398 	 * mode = 0:	Count how many figures in the queue.
18399 	 * mode = 1:	Also return array of GMT_FIGURE structs.
18400 	 * mode = 2:	As 1 but insert session figure in the list.
18401 	 * If there are errors encountered then report them and return -1.
18402 	 * Else we return how many found and the pointer figs. */
18403 	char line[PATH_MAX] = {""};
18404 	unsigned int n_alloc = GMT_TINY_CHUNK, k = 0;
18405 	int err, n;
18406 	struct GMT_FIGURE *fig = NULL;
18407 	FILE *fp = NULL;
18408 
18409 	if ((fp = gmtinit_open_figure_file (GMT->parent, 0, &err)) == NULL && mode < 2) {	/* No such file, nothing to do */
18410 		return 0;
18411 	}
18412 	/* Here the file gmt.figures may exists and we must read in the entries */
18413 	if (mode > 0) fig = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_FIGURE);
18414 	if (mode == 2) {	/* Insert default session figure */
18415 		gmtinit_get_session_name_format (GMT->parent, fig[0].prefix, fig[0].formats);
18416 		fig[0].ID = 0;	k = 1;
18417 	}
18418 	while (fp && fgets (line, PATH_MAX, fp)) {
18419 		if (line[0] == '#' || line[0] == '\n')
18420 			continue;
18421 		if (mode >= 1) {
18422 			gmt_chop (line);
18423 			if ((n = sscanf (line, "%d %s %s %s", &fig[k].ID, fig[k].prefix, fig[k].formats, fig[k].options)) < 3) {
18424 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to read from figure file\n");
18425 				fclose (fp);
18426 				gmt_M_free (GMT, fig);
18427 				return 0;
18428 			}
18429 			if (n == 3) fig[k].options[0] = '\0';
18430 			if (++k >= n_alloc) {
18431 				n_alloc += GMT_TINY_CHUNK;
18432 				fig = gmt_M_memory (GMT, fig, n_alloc, struct GMT_FIGURE);
18433 			}
18434 		}
18435 		else	/* Just count them */
18436 			k++;
18437 	}
18438 	if (fp) fclose (fp);
18439 	if (mode > 0) {
18440 		if (k == 0)	/* Blank file, nothing to report */
18441 			gmt_M_free (GMT, fig);
18442 		else if (k < n_alloc)
18443 			fig = gmt_M_memory (GMT, fig, k, struct GMT_FIGURE);
18444 		*figs = fig;	/* Pass out what we found */
18445 	}
18446 	return (k);
18447 }
18448 
gmtlib_fig_is_ps(struct GMT_CTRL * GMT)18449 bool gmtlib_fig_is_ps (struct GMT_CTRL *GMT) {
18450 	int n_figs;
18451 	unsigned int pos = 0;
18452 	bool PS = false;
18453 	char p[GMT_LEN64] = {""};
18454 	struct GMT_FIGURE *fig = NULL;
18455 
18456 	if ((n_figs = gmtinit_read_figures (GMT, 2, &fig)) == GMT_NOTSET) {
18457 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to determine number of figures\n");
18458 		return false;
18459 	}
18460 	n_figs--;	/* Id of current figure */
18461 	while (gmt_strtok (fig[n_figs].formats, ",", &pos, p)) {	/* Check each format to make sure each is OK */
18462 		if (!strcmp (p, "ps")) PS = true;
18463 	}
18464 	if (!PS && strchr (fig[n_figs].options, 'P')) PS = true;	/* Down want square paper size when P is given explicitly */
18465 	gmt_M_free (GMT, fig);
18466 	return (PS);
18467 }
18468 
gmtinit_put_session_name(struct GMTAPI_CTRL * API,char * arg)18469 GMT_LOCAL int gmtinit_put_session_name (struct GMTAPI_CTRL *API, char *arg) {
18470 	/* Write the session name to file GMT_SESSION_FILE unless default. */
18471 	FILE *fp = NULL;
18472 	char file[PATH_MAX] = {""}, *c = NULL;
18473 	bool restore = false;
18474 
18475 	if (arg == NULL) return GMT_NOERROR;	/* Nothing to do, which means we accept the defaults */
18476 	if (arg[0] == '\0') return GMT_NOERROR;	/* Nothing to do, which means we accept the defaults */
18477 	GMT_Report (API, GMT_MSG_DEBUG, "Set session name to be %s\n", arg);
18478 	snprintf (file, PATH_MAX, "%s/%s", API->gwf_dir, GMT_SESSION_FILE);
18479 	if ((fp = fopen (file, "w")) == NULL) {
18480 		GMT_Report (API, GMT_MSG_ERROR, "Failed to create session file %s\n", file);
18481 		return GMT_ERROR_ON_FOPEN;
18482 	}
18483 	if ((c = strrchr (arg, ' ')) && c[1] && gmt_char_count (arg, ' ') > 1) {	/* Determine if last arg is psconvert options or not */
18484 		unsigned int bad = 0, pos = 0;
18485 		char p[GMT_LEN256] = {""};
18486 		while (gmt_strtok (&c[1], ",", &pos, p)) {	/* Check args to determine what kind it is */
18487 			if (!strchr (GMT_PSCONVERT_LIST, p[0])) {	/* Check if valid psconvert options */
18488 				bad++;
18489 			}
18490 		}
18491 		if (bad == 0 && strcmp (API->GMT->current.setting.ps_convert, &c[1])) {	/* Got psconvert options, and if different we update defaults */
18492 			strncpy (API->GMT->current.setting.ps_convert, &c[1], GMT_LEN256-1);
18493 			GMT_keyword_updated[GMTCASE_PS_CONVERT] = true;	/* To make sure it will write it to gmt.conf */
18494 			c[0] = '\0';
18495 			restore = true;
18496 		}
18497 	}
18498 	fprintf (fp, "%s\n", arg);
18499 	if (restore) c[0] = ' ';
18500 	fclose (fp);
18501 	return GMT_NOERROR;
18502 }
18503 
gmtinit_get_graphics_formats(struct GMT_CTRL * GMT,char * formats,char fmt[],int gcode[],int * quality,int * monochrome)18504 GMT_LOCAL int gmtinit_get_graphics_formats (struct GMT_CTRL *GMT, char *formats, char fmt[], int gcode[], int *quality, int *monochrome) {
18505 	/* Count up how many graphics formats were selected and what their codes were.
18506 	 * We know the arguments are all valid formats since checked by figure or begin */
18507 	int k, n = 0;
18508 	unsigned int pos = 0;
18509 	char p[GMT_LEN32] = {""}, *c = NULL;
18510 
18511 	while ((gmt_strtok (formats, ",", &pos, p))) {
18512 		if ((k = gmt_get_graphics_id (GMT, p)) != GMT_NOTSET) {	/* Valid code */
18513 			gcode[n] = k;
18514 			fmt[n++] = gmt_session_code[k];
18515 			if (gmt_session_code[k] == 'j' && (c = strstr (p, "+q"))) {	/* Check for modifier =q for JPG */
18516 				*quality = atoi (&c[2]);
18517 				if (*quality < 0 || *quality > 100) {
18518 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad JPEG quality argument (%s} - reset to %d\n", c, GMT_JPEG_DEF_QUALITY);
18519 					*quality = GMT_JPEG_DEF_QUALITY;
18520 				}
18521 			}
18522 			if (strchr ("bgjt", gmt_session_code[k]) && strstr (p, "+m"))	/* Want monochrome image */
18523 				*monochrome = 1;
18524 		}
18525 	}
18526 	return (n);
18527 }
18528 
gmtinit_check_if_autosize(struct GMTAPI_CTRL * API,int ID)18529 GMT_LOCAL bool gmtinit_check_if_autosize (struct GMTAPI_CTRL *API, int ID) {
18530 	/* Check if the BoundingBox line in the half-baked PostScript file has max dimension (32767x32767)
18531 	 * which we used to enforce automatic cropping to actual size [and possible extra margins] */
18532 	char file[PATH_MAX] = {""};
18533 	FILE *fp;
18534 	snprintf (file, PATH_MAX, "%s/gmt_%d.ps-", API->gwf_dir, ID);	/* Current half-baked PostScript file */
18535 	if ((fp = fopen (file, "r")) == NULL) {	/* This is an unmitigated disaster */
18536 		GMT_Report (API, GMT_MSG_ERROR, "Failed to open half-baked PostScript file %s\n", file);
18537 		return false;
18538 	}
18539 	gmt_fgets (API->GMT, file, PATH_MAX, fp);	/* Skip first line */
18540 	gmt_fgets (API->GMT, file, PATH_MAX, fp);	/* Get second line with BoundingBox code */
18541 	fclose (fp);
18542 	if (strstr (file, "32767 32767")) return true;	/* Max paper size means auto-sized media */
18543 	return false;
18544 }
18545 
gmtinit_A_was_given(char * text)18546 GMT_LOCAL bool gmtinit_A_was_given (char *text) {
18547 	/* Determine if A is one of the arguments */
18548 	size_t k = 1;
18549 	if (!text || !text[0]) return false;	/* No args means -A was not given */
18550 	if (text[0] == 'A') return true;	/* A was given as first option */
18551 	while (text[k]) {
18552 		if (text[k] == 'A' && text[k-1] == ',') return true;	/* Found A as first letter after comma */
18553 		k++;
18554 	}
18555 	return false;
18556 }
18557 
gmtinit_cannot_crop(char * p)18558 GMT_LOCAL bool gmtinit_cannot_crop (char *p) {
18559 	if (p[0] != 'A') return false;	/* Not -A option */
18560 	if (strstr (p, "+n")) return false;	/* No cropping allowed */
18561 	if (p[1] == '+') return false;	/* Gave a modifier unrelated to cropping */
18562 	return true;	/* Cannot do cropping via -A when a specific media size was given */
18563 }
18564 
gmtinit_process_figures(struct GMTAPI_CTRL * API,char * show)18565 GMT_LOCAL int gmtinit_process_figures (struct GMTAPI_CTRL *API, char *show) {
18566 	/* Loop over all registered figures and their selected formats and
18567 	 * convert the hidden PostScript figures to selected graphics.
18568 	 * If show is not NULL then we display the first graphics listed (if more than one) via gmt docs */
18569 
18570 	char cmd[GMT_BUFSIZ] = {""}, fmt[GMT_LEN16] = {""}, option[GMT_LEN256] = {""}, p[GMT_LEN256] = {""}, dir[PATH_MAX] = {""}, legend_justification[4] = {""}, mark;
18571 	char pen[GMT_LEN32] = {""}, fill[GMT_LEN32] = {""}, off[GMT_LEN32] = {""}, device_extra[GMT_LEN8] = {""}, *do_gray[2] = {"", "+m"};
18572 	char *copy = NULL, *ptr = NULL, *format = NULL, *c = NULL;
18573 	struct GMT_FIGURE *fig = NULL;
18574 	bool not_PS, auto_size;
18575 	int error, k, f, nf, n_figs, n_orig, gcode[GMT_LEN16], jpeg_quality = GMT_JPEG_DEF_QUALITY, monochrome = 0;
18576 	unsigned int pos = 0;
18577 	double legend_width = 0.0, legend_scale = 1.0;
18578 
18579  	if (API->gwf_dir == NULL) {
18580 		GMT_Report (API, GMT_MSG_ERROR, "No workflow directory set\n");
18581 		return GMT_NOT_A_VALID_DIRECTORY;
18582 	}
18583 
18584 	if ((n_orig = gmtinit_read_figures (API->GMT, 0, NULL)) == GMT_NOTSET) {
18585 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open gmt.figures for reading\n");
18586 		return GMT_ERROR_ON_FOPEN;
18587 	}
18588 	if ((n_figs = gmtinit_read_figures (API->GMT, 2, &fig)) == GMT_NOTSET) {	/* Auto-insert the hidden gmt_0.ps- file which may not have been used */
18589 		GMT_Report (API, GMT_MSG_ERROR, "Unable to open gmt.figures for reading\n");
18590 		return GMT_ERROR_ON_FOPEN;
18591 	}
18592 
18593 	if (n_figs) GMT_Report (API, GMT_MSG_INFORMATION, "Process GMT figure queue: %d figures found\n", n_figs);
18594 
18595 	for (k = 0; k < n_figs; k++) {
18596 		if (!strcmp (fig[k].prefix, "-")) continue;	/* Unnamed outputs are left for manual psconvert calls by external APIs */
18597 		GMT_Report (API, GMT_MSG_INFORMATION, "Processing GMT figure #%d [%s %s %s]\n", fig[k].ID, fig[k].prefix, fig[k].formats, fig[k].options);
18598 		/* Go through the format list and build array for -T arguments */
18599 		nf = gmtinit_get_graphics_formats (API->GMT, fig[k].formats, fmt, gcode, &jpeg_quality, &monochrome);
18600 		if (n_orig && k) {	/* Specified one or more figs via gmt figure so must switch to the current figure and update the history */
18601 			if ((error = GMT_Call_Module (API, "figure", GMT_MODULE_CMD, fig[k].prefix))) {
18602 				GMT_Report (API, GMT_MSG_ERROR, "Failed to switch to figure%s\n", fig[k].prefix);
18603 				gmt_M_free (API->GMT, fig);
18604 				return error;
18605 			}
18606 			gmtinit_get_history (API->GMT);	/* Make sure we have the latest history for this figure */
18607 		}
18608 		copy = strdup (fig[k].formats);	ptr = copy;
18609 		for (f = 0; f < nf; f++) {	/* Loop over all desired output formats */
18610 			format = strsep (&ptr, ",");	/* Name of next format as user specified it */
18611 			device_extra[0] = '\0';	/* Reset device arguments */
18612 			if (fmt[f] == 'j' && jpeg_quality != GMT_JPEG_DEF_QUALITY)
18613 				sprintf (device_extra, "+q%d", jpeg_quality);	/* Need to pass quality modifier */
18614 			mark = '-';	/* This is the last char in extension for a half-baked GMT PostScript file */
18615 			snprintf (cmd, GMT_BUFSIZ, "%s/gmt_%d.ps%c", API->gwf_dir, fig[k].ID, mark);	/* Check if the file exists */
18616 			if (access (cmd, F_OK)) {	/* No such file, check if the fully baked file is there instead */
18617 				mark = '+';	/* This is the last char in extension for a fully-baked GMT PostScript file */
18618 				snprintf (cmd, GMT_BUFSIZ, "%s/gmt_%d.ps%c", API->gwf_dir, fig[k].ID, mark);	/* Check if the file exists */
18619 				if (access (cmd, F_OK)) {	/* No such file ether, give up; warn if a fig set via gmt figure (k > 0) and it is not the movie_background case which may not have a plot to go with it */
18620 					if (k && strcmp (fig[k].prefix, "movie_background")) GMT_Report (API, GMT_MSG_WARNING, "Figure # %d (%s) was registered but no matching PostScript-|+ file found - skipping\n", fig[k].ID, fig[k].prefix);
18621 					continue;
18622 				}
18623 			}
18624 			if (gmt_get_legend_info (API, &legend_width, &legend_scale, legend_justification, pen, fill, off)) {	/* Unplaced legend file */
18625 				/* Default to white legend with 1p frame offset 0.2 cm from selected justification point [TR] */
18626 				bool active = API->GMT->common.l.active;	/* Must temporarily turn off -l since should not be passed to legend and plot */
18627 				API->GMT->common.l.active = false;
18628 				snprintf (cmd, GMT_BUFSIZ, "-Dj%s+w%gi+o%s -F+p%s+g%s -S%g", legend_justification, legend_width, off, pen, fill, legend_scale);
18629 				if ((error = GMT_Call_Module (API, "legend", GMT_MODULE_CMD, cmd))) {
18630 					GMT_Report (API, GMT_MSG_ERROR, "Failed to place auto-legend on figure %s\n", fig[k].prefix);
18631 					gmt_M_free (API->GMT, fig);
18632 					gmt_M_str_free (copy);
18633 					return error;
18634 				}
18635 				API->GMT->common.l.active = active;
18636 			}
18637 
18638 			/* Here the file exists and we can call psconvert. Note we still pass *.ps- even if *.ps+ was found since psconvert will do the same check */
18639 			gmt_filename_set (fig[k].prefix);
18640 			if ((c = strrchr (fig[k].prefix, '/'))) {	/* Must pass any leading directory in the file name via -D and file prefix via -F */
18641 				char *file_only = &c[1];	/* The file name */
18642 				c[0] = '\0';		/* Temporarily chop off the file to yield directory only */
18643 				snprintf (cmd, GMT_BUFSIZ, "'%s/gmt_%d.ps-' -T%c%s%s -D%s -F%s", API->gwf_dir, fig[k].ID, fmt[f], device_extra, do_gray[monochrome], fig[k].prefix, file_only);
18644 				c[0] = '/';		/* Restore last slash */
18645 			}
18646 			else	/* Place products in current directory */
18647 				snprintf (cmd, GMT_BUFSIZ, "'%s/gmt_%d.ps-' -T%c%s%s -F%s", API->gwf_dir, fig[k].ID, fmt[f], device_extra, do_gray[monochrome], fig[k].prefix);
18648 			gmt_filename_get (fig[k].prefix);
18649 			not_PS = (fmt[f] != 'p');	/* Do not add convert options if plain PS */
18650 			/* Append psconvert optional settings */
18651 			dir[0] = '\0';	/* No directory via D<dir> convert option */
18652 			auto_size = gmtinit_check_if_autosize (API, fig[k].ID);	/* Determine if the PostScript file has auto size enabled */
18653 			if (fig[k].options[0]) {	/* Append figure-specific psconvert settings */
18654 				pos = 0;	/* Reset position counter */
18655 				while ((gmt_strtok (fig[k].options, ",", &pos, p))) {
18656 					if (!strcmp (p, "A+n")) p[2] = 'M';	/* This means crop to media [deprecated] */
18657 					if (!auto_size && gmtinit_cannot_crop (p)) continue;	/* Cannot do cropping when a specific media size was given, unless crop is off via +n */
18658 					if (not_PS || p[0] == 'M') {	/* Only -M is allowed if PS is the format */
18659 						snprintf (option, GMT_LEN256, " -%s", p);	/* Create proper ps_convert option syntax */
18660 						strcat (cmd, option);
18661 						if (p[0] == 'D') strncpy (dir, &p[1], GMT_LEN1024-1);	/* Needed in show */
18662 					}
18663 				}
18664 				if (not_PS && !gmtinit_A_was_given (fig[k].options)) {	/* -A not given and not PostScript */
18665 					strcat (cmd, (auto_size) ? " -A" : " -A+M");	/* Must always add -A unless when media size is given */
18666 				}
18667 			}
18668 			else if (API->GMT->current.setting.ps_convert[0]) {	/* Supply chosen session settings for psconvert */
18669 				pos = 0;	/* Reset position counter */
18670 				while ((gmt_strtok (API->GMT->current.setting.ps_convert, ",", &pos, p))) {
18671 					if (!strcmp (p, "A+n")) p[2] = 'M';	/* This means crop to media */
18672 					if (!auto_size && gmtinit_cannot_crop (p)) continue;	/* Cannot do cropping when a specific media size was given */
18673 					if (not_PS || p[0] == 'M') {	/* Only -M is allowed if PS is the formst */
18674 						snprintf (option, GMT_LEN256, " -%s", p);	/* Create proper ps_convert option syntax */
18675 						strcat (cmd, option);
18676 						if (p[0] == 'D') strcpy (dir, &p[1]);	/* Needed in show */
18677 					}
18678 				}
18679 				if (not_PS && !gmtinit_A_was_given (API->GMT->current.setting.ps_convert)) {	/* -A not given and not PostScript */
18680 					strcat (cmd, (auto_size) ? " -A" : " -A+M");	/* Must always add -A unless when media size is given */
18681 				}
18682 			}
18683 			else if (not_PS && auto_size) /* No specific settings but must always add -A if not PostScript unless when media size is given */
18684 				strcat (cmd, " -A");
18685 			GMT_Report (API, GMT_MSG_DEBUG, "psconvert: %s\n", cmd);
18686 			if ((error = GMT_Call_Module (API, "psconvert", GMT_MODULE_CMD, cmd))) {
18687 				GMT_Report (API, GMT_MSG_ERROR, "Failed to call psconvert\n");
18688 				gmt_M_free (API->GMT, fig);
18689 				gmt_M_str_free (copy);
18690 				return error;
18691 			}
18692 			if (!strncmp (format, "jpeg", 4U) || !strncmp (format, "tiff", 4U)) {	/* Must rename file to have .jpeg or .tiff extensions */
18693 				/* Since psconvert cannot tell from j and t if the extensions should be 3 or 4 characters... */
18694 				char old_name[PATH_MAX] = {""}, new_name[PATH_MAX] = {""}, ext[GMT_LEN8] = {""};
18695 				strcpy (ext, format);	/* Set extension */
18696 				ext[2] = ext[3];	ext[3] = '\0';	/* Shorten to what psconvert used */
18697 				if (dir[0]) {
18698 					snprintf (old_name, PATH_MAX, "%s/%s.%s", dir, fig[k].prefix, ext);
18699 					snprintf (new_name, PATH_MAX, "%s/%s.%s", dir, fig[k].prefix, format);
18700 				}
18701 				else {
18702 					snprintf (old_name, PATH_MAX, "%s.%s", fig[k].prefix, ext);
18703 					snprintf (new_name, PATH_MAX, "%s.%s", fig[k].prefix, format);
18704 				}
18705 				if (gmt_rename_file (API->GMT, old_name, new_name, GMT_RENAME_FILE))	/* Rename newly copied file to existing file */
18706 					GMT_Report (API, GMT_MSG_WARNING, "Failed to rename file from %s to %s\n", old_name, new_name);
18707 			}
18708 
18709 			if (show && f == 0) {	/* Open the first plot in the viewer via call to gmt docs */
18710 				size_t start = 0, end = strlen (fig[k].prefix) - 1;
18711 				char ext[GMT_LEN8] = {""};
18712 				strcpy (ext, gmt_session_format[gcode[f]]);	/* Set extension */
18713 				gmt_str_tolower (ext);	/* In case it was PNG */
18714 				/* File names with spaces will be given in single quotes - remove those here when making single command string */
18715 				if (fig[k].prefix[0] == '\'') start = 1, fig[k].prefix[end] = '\0';	/* Remove the quote */
18716 				if (dir[0])
18717 					snprintf (cmd, GMT_BUFSIZ, "%s/%s.%s", dir, &fig[k].prefix[start], ext);
18718 				else
18719 					snprintf (cmd, GMT_BUFSIZ, "%s.%s", &fig[k].prefix[start], ext);
18720 				if (fig[k].prefix[0] == '\'') fig[k].prefix[end] = '\'';	/* Restore the quote */
18721 				gmt_filename_set (cmd);	/* Protect filename spaces by substitution */
18722 				if ((error = GMT_Call_Module (API, "docs", GMT_MODULE_CMD, cmd))) {
18723 					GMT_Report (API, GMT_MSG_ERROR, "Failed to call docs\n");
18724 					gmt_M_free (API->GMT, fig);
18725 					gmt_M_str_free (copy);
18726 					return error;
18727 				}
18728 			}
18729 		}
18730 		gmt_M_str_free (copy);
18731 	}
18732 	gmt_M_free (API->GMT, fig);
18733 
18734 	return GMT_NOERROR;
18735 }
18736 
gmtinit_set_current_figure(struct GMTAPI_CTRL * API,char * prefix,int this_k)18737 GMT_LOCAL int gmtinit_set_current_figure (struct GMTAPI_CTRL *API, char *prefix, int this_k) {
18738 	/* Write the current figure number and prefix to the gmt.current file */
18739 	FILE *fp = NULL;
18740 	char file[PATH_MAX] = {""};
18741 	if (API->gwf_dir == NULL) {
18742 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_set_current_figure: No workflow directory set\n");
18743 		return GMT_NOT_A_VALID_DIRECTORY;
18744 	}
18745 	snprintf (file, PATH_MAX, "%s/gmt.current", API->gwf_dir);
18746 	if ((fp = fopen (file, "w")) == NULL) {
18747 		GMT_Report (API, GMT_MSG_ERROR, "gmtinit_set_current_figure: Could not create file %s\n", file);
18748 		return GMT_ERROR_ON_FOPEN;
18749 	}
18750 	fprintf (fp, "%d\t%s\n", this_k, prefix);
18751 	fclose (fp);
18752 	return GMT_NOERROR;
18753 }
18754 
gmt_get_current_figure(struct GMTAPI_CTRL * API)18755 int gmt_get_current_figure (struct GMTAPI_CTRL *API) {
18756 	/* Get the current figure number and prefix from the gmt.current file */
18757 	FILE *fp = NULL;
18758 	int fig_no = 0;
18759 	char file[PATH_MAX] = {""};
18760 	if (API->gwf_dir == NULL) {
18761 		GMT_Report (API, GMT_MSG_ERROR, "gmt_get_current_figure: No workflow directory set\n");
18762 		return GMT_NOT_A_VALID_DIRECTORY;
18763 	}
18764 	snprintf (file, PATH_MAX, "%s/gmt.current", API->gwf_dir);
18765 	/* See if there is a gmt.current file to read */
18766 	if (access (file, R_OK))	/* No gmt.current file available so return 0 */
18767 		return fig_no;
18768 	/* Dealing with many figures so get the current one */
18769 	if ((fp = fopen (file, "r")) == NULL) {
18770 		GMT_Report (API, GMT_MSG_ERROR, "gmt_get_current_figure: Could not open file %s\n", file);
18771 		return GMT_ERROR_ON_FOPEN;
18772 	}
18773 	if (fscanf (fp, "%d", &fig_no) != 1) {
18774 		GMT_Report (API, GMT_MSG_ERROR, "gmt_get_current_figure: Could not read fig number from file %s\n", file);
18775 		fclose (fp);
18776 		return GMT_DATA_READ_ERROR;
18777 	}
18778 	fclose (fp);
18779 	return (fig_no);
18780 }
18781 
gmtlib_get_graphics_item(struct GMTAPI_CTRL * API,int * fig,int * subplot,char * panel,int * inset)18782 void gmtlib_get_graphics_item (struct GMTAPI_CTRL *API, int *fig, int *subplot, char *panel, int *inset) {
18783 	/* Determine figure number, subplot panel, inset for current graphics item */
18784 	*fig = gmt_get_current_figure (API);	/* Return figure number 1-? or 0 if a session plot */
18785 	*subplot = gmt_subplot_status (API, *fig);	/* Get information about subplot, if active */
18786 	panel[0] = '\0';
18787 	if ((*subplot) & GMT_SUBPLOT_ACTIVE) {	/* subplot is active */
18788 		if (((*subplot) & GMT_PANEL_NOTSET) == 0) {
18789 			int row, col;
18790 			if (gmtinit_get_current_panel (API, *fig, &row, &col, NULL, NULL, NULL) == 0)	/* panel set */
18791 				sprintf (panel, "%u-%u", row, col);
18792 		}
18793 	}
18794 	*inset = gmtinit_get_inset_dimensions (API, *fig, NULL);	/* True if inset is active */
18795 	GMT_Report (API, GMT_MSG_DEBUG, "gmtlib_get_graphics_item: Fig: %d Subplot: %d Panel: (%s) Inset: %d\n", *fig, *subplot, panel, *inset);
18796 }
18797 
gmt_is_integer(char * L)18798 bool gmt_is_integer (char *L) {
18799 	/* Return true if string L is not an integer or is empty */
18800 	if (!L || L[0] == '\0') return false;
18801 	for (size_t k = 0; k < strlen (L); k++) {
18802 		if (!isdigit (L[k])) return false;	/* Got a bad boy */
18803 	}
18804 	return true;	/* Everything came up roses */
18805 }
18806 
gmt_add_figure(struct GMTAPI_CTRL * API,char * arg,char * parfile)18807 int gmt_add_figure (struct GMTAPI_CTRL *API, char *arg, char *parfile) {
18808 	/* Add another figure to the gmt.figure queue.
18809 	 * arg = "[prefix] [format] [options]"
18810 	 * Rules: No prefix may start with a hyphen
18811 	 *		  prefix is required on the command line
18812 	 *		  format defaults to pdf on command line
18813 	 * The hidden gmt_###.ps- file starts numbering at 1 to
18814 	 * indicate it was set via gmt figure.  Scripts that are
18815 	 * not using gmt figure just builds gmt_0.ps-.
18816 	 * Since gmt_add_figure is called by figure.c and arguments
18817 	 * have been vetted we don't need to check much here except
18818 	 * when gmt figure is called to select an earlier plot and
18819 	 * make it the current figure.
18820 	 */
18821 	int n = 0, n_figs, this_k = 0, k, err;
18822 	bool found = false;
18823 	char prefix[GMT_LEN256] = {""}, formats[GMT_LEN64] = {""}, options[GMT_LEN128] = {""};
18824 	char *L = NULL, line[GMT_LEN256] = {""}, file[PATH_MAX] = {""};
18825 	static char *F_name[2] = {"label", "prog_indicator"};
18826 	struct GMT_FIGURE *fig = NULL;
18827 	FILE *fp = NULL, *fpM[2] = {NULL, NULL};
18828 
18829 	if (API->gwf_dir == NULL) {
18830 		GMT_Report (API, GMT_MSG_ERROR, "gmt figure: No workflow directory set\n");
18831 		return GMT_NOT_A_VALID_DIRECTORY;
18832 	}
18833 	if (API->external && arg == NULL) {	/* For external calls, if no args we supply - - */
18834 		/* This means the external API will call psconvert directly */
18835 		prefix[0] = formats[0] = '-';
18836 	}
18837 	else {	/* For regular command line use, the figure prefix is required; the rest is optional */
18838 		if (arg == NULL || arg[0] == '\0') {	/* This is clearly not allowed */
18839 			GMT_Report (API, GMT_MSG_ERROR, "gmt figure: No argument given\n");
18840 			return GMT_ARG_IS_NULL;
18841 		}
18842 		/* Separate prefix, format, and convert arguments.  We know n >= 1 here */
18843 
18844 		n = sscanf (arg, "%s %s %s", prefix, formats, options);
18845 	}
18846 	/* See what we have registered so far */
18847 	n_figs = gmtinit_read_figures (API->GMT, 1, &fig);	/* Number and names of figures so far [0] */
18848 	/* Did we already register this figure? */
18849 	for (k = 0; !found && k < n_figs; k++) {
18850 		if (!strcmp (prefix, fig[k].prefix)) {
18851 			found = true;
18852 			this_k = k + 1;	/* Since 0 is the hidden file used when figure is not set */
18853 		}
18854 	}
18855 	gmt_M_free (API->GMT, fig);
18856 	if (found && !(formats[0] == '-' || n == 1)) {
18857 		/* Want to make an already registered figure the current figure */
18858 		GMT_Report (API, GMT_MSG_ERROR, "gmt figure: Cannot select an existing figure and change its format.\n");
18859 		return GMT_RUNTIME_ERROR;
18860 	}
18861 	else if (!found) {	/* Here we have a new valid entry */
18862 		this_k = n_figs + 1;	/* This is the ID number of the new figure we are adding */
18863 		if (n == 1) /* Only got a prefix so must set the default pdf format */
18864 			strncpy (formats, gmt_session_format[API->GMT->current.setting.graphics_format], GMT_LEN64-1);
18865 		if ((fp = gmtinit_open_figure_file (API, 1, &err)) == NULL)	/* Failure to open existing or create new file */
18866 			return GMT_ERROR_ON_FOPEN;
18867 		/* Append the new entry */
18868 		fprintf (fp, "%d\t%s\t%s", this_k, prefix, formats);
18869 		if (options[0])	{	/* Append the options string */
18870 			GMT_Report (API, GMT_MSG_DEBUG, "New figure: %d\t%s\t%s\t%s\n", this_k, prefix, formats, options);
18871 			fprintf (fp, "\t%s", options);
18872 		}
18873 		else
18874 			GMT_Report (API, GMT_MSG_DEBUG, "New figure: %d\t%s\t%s", this_k, prefix, formats);
18875 
18876 		fprintf (fp, "\n");
18877 		fclose (fp);
18878 	}
18879 	/* Set the current figure */
18880 	if (gmtinit_set_current_figure (API, prefix, this_k))
18881 		return GMT_ERROR_ON_FOPEN;
18882 
18883 	if (parfile == NULL) return GMT_NOERROR;	/* Not a movie so no parameter file */
18884 
18885 	/* See if movie set up a set of frame labels and/or progress indicators - there may be none */
18886 
18887 	if ((fp = fopen (parfile, "r")) == NULL) {
18888 		GMT_Report (API, GMT_MSG_ERROR, "gmt figure: Unable to open movie parameter file %s\n", parfile);
18889 		return GMT_RUNTIME_ERROR;
18890 	}
18891 
18892 	while (fgets (line, GMT_LEN256, fp)) {
18893 		if (!(strncmp (line, "REM ", 4U) == 0 || strncmp (line, "# ", 2U) == 0)) continue;	/* Not a comment */
18894 		if (!strchr (line, '|')) continue;	/* No bars means it cannot be a movie label or progress indicator */
18895 		if ((L = strstr (line, "MOVIE_L: ")))	/* Found a movie label */
18896 			k = 0;
18897 		else if ((L = strstr (line, "MOVIE_P: ")))	/* Found a movie progress indicator */
18898 			k = 1;
18899 		else
18900 			continue;	/* Found a regular comment - skip */
18901 
18902 		if (fpM[k] == NULL) {	/* It is a first time for everything */
18903 			snprintf (file, PATH_MAX, "%s/gmt.movie%ss", API->gwf_dir, F_name[k]);
18904 			if ((fpM[k] = fopen (file, "w")) == NULL) {
18905 				GMT_Report (API, GMT_MSG_ERROR, "Cannot create file %s\n", file);
18906 				fclose (fp);
18907 				return GMT_ERROR_ON_FOPEN;
18908 			}
18909 			fprintf (fpM[k], "# movie %s information file\n", F_name[k]);
18910 		}
18911 		fprintf (fpM[k], "%s", &L[9]);
18912 	}
18913 	fclose (fp);
18914 
18915 	for (k = 0; k < 2; k++)
18916 		if (fpM[k]) fclose (fpM[k]);
18917 
18918 	return GMT_NOERROR;
18919 }
18920 
gmtlib_fixed_paper_size(struct GMTAPI_CTRL * API)18921 bool gmtlib_fixed_paper_size (struct GMTAPI_CTRL *API) {	/* Return true if this should have a fixed paper size */
18922 	int no = gmt_get_current_figure (API);	/* Get figure number 1-? or 0 if a session plot */
18923 	bool fixed = false;
18924 
18925 	if (no == 0) {	/* Session figure */
18926 		if (API->GMT->current.setting.ps_convert[0] && strstr (API->GMT->current.setting.ps_convert, "+n"))
18927 			fixed = true;	/* GMT default margin */
18928 	}
18929 	else {	/* Must check a specific figure other than session */
18930 		int n_figs;
18931 		struct GMT_FIGURE *fig = NULL;
18932 		if ((n_figs = gmtinit_read_figures (API->GMT, 1, &fig)) == GMT_NOTSET) {	/* Auto-insert the hidden gmt_0.ps- file which may not have been used */
18933 			GMT_Report (API, GMT_MSG_ERROR, "Unable to open gmt.figures for reading\n");
18934 			return GMT_ERROR_ON_FOPEN;
18935 		}
18936 		no--;	/* Since now 0 is figure 1 */
18937 		if (fig[no].options[0] && strstr (fig[no].options, "+n"))
18938 			fixed = true;	/* GMT default margin */
18939 		gmt_M_free (API->GMT, fig);
18940 	}
18941 	return fixed;
18942 }
18943 
gmt_truncate_file(struct GMTAPI_CTRL * API,char * file,size_t size)18944 int gmt_truncate_file (struct GMTAPI_CTRL *API, char *file, size_t size) {
18945 	/* Trim back the file to what it was when it was young and half-baked */
18946 	if (!file || file[0] == '\0' || size == 0) return GMT_NOERROR;
18947 #ifdef WIN32
18948 	{
18949 		FILE *fp = NULL;
18950 
18951 		if ((fp = fopen (file, "a")) == NULL) {
18952 			GMT_Report (API, GMT_MSG_ERROR, "Cannot open file %s\n", file);
18953 			return GMT_FILE_NOT_FOUND;
18954 		}
18955 		if (_chsize (fileno (fp), (long)size)) {
18956 			GMT_Report (API, GMT_MSG_ERROR, "Failed to truncate file %s (via _chsize) back to %" PRIuS " bytes\n", file, size);
18957 			fclose (fp);
18958 			return errno;
18959 		}
18960 		fclose (fp);
18961 	}
18962 #else
18963 	if (truncate (file, size)) {
18964 		GMT_Report (API, GMT_MSG_ERROR, "Failed to truncate file %s (via truncate) back to %" PRIuS " bytes\n", file, size);
18965 		return errno;
18966 	}
18967 #endif
18968 	return GMT_NOERROR;
18969 }
18970 
gmtinit_set_legend_filename(struct GMTAPI_CTRL * API,char * file)18971 GMT_LOCAL void gmtinit_set_legend_filename (struct GMTAPI_CTRL *API, char *file) {
18972 	char panel[GMT_LEN16] = {""};
18973 	int fig, subplot, inset;
18974 
18975 	file[0] = '\0';	/* Initialize the path */
18976 	gmtlib_get_graphics_item (API, &fig, &subplot, panel, &inset);	/* Determine current plot item */
18977 	/* Set the correct output file name given the CPT level */
18978 	if (inset)	/* Only one inset may be active at any given time */
18979 		snprintf (file, PATH_MAX, "%s/gmt.inset.legend", API->gwf_dir);
18980 	else if (subplot & GMT_SUBPLOT_ACTIVE) {	/* Either subplot master or a panel-specific legend */
18981 		if (subplot & GMT_PANEL_NOTSET)	/* Master for all subplot panels */
18982 			snprintf (file, PATH_MAX, "%s/gmt.%d.subplot.legend", API->gwf_dir, fig);
18983 		else	/* CPT for just this subplot */
18984 			snprintf (file, PATH_MAX, "%s/gmt.%d.panel.%s.legend", API->gwf_dir, fig, panel);
18985 	}
18986 	else if (fig)	/* Limited to one figure only */
18987 		snprintf (file, PATH_MAX, "%s/gmt.%d.legend", API->gwf_dir, fig);
18988 	else
18989 		snprintf (file, PATH_MAX, "%s/gmt.legend", API->gwf_dir);
18990 }
18991 
18992 
gmt_legend_file(struct GMTAPI_CTRL * API,char * file)18993 int gmt_legend_file (struct GMTAPI_CTRL *API, char *file) {
18994 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC) return 0;	/* Only available in modern mode */
18995 	gmtinit_set_legend_filename (API, file);
18996 	if (access (file, R_OK) == 0)
18997 		return true;
18998 	else {	/* No such file, wipe filename */
18999 		file[0] = '\0';
19000 		return false;
19001 	}
19002 }
19003 
gmtinit_draw_legend_line(struct GMTAPI_CTRL * API,FILE * fp,struct GMT_LEGEND_ITEM * item,unsigned int code)19004 GMT_LOCAL void gmtinit_draw_legend_line (struct GMTAPI_CTRL *API, FILE *fp, struct GMT_LEGEND_ITEM *item, unsigned int code) {
19005 	if (item->draw & code) {	/* Want to draw a line */
19006 		char *type = "DV";
19007 		code--;	/* Make 1 and 2 become 0 and 1 for index use */
19008 		if (item->pen[code][0])
19009 			fprintf (fp, "%c %s\n", type[code], item->pen[code]);
19010 		else
19011 			fprintf (fp, "%c %s\n", type[code], gmt_putpen (API->GMT, &API->GMT->current.setting.map_default_pen));
19012 	}
19013 }
19014 
gmt_add_legend_item(struct GMTAPI_CTRL * API,struct GMT_SYMBOL * S,bool do_fill,struct GMT_FILL * fill,bool do_line,struct GMT_PEN * pen,struct GMT_LEGEND_ITEM * item)19015 void gmt_add_legend_item (struct GMTAPI_CTRL *API, struct GMT_SYMBOL *S, bool do_fill, struct GMT_FILL *fill, bool do_line, struct GMT_PEN *pen, struct GMT_LEGEND_ITEM *item) {
19016 	/* Adds a new entry to the auto-legend information file hidden in the session directory */
19017 	char file[PATH_MAX] = {""}, label[GMT_LEN128] = {""};
19018 	bool gap_done = false;
19019 	double size = 0.0;
19020 	FILE *fp = NULL;
19021 
19022 	/* -l[<label>][+D[<pen>]][+G<gap>][+H<header>][+L[<code>/]<label>][+N<cols>][+S<size>][+V[<pen>]][+f<font>][+g<fill>][+j<just>][+o<off>][+p<pen>][+w<length>][+s<scale>]
19023 	 *
19024 	 * +D corresponds to command D in the legend codes and draws a horizontal line.
19025 	 * +G corresponds to command G in the legend codes and adds a gap before current item.
19026 	 * +H corresponds to command H in the legend codes and places a legend header (see +f).
19027 	 * +L corresponds to command L in the legend codes and places a line subheader (see +f).
19028 	 * +N corresponds to command N in the legend codes and changes the number of columns.
19029 	 * +S sets symbol size or line length for symbols that otherwise won't have a dimension.
19030 	 * +V corresponds to command V in the legend codes and starts/ends a vertical line.
19031 	 * +f sets the font to use the header string [FONT_TITLE].
19032 	 * +g sets the frame fill [white].
19033 	 * +j -Dj?? as to where to place legend
19034 	 * +o sets the frame offset [0.2c].
19035 	 * +p sets the frame pen [1p].
19036 	 * +s specifies an overall symbol scaling factor, as in -S<factor> [1].
19037 	 * +w specifies the legend width, as in -D+w<width>.
19038 	 *
19039 	 * S can be given as NULL for lines but then item->size must be set (+size).
19040 	 */
19041 
19042 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC) return;	/* Only available in modern mode */
19043 	if (item == NULL) return;	/* Nothing given */
19044 
19045 	gmtinit_set_legend_filename (API, file);	/* Get the legend filename given current scope */
19046 
19047 	/* OK, do we append or create? */
19048 
19049 	if (access (file, R_OK)) {	/* Must create this legend file */
19050 		char justcode[4] = {""};
19051 		if ((fp = fopen (file, "w")) == NULL) {
19052 			GMT_Report (API, GMT_MSG_ERROR, "Unable to create current legend file %s !\n", file);
19053 			return;
19054 		}
19055 		if (item->scale == 0.0) item->scale = 1.0;	/* Default scaling set 1:1 */
19056 		if (item->just == 0) item->just = PSL_TR;	/* Default justification is top right corner */
19057 		gmt_just_to_code (API->GMT, item->just, justcode);
19058 		fprintf (fp, "# Auto-generated legend information file\n");
19059 		/* The next two legend comments will be parsed by gmt legend and acted upon */
19060 		fprintf (fp, "# LEGEND_JUSTIFICATION: %s\n", justcode);
19061 		fprintf (fp, "# LEGEND_SCALING: %g\n", item->scale);
19062 		fprintf (fp, "# LEGEND_FRAME: ");
19063 		if (item->pen[GMT_LEGEND_PEN_P][0])
19064 			fprintf (fp, " %s", item->pen[GMT_LEGEND_PEN_P]);
19065 		else
19066 			fprintf (fp, " 1p");
19067 		if (item->fill[0])
19068 			fprintf (fp, " %s", item->fill);
19069 		else
19070 			fprintf (fp, " white");
19071 		if (item->off[0])
19072 			fprintf (fp, " %s\n", item->off);
19073 		else
19074 			fprintf (fp, " 0.2c\n");
19075 		if (item->width > 0.0)	/* Specified legend width directly */
19076 			fprintf (fp, "# LEGEND_WIDTH: %gi\n", item->width);
19077 		if (item->ncols > 1)	/* Specified +n up front */
19078 			fprintf (fp, "# LEGEND_NCOLS: %d\n", item->ncols);
19079 		if (item->gap[0]) {	/* Want to place a gap first, even before any title */
19080 			fprintf (fp, "G %s\n", item->gap);
19081 			gap_done = true;	/* So we don't do it again below */
19082 		}
19083 		if (item->header[0]) {	/* Want to place a centered legend header first */
19084 			if (item->font[0])	/* Use the given font */
19085 				fprintf (fp, "H %s %s\n", item->font, item->header);
19086 			else	/* Default to FONT_TITLE setting */
19087 				fprintf (fp, "H - %s\n", item->header);
19088 		}
19089 	}
19090 	else {	/* Append to an existing file, meaning we already wrote the part above */
19091 		if ((fp = fopen (file, "a")) == NULL) {	/* Cannot append to an existing file? */
19092 			GMT_Report (API, GMT_MSG_ERROR, "Unable to append to existing current legend file %s !\n", file);
19093 			return;
19094 		}
19095 		if (item->ID == 0) {	/* Only warn unless auto-legend for multiple lines or polygons */
19096 			if (item->scale > 0.0)
19097 				GMT_Report (API, GMT_MSG_WARNING, "Your -l+s<scale> is ignored - only applicable to the first instance of -l.\n");
19098 			if (item->just)
19099 				GMT_Report (API, GMT_MSG_WARNING, "Your -l+j<just> is ignored - only applicable to the first instance of -l.\n");
19100 			if (item->width)
19101 				GMT_Report (API, GMT_MSG_WARNING, "Your -l+w<width> is ignored - only applicable to the first instance of -l.\n");
19102 		}
19103 	}
19104 
19105 	GMT_Report (API, GMT_MSG_DEBUG, "Add record to current legend file%s\n", file);
19106 	if (!gap_done && item->gap[0])	/* Always place a gap first, if requested, and not already done before title */
19107 		fprintf (fp, "G %s\n", item->gap);
19108 	/* Horizontal lines are normally drawn before the symbol placement */
19109 	if ((item->draw & GMT_LEGEND_DRAW_D) && ((item->draw & GMT_LEGEND_DRAW_V) == 0 || item->pen[GMT_LEGEND_PEN_V][0] == '\0'))
19110 		gmtinit_draw_legend_line (API, fp, item, GMT_LEGEND_DRAW_D);	/* Draw horizontal line, if requested, before symbol */
19111 	if (item->subheader[0]) {	/* Want to place a line header first */
19112 		if (item->font[0])	/* Use the given font */
19113 			fprintf (fp, "L %s %c %s\n", item->font, item->code, item->subheader);
19114 		else	/* Default to FONT_LABEL setting */
19115 			fprintf (fp, "L - %c %s\n", item->code, item->subheader);
19116 	}
19117 	if (item->ncols > 0)	/* Specified a different number of columns */
19118 		fprintf (fp, "N %d\n", item->ncols);
19119 	if (((item->draw & GMT_LEGEND_DRAW_V) && item->pen[GMT_LEGEND_PEN_V][0] == '\0'))	/* Initialize the vertical line setting */
19120 		gmtinit_draw_legend_line (API, fp, item, GMT_LEGEND_DRAW_V);
19121 	/* Get the symbol size */
19122 	if (item->size > 0.0)	/* Hard-wired symbol size given */
19123 		size = item->size;
19124 	else if (S)
19125 		size = S->size_x;	/* Use the symbol size given */
19126 	else
19127 		GMT_Report (API, GMT_MSG_INFORMATION, "No size or length given and no symbol present - default to line length of 0.5 cm.\n");
19128 
19129 	/* Finalize label */
19130 	if (item->label_type == GMT_LEGEND_LABEL_FORMAT)	/* Integer format string */
19131 		snprintf (label, GMT_LEN128, item->label, item->ID);
19132 	else if (item->label_type == GMT_LEGEND_LABEL_LIST) {	/* Got list of labels, pick the current one via ID */
19133 		char *word = gmt_get_word (item->label, ",", item->ID);
19134 		if (word) {	/* Still more labels in the list */
19135 			strncpy (label, word, GMT_LEN128-1);
19136 			gmt_M_str_free (word);
19137 		}
19138 	}
19139 	else	/* Got a fixed label */
19140 		strncpy (label, item->label, GMT_LEN128-1);
19141 	/* Place the symbol command */
19142 	if (S == NULL || S->symbol == GMT_SYMBOL_LINE) {	/* Line for legend entry */
19143 		if (pen == NULL) pen = &(API->GMT->current.setting.map_default_pen);	/* Must have pen to draw line */
19144 		if (size > 0.0)	/* Got a line length in inches */
19145 			fprintf (fp, "S - - %gi - %s - %s\n", size, gmt_putpen (API->GMT, pen), label);
19146 		else	/* Let the legend module supply a default length */
19147 			fprintf (fp, "S - - - - %s - %s\n", gmt_putpen (API->GMT, pen), label);
19148 	}
19149 	else {	/* Regular symbol */
19150 		if (!(do_fill || do_line)) do_line = true;	/* If neither fill nor pen is selected, plot will draw line, so override do_line here */
19151 		if (do_line && pen == NULL) pen = &(API->GMT->current.setting.map_default_pen);	/* Must have pen to draw line */
19152 		if (S->size_x > 0.0 && S->size_y > 0.0 && S->symbol == PSL_RECT)
19153 			fprintf (fp, "S - %c %gi,%gi %s %s - %s\n", S->symbol, size, S->size_y, (do_fill) ? gmtlib_putfill (API->GMT, fill) : "-", (do_line) ? gmt_putpen (API->GMT, pen) : "-", label);
19154 		else if (S->symbol == 'q') {	/* Quoted line [Experimental] */
19155 			char scode[GMT_LEN64] = {""};
19156 			sprintf (scode, "qn1:+ltext");
19157 			fprintf (fp, "S - %s - %s %s - %s\n", scode, (do_fill) ? gmtlib_putfill (API->GMT, fill) : "-", (do_line) ? gmt_putpen (API->GMT, pen) : "-", label);
19158 		}
19159 		else if (S->symbol == '~') {	/* Decorated line [Experimental] */
19160 			char scode[GMT_LEN64] = {""};
19161 			sprintf (scode, "~n1:+s%s%s", S->D.symbol_code, S->D.size);
19162 			if (S->D.pen[0])  strcat (scode, "+p"), strcat (scode, S->D.pen);
19163 			if (S->D.fill[0]) strcat (scode, "+g"), strcat (scode, S->D.fill);
19164 			fprintf (fp, "S - %s - %s %s - %s\n", scode, (do_fill) ? gmtlib_putfill (API->GMT, fill) : "-", (do_line) ? gmt_putpen (API->GMT, pen) : "-", label);
19165 		}
19166 		else
19167 			fprintf (fp, "S - %c %gi %s %s - %s\n", S->symbol, size, (do_fill) ? gmtlib_putfill (API->GMT, fill) : "-", (do_line) ? gmt_putpen (API->GMT, pen) : "-", label);
19168 	}
19169 
19170 	if (item->draw & GMT_LEGEND_DRAW_V && item->pen[GMT_LEGEND_PEN_V][0]) {	/* Must end with horizontal, then vertical line */
19171 		gmtinit_draw_legend_line (API, fp, item, GMT_LEGEND_DRAW_D);		/* Draw horizontal line, if requested, after last symbol */
19172 		gmtinit_draw_legend_line (API, fp, item, GMT_LEGEND_DRAW_V);
19173 	}
19174 	fclose (fp);
19175 }
19176 
gmt_get_legend_info(struct GMTAPI_CTRL * API,double * width,double * scale,char justification[],char pen[],char fill[],char off[])19177 bool gmt_get_legend_info (struct GMTAPI_CTRL *API, double *width, double *scale, char justification[], char pen[], char fill[], char off[]) {
19178 	/* Determine if there is a hidden legend file that has not been placed */
19179 	char file[PATH_MAX] = {""}, label[GMT_LEN128] = {""}, size[GMT_LEN32] = {""}, dim[GMT_LEN32] = {""}, *c = NULL;
19180 	size_t L, N_max = 0;
19181 	int ncols = 1;
19182 	double W, W_max = 0.0;
19183 	FILE *fp = NULL;
19184 
19185 	*scale = 1.0;	/* Default scaling */
19186 	*width = 0.0;	/* Not set yet */
19187 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC) return false;	/* This is a modern mode only feature */
19188 	if (!gmt_legend_file (API, file)) return false;			/* There is no legend file in the scope */
19189 	if ((fp = fopen (file, "r")) == NULL) {	/* Unable to open for reading */
19190 		GMT_Report (API, GMT_MSG_ERROR, "Failed to open file %s for reading\n", file);
19191 		return false;
19192 	}
19193 	strcpy (justification, "TR");	/* Default legend placement */
19194 	while (fgets (file, PATH_MAX, fp)) {	/* Recycle the array file to read the records here */
19195 		if (strstr (file, "# LEGEND_JUSTIFICATION:"))	/* Need to override the default justification */
19196 			sscanf (&file[2], "%*s %s\n", justification);
19197 		else if (strstr (file, "# LEGEND_SCALING:"))	/* Need to override the default justification */
19198 			sscanf (&file[2], "%*s %lf\n", scale);
19199 		else if (strstr (file, "# LEGEND_FRAME:"))	/* Need to override the default frame settings */
19200 			sscanf (&file[2], "%*s %s %s %s\n", pen, fill, off);
19201 		else if (strstr (file, "# LEGEND_WIDTH:")) {	/* Need to explicitly set legend width */
19202 			sscanf (&file[2], "%*s %s\n", dim);
19203 			*width = gmt_M_to_inch (API->GMT, dim);
19204 		}
19205 		else if (strstr (file, "# LEGEND_NCOLS:")) {	/* Need to explicitly set number of columns */
19206 			sscanf (&file[2], "%*s %s\n", dim);
19207 			ncols = atoi (dim);
19208 		}
19209 		if (file[0] != 'S') continue;	/* Only examine symbol requests */
19210 		sscanf (file, "%*s %*s %*s %s %*s %*s %*s %[^\n]\n", size, label);
19211 		if ((L = strlen (label)) > N_max) N_max = L;
19212 		if (size[0] != '-') {	/* Gave a symbol size(width) but watch for two args */
19213 			if ((c = strchr (size, ','))) {	/* Probably got width,height for rectangle */
19214 				c[0] = '\0';
19215 				W = gmt_M_to_inch (API->GMT, size);
19216 				c[0] = ',';
19217 			}
19218 			else
19219 				W = gmt_M_to_inch (API->GMT, size);
19220 			if (W > W_max) W_max = W;
19221 		}
19222 	}
19223 	fclose (fp);
19224 
19225 	if (*width == 0.0)	/* Best estimate of legend box width from longest string in the labels and space needed for symbols, plus 5 % */
19226 		*width = ncols * (GMT_LEGEND_DX2_MUL * (*scale) * W_max + N_max * 1.05 * GMT_LET_WIDTH * API->GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH);
19227 	if (ncols == 1) *width = 0.0;	/* Set in PostScript */
19228 	return true;
19229 }
19230 
19231 /*! . */
gmtlib_terminate_session()19232 void gmtlib_terminate_session () {
19233 	/* If a modern mode session catches a CTRL-C interrupt then we must terminate
19234 	 * the session so not to leave behind a mess that requires "gmt clear sessions".
19235 	 */
19236 
19237 	bool die;
19238 	char dir[PATH_MAX] = {""};
19239 	struct GMTAPI_CTRL *API = NULL;
19240 
19241 	if (global_API == NULL) return;	/* No session so nothing to clean up */
19242 	API = gmt_get_api_ptr (global_API);	/* COnvert void pointer to GMTAPI_CTRL pointer */
19243 	if (API->session_dir == NULL || API->session_name == NULL) return;	/* Cannot check */
19244 	snprintf (dir, PATH_MAX, "%s/gmt_session.%s", API->session_dir, API->session_name);
19245 	GMT_Report (API, GMT_MSG_DEBUG, "Remove session directory %s before exiting due to Ctrl-C\n", dir);
19246 	if (!access (dir, F_OK)) {	/* Session directory exist, try to remove it */
19247 		if (gmt_remove_dir (API, dir, false))
19248 			GMT_Report (API, GMT_MSG_WARNING, "Unable to remove session directory %s [permissions?]\n", dir);
19249 	}
19250 	die = !API->external;	/* Only call exit from the CLI */
19251 	GMT_Destroy_Session (API);	/* Try to get out cleanly */
19252 	if (die) exit (0);
19253 }
19254 
19255 /*! . */
gmt_manage_workflow(struct GMTAPI_CTRL * API,unsigned int mode,char * text)19256 int gmt_manage_workflow (struct GMTAPI_CTRL *API, unsigned int mode, char *text) {
19257 	/* Manage the GMT workflow.  Mode can take the following values:
19258 	 *   GMT_BEGIN_WORKFLOW: Start a new GMT workflow and initialize a work flow directory.
19259 	 *   GMT_USE_WORKFLOW:	Continue using the current work flow directory
19260 	 *   GMT_END_WORKFLOW:	Finalize the workflow.
19261 	 */
19262 
19263 	/* Set workflow directory */
19264 	char file[PATH_MAX] = {""}, dir[PATH_MAX] = {""};
19265 	static char *type[2] = {"classic", "modern"}, *smode[3] = {"Use", "Begin", "End"}, *fstatus[4] = {"found", "not found", "created", "removed"};
19266 	int err = 0, fig, error = GMT_NOERROR;
19267 	unsigned int clean_start = 0;
19268 	struct stat S;
19269 
19270 	snprintf (dir, PATH_MAX, "%s/gmt_session.%s", API->session_dir, API->session_name);
19271 	API->gwf_dir = strdup (dir);
19272 	err = stat (API->gwf_dir, &S);	/* Stat the gwf_dir path (which may not exist) */
19273 	clean_start = (mode & GMT_CLEAN_WORKFLOW);
19274 	mode -= clean_start;
19275 
19276 	switch (mode) {
19277 		case GMT_BEGIN_WORKFLOW:	/* Must create a new temporary directory */
19278 			GMT_Report (API, GMT_MSG_INFORMATION, "Creating a workflow directory %s\n", API->gwf_dir);
19279 			/* We only get here when gmt begin is called */
19280 			if (err == 0 && !S_ISDIR (S.st_mode)) {	/* Path already exists, but it is not a directory */
19281 				GMT_Report (API, GMT_MSG_ERROR, "A file named %s already exist and prevents us creating a workflow directory by that name\n", API->gwf_dir);
19282 				error = GMT_RUNTIME_ERROR;
19283 			}
19284 			else if (err == 0 && S_ISDIR (S.st_mode) && (S.st_mode & S_IWUSR) == 0) {	/* Directory already exists and is not writable */
19285 				GMT_Report (API, GMT_MSG_ERROR, "Workflow directory %s already exist but is not writable\n", API->gwf_dir);
19286 				error = GMT_RUNTIME_ERROR;
19287 			}
19288 			else {	/* Create the new workflow directory */
19289 				/* To avoid the weird CID 167015 that says:
19290 				toctou: Calling function mkdir that uses API->gwf_dir after a check function.
19291 				This can cause a time-of-check, time-of-use race condition.
19292 				we will use "dir" instead of "API->gwf_dir" below  */
19293 				if (err == 0 && S.st_mode & S_IWUSR) {	/* Remove abandoned directory */
19294 					GMT_Report (API, GMT_MSG_DEBUG, "Workflow directory %s already exist and is writable (we will delete are recreate)\n", API->gwf_dir);
19295 					error = gmt_remove_dir (API, dir, true);	/* Remove and recreate */
19296 				}
19297 				else {	/* Can create a fresh new directory */
19298 					if (gmt_mkdir (dir)) {
19299 						GMT_Report (API, GMT_MSG_ERROR, "Unable to create a workflow directory : %s\n", API->gwf_dir);
19300 						error = GMT_RUNTIME_ERROR;
19301 					}
19302 				}
19303 			}
19304 			if (error) return (error);		/* Bail at this point */
19305 			gmt_reset_history (API->GMT);	/* No old classic history shall affect a new modern mode session */
19306 
19307 			gmt_conf_SI (API->GMT);			/* Get the original system defaults */
19308 			if (!clean_start) {
19309 				/*  Overload any user-supplied defaults via a gmt.conf file but reset PS_MEDIA to the original system default */
19310 				if (gmt_getdefaults (API->GMT, NULL) == GMT_NOERROR)	/* Ingested a gmt.conf file */
19311 					gmtinit_setautopagesize (API->GMT);	/* Reset to auto */
19312 			}
19313 			snprintf (dir, PATH_MAX, "%s/%s", API->gwf_dir, GMT_SETTINGS_FILE);	/* Reuse dir string for saving gmt.conf to this dir */
19314 			API->GMT->current.setting.run_mode = GMT_MODERN;	/* Enable modern mode here so putdefaults can skip writing PS_MEDIA if not PostScript output */
19315 			error = gmtinit_put_session_name (API, text);		/* Store session name, possibly setting psconvert options */
19316 			gmt_putdefaults (API->GMT, dir);		/* Write current GMT defaults to this sessions gmt.conf file in the workflow directory */
19317 			API->GMT->current.setting.history_orig = API->GMT->current.setting.history;	/* Temporarily turn off history so nothing is copied into the workflow dir */
19318 			API->GMT->current.setting.history = GMT_HISTORY_OFF;	/* Turn off so that no history is copied into the workflow directory */
19319 			GMT_Report (API, GMT_MSG_DEBUG, "%s Workflow.  Session ID = %s. Directory %s %s.\n", smode[mode], API->session_name, API->gwf_dir, fstatus[2]);
19320 			global_API = API;	/* Make a pointer just for the no-argument gmtlib_terminate_session */
19321 			break;
19322 		case GMT_USE_WORKFLOW:
19323 			/* We always get here except when gmt begin | end are called. */
19324 			/* If the workflow directory exists then we are in modern mode, else in classic */
19325 			API->GMT->current.setting.run_mode = (err == 0) ? GMT_MODERN : GMT_CLASSIC;
19326 			break;
19327 		case GMT_END_WORKFLOW:
19328 			/* We only get here when gmt end is called */
19329 			/* Check if a subplot was left hanging */
19330 			fig = gmt_get_current_figure (API);	/* Get current figure number */
19331 			snprintf (file, PATH_MAX, "%s/gmt.subplot.%d", API->gwf_dir, fig);
19332 			if (!access (file, R_OK))	/* subplot end was never called */
19333 				GMT_Report (API, GMT_MSG_WARNING, "subplot was never completed - plot items in last panel may be missing\n");
19334 			GMT_Report (API, GMT_MSG_DEBUG, "%s Workflow.  Session ID = %s. Directory %s %s.\n", smode[mode], API->session_name, API->gwf_dir, fstatus[3]);
19335 			if ((error = gmtinit_process_figures (API, text)))
19336 				GMT_Report (API, GMT_MSG_ERROR, "gmtinit_process_figures returned error %d\n", error);
19337 			GMT_Report (API, GMT_MSG_INFORMATION, "Destroying the current workflow directory %s\n", API->gwf_dir);
19338 			if (gmt_remove_dir (API, dir, false))
19339 				GMT_Report (API, GMT_MSG_WARNING, "Unable to remove directory %s [permissions?]\n", dir);
19340 			API->GMT->current.setting.run_mode = GMT_CLASSIC;	/* Disable modern mode */
19341 			break;
19342 		default:
19343 			GMT_Report (API, GMT_MSG_ERROR, "Illegal mode (%d) passed to gmt_manage_workflow\n", mode);
19344 			break;
19345 	}
19346 	if (API->GMT->current.setting.run_mode == GMT_MODERN) {
19347 		GMT_Report (API, GMT_MSG_DEBUG, "GMT now running in %s mode [Session ID = %s]\n", type[API->GMT->current.setting.run_mode], API->session_name);
19348 		API->GMT->current.setting.use_modern_name = true;
19349 	}
19350 	return error;
19351 }
19352 
19353 #if 0	/* Maybe use later - things seems to work OK for now with what we have */
19354 
19355 /*! Codes from gmt file types */
19356 enum GMT_enum_ftypes {
19357 	GMT_IS_URL = 1,
19358 	GMT_IS_QUERY = 2,
19359 	GMT_IS_CDF_ATTR = 4,
19360 	GMT_IS_GRD_ATTR = 8,
19361 	GMT_IS_GDAL_ATTR = 16,
19362 	GMT_IS_CACHE = 32,
19363 	GMT_IS_DATA = 64};
19364 
19365 unsigned int gmt_file_type (struct GMT_CTRL *GMT, const char *file, unsigned int pos[]) {
19366 	/* Determine what kind of file argument was given.  Return a code
19367 	 * that reflects a sum of several bit flags:
19368 	 * 1:	Gave an URL, 0 otherwise
19369 	 * 2:	Gave an URL that is a query, 0 otherwise
19370 	 * 4:	Specified a netCDF slice/layer attribute, 0 otherwise
19371 	 * 8:	Specified a GMT grid format =<id>[<modifiers>] attribute, 0 otherwise
19372 	 * 16:	GDAL file called via =gd[...]
19373 	 * 32:	Special @earth_relief grid to be downloaded if not found
19374 	 */
19375 	unsigned int code = 0;
19376 	char *a = NULL, *c = NULL, *e = NULL, *p = NULL, *q = NULL, *s = NULL;
19377 
19378 	pos[0] = pos[1] = 0;	/* Initialize */
19379 	if (file == NULL) return 0;	/* Nada */
19380 	a = strchr  (file, '&');	/* Address of the first ? or NULL */
19381 	e = strchr  (file, '=');	/* Address of the first = or NULL */
19382 	p = strrchr (file, '+');	/* Address of the last + or NULL */
19383 	q = strchr  (file, '?');	/* Address of the first ? or NULL */
19384 	s = strrchr (file, '/');	/* Address of the last / or NULL */
19385 	/* First distinguish between cache, urls, and special data files from the GMT server */
19386 	if (gmt_file_is_cache (GMT->parent, file)) {	/* Special @<filename> syntax for GMT cache files */
19387 		code |= GMT_CACHE_FILE;
19388 		pos[0] = 1;	/* Must skip the first character to find the file name */
19389 	}
19390 	else if (gmt_M_file_is_url (file))	/* Full URL given */
19391 		code |= GMT_IS_URL;
19392 	else if (!strncmp (file, GMT_TOPO_PREFIX, strlen(GMT_TOPO_PREFIX)) && strstr (file, ".grd"))	/* Useful data set distributed by GMT */
19393 		code |= GMT_IS_DATA;
19394 
19395 	/* Now try to detect subtleries like netcdf slices and grid attributes */
19396 
19397 	if ((c = strstr (file, "=gd"))) {
19398 		code |= GMT_IS_GDAL_ATTR;	/* Only GDAL references would have these characters */
19399 		pos[1] = (unsigned int) (c - file);
19400 	}
19401 	else if ((code & GMT_IS_URL) && q && (a || (e && e > s))) {	/* Only URL quieries have ampersands in them */
19402 		code |= GMT_IS_QUERY;
19403 		pos[1] = (unsigned int) (q - file);
19404 	}
19405 	else if (q) {	/* Question-mark is more complicated */
19406 		if (strchr (file, '[') || strchr (file, '('))	/* Assume netCDF slicing: file.nc?pressure[2,1] or file.nc?pressure(24,10) */
19407 			code |= GMT_IS_CDF_ATTR;
19408 		else if (s && s > q)	/* Assume netCDF variable selections: file.nc?time/lat/lon */
19409 			code |= GMT_IS_CDF_ATTR;
19410 		else if (e && (s == NULL || s > e))	/* Must be a GMT grid with old-style attributes: junk.grd=bf/0/1/32767 */
19411 			code |= GMT_IS_GRD_ATTR;
19412 		else if (e && p && p > e)	/* Must be a GMT grid with new-style attributes: junk.grd=bf+s<scale>+o<scale>+n<nan> */
19413 			code |= GMT_IS_GRD_ATTR;
19414 		else 	/* Assume netCDF variable selection: file.nc?slp */
19415 			code |= GMT_IS_CDF_ATTR;
19416 		pos[1] = (unsigned int) (q - file);
19417 	}
19418 	else if (e)	{
19419 		pos[1] = (unsigned int) (e - file);
19420 		if (s == NULL || s > e)	/* Must be a GMT grid with old-style attributes: junk.grd=bf/0/1/32767 */
19421 			code |= GMT_IS_GRD_ATTR;
19422 		else if (p && p > e)	/* Must be a GMT grid with new-style attributes: junk.grd=bf+s<scale>+o<scale>+n<nan> */
19423 			code |= GMT_IS_GRD_ATTR;
19424 		else if (strlen (e) == 3)	/* Must be a GMT grid with format only: junk.grd=bf */
19425 			code |= GMT_IS_GRD_ATTR;
19426 		else	/* Weird file with = in the name? */
19427 			pos[1] = 0;
19428 	}
19429 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_file_type: File %s returning code = %u, with pos[0] = %u and pos[1] = %u\n", file, code, pos[0], pos[1]);
19430 	return code;
19431 }
19432 #endif
19433 
gmt_auto_offsets_for_colorbar(struct GMT_CTRL * GMT,double offset[],int justify,struct GMT_OPTION * options)19434 void gmt_auto_offsets_for_colorbar (struct GMT_CTRL *GMT, double offset[], int justify, struct GMT_OPTION *options) {
19435 	/* We wish to examine the previous -B setting for information as to which axes was
19436 	 * annotated and possibly labeled, and if the colorbar is requested to be placed on
19437 	 * such an axis side we need to make more space by increasing the offset. This is
19438 	 * only possible under modern mode since classic updated -B in the history. */
19439 
19440 	char side, axis, B_delim[2] = {30, 0}, p[GMT_BUFSIZ] = {""};	/* Use ASCII 30 RS Record Separator between -B strings */
19441 	char file[PATH_MAX] = {""};
19442 	char *frame_axes = (!strcmp (GMT->current.setting.map_frame_axes, "auto")) ? "WrStZ" : GMT->current.setting.map_frame_axes;
19443 	unsigned int pos = 0, sides[5];
19444 	bool add_label = false, add_annot = false, axis_set = false, was;
19445 	double GMT_LETTER_HEIGHT = 0.736;
19446 	struct GMT_OPTION *opt = NULL;
19447 	char *c = NULL;
19448 	unsigned int n_errors = 0;
19449 	int fig;
19450 	FILE *fp = NULL;
19451 	/* Initialize the default settings before considering any -B history */
19452 	gmt_set_undefined_defaults (GMT, 0.0, false);	/* Must set undefined to their reference values for now */
19453 
19454 	offset[GMT_OUT] = GMT->current.setting.map_label_offset[GMT_Y] + GMT->current.setting.map_frame_width;
19455 	offset[GMT_IN]  = GMT->current.setting.map_label_offset[GMT_Y];
19456 
19457 	if (GMT->current.setting.run_mode == GMT_CLASSIC) return;	/* No can do */
19458 
19459 	switch (justify) {	/* Only the four sides are automated */
19460 		case PSL_TC: side = 'N'; axis = 'x'; break;
19461 		case PSL_BC: side = 'S'; axis = 'x'; break;
19462 		case PSL_ML: side = 'W'; axis = 'y'; break;
19463 		case PSL_MR: side = 'E'; axis = 'y'; break;
19464 		default: return; break;	/* No auto-adjust for the rest */
19465 	}
19466 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Determined colorbar side = %c and axis = %c\n", side, axis);
19467 	fig = gmt_get_current_figure (GMT->parent);	/* Get current figure number */
19468 
19469 	snprintf (file, PATH_MAX, "%s/gmt.frame.%d", GMT->parent->gwf_dir, fig);
19470 	if ((fp = fopen (file, "r")) == NULL) {
19471 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No file %s with frame information - no adjustments made\n", file);
19472 		return;
19473 	}
19474 	fgets (file, PATH_MAX, fp);	fclose (fp);	/* Recycle file to hold the -B arguments */
19475 	while (file[0] && gmt_strtok (file, B_delim, &pos, p)) {	/* Parse the -B options from last call */
19476 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "B item = %s\n", p);
19477 		if (p[0] == axis && strstr (p, "+l")) add_label = true;	/* User specified an axis label on that side */
19478 		if (strchr ("WESNwesn", p[0])) {	/* Gave a -B<axis> option */
19479 			axis_set = true;
19480 			if (strchr (p, side)) add_annot = true;
19481 		}
19482 	}
19483 	/* If -BWE.. was not set we must rely on MAP_FRAME_AXES default setting */
19484 	if (!axis_set && strchr (frame_axes, side)) add_annot = true;
19485 	if (add_label && gmt_M_is_geographic (GMT, GMT_IN)) add_label = false;	/* Not allowed anyway */
19486 	/* Time to make updates, if any */
19487 	if (add_annot) {
19488 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Adding annotation space\n");
19489 		offset[GMT_OUT] += MAX(0,GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER]);	/* Any tick length */
19490 		offset[GMT_OUT] += (GMT_LETTER_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH) + MAX (0.0, GMT->current.setting.map_annot_offset[GMT_PRIMARY]);	/* Allow for space between axis and annotations */
19491 	}
19492 	if (add_label) {
19493 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Adding label space\n");
19494 		offset[GMT_OUT] += (GMT_LETTER_HEIGHT * GMT->current.setting.font_label.size / PSL_POINTS_PER_INCH) + MAX (0.0, GMT->current.setting.map_label_offset[GMT_Y]);
19495 	}
19496 	/* Because the next call will reset frame sides i will make a copy and override the override here */
19497 	gmt_M_memcpy (sides, GMT->current.map.frame.side, 5U, unsigned int);
19498 	was = GMT->current.map.frame.draw;
19499 	gmtinit_conf_modern_override (GMT);	/* Reset */
19500 	(void)gmt_getdefaults (GMT, NULL);
19501 	for (opt = options; opt; opt = opt->next) {
19502 		if (opt->option != '-') continue;   /* Not a parameter setting */
19503 		if ((c = strchr (opt->arg, '=')) == NULL) continue;
19504 		c[0] = '\0';  /* Remove = */
19505 		n_errors += gmtlib_setparameter (GMT, opt->arg, &c[1], false);
19506 	}
19507 	gmt_M_memcpy (GMT->current.map.frame.side, sides, 5U, unsigned int);
19508 	GMT->current.map.frame.draw = was;
19509 }
19510 
gmt_count_char(struct GMT_CTRL * GMT,char * txt,char it)19511 unsigned int gmt_count_char (struct GMT_CTRL *GMT, char *txt, char it) {
19512 	unsigned int i, n;
19513 	gmt_M_unused (GMT);
19514 	for (i = n = 0; txt[i]; i++) if (txt[i] == it) n++;
19515 	return (n);
19516 }
19517 
19518 /*! Return the number of CPU cores */
gmtlib_get_num_processors()19519 int gmtlib_get_num_processors() {
19520 	static int n_cpu = 0;
19521 
19522 	if (n_cpu > 0)
19523 		/* we already know the answer. do not query again. */
19524 		return n_cpu;
19525 
19526 #if defined WIN32
19527 	{
19528 		SYSTEM_INFO sysinfo;
19529 		GetSystemInfo ( &sysinfo );
19530 		n_cpu = sysinfo.dwNumberOfProcessors;
19531 	}
19532 #elif defined HAVE_SC_NPROCESSORS_ONLN
19533 	n_cpu = (int)sysconf (_SC_NPROCESSORS_ONLN);
19534 #elif defined HAVE_SC_NPROC_ONLN
19535 	n_cpu = (int)sysconf (_SC_NPROC_ONLN);
19536 #elif defined HAVE_SYSCTL_HW_NCPU
19537 	{
19538 		size_t size = sizeof(n_cpu);
19539 		int mib[] = { CTL_HW, HW_NCPU };
19540 		sysctl(mib, 2, &n_cpu, &size, NULL, 0);
19541 	}
19542 #endif
19543 	if (n_cpu < 1)
19544 		n_cpu = 1; /* fallback */
19545 	return n_cpu;
19546 }
19547 
gmt_report_usage(struct GMTAPI_CTRL * API,struct GMT_OPTION * options,unsigned int special,int (* usage)(struct GMTAPI_CTRL *,int))19548 int gmt_report_usage (struct GMTAPI_CTRL *API, struct GMT_OPTION *options, unsigned int special, int (*usage)(struct GMTAPI_CTRL *, int)) {
19549 	/* Handle the way classic and modern mode modules report their usage messages.
19550 	 * Set special == 1 if the classic module can be run with no options at all and still be expected to do things (e.g., silently read stdin) */
19551 	int code = GMT_NOERROR;	/* Default is no usage message was requested and we move on to parsing the arguments */
19552 	if (API->GMT->current.setting.run_mode == GMT_MODERN) {	/* Under modern mode we always require an option like -? or -^ to call usage */
19553 		if (options) {	/* Modern mode will only print the usage if one of the usage-options are given (but see exception for one-liners) */
19554 			if (options->option == GMT_OPT_USAGE)	/* -? Return the usage message */
19555 				code = GMT_OPT_USAGE;
19556 			else if (options->option == GMT_OPT_SYNOPSIS)	/* -^ or - Return the synopsis message */
19557 				code = GMT_SYNOPSIS;
19558 			else if (options->option == '+' && !options->arg[0])	/* -+ Return the extended synopsis message */
19559 				code = GMT_OPT_USAGE, API->GMT->common.synopsis.extended = true;
19560 		}
19561 		else if (API->usage)	/* One-liner modern mode with no args must give usage */
19562 			code = GMT_OPT_USAGE;
19563 	}
19564 	else if (special) {	/* Some classic modules require an option to show usage, otherwise expect input (e.g., gmtinfo) */
19565 		if (options && options->option == GMT_OPT_USAGE)
19566 			code = GMT_OPT_USAGE;
19567 		if (options && options->option == GMT_OPT_SYNOPSIS)
19568 			code = GMT_SYNOPSIS;
19569 	}
19570 	else {	/* Regular classic module behavior */
19571 		if (!options || options->option == GMT_OPT_USAGE)
19572 			code = GMT_OPT_USAGE;
19573 		else if (options->option == GMT_OPT_SYNOPSIS)
19574 			code = GMT_SYNOPSIS;
19575 	}
19576 
19577 	if (code)	/* Must call the usage function */
19578 		usage (API, code);
19579 	return (code);
19580 }
19581 
gmtlib_reparse_i_option(struct GMT_CTRL * GMT,uint64_t n_columns)19582 void gmtlib_reparse_i_option (struct GMT_CTRL *GMT, uint64_t n_columns) {
19583 	char text[GMT_LEN8] = {""}, token[PATH_MAX] = {""};
19584 	bool o_trailing = GMT->current.io.trailing_text[GMT_OUT];	/* Since -i parsing below will wipe any -o setting that excludes trailing text */
19585 	size_t k;
19586 	if (n_columns == 0) return;	/* Cannot update the string */
19587 	for (k = strlen (GMT->common.i.string) - 1; k && !(GMT->common.i.string[k] == ':' || GMT->common.i.string[k] == '-'); k--);	/* Find the last : or - in open-ended sequence */
19588 	strncpy (token, GMT->common.i.string, k+1);	/* Get duplicate, this ends with - or : */
19589 	sprintf (text, "%d", (int)n_columns-1);
19590 	strcat (token, text);	/* Add explicit last column to include */
19591 	if (GMT->common.i.string[k+1] == ',') strncat (token, &GMT->common.i.string[k+1],PATH_MAX-1);	/* Probably trailing text selections */
19592 	GMT->common.i.active = false;	/* So we can parse again */
19593 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reparse -i%s\n", token);
19594 	gmt_parse_common_options (GMT, "i", 'i', token);	/* Re-parse updated -i */
19595 	GMT->current.io.trailing_text[GMT_OUT] = o_trailing;	/* Reset to what was parsed initially */
19596 }
19597 
gmtlib_reparse_o_option(struct GMT_CTRL * GMT,uint64_t n_columns)19598 void gmtlib_reparse_o_option (struct GMT_CTRL *GMT, uint64_t n_columns) {
19599 	char text[GMT_LEN8] = {""}, token[PATH_MAX] = {""};
19600 	size_t k;
19601 	if (n_columns == 0) {
19602 		GMT->current.io.output = gmtlib_ascii_output_trailing_text;	/* Just print trailing text */
19603 		return;	/* Cannot update the string */
19604 	}
19605 	for (k = strlen (GMT->common.o.string) - 1; k && !(GMT->common.o.string[k] == ':' || GMT->common.o.string[k] == '-'); k--);	/* Find the last : or - in open-ended sequence */
19606 	strncpy (token, GMT->common.o.string, k+1);	/* Get duplicate, this ends with - or : */
19607 	sprintf (text, "%d", (int)n_columns-1);
19608 	strcat (token, text);	/* Add explicit last column to include */
19609 	if (GMT->common.o.string[k+1] == ',') strncat (token, &GMT->common.o.string[k+1],PATH_MAX-1);	/* Probably trailing text selections */
19610 	GMT->common.o.active = false;	/* So we can parse again */
19611 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reparse -o%s\n", token);
19612 	gmt_parse_common_options (GMT, "o", 'o', token);	/* Re-parse updated -o */
19613 }
19614