From 548cd7c44f33fd2297bed1cfb08026726610ac0e Mon Sep 17 00:00:00 2001 From: Esteban82 Date: Thu, 3 Sep 2026 12:38:25 -0300 Subject: [PATCH 1/4] Avoid hanging on stdin when no input file is given and stdin is a terminal gmtapi_init_import() (called by GMT_Init_IO, used by every table-reading module) registered stdin as the input source whenever no files were given, without checking whether stdin is an interactive terminal. When a module was run with no input file and no piped/redirected data, it would block forever waiting for keyboard input. Now we detect an interactive stdin via isatty()/_isatty() and fail fast with a clear error message instead. --- src/gmt_api.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/gmt_api.c b/src/gmt_api.c index 3fd158db1c7..fed85ef3205 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -220,6 +220,14 @@ #include #include "gmt_gsformats.h" +#ifdef _WIN32 +# include +# define gmt_isatty(fd) _isatty(fd) +#else +# include +# define gmt_isatty(fd) isatty(fd) +#endif + #ifdef HAVE_DIRENT_H_ # include #endif @@ -7868,6 +7876,10 @@ GMT_LOCAL int gmtapi_init_import (struct GMTAPI_CTRL *API, enum GMT_enum_family /* Note that n_reg can have changed if we added file args above */ if ((mode & GMT_ADD_STDIO_ALWAYS) || ((mode & GMT_ADD_STDIO_IF_NONE) && n_reg == 0)) { /* Wish to register stdin pointer as a source */ + if (n_reg == 0 && gmt_isatty (fileno (stdin))) { + GMT_Report (API, GMT_MSG_ERROR, "No input file given and standard input is a terminal - refusing to wait forever. Provide a file or pipe/redirect data.\n"); + return_value (API, GMT_RUNTIME_ERROR, GMT_NOTSET); + } if ((object_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_STREAM, geometry, GMT_IN, NULL, API->GMT->session.std[GMT_IN])) == GMT_NOTSET) return_value (API, API->error, GMT_NOTSET); /* Failure to register stdin */ n_reg++; /* Add the single item */ From 0c45e9edc1bd6accc4368d3160d535179b4eb25f Mon Sep 17 00:00:00 2001 From: Esteban82 Date: Thu, 3 Sep 2026 13:30:07 -0300 Subject: [PATCH 2/4] Use GMT's portability convention for the isatty check Follow the existing gmt_notposix.h pattern used for access/fileno rather than an ad-hoc #ifdef _WIN32 block in gmt_api.c: CMake now probes for isatty/_isatty, and gmt_notposix.h maps _isatty to isatty on Windows. The / includes added earlier were redundant, since gmt_notposix.h (via gmt_dev.h) already provides both. Also test the stream actually being registered, API->GMT->session.std[GMT_IN], instead of the global stdin. --- cmake/modules/ConfigureChecks.cmake | 3 +++ src/gmt_api.c | 10 +--------- src/gmt_config.h.in | 2 ++ src/gmt_notposix.h | 7 +++++++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cmake/modules/ConfigureChecks.cmake b/cmake/modules/ConfigureChecks.cmake index 4caccfbc5f8..62fc96f169a 100644 --- a/cmake/modules/ConfigureChecks.cmake +++ b/cmake/modules/ConfigureChecks.cmake @@ -192,15 +192,18 @@ check_symbol_exists (vsnprintf stdio.h HAVE_VSNPRINTF_) if (HAVE_UNISTD_H_) check_symbol_exists (access unistd.h HAVE_ACCESS) check_symbol_exists (getpid unistd.h HAVE_GETPID) + check_symbol_exists (isatty unistd.h HAVE_ISATTY) else (HAVE_UNISTD_H_) # in MinGW: check_symbol_exists (access io.h HAVE_ACCESS) check_symbol_exists (_getpid process.h HAVE__GETPID) + check_symbol_exists (isatty io.h HAVE_ISATTY) endif (HAVE_UNISTD_H_) if (WIN32) check_symbol_exists (_access io.h HAVE__ACCESS) check_symbol_exists (_fileno stdio.h HAVE__FILENO) + check_symbol_exists (_isatty io.h HAVE__ISATTY) check_symbol_exists (_getcwd direct.h HAVE__GETCWD) check_symbol_exists (_mkdir direct.h HAVE__MKDIR) check_symbol_exists (_setmode io.h HAVE__SETMODE) diff --git a/src/gmt_api.c b/src/gmt_api.c index fed85ef3205..1e8155b79ca 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -220,14 +220,6 @@ #include #include "gmt_gsformats.h" -#ifdef _WIN32 -# include -# define gmt_isatty(fd) _isatty(fd) -#else -# include -# define gmt_isatty(fd) isatty(fd) -#endif - #ifdef HAVE_DIRENT_H_ # include #endif @@ -7876,7 +7868,7 @@ GMT_LOCAL int gmtapi_init_import (struct GMTAPI_CTRL *API, enum GMT_enum_family /* Note that n_reg can have changed if we added file args above */ if ((mode & GMT_ADD_STDIO_ALWAYS) || ((mode & GMT_ADD_STDIO_IF_NONE) && n_reg == 0)) { /* Wish to register stdin pointer as a source */ - if (n_reg == 0 && gmt_isatty (fileno (stdin))) { + if (n_reg == 0 && isatty (fileno (API->GMT->session.std[GMT_IN]))) { GMT_Report (API, GMT_MSG_ERROR, "No input file given and standard input is a terminal - refusing to wait forever. Provide a file or pipe/redirect data.\n"); return_value (API, GMT_RUNTIME_ERROR, GMT_NOTSET); } diff --git a/src/gmt_config.h.in b/src/gmt_config.h.in index 339c6d2d559..030a8f2f58e 100644 --- a/src/gmt_config.h.in +++ b/src/gmt_config.h.in @@ -100,6 +100,8 @@ #cmakedefine HAVE_FCNTL #cmakedefine HAVE_FILENO #cmakedefine HAVE__FILENO +#cmakedefine HAVE_ISATTY +#cmakedefine HAVE__ISATTY #cmakedefine HAVE_FSEEKO #cmakedefine HAVE_FTELLO #cmakedefine HAVE__FSEEKI64 diff --git a/src/gmt_notposix.h b/src/gmt_notposix.h index 333467fbe0b..168965cf6c3 100644 --- a/src/gmt_notposix.h +++ b/src/gmt_notposix.h @@ -495,6 +495,13 @@ # define fileno _fileno #endif +/* isatty is usually in unistd.h; we use a macro here + * since the same function under WIN32 is prefixed with _ + * and defined in io.h */ +#if defined HAVE__ISATTY && !defined HAVE_ISATTY +# define isatty _isatty +#endif + /* rmdir is usually in unistd.h; we use a macro here * since the same function under WIN32 is prefixed with _ * and defined in direct.h */ From 8ed0ea838f6c53a2092caccd08cb27272abc06c2 Mon Sep 17 00:00:00 2001 From: Esteban82 Date: Thu, 3 Sep 2026 15:01:05 -0300 Subject: [PATCH 3/4] Change to warning --- src/gmt_api.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gmt_api.c b/src/gmt_api.c index 1e8155b79ca..716c0bda855 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -7868,10 +7868,9 @@ GMT_LOCAL int gmtapi_init_import (struct GMTAPI_CTRL *API, enum GMT_enum_family /* Note that n_reg can have changed if we added file args above */ if ((mode & GMT_ADD_STDIO_ALWAYS) || ((mode & GMT_ADD_STDIO_IF_NONE) && n_reg == 0)) { /* Wish to register stdin pointer as a source */ - if (n_reg == 0 && isatty (fileno (API->GMT->session.std[GMT_IN]))) { - GMT_Report (API, GMT_MSG_ERROR, "No input file given and standard input is a terminal - refusing to wait forever. Provide a file or pipe/redirect data.\n"); - return_value (API, GMT_RUNTIME_ERROR, GMT_NOTSET); - } + if (n_reg == 0 && isatty (fileno (API->GMT->session.std[GMT_IN]))) + GMT_Report (API, GMT_MSG_WARNING, "No input file given - reading table data from standard input (this terminal). " + "Type your data and press Ctrl-D when done, or provide a file, or pipe/redirect data instead.\n"); if ((object_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_STREAM, geometry, GMT_IN, NULL, API->GMT->session.std[GMT_IN])) == GMT_NOTSET) return_value (API, API->error, GMT_NOTSET); /* Failure to register stdin */ n_reg++; /* Add the single item */ From 76cacf9871c86a1beaca0fa1b632dfc5086a5df7 Mon Sep 17 00:00:00 2001 From: Esteban82 Date: Thu, 3 Sep 2026 15:09:50 -0300 Subject: [PATCH 4/4] Improve message --- src/gmt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmt_api.c b/src/gmt_api.c index 716c0bda855..7161b2dec29 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -7870,7 +7870,7 @@ GMT_LOCAL int gmtapi_init_import (struct GMTAPI_CTRL *API, enum GMT_enum_family if ((mode & GMT_ADD_STDIO_ALWAYS) || ((mode & GMT_ADD_STDIO_IF_NONE) && n_reg == 0)) { /* Wish to register stdin pointer as a source */ if (n_reg == 0 && isatty (fileno (API->GMT->session.std[GMT_IN]))) GMT_Report (API, GMT_MSG_WARNING, "No input file given - reading table data from standard input (this terminal). " - "Type your data and press Ctrl-D when done, or provide a file, or pipe/redirect data instead.\n"); + "Type your data and press Ctrl-D when done. To use a file or a pipe instead, press Ctrl-C now and rerun the command with that input.\n"); if ((object_ID = GMT_Register_IO (API, family|GMT_VIA_MODULE_INPUT, GMT_IS_STREAM, geometry, GMT_IN, NULL, API->GMT->session.std[GMT_IN])) == GMT_NOTSET) return_value (API, API->error, GMT_NOTSET); /* Failure to register stdin */ n_reg++; /* Add the single item */