From ca19ecc7e1466c6e8439c1bcf7047469cc842f59 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 17 Nov 2023 08:44:41 +0100 Subject: [PATCH 1/4] Relax column content check for pstext No neeed to scan pstext input for absolute time beyond col 2. --- src/gmt_io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gmt_io.c b/src/gmt_io.c index 0032c6a283b..1e78d865e61 100644 --- a/src/gmt_io.c +++ b/src/gmt_io.c @@ -3528,21 +3528,26 @@ GMT_LOCAL unsigned int gmtio_examine_current_record (struct GMT_CTRL *GMT, char * (which is what we are handling here) a huge variety of datetime strings are possible via * the FORMAT_DATE_IN, FORMAT_CLOCK_IN settings. */ - unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, k, *type = NULL; + unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, n_cols_to_check = 0, k, *type = NULL; int got; enum gmt_col_enum phys_col_type; - bool found_text = false; + bool found_text = false, text = (strncmp (GMT->init.module_name, "pstext", 6U) == 0); char token[GMT_BUFSIZ], message[GMT_BUFSIZ] = {""}; double value; static char *flavor[4] = {"", "Numerical only", "Text only", "Numerical with trailing text"}; type = gmt_M_memory (GMT, NULL, GMT_MAX_COLUMNS, unsigned int); *tpos = pos; + if (text) n_cols_to_check = 2; /* Only check the first two columns for pstext */ while (!found_text && (gmt_strtok (record, GMT->current.io.scan_separators, &pos, token))) { if ((phys_col_type = gmt_get_column_type (GMT, GMT_IN, col)) == GMT_IS_STRING) { /* Explicit start of trailing text via -fs */ found_text = true; /* We stop right here, return flag as nan and hence n_columns will be col. */ got = GMT_IS_NAN; } + else if (text && col >= n_cols_to_check) { + got = GMT_IS_NAN; + col++; + } else { /* Auto-guess from examining the token */ phys_col_type = gmtio_physical_coltype (GMT, col); if (phys_col_type == GMT_IS_ABSTIME || gmtlib_maybe_abstime (GMT, token)) { /* Is or might be ISO Absolute time; if not we got junk (and got -> GMT_IS_NAN) */ From 05ff8fb0c4df3142b69571afb483501cfdbfebb0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 17 Nov 2023 09:32:32 +0100 Subject: [PATCH 2/4] Special treatment for pstext It has odd input formats so we help gmtio_examine_current_record with parsing. --- src/gmt_io.c | 8 ++++---- src/gmt_private.h | 1 + src/pstext.c | 9 ++++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/gmt_io.c b/src/gmt_io.c index 1e78d865e61..ccb2d55fbbc 100644 --- a/src/gmt_io.c +++ b/src/gmt_io.c @@ -3390,6 +3390,7 @@ bool gmtlib_maybe_abstime (struct GMT_CTRL *GMT, char *txt) { gmt_M_unused (GMT); if (L > 24U) return false; /* Most likely too long to be an absolute time, e.g. 31-SEP-2000T13:45:31.3333 is 24 char long */ + if (L < 4U) return false; /* Most likely too short to be an absolute time, e.g. 2001T is 5 char long */ for (k = 0; k < L; k++) { /* Count slashes and dashes */ if (txt[k] == '-') { n_dash++; if (start == -1) start = k; } else if (txt[k] == '/') { n_slash++; if (start == -1) start = k; } @@ -3528,23 +3529,22 @@ GMT_LOCAL unsigned int gmtio_examine_current_record (struct GMT_CTRL *GMT, char * (which is what we are handling here) a huge variety of datetime strings are possible via * the FORMAT_DATE_IN, FORMAT_CLOCK_IN settings. */ - unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, n_cols_to_check = 0, k, *type = NULL; + unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, k, *type = NULL, n_numeric_cols = GMT->parent->n_numerical_columns; int got; enum gmt_col_enum phys_col_type; - bool found_text = false, text = (strncmp (GMT->init.module_name, "pstext", 6U) == 0); + bool found_text = false; char token[GMT_BUFSIZ], message[GMT_BUFSIZ] = {""}; double value; static char *flavor[4] = {"", "Numerical only", "Text only", "Numerical with trailing text"}; type = gmt_M_memory (GMT, NULL, GMT_MAX_COLUMNS, unsigned int); *tpos = pos; - if (text) n_cols_to_check = 2; /* Only check the first two columns for pstext */ while (!found_text && (gmt_strtok (record, GMT->current.io.scan_separators, &pos, token))) { if ((phys_col_type = gmt_get_column_type (GMT, GMT_IN, col)) == GMT_IS_STRING) { /* Explicit start of trailing text via -fs */ found_text = true; /* We stop right here, return flag as nan and hence n_columns will be col. */ got = GMT_IS_NAN; } - else if (text && col >= n_cols_to_check) { + else if (n_numeric_cols && col >= n_numeric_cols) { /* No point trying to determine what text, font, justification it is */ got = GMT_IS_NAN; col++; } diff --git a/src/gmt_private.h b/src/gmt_private.h index 08692980dea..b6ec9f32d99 100644 --- a/src/gmt_private.h +++ b/src/gmt_private.h @@ -154,6 +154,7 @@ struct GMTAPI_CTRL { unsigned int verbose; /* Used until GMT is set up */ unsigned int n_tmp_headers; /* Number of temporarily held table headers */ unsigned int terminal_width; /* Width of the terminal */ + unsigned int n_numerical_columns; /* 0 except for pstext where there will be 2-4 numerical leading columns before text */ bool registered[2]; /* true if at least one source/destination has been registered (in and out) */ bool io_enabled[2]; /* true if access has been allowed (in and out) */ bool module_input; /* true when we are about to read inputs to the module (command line) */ diff --git a/src/pstext.c b/src/pstext.c index d867afce79d..0e10cbda5ef 100644 --- a/src/pstext.c +++ b/src/pstext.c @@ -77,7 +77,7 @@ struct PSTEXT_CTRL { bool no_input; /* True if we give a single static text and place it via +c */ struct GMT_FONT font; double angle; - int justify, R_justify, nread, first, w_col; + int justify, R_justify, nread, nread_numerics, first, w_col; unsigned int get_text; /* 0 = from data record, 1 = segment label (+l), 2 = segment header (+h), 3 = specified text (+t), 4 = format z using text (+z) */ char read[4]; /* Contains a|A, c, f, and/or j in order required to be read from input */ char *text; @@ -489,6 +489,7 @@ static int parse (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *Ctrl, struct GMT_OPT if (p[1] == '+' || p[1] == '\0') { /* Must read angle from input */ Ctrl->F.read[Ctrl->F.nread] = p[0]; Ctrl->F.nread++; + Ctrl->F.nread_numerics++; } else /* Gave a fixed angle here */ Ctrl->F.angle = atof (&p[1]); @@ -862,6 +863,11 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { GMT_Report (API, GMT_MSG_INFORMATION, "Processing input text table data\n"); pstext_load_parameters_pstext (GMT, &T, Ctrl); /* Pass info from Ctrl to T */ tcol_f = 2 + Ctrl->Z.active; tcol_s = tcol_f + 1; + /* Since pstext input is complicated we need to help gmtio_examine_current_record by determining how many leading numerical columns to expect */ + API->n_numerical_columns = 2; + if (Ctrl->F.get_text == GET_CMD_FORMAT) API->n_numerical_columns++; /* Expect a 3rd column value for formatting */ + if (Ctrl->Z.active) API->n_numerical_columns++; /* Expect a 3-D z coordinate */ + if (Ctrl->F.nread_numerics) API->n_numerical_columns++; /* Will read angle from input file */ n_expected_cols = 2 + Ctrl->Z.active + Ctrl->F.nread + GMT->common.t.n_transparencies; /* Normal number of columns to read, plus any text. This includes x,y */ if (Ctrl->M.active) n_expected_cols += 3; @@ -1503,6 +1509,7 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { GMT->current.map.is_world = old_is_world; GMT->current.io.scan_separators = GMT_TOKEN_SEPARATORS; /* Reset */ + API->n_numerical_columns = 0; gmt_map_basemap (GMT); gmt_plane_perspective (GMT, -1, 0.0); From 7437fa3114061c9bb86fc514a6c16e8a81559c61 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 17 Nov 2023 10:16:53 +0100 Subject: [PATCH 3/4] Improved pstext parsing --- src/gmt_api.c | 1 + src/gmt_io.c | 3 ++- src/gmt_support.c | 2 +- src/pstext.c | 8 +++++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/gmt_api.c b/src/gmt_api.c index 8a8603a9645..3fc720d88f1 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -8408,6 +8408,7 @@ void * GMT_Create_Session (const char *session, unsigned int pad, unsigned int m API->runmode = mode & GMT_SESSION_RUNMODE; /* If nonzero we set up modern GMT run-mode, else classic */ API->no_history = mode & GMT_SESSION_NOHISTORY; /* If nonzero we disable the gmt.history mechanism (shorthands) entirely */ if (API->internal) API->leave_grid_scaled = 1; /* Do NOT undo grid scaling after write since modules do not reuse grids we save some CPU */ + API->n_numerical_columns = GMT_NOTSET; if (session) { /* Pick up a tag for this session */ char *tmptag = strdup (session); API->session_tag = strdup (basename (tmptag)); /* Only used in reporting and error messages */ diff --git a/src/gmt_io.c b/src/gmt_io.c index ccb2d55fbbc..3db5aa2670e 100644 --- a/src/gmt_io.c +++ b/src/gmt_io.c @@ -3529,7 +3529,8 @@ GMT_LOCAL unsigned int gmtio_examine_current_record (struct GMT_CTRL *GMT, char * (which is what we are handling here) a huge variety of datetime strings are possible via * the FORMAT_DATE_IN, FORMAT_CLOCK_IN settings. */ - unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, k, *type = NULL, n_numeric_cols = GMT->parent->n_numerical_columns; + unsigned int ret_val = GMT_READ_DATA, pos = 0, col = 0, k, *type = NULL; + unsigned int n_numeric_cols = (GMT->parent->n_numerical_columns == GMT_NOTSET) ? UINT_MAX : GMT->parent->n_numerical_columns; int got; enum gmt_col_enum phys_col_type; bool found_text = false; diff --git a/src/gmt_support.c b/src/gmt_support.c index 445364f62f4..cbbb34c78d1 100644 --- a/src/gmt_support.c +++ b/src/gmt_support.c @@ -18272,7 +18272,7 @@ bool gmt_no_pstext_input (struct GMTAPI_CTRL *API, char *arg) { /* Determine if -F is such that there is nothing to read */ if (strstr (arg, "+c") == NULL) return false; /* Without +c there will be input */ if (strstr (arg, "+t") == NULL) return false; /* Without +t there will be input */ - if ((c = strstr (arg, "+A")) && (c[2] == '+' || c[2] == '\0')) return false; /* With +a and no arg there must be input */ + if ((c = strstr (arg, "+A")) && (c[2] == '+' || c[2] == '\0')) return false; /* With +A and no arg there must be input */ if ((c = strstr (arg, "+a")) && (c[2] == '+' || c[2] == '\0')) return false; /* With +a and no arg there must be input */ if ((c = strstr (arg, "+j")) && (c[2] == '+' || c[2] == '\0')) return false; /* With +j and no arg there must be input */ if ((c = strstr (arg, "+f")) && (c[2] == '+' || c[2] == '\0')) return false; /* With +f and no arg there must be input */ diff --git a/src/pstext.c b/src/pstext.c index 0e10cbda5ef..f0a610394b9 100644 --- a/src/pstext.c +++ b/src/pstext.c @@ -75,6 +75,7 @@ struct PSTEXT_CTRL { bool get_xy_from_justify; /* True if +c was given and we just get it from input */ bool word; /* True if we are to select a single word from the trailing text as the label */ bool no_input; /* True if we give a single static text and place it via +c */ + bool no_xy_coord; /* If -F+c given then we dont read/parse two coordinates */ struct GMT_FONT font; double angle; int justify, R_justify, nread, nread_numerics, first, w_col; @@ -526,6 +527,7 @@ static int parse (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *Ctrl, struct GMT_OPT if (!explicit_justify) /* If not set explicitly, default to same justification as corner */ Ctrl->F.justify = Ctrl->F.R_justify; } + Ctrl->F.no_xy_coord = true; /* Not reading lon,lat or x,y in this case */ break; case 'l': /* Segment label request */ if (Ctrl->F.get_text) { @@ -864,11 +866,11 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { pstext_load_parameters_pstext (GMT, &T, Ctrl); /* Pass info from Ctrl to T */ tcol_f = 2 + Ctrl->Z.active; tcol_s = tcol_f + 1; /* Since pstext input is complicated we need to help gmtio_examine_current_record by determining how many leading numerical columns to expect */ - API->n_numerical_columns = 2; + API->n_numerical_columns = (Ctrl->F.no_xy_coord) ? 0 : 2; /* Normally first 2 columns are x/y coordinates but not if -F+c */ if (Ctrl->F.get_text == GET_CMD_FORMAT) API->n_numerical_columns++; /* Expect a 3rd column value for formatting */ if (Ctrl->Z.active) API->n_numerical_columns++; /* Expect a 3-D z coordinate */ if (Ctrl->F.nread_numerics) API->n_numerical_columns++; /* Will read angle from input file */ - +fprintf (stderr, "ncol = %d\n", API->n_numerical_columns); n_expected_cols = 2 + Ctrl->Z.active + Ctrl->F.nread + GMT->common.t.n_transparencies; /* Normal number of columns to read, plus any text. This includes x,y */ if (Ctrl->M.active) n_expected_cols += 3; no_in_txt = (Ctrl->F.get_text > 1); /* No text in the input record */ @@ -1509,7 +1511,7 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) { GMT->current.map.is_world = old_is_world; GMT->current.io.scan_separators = GMT_TOKEN_SEPARATORS; /* Reset */ - API->n_numerical_columns = 0; + API->n_numerical_columns = GMT_NOTSET; gmt_map_basemap (GMT); gmt_plane_perspective (GMT, -1, 0.0); From eb08ad84ead5a4f87e9f4f4315de8987a5b23dbd Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 17 Nov 2023 10:27:33 +0100 Subject: [PATCH 4/4] Update gmt_io.c --- src/gmt_io.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gmt_io.c b/src/gmt_io.c index 3db5aa2670e..b8c872ee472 100644 --- a/src/gmt_io.c +++ b/src/gmt_io.c @@ -3398,11 +3398,16 @@ bool gmtlib_maybe_abstime (struct GMT_CTRL *GMT, char *txt) { } if ((c = strchr (txt, 'T'))) { /* Maybe the T between date and clock */ size_t first = (size_t) (c - txt); /* Position of that T must be either 0, 4, or between 9 and 12 */ - if (first == 0 || first == 4 || (first > 8 && first < 13)) return true; /* Absolute date detected (most likely) */ + if (first == 0 || first == 4 || (first > 8 && first < 13)) { + if (txt[first+1] == '\0' || isdigit (txt[first+1])) /* Absolute date detected (most likely), e.g. 2001T, T14:45 */ + return true; + else + return false; + } } - else if (n_colon && n_slash == 0 && n_dash == 0) /* No 'T', got xxx..:yy...[:ss...] probably */ + else if (n_colon && n_slash == 0 && n_dash <= 1) /* No 'T', got [-]xxx..:yy...[:ss...] probably */ return false; - else if (txt[0] == '-' || txt[0] == '+') /* No 'T' and start with a sign is likely non-time related */ + else if (txt[0] == '-' || txt[0] == '+') /* No 'T' and starts with a sign is likely non-time related */ return false; /* datestring most likely do not start with a sign. */ /* Maybe user forgot the 'T' flag? */ if (start > 0 && ((n_dash + n_slash) == 2) && (n_dash == 2 || n_slash == 2)) /* Absolute date detected (most likely) */