Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

input.c

Blame
  • input.c 6.66 KiB
    /*
     * psql - the PostgreSQL interactive terminal
     *
     * Copyright (c) 2000-2006, PostgreSQL Global Development Group
     *
     * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.53 2006/03/21 13:38:12 momjian Exp $
     */
    #include "postgres_fe.h"
    
    #include "pqexpbuffer.h"
    #include "input.h"
    #include "settings.h"
    #include "tab-complete.h"
    #include "common.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;
    char	   *psql_history;
    
    /*
     *	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
    
    enum histcontrol
    {
    	hctl_none = 0,
    	hctl_ignorespace = 1,
    	hctl_ignoredups = 2,
    	hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups
    };
    #endif
    
    #ifdef HAVE_ATEXIT
    static void finishInput(void);
    #else
    /* designed for use with on_exit() */
    static void finishInput(int, void *);
    #endif
    
    
    #ifdef USE_READLINE
    static enum histcontrol
    GetHistControlConfig(void)
    {
    	enum histcontrol HC;
    	const char *var;
    
    	var = GetVariable(pset.vars, "HISTCONTROL");
    
    	if (!var)
    		HC = hctl_none;
    	else if (strcmp(var, "ignorespace") == 0)
    		HC = hctl_ignorespace;
    	else if (strcmp(var, "ignoredups") == 0)
    		HC = hctl_ignoredups;
    	else if (strcmp(var, "ignoreboth") == 0)
    		HC = hctl_ignoreboth;
    	else
    		HC = hctl_none;
    
    	return HC;
    }
    #endif
    
    
    static char *
    gets_basic(const char prompt[])
    {
    	fputs(prompt, stdout);
    	fflush(stdout);
    	return gets_fromFile(stdin);
    }
    
    
    /*
     * gets_interactive()
     *
     * Gets a line of interactive input, using readline of desired.
     * The result is malloc'ed.
     */
    char *
    gets_interactive(const char *prompt)
    {
    #ifdef USE_READLINE
    	char	   *s;
    
    	if (useReadline)
    		/* On some platforms, readline is declared as readline(char *) */
    		s = readline((char *) prompt);
    	else
    		s = gets_basic(prompt);
    
    	return s;
    #else
    	return gets_basic(prompt);
    #endif
    }
    
    
    /* Put the line in the history buffer and also add the trailing \n */
    void
    pg_append_history(char *s, PQExpBuffer history_buf)
    {
    #ifdef USE_READLINE
    
    	int slen;
    	if (useReadline && useHistory && s && s[0])
    	{
    		slen = strlen(s);
    		if (s[slen-1] == '\n')
    			appendPQExpBufferStr(history_buf, s);
    		else
    		{
    			appendPQExpBufferStr(history_buf, s);
    			appendPQExpBufferChar(history_buf, '\n');
    		}
    	}	
    #endif	
    }
    
    
    /*
     *	Feed the string to readline
     */
    void
    pg_write_history(char *s)
    {
    #ifdef USE_READLINE
    	static char *prev_hist;
    	int slen, i;
    	
    	if (useReadline && useHistory )
    	{
    		enum histcontrol HC;
    		
    		/* Flushing of empty buffer should do nothing */
    		if  (*s == 0)
    			return;
    		
    		prev_hist = NULL;
    			
    		HC = GetHistControlConfig();
    
    		if (((HC & hctl_ignorespace) && s[0] == ' ') ||
    		  ((HC & hctl_ignoredups) && prev_hist && strcmp(s, prev_hist) == 0))
    		{
    			/* Ignore this line as far as history is concerned */
    		}
    		else
    		{
    			free(prev_hist);
    			slen = strlen(s);
    			/* Trim the trailing \n's */
    			for (i = slen-1; i >= 0 && s[i] == '\n'; i--)
    				;
    			s[i + 1] = '\0';
    			prev_hist = pg_strdup(s);
    			add_history(s);
    		}
    	}
    #endif
    }
    
    void
    pg_clear_history(PQExpBuffer history_buf)
    {
    #ifdef USE_READLINE	
    	if (useReadline && useHistory)
    		resetPQExpBuffer(history_buf);
    #endif
    }
    
    
    /*
     * gets_fromFile
     *
     * Gets a line of noninteractive input from a file (which could be stdin).
     */
    char *
    gets_fromFile(FILE *source)
    {
    	PQExpBufferData buffer;
    	char		line[1024];
    
    	initPQExpBuffer(&buffer);
    
    	while (fgets(line, sizeof(line), source) != NULL)
    	{
    		appendPQExpBufferStr(&buffer, line);
    		if (buffer.data[buffer.len - 1] == '\n')
    		{
    			buffer.data[buffer.len - 1] = '\0';
    			return buffer.data;
    		}
    	}
    
    	if (buffer.len > 0)
    		return buffer.data;		/* EOF after reading some bufferload(s) */
    
    	/* EOF, so return null */
    	termPQExpBuffer(&buffer);
    	return NULL;
    }
    
    
    #ifdef USE_READLINE
    static void
    encode_history(void)
    {
    	HIST_ENTRY *cur_hist;
    	char *cur_ptr;
    
    	for (history_set_pos(0), cur_hist = current_history();
    		 cur_hist; cur_hist = next_history())
    		for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
    			if (*cur_ptr == '\n')
    				*cur_ptr = NL_IN_HISTORY;
    }
    
    static void
    decode_history(void)
    {
    	HIST_ENTRY *cur_hist;
    	char *cur_ptr;
    
    	for (history_set_pos(0), cur_hist = current_history();
    		 cur_hist; cur_hist = next_history())
    		for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
    			if (*cur_ptr == NL_IN_HISTORY)
    				*cur_ptr = '\n';
    }
    #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)
    	{
    		char		home[MAXPGPATH];
    
    		useReadline = true;
    		initialize_readline();
    
    		useHistory = true;
    		if (GetVariable(pset.vars, "HISTSIZE") == NULL)
    			SetVariable(pset.vars, "HISTSIZE", "500");
    		using_history();
    
    		if (GetVariable(pset.vars, "HISTFILE") == NULL)
    		{
    			if (get_home_path(home))
    			{
    				psql_history = pg_malloc(strlen(home) + 1 +
    										 strlen(PSQLHISTORY) + 1);
    				snprintf(psql_history, MAXPGPATH, "%s/%s", home, PSQLHISTORY);
    			}
    		}
    		else
    		{
    			psql_history = pg_strdup(GetVariable(pset.vars, "HISTFILE"));
    			expand_tilde(&psql_history);
    		}
    
    		if (psql_history)
    			read_history(psql_history);
    			
    		decode_history();
    	}
    #endif
    
    #ifdef HAVE_ATEXIT
    	atexit(finishInput);
    #else
    	on_exit(finishInput, NULL);
    #endif
    }
    
    
    /* This function is designed for saving the readline history when user 
     * run \s command or when psql finishes. 
     * We have an argument named encodeFlag to handle those cases differently
     * In that case of call via \s we don't really need to encode \n as \x01,
     * but when we save history for Readline we must do that conversion
     */
    bool
    saveHistory(char *fname, bool encodeFlag)
    {
    #ifdef USE_READLINE
    	if (useHistory && fname)
    	{
    		if (encodeFlag)
    			encode_history();
    		if (write_history(fname) == 0)
    			return true;
    
    		psql_error("could not save history to file \"%s\": %s\n", fname, strerror(errno));
    	}
    #else
    	psql_error("history is not supported by this installation\n");
    #endif
    
    	return false;
    }
    
    
    static void
    #ifdef HAVE_ATEXIT
    finishInput(void)
    #else
    finishInput(int exitstatus, void *arg)
    #endif
    {
    #ifdef USE_READLINE
    	if (useHistory && psql_history)
    	{
    		int			hist_size;
    
    		hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
    		if (hist_size >= 0)
    			stifle_history(hist_size);
    
    		saveHistory(psql_history, true);
    		free(psql_history);
    		psql_history = NULL;
    	}
    #endif
    }