Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce mode setting from GMT_Create_Session in API-driven programs #4525

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/begin.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ EXTERN_MSC int GMT_begin (void *V_API, int mode, void *args) {
/*----------------------- Standard module initialization and parsing ----------------------*/

if (API == NULL) return (GMT_NOT_A_SESSION);
if (gmt_modern_in_classic_session (API, "begin")) /* If not command-line or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
return (GMT_RUNTIME_ERROR);
if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE)); /* Return the purpose of program */
options = GMT_Create_Options (API, mode, args); if (API->error) return (API->error); /* Set or get option list */

Expand Down
2 changes: 2 additions & 0 deletions src/end.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ EXTERN_MSC int GMT_end (void *V_API, int mode, void *args) {
/*----------------------- Standard module initialization and parsing ----------------------*/

if (API == NULL) return (GMT_NOT_A_SESSION);
if (gmt_modern_in_classic_session (API, "end")) /* If not command-line or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
return (GMT_RUNTIME_ERROR);
if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE)); /* Return the purpose of program */
options = GMT_Create_Options (API, mode, args); if (API->error) return (API->error); /* Set or get option list */

Expand Down
2 changes: 2 additions & 0 deletions src/figure.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ EXTERN_MSC int GMT_figure (void *V_API, int mode, void *args) {
/*----------------------- Standard module initialization and parsing ----------------------*/

if (API == NULL) return (GMT_NOT_A_SESSION);
if (gmt_modern_in_classic_session (API, "figure")) /* If not command-line or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
return (GMT_RUNTIME_ERROR);
if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE)); /* Return the purpose of program */
options = GMT_Create_Options (API, mode, args); if (API->error) return (API->error); /* Set or get option list */

Expand Down
2 changes: 1 addition & 1 deletion src/gmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ int main (int argc, char *argv[]) {
gmt_main = !strcmp (module, PROGRAM_NAME); /* true if running the main program, false otherwise */

/* Initialize new GMT session */
if ((api_ctrl = GMT_Create_Session (argv[0], GMT_PAD_DEFAULT, mode, NULL)) == NULL)
if ((api_ctrl = GMT_Create_Session (argv[0], GMT_PAD_DEFAULT, mode | GMT_SESSION_CMDLINE, NULL)) == NULL)
return GMT_RUNTIME_ERROR;

api_ctrl->internal = true; /* This is a proper GMT commandline session (external programs will default to false) */
Expand Down
9 changes: 9 additions & 0 deletions src/gmt_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -15942,3 +15942,12 @@ int64_t gmt_eliminate_duplicates (struct GMTAPI_CTRL *API, struct GMT_DATASET *D

return (n_dup);
}

bool gmt_modern_in_classic_session (struct GMTAPI_CTRL *API, const char *module) {
/* If not commandline or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
if (API->runmode) return false; /* Already in modern mode */
if (API->external) return false; /* An external call from MATLAB, Julia, or Python */
if (API->cmdline) return false; /* A standard command-line call via gmt program */
GMT_Report (API, GMT_MSG_ERROR, "Cannot call module \"%s\" in a classic session created via a call to GMT_Create_Session\n", module);
return true; /* Sorry, calling a modern-mode only module from a classic session started from GMT_Create_Session in a C/C++ program is not allowed */
}
2 changes: 2 additions & 0 deletions src/gmt_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ enum GMT_enum_basemap {
GMT_BASEMAP_ANNOT_BEFORE = 0,
GMT_BASEMAP_ANNOT_AFTER = 4};

#define GMT_SESSION_CMDLINE 64 /* Passed when GMT_Create_Session is called from the command-line gmt.c driver only */

/*! Handling of periodic data */
enum GMT_time_period {
GMT_CYCLE_SEC = 1,
Expand Down
34 changes: 28 additions & 6 deletions src/gmt_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -18342,8 +18342,29 @@ struct GMT_CTRL *gmt_begin (struct GMTAPI_CTRL *API, const char *session, unsign
return NULL;
}

/* Set up hash table for GMT_keywords (used in gmt_conf) */

if (gmt_hash_init (GMT, keys_hashnode, GMT_keywords, GMT_N_KEYS, GMT_N_KEYS)) { /* Initialize hash table for GMT defaults */
gmtinit_free_GMT_ctrl (GMT); /* Deallocate control structure */
return NULL;
}

/* Set up hash table for colornames (used to convert <colorname> to <r/g/b>) */

if (gmt_hash_init (GMT, GMT->session.rgb_hashnode, gmt_M_color_name, GMT_N_COLOR_NAMES, GMT_N_COLOR_NAMES)) {
gmtinit_free_GMT_ctrl (GMT); /* Deallocate control structure */
return NULL;
}

GMT_Report (API, GMT_MSG_DEBUG, "Enter: gmt_manage_workflow\n");
if (gmt_manage_workflow (API, GMT_USE_WORKFLOW, NULL)) {
if (API->runmode && !API->external && !API->cmdline) { /* Special case */
/* If external C call then there is no gmt.c driver and only one GMT_Create_Session, so we must init a new session here, if in modern mode */
mode = GMT_BEGIN_WORKFLOW;
GMT->current.setting.run_mode = GMT_MODERN; /* Set here so we get the benefits of a "gmt module" initialization for PSL */
}
else /* Regular use */
mode = GMT_USE_WORKFLOW;
if (gmt_manage_workflow (API, mode, NULL)) {
GMT_Report (API, GMT_MSG_ERROR, "Could not initialize the GMT workflow - Aborting.\n");
gmtinit_free_GMT_ctrl (GMT); /* Deallocate control structure */
return NULL;
Expand Down Expand Up @@ -18384,7 +18405,7 @@ struct GMT_CTRL *gmt_begin (struct GMTAPI_CTRL *API, const char *session, unsign
gmt_reload_settings (GMT); /* Initialize the standard GMT system default settings and overload with user's settings */
GMT_Report (API, GMT_MSG_DEBUG, "Exit: gmt_reload_settings\n");

if (API->runmode) GMT->current.setting.run_mode = GMT_MODERN; /* Enforced at API Creation */
if (API->runmode) GMT->current.setting.run_mode = GMT_MODERN; /* Enforced at API Creation but set AFTER gmt_reload_settings */

/* There is no longer a -m option in GMT so multi segments are now always true.
However, in GMT_COMPAT mode the -mi and -mo options WILL turn off multi in the other direction. */
Expand Down Expand Up @@ -18753,19 +18774,20 @@ GMT_LOCAL int gmtinit_get_graphics_formats (struct GMT_CTRL *GMT, char *formats,
}

GMT_LOCAL bool gmtinit_check_if_autosize (struct GMTAPI_CTRL *API, int ID) {
/* Check if the BoundingBox line in the half-baked PostScript file has max dimension (32767x32767)
/* Check if the BoundingBox line in the half-baked PostScript file has max dimension (GMT_PAPER_DIM x GMT_PAPER_DIM)
* which we used to enforce automatic cropping to actual size [and possible extra margins] */
char file[PATH_MAX] = {""};
char file[PATH_MAX] = {""}, def_dim[GMT_LEN32] = {""};
FILE *fp;
snprintf (file, PATH_MAX, "%s/gmt_%d.ps-", API->gwf_dir, ID); /* Current half-baked PostScript file */
if ((fp = fopen (file, "r")) == NULL) { /* This is an unmitigated disaster */
GMT_Report (API, GMT_MSG_ERROR, "Failed to open half-baked PostScript file %s\n", file);
return false;
}
sprintf (def_dim, "%d %d", GMT_PAPER_DIM, GMT_PAPER_DIM); /* Create the comparison string */
gmt_fgets (API->GMT, file, PATH_MAX, fp); /* Skip first line */
gmt_fgets (API->GMT, file, PATH_MAX, fp); /* Get second line with BoundingBox code */
fclose (fp);
if (strstr (file, "32767 32767")) return true; /* Max paper size means auto-sized media */
if (strstr (file, def_dim)) return true; /* Max paper size means auto-sized media */
return false;
}

Expand Down Expand Up @@ -19578,7 +19600,7 @@ int gmt_manage_workflow (struct GMTAPI_CTRL *API, unsigned int mode, char *text)
gmtinit_setautopagesize (API->GMT); /* Reset to auto */
}
snprintf (dir, PATH_MAX, "%s/%s", API->gwf_dir, GMT_SETTINGS_FILE); /* Reuse dir string for saving gmt.conf to this dir */
API->GMT->current.setting.run_mode = GMT_MODERN; /* Enable modern mode here so putdefaults can skip writing PS_MEDIA if not PostScript output */
API->GMT->current.setting.run_mode = GMT_MODERN; /* Enable modern mode here AFTER gmt_conf call so putdefaults can skip writing PS_MEDIA if not PostScript output */
error = gmtinit_put_session_name (API, text); /* Store session name, possibly setting psconvert options */
gmt_putdefaults (API->GMT, dir); /* Write current GMT defaults to this sessions gmt.conf file in the workflow directory */
API->GMT->current.setting.history_orig = API->GMT->current.setting.history; /* Temporarily turn off history so nothing is copied into the workflow dir */
Expand Down
1 change: 1 addition & 0 deletions src/gmt_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ struct GMTAPI_CTRL {
int current_item[2]; /* Array number of current dataset being processed (in and out)*/
unsigned int pad; /* Session default for number of rows/cols padding for grids [2] */
unsigned int external; /* 1 if called via external API (MATLAB, Python) [0] */
unsigned int cmdline; /* 1 if called via gmt.c [0] */
unsigned int runmode; /* nonzero for GMT modern runmode [0 = classic] */
enum GMT_enum_fmt shape; /* GMT_IS_COL_FORMAT (2) if column-major (MATLAB, Fortran), GMT_IS_ROW_FORMAT (1) if row-major (Python, C/C++) [1] */
unsigned int leave_grid_scaled; /* 1 if we don't want to unpack a grid after we packed it for writing [0] */
Expand Down
1 change: 1 addition & 0 deletions src/gmt_prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ EXTERN_MSC void gmt_polar_to_cart (struct GMT_CTRL *GMT, double r, double theta,
EXTERN_MSC void gmt_cart_to_polar (struct GMT_CTRL *GMT, double *r, double *theta, double *a, bool degrees);

/* From gmt_api.c */
EXTERN_MSC bool gmt_modern_in_classic_session (struct GMTAPI_CTRL *API, const char *module);
EXTERN_MSC int64_t gmt_eliminate_duplicates (struct GMTAPI_CTRL *API, struct GMT_DATASET *D, uint64_t cols[], uint64_t ncols, bool text);
EXTERN_MSC unsigned int gmt_whole_earth (struct GMT_CTRL *GMT, double we_in[], double we_out[]);
EXTERN_MSC int gmt_copy (struct GMTAPI_CTRL *API, enum GMT_enum_family family, unsigned int direction, char *ifile, char *ofile);
Expand Down
2 changes: 2 additions & 0 deletions src/inset.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ EXTERN_MSC int GMT_inset (void *V_API, int mode, void *args) {
/*----------------------- Standard module initialization and parsing ----------------------*/

if (API == NULL) return (GMT_NOT_A_SESSION);
if (gmt_modern_in_classic_session (API, "inset")) /* If not command-line or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
return (GMT_RUNTIME_ERROR);
if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE)); /* Return the purpose of program */
options = GMT_Create_Options (API, mode, args); if (API->error) return (API->error); /* Set or get option list */

Expand Down
2 changes: 2 additions & 0 deletions src/subplot.c
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,8 @@ EXTERN_MSC int GMT_subplot (void *V_API, int mode, void *args) {
/*----------------------- Standard module initialization and parsing ----------------------*/

if (API == NULL) return (GMT_NOT_A_SESSION);
if (gmt_modern_in_classic_session (API, "subplot")) /* If not command-line or external it means use of C API directly, so GMT_Create_Session needs to set modern mode first */
return (GMT_RUNTIME_ERROR);
if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE)); /* Return the purpose of program */
options = GMT_Create_Options (API, mode, args); if (API->error) return (API->error); /* Set or get option list */

Expand Down
29 changes: 29 additions & 0 deletions src/testapi_map.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "gmt.h"
/* Used to examine https://github.com/GenericMappingTools/gmt/issues/4518
* Give an graphic extension to replicate that, otherwise we make a PS that can be
* compared with the original PS so the test can run as normal.
* Run testapi_map jpg to show the result is properly cropped.
* [add "| 458752" to GMT_SESSION_RUNMODE to simulate -Vd ]
*/
int main (int argc, char *argv[]) {
void *API;

/* Initialize the GMT session */
if ((API = GMT_Create_Session ("GMT_plot", 2, GMT_SESSION_RUNMODE, NULL)) == NULL)
return EXIT_FAILURE;
if (argc > 1) { /* Gave a particular graphics format (we hope - no checking here) */
char string[64] = {""};
sprintf (string, "apimap %s", argv[1]);
GMT_Call_Module (API, "begin", GMT_MODULE_CMD, string);
}
else /* Default to PostScript */
GMT_Call_Module (API, "begin", GMT_MODULE_CMD, "apimap ps");
GMT_Call_Module (API, "basemap", GMT_MODULE_CMD, "-BWESN -Bxa30mg30m -Bya20mg20m -JM7.27/42.27/15c -R5.5/41.425/9.0/43.1r");
if (argc > 1)
GMT_Call_Module (API, "end", GMT_MODULE_CMD, "show");
else
GMT_Call_Module (API, "end", GMT_MODULE_CMD, NULL);
/* Destroy session */
if (GMT_Destroy_Session (API))
return EXIT_FAILURE;
}
Binary file added test/api/apimap.ps
Binary file not shown.
6 changes: 6 additions & 0 deletions test/api/apimap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
#
# Test the C API for simulating a modern mode basemap plot
# See https://github.com/GenericMappingTools/gmt/issues/4518
ps=apimap.ps
testapi_map > $ps
1 change: 1 addition & 0 deletions test/gmtest.in
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ for apiprog in \
testapi_cube \
testapi_makecpt \
testapi_matrix \
testapi_map \
testapi_matrix_pad \
testapi_matrix_plot \
testapi_mixmatrix \
Expand Down