/* * psql - the PostgreSQL interactive terminal * * Copyright (c) 2000-2023, PostgreSQL Global Development Group * * src/bin/psql/input.c */ #include "postgres_fe.h" #ifndef WIN32 #include #endif #include #include #include "common.h" #include "common/logging.h" #include "input.h" #include "settings.h" #include "tab-complete.h" #ifndef WIN32 #define PSQLHISTORY ".psql_history" #else #define PSQLHISTORY "psql_history" #endif /* Runtime options for turning off readline and history */ /* (of course there is no runtime command for doing that :) */ #ifdef USE_READLINE static bool useReadline; static bool useHistory; static char *psql_history; static int history_lines_added; /* * Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY * * It is assumed NL_IN_HISTORY will never be entered by the user * nor appear inside a multi-byte string. 0x00 is not properly * handled by the readline routines so it can not be used * for this purpose. */ #define NL_IN_HISTORY 0x01 #endif static void finishInput(void); /* * gets_interactive() * * Gets a line of interactive input, using readline if desired. * * prompt: the prompt string to be used * query_buf: buffer containing lines already read in the current command * (query_buf is not modified here, but may be consulted for tab completion) * * The result is a malloc'd string. * * Caller *must* have set up sigint_interrupt_jmp before calling. */ char * gets_interactive(const char *prompt, PQExpBuffer query_buf) { #ifdef USE_READLINE if (useReadline) { char *result; /* * Some versions of readline don't notice SIGWINCH signals that arrive * when not actively reading input. The simplest fix is to always * re-read the terminal size. This leaves a window for SIGWINCH to be * missed between here and where readline() enables libreadline's * signal handler, but that's probably short enough to be ignored. */ #ifdef HAVE_RL_RESET_SCREEN_SIZE rl_reset_screen_size(); #endif /* Make current query_buf available to tab completion callback */ tab_completion_query_buf = query_buf; /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; /* On some platforms, readline is declared as readline(char *) */ result = readline((char *) prompt); /* Disable SIGINT again */ sigint_interrupt_enabled = false; /* Pure neatnik-ism */ tab_completion_query_buf = NULL; return result; } #endif fputs(prompt, stdout); fflush(stdout); return gets_fromFile(stdin); } /* * Append the line to the history buffer, making sure there is a trailing '\n' */ void pg_append_history(const char *s, PQExpBuffer history_buf) { #ifdef USE_READLINE if (useHistory && s) { appendPQExpBufferStr(history_buf, s); if (!s[0] || s[strlen(s) - 1] != '\n') appendPQExpBufferChar(history_buf, '\n'); } #endif } /* * Emit accumulated history entry to readline's history mechanism, * then reset the buffer to empty. * * Note: we write nothing if history_buf is empty, so extra calls to this * function don't hurt. There must have been at least one line added by * pg_append_history before we'll do anything. */ void pg_send_history(PQExpBuffer history_buf) { #ifdef USE_READLINE static char *prev_hist = NULL; char *s = history_buf->data; int i; /* Trim any trailing \n's (OK to scribble on history_buf) */ for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--) ; s[i + 1] = '\0'; if (useHistory && s[0]) { if (((pset.histcontrol & hctl_ignorespace) && s[0] == ' ') || ((pset.histcontrol & hctl_ignoredups) && prev_hist && strcmp(s, prev_hist) == 0)) { /* Ignore this line as far as history is concerned */ } else { /* Save each previous line for ignoredups processing */ free(prev_hist); prev_hist = pg_strdup(s); /* And send it to readline */ add_history(s); /* Count lines added to history for use later */ history_lines_added++; } } resetPQExpBuffer(history_buf); #endif } /* * gets_fromFile * * Gets a line of noninteractive input from a file (which could be stdin). * The result is a malloc'd string, or NULL on EOF or input error. * * Caller *must* have set up sigint_interrupt_jmp before calling. * * Note: we re-use a static PQExpBuffer for each call. This is to avoid * leaking memory if interrupted by SIGINT. */ char * gets_fromFile(FILE *source) { static PQExpBuffer buffer = NULL; char line[1024]; if (buffer == NULL) /* first time through? */ buffer = createPQExpBuffer(); else resetPQExpBuffer(buffer); for (;;) { char *result; /* Enable SIGINT to longjmp to sigint_interrupt_jmp */ sigint_interrupt_enabled = true; /* Get some data */ result = fgets(line, sizeof(line), source); /* Disable SIGINT again */ sigint_interrupt_enabled = false; /* EOF or error? */ if (result == NULL) { if (ferror(source)) { pg_log_error("could not read from input file: %m"); return NULL; } break; } appendPQExpBufferStr(buffer, line); if (PQExpBufferBroken(buffer)) { pg_log_error("out of memory"); return NULL; } /* EOL? */ if (buffer->len > 0 && buffer->data[buffer->len - 1] == '\n') { buffer->data[buffer->len - 1] = '\0'; return pg_strdup(buffer->data); } } if (buffer->len > 0) /* EOF after reading some bufferload(s) */ return pg_strdup(buffer->data); /* EOF, so return null */ return NULL; } #ifdef USE_READLINE /* * Macros to iterate over each element of the history list in order * * You would think this would be simple enough, but in its inimitable fashion * libedit has managed to break it: in libreadline we must use next_history() * to go from oldest to newest, but in libedit we must use previous_history(). * To detect what to do, we make a trial call of previous_history(): if it * fails, then either next_history() is what to use, or there's zero or one * history entry so that it doesn't matter which direction we go. * * In case that wasn't disgusting enough: the code below is not as obvious as * it might appear. In some libedit releases history_set_pos(0) fails until * at least one add_history() call has been done. This is not an issue for * printHistory() or encode_history(), which cannot be invoked before that has * happened. In decode_history(), that's not so, and what actually happens is * that we are sitting on the newest entry to start with, previous_history() * fails, and we iterate over all the entries using next_history(). So the * decode_history() loop iterates over the entries in the wrong order when * using such a libedit release, and if there were another attempt to use * BEGIN_ITERATE_HISTORY() before some add_history() call had happened, it * wouldn't work. Fortunately we don't care about either of those things. * * Usage pattern is: * * BEGIN_ITERATE_HISTORY(varname); * { * loop body referencing varname->line; * } * END_ITERATE_HISTORY(); */ #define BEGIN_ITERATE_HISTORY(VARNAME) \ do { \ HIST_ENTRY *VARNAME; \ bool use_prev_; \ \ history_set_pos(0); \ use_prev_ = (previous_history() != NULL); \ history_set_pos(0); \ for (VARNAME = current_history(); VARNAME != NULL; \ VARNAME = use_prev_ ? previous_history() : next_history()) \ { \ (void) 0 #define END_ITERATE_HISTORY() \ } \ } while(0) /* * Convert newlines to NL_IN_HISTORY for safe saving in readline history file */ static void encode_history(void) { BEGIN_ITERATE_HISTORY(cur_hist); { char *cur_ptr; /* some platforms declare HIST_ENTRY.line as const char * */ for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++) { if (*cur_ptr == '\n') *cur_ptr = NL_IN_HISTORY; } } END_ITERATE_HISTORY(); } /* * Reverse the above encoding */ static void decode_history(void) { BEGIN_ITERATE_HISTORY(cur_hist); { char *cur_ptr; /* some platforms declare HIST_ENTRY.line as const char * */ for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++) { if (*cur_ptr == NL_IN_HISTORY) *cur_ptr = '\n'; } } END_ITERATE_HISTORY(); } #endif /* USE_READLINE */ /* * Put any startup stuff related to input in here. It's good to maintain * abstraction this way. * * The only "flag" right now is 1 for use readline & history. */ void initializeInput(int flags) { #ifdef USE_READLINE if (flags & 1) { const char *histfile; char home[MAXPGPATH]; useReadline = true; /* set appropriate values for Readline's global variables */ initialize_readline(); #ifdef HAVE_RL_VARIABLE_BIND /* set comment-begin to a useful value for SQL */ (void) rl_variable_bind("comment-begin", "-- "); #endif /* this reads ~/.inputrc, so do it after rl_variable_bind */ rl_initialize(); useHistory = true; using_history(); history_lines_added = 0; histfile = GetVariable(pset.vars, "HISTFILE"); if (histfile == NULL) { char *envhist; envhist = getenv("PSQL_HISTORY"); if (envhist != NULL && strlen(envhist) > 0) histfile = envhist; } if (histfile == NULL) { if (get_home_path(home)) psql_history = psprintf("%s/%s", home, PSQLHISTORY); } else { psql_history = pg_strdup(histfile); expand_tilde(&psql_history); } if (psql_history) { read_history(psql_history); decode_history(); } } #endif atexit(finishInput); } /* * This function saves the readline history when psql exits. * * fname: pathname of history file. (Should really be "const char *", * but some ancient versions of readline omit the const-decoration.) * * max_lines: if >= 0, limit history file to that many entries. */ #ifdef USE_READLINE static bool saveHistory(char *fname, int max_lines) { int errnum; /* * Suppressing the write attempt when HISTFILE is set to /dev/null may * look like a negligible optimization, but it's necessary on e.g. macOS, * where write_history will fail because it tries to chmod the target * file. */ if (strcmp(fname, DEVNULL) != 0) { /* * Encode \n, since otherwise readline will reload multiline history * entries as separate lines. (libedit doesn't really need this, but * we do it anyway since it's too hard to tell which implementation we * are using.) */ encode_history(); /* * On newer versions of libreadline, truncate the history file as * needed and then append what we've added. This avoids overwriting * history from other concurrent sessions (although there are still * race conditions when two sessions exit at about the same time). If * we don't have those functions, fall back to write_history(). */ #if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY) { int nlines; int fd; /* truncate previous entries if needed */ if (max_lines >= 0) { nlines = Max(max_lines - history_lines_added, 0); (void) history_truncate_file(fname, nlines); } /* append_history fails if file doesn't already exist :-( */ fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600); if (fd >= 0) close(fd); /* append the appropriate number of lines */ if (max_lines >= 0) nlines = Min(max_lines, history_lines_added); else nlines = history_lines_added; errnum = append_history(nlines, fname); if (errnum == 0) return true; } #else /* don't have append support */ { /* truncate what we have ... */ if (max_lines >= 0) stifle_history(max_lines); /* ... and overwrite file. Tough luck for concurrent sessions. */ errnum = write_history(fname); if (errnum == 0) return true; } #endif pg_log_error("could not save history to file \"%s\": %m", fname); } return false; } #endif /* * Print history to the specified file, or to the console if fname is NULL * (psql \s command) * * We used to use saveHistory() for this purpose, but that doesn't permit * use of a pager; moreover libedit's implementation behaves incompatibly * (preferring to encode its output) and may fail outright when the target * file is specified as /dev/tty. */ bool printHistory(const char *fname, unsigned short int pager) { #ifdef USE_READLINE FILE *output; bool is_pager; if (!useHistory) return false; if (fname == NULL) { /* use pager, if enabled, when printing to console */ output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL); is_pager = true; } else { output = fopen(fname, "w"); if (output == NULL) { pg_log_error("could not save history to file \"%s\": %m", fname); return false; } is_pager = false; } BEGIN_ITERATE_HISTORY(cur_hist); { fprintf(output, "%s\n", cur_hist->line); } END_ITERATE_HISTORY(); if (is_pager) ClosePager(output); else fclose(output); return true; #else pg_log_error("history is not supported by this installation"); return false; #endif } static void finishInput(void) { #ifdef USE_READLINE if (useHistory && psql_history) { (void) saveHistory(psql_history, pset.histsize); free(psql_history); psql_history = NULL; } #endif }