Skip to content

Commit

Permalink
fix segfault from readline; avoid unnecessary string copy
Browse files Browse the repository at this point in the history
  • Loading branch information
t-kalinowski committed Sep 9, 2024
1 parent 8c0c5ee commit 1cbc4ad
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 16 deletions.
4 changes: 2 additions & 2 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -824,12 +824,12 @@ BEGIN_RCPP
END_RCPP
}
// readline
SEXP readline(const std::string& prompt);
SEXP readline(const char* prompt);
RcppExport SEXP _reticulate_readline(SEXP promptSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< const std::string& >::type prompt(promptSEXP);
Rcpp::traits::input_parameter< const char* >::type prompt(promptSEXP);
rcpp_result_gen = Rcpp::wrap(readline(prompt));
return rcpp_result_gen;
END_RCPP
Expand Down
27 changes: 13 additions & 14 deletions src/readline.cpp
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@

#include <R.h>
#include <Rinternals.h>

#include <string>
#include <cstring> // for strlen, strchr
#define READLINE_BUFFER_SIZE (8192)
extern "C" int R_ReadConsole(const char*, unsigned char*, int, int);

// [[Rcpp::export]]
SEXP readline(const std::string& prompt)
SEXP readline(const char* prompt)
{
// read user input (ensure null termination)
char buffer[READLINE_BUFFER_SIZE];
R_ReadConsole(prompt.c_str(), (unsigned char*) buffer, READLINE_BUFFER_SIZE, 1);
buffer[READLINE_BUFFER_SIZE - 1] = '\0';
if (R_ReadConsole(prompt, (unsigned char*) buffer, READLINE_BUFFER_SIZE, 1) == 0)
return R_NilValue;

// construct resulting string
std::string result(buffer, buffer + strlen(buffer));
buffer[READLINE_BUFFER_SIZE - 1] = '\0'; // Ensure null termination

// truncate to location of inserted newline. if not found, assume
// the user canceled input with e.g. R_EOF
std::string::size_type index = result.find('\n');
if (index == std::string::npos)
return R_NilValue;
// Find the location of the newline character, if any
char* newline_pos = strchr(buffer, '\n');
if (newline_pos == nullptr)
return R_NilValue; // If no newline found, assume user canceled

// Determine length up to the newline (excluding the trailing newline)
size_t input_length = newline_pos - buffer;

// return result (leaving out trailing newline)
SEXP resultSEXP = PROTECT(Rf_allocVector(STRSXP, 1));
SET_STRING_ELT(resultSEXP, 0, Rf_mkCharLen(buffer, index));
SET_STRING_ELT(resultSEXP, 0, Rf_mkCharLen(buffer, input_length));
UNPROTECT(1);
return resultSEXP;
}

0 comments on commit 1cbc4ad

Please sign in to comment.