input.c 3.82 KB
Newer Older
Peter Eisentraut's avatar
Peter Eisentraut committed
1 2 3
/*
 * psql - the PostgreSQL interactive terminal
 *
4
 * Copyright 2000 by PostgreSQL Global Development Group
Peter Eisentraut's avatar
Peter Eisentraut committed
5
 *
6
 * $Header: /cvsroot/pgsql/src/bin/psql/input.c,v 1.21 2002/09/06 02:33:46 momjian Exp $
Peter Eisentraut's avatar
Peter Eisentraut committed
7
 */
8
#include "postgres_fe.h"
9 10
#include "input.h"

11 12
#include <errno.h>

13
#include "pqexpbuffer.h"
14 15
#include "settings.h"
#include "tab-complete.h"
16
#include "common.h"
17 18 19 20 21 22 23 24

/* 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;
#endif

25
#ifdef HAVE_ATEXIT
26 27
static void finishInput(void);

28 29
#else
/* designed for use with on_exit() */
30
static void finishInput(int, void *);
31 32
#endif

33
#define PSQLHISTORY	".psql_history"
34

35 36 37 38 39 40 41 42

/*
 * gets_interactive()
 *
 * Gets a line of interactive input, using readline of desired.
 * The result is malloced.
 */
char *
43
gets_interactive(char *prompt)
44
{
Bruce Momjian's avatar
Bruce Momjian committed
45
	char	   *s;
46

47
#ifdef USE_READLINE
48 49
	const char *var;
	static char *prev_hist = NULL;
50
#endif
51 52

#ifdef USE_READLINE
Bruce Momjian's avatar
Bruce Momjian committed
53
	if (useReadline)
54
		s = readline(prompt);
Bruce Momjian's avatar
Bruce Momjian committed
55 56
	else
	{
57
#endif
Bruce Momjian's avatar
Bruce Momjian committed
58 59 60
		fputs(prompt, stdout);
		fflush(stdout);
		s = gets_fromFile(stdin);
61
#ifdef USE_READLINE
Bruce Momjian's avatar
Bruce Momjian committed
62
	}
63 64
#endif

65
#ifdef USE_READLINE
66 67 68 69 70 71 72 73 74 75 76 77 78
	if (useHistory && s && s[0] != '\0')
	{
		var = GetVariable(pset.vars, "HISTCONTROL");
		if (!var || (var
					 && !((strcmp(var, "ignorespace") == 0 || strcmp(var, "ignoreboth") == 0) && s[0] == ' ')
					 && !((strcmp(var, "ignoredups") == 0 || strcmp(var, "ignoreboth") == 0) && prev_hist && strcmp(s, prev_hist) == 0)
					 ))
		{
			free(prev_hist);
			prev_hist = strdup(s);
			add_history(s);
		}
	}
79 80
#endif

Bruce Momjian's avatar
Bruce Momjian committed
81
	return s;
82 83 84 85 86 87 88 89 90 91 92 93
}



/*
 * gets_fromFile
 *
 * Gets a line of noninteractive input from a file (which could be stdin).
 */
char *
gets_fromFile(FILE *source)
{
Bruce Momjian's avatar
Bruce Momjian committed
94 95 96 97 98
	PQExpBufferData buffer;
	char		line[1024];

	initPQExpBuffer(&buffer);

99
	while (fgets(line, sizeof(line), source) != NULL)
Bruce Momjian's avatar
Bruce Momjian committed
100 101 102 103 104 105 106
	{
		appendPQExpBufferStr(&buffer, line);
		if (buffer.data[buffer.len - 1] == '\n')
		{
			buffer.data[buffer.len - 1] = '\0';
			return buffer.data;
		}
107 108
	}

Bruce Momjian's avatar
Bruce Momjian committed
109 110
	if (buffer.len > 0)
		return buffer.data;		/* EOF after reading some bufferload(s) */
111

Bruce Momjian's avatar
Bruce Momjian committed
112 113 114
	/* EOF, so return null */
	termPQExpBuffer(&buffer);
	return NULL;
115 116 117 118 119 120 121 122 123 124 125
}



/*
 * 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
126
initializeInput(int flags)
127 128
{
#ifdef USE_READLINE
Bruce Momjian's avatar
Bruce Momjian committed
129 130 131
	if (flags == 1)
	{
		useReadline = true;
132
		initialize_readline();
Bruce Momjian's avatar
Bruce Momjian committed
133
	}
134 135
#endif

136
#ifdef USE_READLINE
Bruce Momjian's avatar
Bruce Momjian committed
137 138 139 140 141
	if (flags == 1)
	{
		const char *home;

		useHistory = true;
142
		SetVariable(pset.vars, "HISTSIZE", "500");
Bruce Momjian's avatar
Bruce Momjian committed
143 144 145 146
		using_history();
		home = getenv("HOME");
		if (home)
		{
147
			char	   *psql_history = (char *) malloc(strlen(home) + 1 +
148
												strlen(PSQLHISTORY) + 1);
Bruce Momjian's avatar
Bruce Momjian committed
149 150 151

			if (psql_history)
			{
152
				sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
Bruce Momjian's avatar
Bruce Momjian committed
153 154 155 156
				read_history(psql_history);
				free(psql_history);
			}
		}
157 158
	}
#endif
159

160
#ifdef HAVE_ATEXIT
161
	atexit(finishInput);
162
#else
163
	on_exit(finishInput, NULL);
164
#endif
165 166 167 168 169
}



bool
170
saveHistory(char *fname)
171
{
172
#ifdef USE_READLINE
173
	if (useHistory && fname)
Bruce Momjian's avatar
Bruce Momjian committed
174
	{
175
		if (write_history(fname) != 0)
Bruce Momjian's avatar
Bruce Momjian committed
176
		{
177
			psql_error("could not save history to %s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
178 179 180
			return false;
		}
		return true;
181
	}
Bruce Momjian's avatar
Bruce Momjian committed
182 183
	else
		return false;
184
#else
Bruce Momjian's avatar
Bruce Momjian committed
185
	return false;
186 187 188 189 190
#endif
}



191 192
static void
#ifdef HAVE_ATEXIT
193
finishInput(void)
194 195 196
#else
finishInput(int exitstatus, void *arg)
#endif
197
{
198
#ifdef USE_READLINE
Bruce Momjian's avatar
Bruce Momjian committed
199 200 201 202 203 204 205 206
	if (useHistory)
	{
		char	   *home;
		char	   *psql_history;

		home = getenv("HOME");
		if (home)
		{
207
			psql_history = (char *) malloc(strlen(home) + 1 +
208
									strlen(PSQLHISTORY) + 1);
Bruce Momjian's avatar
Bruce Momjian committed
209 210
			if (psql_history)
			{
211 212 213 214
				const char *var = GetVariable(pset.vars, "HISTSIZE");

				if (var)
					stifle_history(atoi(var));
215
				sprintf(psql_history, "%s/%s", home, PSQLHISTORY);
Bruce Momjian's avatar
Bruce Momjian committed
216 217 218 219
				write_history(psql_history);
				free(psql_history);
			}
		}
220 221 222
	}
#endif
}