command.c 39.6 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/command.c,v 1.69 2002/03/07 17:54:39 momjian Exp $
Peter Eisentraut's avatar
Peter Eisentraut committed
7
 */
8
#include "postgres_fe.h"
9 10
#include "command.h"

Peter Eisentraut's avatar
Peter Eisentraut committed
11
#include <errno.h>
12
#include <assert.h>
13
#include <ctype.h>
Peter Eisentraut's avatar
Peter Eisentraut committed
14 15 16
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
17
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
18
#include <sys/types.h>			/* for umask() */
19 20
#include <sys/stat.h>			/* for stat() */
#include <fcntl.h>				/* open() flags */
Bruce Momjian's avatar
Bruce Momjian committed
21
#include <unistd.h>				/* for geteuid(), getpid(), stat() */
22 23
#else
#include <win32.h>
24
#include <io.h>
25
#include <fcntl.h>
26 27
#endif

28 29
#include "libpq-fe.h"
#include "pqexpbuffer.h"
30

31
#include "common.h"
32
#include "copy.h"
33
#include "describe.h"
34
#include "help.h"
35
#include "input.h"
36
#include "large_obj.h"
37
#include "mainloop.h"
38
#include "print.h"
39
#include "settings.h"
40
#include "variables.h"
41

42 43
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
44 45 46
#else
/* Grand unified hard-coded badness */
#define pg_encoding_to_char(x) "SQL_ASCII"
47 48
#endif

49

50
/* functions for use in this file */
51

Bruce Momjian's avatar
Bruce Momjian committed
52 53
static backslashResult exec_command(const char *cmd,
			 const char *options_string,
54
			 const char **continue_parse,
Peter Eisentraut's avatar
Peter Eisentraut committed
55
			 PQExpBuffer query_buf);
56

57
/* different ways for scan_option to handle parameter words */
58 59
enum option_type
{
60 61 62 63
	OT_NORMAL,					/* normal case */
	OT_SQLID,					/* treat as SQL identifier */
	OT_SQLIDHACK,				/* SQL identifier, but don't downcase */
	OT_FILEPIPE					/* it's a file or pipe */
64
};
65 66 67

static char *scan_option(char **string, enum option_type type,
						 char *quote, bool semicolon);
68
static char *unescape(const unsigned char *source, size_t len);
69

70 71
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
static bool do_connect(const char *new_dbname, const char *new_user);
72 73
static bool do_shell(const char *command);

74 75 76 77 78 79 80 81


/*----------
 * HandleSlashCmds:
 *
 * Handles all the different commands that start with '\',
 * ordinarily called by MainLoop().
 *
82 83
 * 'line' is the current input line, which should not start with a '\'
 * but with the actual command name
84 85 86 87
 * (that is taken care of by MainLoop)
 *
 * 'query_buf' contains the query-so-far, which may be modified by
 * execution of the backslash command (for example, \r clears it)
88
 * query_buf can be NULL if there is no query so far.
89 90 91 92 93 94
 *
 * Returns a status code indicating what action is desired, see command.h.
 *----------
 */

backslashResult
95
HandleSlashCmds(const char *line,
Bruce Momjian's avatar
Bruce Momjian committed
96
				PQExpBuffer query_buf,
Peter Eisentraut's avatar
Peter Eisentraut committed
97
				const char **end_of_cmd)
98
{
Bruce Momjian's avatar
Bruce Momjian committed
99 100
	backslashResult status = CMD_SKIP_LINE;
	char	   *my_line;
101
	char	   *options_string = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
102 103 104 105
	size_t		blank_loc;
	const char *continue_parse = NULL;	/* tell the mainloop where the
										 * backslash command ended */

106
#ifdef USE_ASSERT_CHECKING
107
	assert(line);
108 109
#endif

Bruce Momjian's avatar
Bruce Momjian committed
110 111 112
	my_line = xstrdup(line);

	/*
113 114 115 116
	 * Find the first whitespace. line[blank_loc] will now be the
	 * whitespace character or the \0 at the end
	 *
	 * Also look for a backslash, so stuff like \p\g works.
Bruce Momjian's avatar
Bruce Momjian committed
117
	 */
118
	blank_loc = strcspn(my_line, " \t\n\r\\");
Bruce Momjian's avatar
Bruce Momjian committed
119

120 121 122
	if (my_line[blank_loc] == '\\')
	{
		continue_parse = &my_line[blank_loc];
123
		my_line[blank_loc] = '\0';
124 125 126
		/* If it's a double backslash, we skip it. */
		if (my_line[blank_loc + 1] == '\\')
			continue_parse += 2;
127
	}
Bruce Momjian's avatar
Bruce Momjian committed
128
	/* do we have an option string? */
129
	else if (my_line[blank_loc] != '\0')
130 131
	{
		options_string = &my_line[blank_loc + 1];
Bruce Momjian's avatar
Bruce Momjian committed
132 133
		my_line[blank_loc] = '\0';
	}
134

135
	status = exec_command(my_line, options_string, &continue_parse, query_buf);
136

Bruce Momjian's avatar
Bruce Momjian committed
137 138 139
	if (status == CMD_UNKNOWN)
	{
		/*
140 141 142
		 * If the command was not recognized, try to parse it as a
		 * one-letter command with immediately following argument (a
		 * still-supported, but no longer encouraged, syntax).
Bruce Momjian's avatar
Bruce Momjian committed
143 144 145
		 */
		char		new_cmd[2];

146
		new_cmd[0] = my_line[0];
Bruce Momjian's avatar
Bruce Momjian committed
147 148
		new_cmd[1] = '\0';

149
		/* use line for options, because my_line was clobbered above */
150
		status = exec_command(new_cmd, line + 1, &continue_parse, query_buf);
151

152 153 154 155
		/*
		 * continue_parse must be relative to my_line for calculation
		 * below
		 */
156 157
		continue_parse += my_line - line;

158
#if 0							/* turned out to be too annoying */
159
		if (status != CMD_UNKNOWN && isalpha((unsigned char) new_cmd[0]))
160
			psql_error("Warning: This syntax is deprecated.\n");
Peter Eisentraut's avatar
Peter Eisentraut committed
161
#endif
Bruce Momjian's avatar
Bruce Momjian committed
162
	}
163

Bruce Momjian's avatar
Bruce Momjian committed
164 165
	if (status == CMD_UNKNOWN)
	{
166
		if (pset.cur_cmd_interactive)
167
			fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), my_line);
168 169
		else
			psql_error("invalid command \\%s\n", my_line);
Bruce Momjian's avatar
Bruce Momjian committed
170 171
		status = CMD_ERROR;
	}
172

173
	if (continue_parse && *continue_parse && *(continue_parse + 1) == '\\')
Bruce Momjian's avatar
Bruce Momjian committed
174
		continue_parse += 2;
175

176 177 178 179 180 181 182
	if (end_of_cmd)
	{
		if (continue_parse)
			*end_of_cmd = line + (continue_parse - my_line);
		else
			*end_of_cmd = line + strlen(line);
	}
183

Bruce Momjian's avatar
Bruce Momjian committed
184
	free(my_line);
185

Bruce Momjian's avatar
Bruce Momjian committed
186
	return status;
187 188 189 190 191
}



static backslashResult
Bruce Momjian's avatar
Bruce Momjian committed
192 193
exec_command(const char *cmd,
			 const char *options_string,
194
			 const char **continue_parse,
Peter Eisentraut's avatar
Peter Eisentraut committed
195
			 PQExpBuffer query_buf)
196
{
Bruce Momjian's avatar
Bruce Momjian committed
197 198
	bool		success = true; /* indicate here if the command ran ok or
								 * failed */
199
	bool		quiet = QUIET();
Bruce Momjian's avatar
Bruce Momjian committed
200
	backslashResult status = CMD_SKIP_LINE;
201
	char	   *string,
Peter Eisentraut's avatar
Peter Eisentraut committed
202
			   *string_cpy,
203
			   *val;
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

	/*
	 * The 'string' variable will be overwritten to point to the next
	 * token, hence we need an extra pointer so we can free this at the
	 * end.
	 */
	if (options_string)
		string = string_cpy = xstrdup(options_string);
	else
		string = string_cpy = NULL;

	/*
	 * \a -- toggle field alignment This makes little sense but we keep it
	 * around.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
219 220
	if (strcmp(cmd, "a") == 0)
	{
221 222
		if (pset.popt.topt.format != PRINT_ALIGNED)
			success = do_pset("format", "aligned", &pset.popt, quiet);
Bruce Momjian's avatar
Bruce Momjian committed
223
		else
224
			success = do_pset("format", "unaligned", &pset.popt, quiet);
225
	}
Bruce Momjian's avatar
Bruce Momjian committed
226

227
	/* \C -- override table title (formerly change HTML caption) */
Bruce Momjian's avatar
Bruce Momjian committed
228
	else if (strcmp(cmd, "C") == 0)
229
	{
230
		char	   *opt = scan_option(&string, OT_NORMAL, NULL, true);
231

232
		success = do_pset("title", opt, &pset.popt, quiet);
233 234
		free(opt);
	}
Bruce Momjian's avatar
Bruce Momjian committed
235

236
	/*----------
Bruce Momjian's avatar
Bruce Momjian committed
237 238
	 * \c or \connect -- connect to new database or as different user
	 *
239
	 * \c foo bar  connect to db "foo" as user "bar"
240 241 242 243
	 * \c foo [-]  connect to db "foo" as current user
	 * \c - bar    connect to current db as user "bar"
	 * \c		   connect to default db as default user
	 *----------
Bruce Momjian's avatar
Bruce Momjian committed
244 245 246
	 */
	else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
	{
247 248 249 250
		char	   *opt1,
				   *opt2;
		char		opt1q,
					opt2q;
251

252 253 254 255 256 257 258 259 260 261 262
		/*
		 * Ideally we should treat the arguments as SQL identifiers.  But for
		 * backwards compatibility with 7.2 and older pg_dump files, we have
		 * to take unquoted arguments verbatim (don't downcase them).
		 * For now, double-quoted arguments may be stripped of double quotes
		 * (as if SQL identifiers).  By 7.4 or so, pg_dump files can be
		 * expected to double-quote all mixed-case \connect arguments,
		 * and then we can get rid of OT_SQLIDHACK.
		 */
		opt1 = scan_option(&string, OT_SQLIDHACK, &opt1q, true);
		opt2 = scan_option(&string, OT_SQLIDHACK, &opt2q, true);
263 264

		if (opt2)
Bruce Momjian's avatar
Bruce Momjian committed
265
			/* gave username */
266 267
			success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1,
								 !opt2q && (strcmp(opt2, "-") == 0 || strcmp(opt2, "") == 0) ? "" : opt2);
268
		else if (opt1)
269 270 271 272 273 274 275 276
			/* gave database name */
			success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1, "");
		else
			/* connect to default db as default user */
			success = do_connect(NULL, NULL);

		free(opt1);
		free(opt2);
277
	}
278

Peter Eisentraut's avatar
Peter Eisentraut committed
279 280 281
	/* \cd */
	else if (strcmp(cmd, "cd") == 0)
	{
282 283
		char	   *opt = scan_option(&string, OT_NORMAL, NULL, true);
		char	   *dir;
Peter Eisentraut's avatar
Peter Eisentraut committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

		if (opt)
			dir = opt;
		else
		{
#ifndef WIN32
			struct passwd *pw;

			pw = getpwuid(geteuid());
			if (!pw)
			{
				psql_error("could not get home directory: %s\n", strerror(errno));
				exit(EXIT_FAILURE);
			}
			dir = pw->pw_dir;
299 300 301 302 303 304
#else							/* WIN32 */

			/*
			 * On Windows, 'cd' without arguments prints the current
			 * directory, so if someone wants to code this here instead...
			 */
Peter Eisentraut's avatar
Peter Eisentraut committed
305
			dir = "/";
306
#endif   /* WIN32 */
Peter Eisentraut's avatar
Peter Eisentraut committed
307 308 309 310 311 312 313 314 315 316 317 318 319
		}

		if (chdir(dir) == -1)
		{
			psql_error("\\%s: could not change directory to '%s': %s\n",
					   cmd, dir, strerror(errno));
			success = false;
		}

		if (opt)
			free(opt);
	}

Bruce Momjian's avatar
Bruce Momjian committed
320
	/* \copy */
321
	else if (strcasecmp(cmd, "copy") == 0)
322
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
323
		success = do_copy(options_string);
324 325 326
		if (options_string)
			string += strlen(string);
	}
327

Bruce Momjian's avatar
Bruce Momjian committed
328 329 330
	/* \copyright */
	else if (strcmp(cmd, "copyright") == 0)
		print_copyright();
331

Bruce Momjian's avatar
Bruce Momjian committed
332 333 334
	/* \d* commands */
	else if (cmd[0] == 'd')
	{
335 336
		char	   *name;
		bool		show_verbose;
337

338
		name = scan_option(&string, OT_SQLID, NULL, true);
339

340
		show_verbose = strchr(cmd, '+') ? true : false;
341

Bruce Momjian's avatar
Bruce Momjian committed
342 343 344
		switch (cmd[1])
		{
			case '\0':
345
			case '+':
346 347
				if (name)
					success = describeTableDetails(name, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
348
				else
349
					/* standard listing of interesting things */
350
					success = listTables("tvs", NULL, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
351 352
				break;
			case 'a':
353
				success = describeAggregates(name);
Bruce Momjian's avatar
Bruce Momjian committed
354 355
				break;
			case 'd':
356
				success = objectDescription(name);
Bruce Momjian's avatar
Bruce Momjian committed
357 358
				break;
			case 'f':
359
				success = describeFunctions(name, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
360 361
				break;
			case 'l':
362
				success = do_lo_list();
Bruce Momjian's avatar
Bruce Momjian committed
363 364
				break;
			case 'o':
365
				success = describeOperators(name);
Bruce Momjian's avatar
Bruce Momjian committed
366 367
				break;
			case 'p':
368
				success = permissionsList(name);
Bruce Momjian's avatar
Bruce Momjian committed
369 370
				break;
			case 'T':
371
				success = describeTypes(name, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
372 373 374 375 376 377
				break;
			case 't':
			case 'v':
			case 'i':
			case 's':
			case 'S':
378
				success = listTables(&cmd[1], name, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
379
				break;
380 381
			case 'u':
				success = describeUsers(name);
382
				break;
Bruce Momjian's avatar
Bruce Momjian committed
383 384 385
			default:
				status = CMD_UNKNOWN;
		}
386
		free(name);
Bruce Momjian's avatar
Bruce Momjian committed
387
	}
388 389


Bruce Momjian's avatar
Bruce Momjian committed
390 391 392 393 394
	/*
	 * \e or \edit -- edit the current query buffer (or a file and make it
	 * the query buffer
	 */
	else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
395 396 397 398 399 400 401 402 403 404
	{
		char	   *fname;

		if (!query_buf)
		{
			psql_error("no query buffer\n");
			status = CMD_ERROR;
		}
		else
		{
405
			fname = scan_option(&string, OT_NORMAL, NULL, true);
406 407 408 409
			status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
			free(fname);
		}
	}
410

411
	/* \echo and \qecho */
412
	else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
413
	{
414 415 416 417 418 419 420 421 422 423
		char	   *value;
		char		quoted;
		bool		no_newline = false;
		bool		first = true;
		FILE	   *fout;

		if (strcmp(cmd, "qecho") == 0)
			fout = pset.queryFout;
		else
			fout = stdout;
424

425
		while ((value = scan_option(&string, OT_NORMAL, &quoted, false)))
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
		{
			if (!quoted && strcmp(value, "-n") == 0)
				no_newline = true;
			else
			{
				if (first)
					first = false;
				else
					fputc(' ', fout);
				fputs(value, fout);
			}
			free(value);
		}
		if (!no_newline)
			fputs("\n", fout);
Bruce Momjian's avatar
Bruce Momjian committed
441
	}
442

Tatsuo Ishii's avatar
Tatsuo Ishii committed
443
	/* \encoding -- set/show client side encoding */
444
	else if (strcmp(cmd, "encoding") == 0)
445
	{
446
		char	   *encoding = scan_option(&string, OT_NORMAL, NULL, false);
447

448
		if (!encoding)
Tatsuo Ishii's avatar
Tatsuo Ishii committed
449 450
			/* show encoding */
			puts(pg_encoding_to_char(pset.encoding));
451
		else
Tatsuo Ishii's avatar
Tatsuo Ishii committed
452
		{
453
#ifdef MULTIBYTE
Tatsuo Ishii's avatar
Tatsuo Ishii committed
454 455 456 457 458 459 460 461 462 463
			/* set encoding */
			if (PQsetClientEncoding(pset.db, encoding) == -1)
				psql_error("%s: invalid encoding name\n", encoding);

			else
			{
				/* save encoding info into psql internal data */
				pset.encoding = PQclientEncoding(pset.db);
				SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding));
			}
464
#else
465
			psql_error("\\%s: multibyte support is not enabled\n", cmd);
466
#endif
467 468
			free(encoding);
		}
469 470
	}

471
	/* \f -- change field separator */
Bruce Momjian's avatar
Bruce Momjian committed
472
	else if (strcmp(cmd, "f") == 0)
473
	{
474
		char	   *fname = scan_option(&string, OT_NORMAL, NULL, false);
475

476
		success = do_pset("fieldsep", fname, &pset.popt, quiet);
477 478
		free(fname);
	}
479

Bruce Momjian's avatar
Bruce Momjian committed
480 481 482
	/* \g means send query */
	else if (strcmp(cmd, "g") == 0)
	{
483
		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, false);
484

485
		if (!fname)
486
			pset.gfname = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
487
		else
488
			pset.gfname = xstrdup(fname);
489
		free(fname);
Bruce Momjian's avatar
Bruce Momjian committed
490
		status = CMD_SEND;
491
	}
Bruce Momjian's avatar
Bruce Momjian committed
492 493 494

	/* help */
	else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
495
	{
496
		helpSQL(options_string ? &options_string[strspn(options_string, " \t\n\r")] : NULL);
497 498 499 500
		/* set pointer to end of line */
		if (string)
			string += strlen(string);
	}
Bruce Momjian's avatar
Bruce Momjian committed
501 502 503

	/* HTML mode */
	else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
504
	{
505 506
		if (pset.popt.topt.format != PRINT_HTML)
			success = do_pset("format", "html", &pset.popt, quiet);
507
		else
508
			success = do_pset("format", "aligned", &pset.popt, quiet);
509
	}
Bruce Momjian's avatar
Bruce Momjian committed
510 511 512 513 514


	/* \i is include file */
	else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
	{
515
		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);
516

517
		if (!fname)
518 519
		{
			psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
520 521 522
			success = false;
		}
		else
523
		{
524
			success = (process_file(fname) == EXIT_SUCCESS);
525 526
			free(fname);
		}
527 528
	}

Bruce Momjian's avatar
Bruce Momjian committed
529 530
	/* \l is list databases */
	else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
531
		success = listAllDbs(false);
532
	else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
533
		success = listAllDbs(true);
Bruce Momjian's avatar
Bruce Momjian committed
534

535
	/*
536 537
	 * large object things
	 */
Bruce Momjian's avatar
Bruce Momjian committed
538 539
	else if (strncmp(cmd, "lo_", 3) == 0)
	{
540 541
		char	   *opt1,
				   *opt2;
542

543 544
		opt1 = scan_option(&string, OT_NORMAL, NULL, true);
		opt2 = scan_option(&string, OT_NORMAL, NULL, true);
545

Bruce Momjian's avatar
Bruce Momjian committed
546 547
		if (strcmp(cmd + 3, "export") == 0)
		{
548
			if (!opt2)
Bruce Momjian's avatar
Bruce Momjian committed
549
			{
550
				psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
551 552 553
				success = false;
			}
			else
554
				success = do_lo_export(opt1, opt2);
Bruce Momjian's avatar
Bruce Momjian committed
555 556 557 558
		}

		else if (strcmp(cmd + 3, "import") == 0)
		{
559
			if (!opt1)
Bruce Momjian's avatar
Bruce Momjian committed
560
			{
561
				psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
562 563 564
				success = false;
			}
			else
565
				success = do_lo_import(opt1, opt2);
Bruce Momjian's avatar
Bruce Momjian committed
566 567 568
		}

		else if (strcmp(cmd + 3, "list") == 0)
569
			success = do_lo_list();
Bruce Momjian's avatar
Bruce Momjian committed
570 571 572

		else if (strcmp(cmd + 3, "unlink") == 0)
		{
573
			if (!opt1)
Bruce Momjian's avatar
Bruce Momjian committed
574
			{
575
				psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
576 577 578
				success = false;
			}
			else
579
				success = do_lo_unlink(opt1);
Bruce Momjian's avatar
Bruce Momjian committed
580 581 582 583
		}

		else
			status = CMD_UNKNOWN;
584

585 586
		free(opt1);
		free(opt2);
587 588
	}

589

Bruce Momjian's avatar
Bruce Momjian committed
590 591
	/* \o -- set query output */
	else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
592
	{
593
		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, true);
594

595
		success = setQFout(fname);
596 597
		free(fname);
	}
Bruce Momjian's avatar
Bruce Momjian committed
598 599 600 601 602 603

	/* \p prints the current query buffer */
	else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
	{
		if (query_buf && query_buf->len > 0)
			puts(query_buf->data);
604
		else if (!quiet)
605
			puts(gettext("Query buffer is empty."));
606
		fflush(stdout);
607 608
	}

Bruce Momjian's avatar
Bruce Momjian committed
609 610 611
	/* \pset -- set printing parameters */
	else if (strcmp(cmd, "pset") == 0)
	{
612 613
		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
		char	   *opt1 = scan_option(&string, OT_NORMAL, NULL, false);
614

615
		if (!opt0)
Bruce Momjian's avatar
Bruce Momjian committed
616
		{
617
			psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
618 619 620
			success = false;
		}
		else
621 622
			success = do_pset(opt0, opt1, &pset.popt, quiet);

623 624
		free(opt0);
		free(opt1);
625
	}
Bruce Momjian's avatar
Bruce Momjian committed
626 627 628 629 630 631 632 633 634 635

	/* \q or \quit */
	else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
		status = CMD_TERMINATE;

	/* reset(clear) the buffer */
	else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
	{
		resetPQExpBuffer(query_buf);
		if (!quiet)
636
			puts(gettext("Query buffer reset (cleared)."));
637 638
	}

Bruce Momjian's avatar
Bruce Momjian committed
639 640 641
	/* \s save history in a file or show it on the screen */
	else if (strcmp(cmd, "s") == 0)
	{
642
		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);
Bruce Momjian's avatar
Bruce Momjian committed
643

644
		success = saveHistory(fname ? fname : "/dev/tty");
645

646
		if (success && !quiet && fname)
647
			printf(gettext("Wrote history to %s.\n"), fname);
648
		free(fname);
Bruce Momjian's avatar
Bruce Momjian committed
649
	}
650

651
	/* \set -- generalized set variable/option command */
Bruce Momjian's avatar
Bruce Momjian committed
652 653
	else if (strcmp(cmd, "set") == 0)
	{
654
		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
655 656

		if (!opt0)
Bruce Momjian's avatar
Bruce Momjian committed
657 658 659 660
		{
			/* list all variables */

			/*
661 662
			 * XXX This is in utter violation of the GetVariable
			 * abstraction, but I have not bothered to do it better.
Bruce Momjian's avatar
Bruce Momjian committed
663 664 665
			 */
			struct _variable *ptr;

666
			for (ptr = pset.vars; ptr->next; ptr = ptr->next)
Bruce Momjian's avatar
Bruce Momjian committed
667 668 669 670 671
				fprintf(stdout, "%s = '%s'\n", ptr->next->name, ptr->next->value);
			success = true;
		}
		else
		{
672 673 674 675 676 677
			/*
			 * Set variable to the concatenation of the arguments.
			 */
			char	   *newval = NULL;
			char	   *opt;

678
			opt = scan_option(&string, OT_NORMAL, NULL, false);
679 680 681
			newval = xstrdup(opt ? opt : "");
			free(opt);

682
			while ((opt = scan_option(&string, OT_NORMAL, NULL, false)))
683 684 685 686 687 688 689 690 691 692
			{
				newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
				if (!newval)
				{
					psql_error("out of memory\n");
					exit(EXIT_FAILURE);
				}
				strcat(newval, opt);
				free(opt);
			}
693 694

			if (!SetVariable(pset.vars, opt0, newval))
Bruce Momjian's avatar
Bruce Momjian committed
695
			{
696
				psql_error("\\%s: error\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
697 698
				success = false;
			}
699
			free(newval);
Bruce Momjian's avatar
Bruce Momjian committed
700
		}
701
		free(opt0);
702
	}
Bruce Momjian's avatar
Bruce Momjian committed
703 704 705

	/* \t -- turn off headers and row count */
	else if (strcmp(cmd, "t") == 0)
706
		success = do_pset("tuples_only", NULL, &pset.popt, quiet);
Bruce Momjian's avatar
Bruce Momjian committed
707 708 709 710


	/* \T -- define html <table ...> attributes */
	else if (strcmp(cmd, "T") == 0)
711
	{
712
		char	   *value = scan_option(&string, OT_NORMAL, NULL, false);
713

714
		success = do_pset("tableattr", value, &pset.popt, quiet);
715 716 717
		free(value);
	}

718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
	/* \timing -- toggle timing of queries */
	else if (strcmp(cmd, "timing") == 0)
	{
		pset.timing = !pset.timing;
		if (!quiet)
		{
			if (pset.timing)
			{
				puts(gettext(("Timing is on.")));
			}
			else
			{
				puts(gettext(("Timing is off.")));

			}
		}
	}
  
736 737 738
	/* \unset */
	else if (strcmp(cmd, "unset") == 0)
	{
739
		char	   *opt = scan_option(&string, OT_NORMAL, NULL, false);
740 741 742 743 744 745

		if (!opt)
		{
			psql_error("\\%s: missing required argument\n", cmd);
			success = false;
		}
746
		else if (!SetVariable(pset.vars, opt, NULL))
747 748 749 750 751 752
		{
			psql_error("\\%s: error\n", cmd);
			success = false;
		}
		free(opt);
	}
Bruce Momjian's avatar
Bruce Momjian committed
753 754 755 756 757

	/* \w -- write query buffer to file */
	else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
	{
		FILE	   *fd = NULL;
758
		bool		is_pipe = false;
759 760 761 762 763 764 765 766 767
		char	   *fname = NULL;

		if (!query_buf)
		{
			psql_error("no query buffer\n");
			status = CMD_ERROR;
		}
		else
		{
768
			fname = scan_option(&string, OT_FILEPIPE, NULL, true);
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791

			if (!fname)
			{
				psql_error("\\%s: missing required argument\n", cmd);
				success = false;
			}
			else
			{
				if (fname[0] == '|')
				{
					is_pipe = true;
					fd = popen(&fname[1], "w");
				}
				else
					fd = fopen(fname, "w");

				if (!fd)
				{
					psql_error("%s: %s\n", fname, strerror(errno));
					success = false;
				}
			}
		}
792

Bruce Momjian's avatar
Bruce Momjian committed
793 794 795
		if (fd)
		{
			int			result;
796

Bruce Momjian's avatar
Bruce Momjian committed
797 798
			if (query_buf && query_buf->len > 0)
				fprintf(fd, "%s\n", query_buf->data);
799

800
			if (is_pipe)
Bruce Momjian's avatar
Bruce Momjian committed
801 802 803
				result = pclose(fd);
			else
				result = fclose(fd);
804

Bruce Momjian's avatar
Bruce Momjian committed
805 806
			if (result == EOF)
			{
807
				psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
808 809 810
				success = false;
			}
		}
811

812
		free(fname);
813 814
	}

Bruce Momjian's avatar
Bruce Momjian committed
815 816
	/* \x -- toggle expanded table representation */
	else if (strcmp(cmd, "x") == 0)
817
		success = do_pset("expanded", NULL, &pset.popt, quiet);
818 819


820
	/* \z -- list table rights (grant/revoke) */
Bruce Momjian's avatar
Bruce Momjian committed
821
	else if (strcmp(cmd, "z") == 0)
822
	{
823
		char	   *opt = scan_option(&string, OT_SQLID, NULL, true);
824

825
		success = permissionsList(opt);
826 827
		free(opt);
	}
828

829
	/* \! -- shell escape */
Bruce Momjian's avatar
Bruce Momjian committed
830
	else if (strcmp(cmd, "!") == 0)
831
	{
Bruce Momjian's avatar
Bruce Momjian committed
832
		success = do_shell(options_string);
833 834 835 836
		/* wind pointer to end of line */
		if (string)
			string += strlen(string);
	}
Bruce Momjian's avatar
Bruce Momjian committed
837

838
	/* \? -- slash command help */
Bruce Momjian's avatar
Bruce Momjian committed
839
	else if (strcmp(cmd, "?") == 0)
840
		slashUsage();
841

842
#if 0
843 844

	/*
Bruce Momjian's avatar
Bruce Momjian committed
845 846 847 848 849
	 * These commands don't do anything. I just use them to test the
	 * parser.
	 */
	else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0)
	{
850
		int			i = 0;
851
		char	   *value;
Bruce Momjian's avatar
Bruce Momjian committed
852

853
		fprintf(stderr, "+ optstr = |%s|\n", options_string);
854
		while ((value = scan_option(&string, OT_NORMAL, NULL, true)))
855
		{
856
			fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
857 858
			free(value);
		}
Bruce Momjian's avatar
Bruce Momjian committed
859
	}
860 861
#endif

Bruce Momjian's avatar
Bruce Momjian committed
862 863
	else
		status = CMD_UNKNOWN;
864

Bruce Momjian's avatar
Bruce Momjian committed
865 866
	if (!success)
		status = CMD_ERROR;
867

868
	/* eat the rest of the options string */
869
	while ((val = scan_option(&string, OT_NORMAL, NULL, false)))
870 871 872 873
	{
		if (status != CMD_UNKNOWN)
			psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
	}
874

875 876 877
	if (options_string && continue_parse)
		*continue_parse = options_string + (string - string_cpy);
	free(string_cpy);
878

Bruce Momjian's avatar
Bruce Momjian committed
879
	return status;
880 881 882 883
}



884 885 886 887
/*
 * scan_option()
 */
static char *
888
scan_option(char **string, enum option_type type, char *quote, bool semicolon)
889
{
890 891 892 893 894 895 896 897 898 899 900 901
	unsigned int pos = 0;
	char	   *options_string;
	char	   *return_val;

	if (quote)
		*quote = 0;

	if (!string || !(*string))
		return NULL;

	options_string = *string;
	/* skip leading whitespace */
902
	pos += strspn(options_string + pos, " \t\n\r");
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927

	switch (options_string[pos])
	{
			/*
			 * Double quoted string
			 */
		case '"':
			{
				unsigned int jj;
				unsigned short int bslash_count = 0;

				/* scan for end of quote */
				for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
				{
					if (options_string[jj] == '"' && bslash_count % 2 == 0)
						break;

					if (options_string[jj] == '\\')
						bslash_count++;
					else
						bslash_count = 0;
				}

				if (options_string[jj] == 0)
				{
928
					psql_error("parse error at the end of line\n");
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
					*string = &options_string[jj];
					return NULL;
				}

				return_val = malloc(jj - pos + 2);
				if (!return_val)
				{
					psql_error("out of memory\n");
					exit(EXIT_FAILURE);
				}

				/*
				 * If this is expected to be an SQL identifier like option
				 * then we strip out the double quotes
				 */
944

945
				if (type == OT_SQLID || type == OT_SQLIDHACK)
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
				{
					unsigned int k,
								cc;

					bslash_count = 0;
					cc = 0;
					for (k = pos + 1; options_string[k]; k += PQmblen(&options_string[k], pset.encoding))
					{
						if (options_string[k] == '"' && bslash_count % 2 == 0)
							break;

						if (options_string[jj] == '\\')
							bslash_count++;
						else
							bslash_count = 0;

						return_val[cc++] = options_string[k];
					}
					return_val[cc] = '\0';
				}
966 967 968 969 970 971
				else
				{
					strncpy(return_val, &options_string[pos], jj - pos + 1);
					return_val[jj - pos + 1] = '\0';
				}

972 973 974
				*string = options_string + jj + 1;
				if (quote)
					*quote = '"';
975

976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
				return return_val;
			}

			/*
			 * A single quote has a psql internal meaning, such as for
			 * delimiting file names, and it also allows for such escape
			 * sequences as \t.
			 */
		case '\'':
			{
				unsigned int jj;
				unsigned short int bslash_count = 0;

				for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
				{
					if (options_string[jj] == '\'' && bslash_count % 2 == 0)
						break;

					if (options_string[jj] == '\\')
						bslash_count++;
					else
						bslash_count = 0;
				}

				if (options_string[jj] == 0)
				{
1002
					psql_error("parse error at the end of line\n");
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
					*string = &options_string[jj];
					return NULL;
				}

				return_val = unescape(&options_string[pos + 1], jj - pos - 1);
				*string = &options_string[jj + 1];
				if (quote)
					*quote = '\'';
				return return_val;
			}

			/*
			 * Backticks are for command substitution, like in shells
			 */
		case '`':
			{
				bool		error = false;
				FILE	   *fd = NULL;
				char	   *file;
				PQExpBufferData output;
				char		buf[512];
				size_t		result,
							len;

				len = strcspn(options_string + pos + 1, "`");
				if (options_string[pos + 1 + len] == 0)
				{
1030
					psql_error("parse error at the end of line\n");
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
					*string = &options_string[pos + 1 + len];
					return NULL;
				}

				options_string[pos + 1 + len] = '\0';
				file = options_string + pos + 1;

				fd = popen(file, "r");
				if (!fd)
				{
					psql_error("%s: %s\n", file, strerror(errno));
					error = true;
				}

				if (!error)
				{
					initPQExpBuffer(&output);

					do
					{
						result = fread(buf, 1, 512, fd);
						if (ferror(fd))
						{
							psql_error("%s: %s\n", file, strerror(errno));
							error = true;
							break;
						}
						appendBinaryPQExpBuffer(&output, buf, result);
					} while (!feof(fd));
					appendPQExpBufferChar(&output, '\0');

					if (pclose(fd) == -1)
					{
						psql_error("%s: %s\n", file, strerror(errno));
						error = true;
					}
				}

				if (!error)
				{
					if (output.data[strlen(output.data) - 1] == '\n')
						output.data[strlen(output.data) - 1] = '\0';
				}

				if (!error)
					return_val = output.data;
				else
				{
					return_val = xstrdup("");
					termPQExpBuffer(&output);
				}
				options_string[pos + 1 + len] = '`';
				*string = options_string + pos + len + 2;
				if (quote)
					*quote = '`';
				return return_val;
			}

			/*
			 * end of line
			 */
		case 0:
			*string = &options_string[pos];
			return NULL;

			/*
			 * Variable substitution
			 */
		case ':':
			{
				size_t		token_end;
				const char *value;
				char		save_char;

1105
				token_end = strcspn(&options_string[pos + 1], " \t\n\r");
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
				save_char = options_string[pos + token_end + 1];
				options_string[pos + token_end + 1] = '\0';
				value = GetVariable(pset.vars, options_string + pos + 1);
				if (!value)
					value = "";
				return_val = xstrdup(value);
				options_string[pos + token_end + 1] = save_char;
				*string = &options_string[pos + token_end + 1];
				return return_val;
			}

			/*
			 * Next command
			 */
		case '\\':
			*string = options_string + pos;
			return NULL;
			break;

1125
			/*
1126 1127
			 * | could be the beginning of a pipe if so, take rest of line
			 * as command
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
			 */
		case '|':
			if (type == OT_FILEPIPE)
			{
				*string += strlen(options_string + pos);
				return xstrdup(options_string + pos);
				break;
			}
			/* fallthrough for other option types */

1138 1139 1140 1141 1142 1143 1144 1145
			/*
			 * A normal word
			 */
		default:
			{
				size_t		token_end;
				char	   *cp;

1146
				token_end = strcspn(&options_string[pos], " \t\n\r");
1147 1148 1149 1150 1151 1152 1153 1154 1155
				return_val = malloc(token_end + 1);
				if (!return_val)
				{
					psql_error("out of memory\n");
					exit(EXIT_FAILURE);
				}
				strncpy(return_val, &options_string[pos], token_end);
				return_val[token_end] = 0;

1156
				/* Strip any trailing semi-colons for some types */
1157 1158 1159 1160 1161 1162 1163
				if (semicolon)
				{
					int			i;

					for (i = strlen(return_val) - 1; i && return_val[i] == ';'; i--);
					if (i < strlen(return_val) - 1)
						return_val[i + 1] = '\0';
1164 1165
				}

1166 1167
				if (type == OT_SQLID)
					for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))
1168 1169
						if (isupper((unsigned char) *cp))
							*cp = tolower((unsigned char) *cp);
1170 1171 1172 1173 1174 1175

				*string = &options_string[pos + token_end];
				return return_val;
			}

	}
1176 1177 1178 1179
}



1180 1181 1182 1183 1184 1185 1186 1187
/*
 * unescape
 *
 * Replaces \n, \t, and the like.
 *
 * The return value is malloc()'ed.
 */
static char *
1188
unescape(const unsigned char *source, size_t len)
1189
{
1190
	const unsigned char *p;
Bruce Momjian's avatar
Bruce Momjian committed
1191 1192 1193 1194 1195
	bool		esc = false;	/* Last character we saw was the escape
								 * character */
	char	   *destination,
			   *tmp;
	size_t		length;
1196 1197

#ifdef USE_ASSERT_CHECKING
Bruce Momjian's avatar
Bruce Momjian committed
1198
	assert(source);
1199 1200
#endif

1201
	length = Min(len, strlen(source)) + 1;
1202

1203
	tmp = destination = malloc(length);
Bruce Momjian's avatar
Bruce Momjian committed
1204 1205
	if (!tmp)
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1206
		psql_error("out of memory\n");
Bruce Momjian's avatar
Bruce Momjian committed
1207
		exit(EXIT_FAILURE);
1208 1209
	}

1210
	for (p = source; p - source < len && *p; p += PQmblen(p, pset.encoding))
1211
	{
Bruce Momjian's avatar
Bruce Momjian committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
		if (esc)
		{
			char		c;

			switch (*p)
			{
				case 'n':
					c = '\n';
					break;
				case 't':
					c = '\t';
					break;
1224 1225 1226 1227 1228 1229
				case 'b':
					c = '\b';
					break;
				case 'r':
					c = '\r';
					break;
Bruce Momjian's avatar
Bruce Momjian committed
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
				case 'f':
					c = '\f';
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					{
						long int	l;
						char	   *end;

						l = strtol(p, &end, 0);
						c = l;
						p = end - 1;
						break;
					}
				default:
					c = *p;
			}
			*tmp++ = c;
			esc = false;
		}

		else if (*p == '\\')
			esc = true;

		else
		{
1264 1265
			int			i;
			const unsigned char *mp = p;
1266

1267 1268
			for (i = 0; i < PQmblen(p, pset.encoding); i++)
				*tmp++ = *mp++;
Bruce Momjian's avatar
Bruce Momjian committed
1269 1270
			esc = false;
		}
1271 1272
	}

Bruce Momjian's avatar
Bruce Momjian committed
1273 1274
	*tmp = '\0';
	return destination;
1275 1276 1277 1278 1279 1280 1281 1282 1283
}



/* do_connect
 * -- handler for \connect
 *
 * Connects to a database (new_dbname) as a certain user (new_user).
 * The new user can be NULL. A db name of "-" is the same as the old one.
1284
 * (That is, the one currently in pset. But pset.db can also be NULL. A NULL
1285
 * dbname is handled by libpq.)
1286 1287
 * Returns true if all ok, false if the new connection couldn't be established.
 * The old connection will be kept if the session is interactive.
1288
 */
1289
static bool
1290
do_connect(const char *new_dbname, const char *new_user)
1291
{
1292
	PGconn	   *oldconn = pset.db;
Bruce Momjian's avatar
Bruce Momjian committed
1293 1294
	const char *dbparam = NULL;
	const char *userparam = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1295
	const char *pwparam = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1296 1297 1298 1299
	char	   *prompted_password = NULL;
	bool		need_pass;
	bool		success = false;

1300 1301 1302 1303 1304 1305
	/* Delete variables (in case we fail before setting them anew) */
	SetVariable(pset.vars, "DBNAME", NULL);
	SetVariable(pset.vars, "USER", NULL);
	SetVariable(pset.vars, "HOST", NULL);
	SetVariable(pset.vars, "PORT", NULL);
	SetVariable(pset.vars, "ENCODING", NULL);
1306

1307 1308
	/* If dbname is "" then use old name, else new one (even if NULL) */
	if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1309 1310 1311 1312
		dbparam = PQdb(oldconn);
	else
		dbparam = new_dbname;

1313
	/* If user is "" then use the old one */
1314
	if (new_user && PQuser(oldconn) && strcmp(new_user, "") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1315 1316 1317 1318 1319
		userparam = PQuser(oldconn);
	else
		userparam = new_user;

	/* need to prompt for password? */
1320
	if (pset.getPassword)
Tom Lane's avatar
Tom Lane committed
1321
		pwparam = prompted_password = simple_prompt("Password: ", 100, false);
Bruce Momjian's avatar
Bruce Momjian committed
1322 1323

	/*
Tom Lane's avatar
Tom Lane committed
1324 1325
	 * Use old password (if any) if no new one given and we are
	 * reconnecting as same user
Bruce Momjian's avatar
Bruce Momjian committed
1326
	 */
1327 1328
	if (!pwparam && oldconn && PQuser(oldconn) && userparam &&
		strcmp(PQuser(oldconn), userparam) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1329
		pwparam = PQpass(oldconn);
1330

Bruce Momjian's avatar
Bruce Momjian committed
1331 1332 1333
	do
	{
		need_pass = false;
1334
		pset.db = PQsetdbLogin(PQhost(oldconn), PQport(oldconn),
1335
							   NULL, NULL, dbparam, userparam, pwparam);
Bruce Momjian's avatar
Bruce Momjian committed
1336

1337
		if (PQstatus(pset.db) == CONNECTION_BAD &&
1338 1339
			strcmp(PQerrorMessage(pset.db), "fe_sendauth: no password supplied\n") == 0 &&
			!feof(stdin))
Bruce Momjian's avatar
Bruce Momjian committed
1340
		{
1341
			PQfinish(pset.db);
Bruce Momjian's avatar
Bruce Momjian committed
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
			need_pass = true;
			free(prompted_password);
			prompted_password = NULL;
			pwparam = prompted_password = simple_prompt("Password: ", 100, false);
		}
	} while (need_pass);

	free(prompted_password);

	/*
	 * If connection failed, try at least keep the old one. That's
	 * probably more convenient than just kicking you out of the program.
	 */
1355
	if (!pset.db || PQstatus(pset.db) == CONNECTION_BAD)
Bruce Momjian's avatar
Bruce Momjian committed
1356
	{
1357 1358 1359 1360 1361 1362
		if (pset.cur_cmd_interactive)
		{
			psql_error("%s", PQerrorMessage(pset.db));
			PQfinish(pset.db);
			if (oldconn)
			{
1363
				fputs(gettext("Previous connection kept\n"), stderr);
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
				pset.db = oldconn;
			}
			else
				pset.db = NULL;
		}
		else
		{
			/*
			 * we don't want unpredictable things to happen in scripting
			 * mode
			 */
			psql_error("\\connect: %s", PQerrorMessage(pset.db));
			PQfinish(pset.db);
Bruce Momjian's avatar
Bruce Momjian committed
1377 1378
			if (oldconn)
				PQfinish(oldconn);
1379
			pset.db = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1380
		}
1381
	}
Bruce Momjian's avatar
Bruce Momjian committed
1382 1383
	else
	{
1384
		if (!QUIET())
Bruce Momjian's avatar
Bruce Momjian committed
1385 1386
		{
			if (userparam != new_user)	/* no new user */
1387
				printf(gettext("You are now connected to database %s.\n"), dbparam);
Bruce Momjian's avatar
Bruce Momjian committed
1388
			else if (dbparam != new_dbname)		/* no new db */
1389
				printf(gettext("You are now connected as new user %s.\n"), new_user);
1390 1391
			else
/* both new */
1392
				printf(gettext("You are now connected to database %s as user %s.\n"),
1393
					   PQdb(pset.db), PQuser(pset.db));
Bruce Momjian's avatar
Bruce Momjian committed
1394
		}
1395

Bruce Momjian's avatar
Bruce Momjian committed
1396 1397
		if (oldconn)
			PQfinish(oldconn);
1398

Bruce Momjian's avatar
Bruce Momjian committed
1399 1400
		success = true;
	}
1401

1402 1403
	PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
	pset.encoding = PQclientEncoding(pset.db);
Peter Eisentraut's avatar
Peter Eisentraut committed
1404

1405 1406 1407 1408 1409 1410
	/* Update variables */
	SetVariable(pset.vars, "DBNAME", PQdb(pset.db));
	SetVariable(pset.vars, "USER", PQuser(pset.db));
	SetVariable(pset.vars, "HOST", PQhost(pset.db));
	SetVariable(pset.vars, "PORT", PQport(pset.db));
	SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding));
1411

1412
	pset.issuper = test_superuser(PQuser(pset.db));
1413

Bruce Momjian's avatar
Bruce Momjian committed
1414
	return success;
1415 1416 1417 1418
}



1419 1420
/*
 * Test if the given user is a database superuser.
1421
 * (Is used to set up the prompt right.)
1422 1423
 */
bool
1424
test_superuser(const char *username)
1425
{
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
	PGresult   *res;
	char		buf[64 + NAMEDATALEN];
	bool		answer;

	if (!username)
		return false;

	sprintf(buf, "SELECT usesuper FROM pg_user WHERE usename = '%.*s'", NAMEDATALEN, username);
	res = PSQLexec(buf);

	answer =
		(PQntuples(res) > 0 && PQnfields(res) > 0
		 && !PQgetisnull(res, 0, 0)
		 && PQgetvalue(res, 0, 0)
		 && strcmp(PQgetvalue(res, 0, 0), "t") == 0);
	PQclear(res);
	return answer;
1443 1444 1445 1446
}



1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
/*
 * do_edit -- handler for \e
 *
 * If you do not specify a filename, the current query buffer will be copied
 * into a temporary one.
 */

static bool
editFile(const char *fname)
{
1457
	const char *editorName;
Bruce Momjian's avatar
Bruce Momjian committed
1458 1459
	char	   *sys;
	int			result;
1460 1461

#ifdef USE_ASSERT_CHECKING
Bruce Momjian's avatar
Bruce Momjian committed
1462
	assert(fname);
1463
#else
Bruce Momjian's avatar
Bruce Momjian committed
1464 1465
	if (!fname)
		return false;
1466 1467
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
	/* Find an editor to use */
	editorName = getenv("PSQL_EDITOR");
	if (!editorName)
		editorName = getenv("EDITOR");
	if (!editorName)
		editorName = getenv("VISUAL");
	if (!editorName)
		editorName = DEFAULT_EDITOR;

	sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
	if (!sys)
		return false;
	sprintf(sys, "exec %s %s", editorName, fname);
	result = system(sys);
Peter Eisentraut's avatar
Peter Eisentraut committed
1482
	if (result == -1)
1483 1484
		psql_error("could not start editor %s\n", editorName);
	else if (result == 127)
Peter Eisentraut's avatar
Peter Eisentraut committed
1485
		psql_error("could not start /bin/sh\n");
Bruce Momjian's avatar
Bruce Momjian committed
1486 1487 1488
	free(sys);

	return result == 0;
1489 1490 1491 1492 1493 1494 1495
}


/* call this one */
static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf)
{
1496
	char		fnametmp[MAXPGPATH];
Peter Eisentraut's avatar
Peter Eisentraut committed
1497
	FILE	   *stream = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1498 1499
	const char *fname;
	bool		error = false;
1500
	int			fd;
1501

1502
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
1503 1504
	struct stat before,
				after;
1505 1506
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1507 1508
	if (filename_arg)
		fname = filename_arg;
1509

Bruce Momjian's avatar
Bruce Momjian committed
1510 1511 1512
	else
	{
		/* make a temp file to edit */
1513
#ifndef WIN32
1514
		const char *tmpdirenv = getenv("TMPDIR");
1515

1516
		sprintf(fnametmp, "%s/psql.edit.%ld.%ld",
1517 1518
				tmpdirenv ? tmpdirenv : "/tmp",
				(long) geteuid(), (long) getpid());
1519
#else
Bruce Momjian's avatar
Bruce Momjian committed
1520
		GetTempFileName(".", "psql", 0, fnametmp);
1521
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1522
		fname = (const char *) fnametmp;
1523

1524
		fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
1525 1526
		if (fd != -1)
			stream = fdopen(fd, "w");
1527

1528
		if (fd == -1 || !stream)
Bruce Momjian's avatar
Bruce Momjian committed
1529
		{
1530
			psql_error("could not open temporary file %s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544
			error = true;
		}
		else
		{
			unsigned int ql = query_buf->len;

			if (ql == 0 || query_buf->data[ql - 1] != '\n')
			{
				appendPQExpBufferChar(query_buf, '\n');
				ql++;
			}

			if (fwrite(query_buf->data, 1, ql, stream) != ql)
			{
1545
				psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1546 1547 1548 1549 1550 1551 1552
				fclose(stream);
				remove(fname);
				error = true;
			}
			else
				fclose(stream);
		}
1553
	}
Bruce Momjian's avatar
Bruce Momjian committed
1554 1555 1556 1557

#ifndef WIN32
	if (!error && stat(fname, &before) != 0)
	{
1558
		psql_error("%s: %s\n", fname, strerror(errno));
1559 1560 1561 1562
		error = true;
	}
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1563 1564 1565
	/* call editor */
	if (!error)
		error = !editFile(fname);
1566 1567

#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
1568 1569
	if (!error && stat(fname, &after) != 0)
	{
1570
		psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1571 1572
		error = true;
	}
1573

Bruce Momjian's avatar
Bruce Momjian committed
1574 1575
	if (!error && before.st_mtime != after.st_mtime)
	{
1576
#else
Bruce Momjian's avatar
Bruce Momjian committed
1577 1578
	if (!error)
	{
1579
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1580 1581 1582
		stream = fopen(fname, "r");
		if (!stream)
		{
1583
			psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1584 1585 1586 1587 1588 1589 1590 1591
			error = true;
		}
		else
		{
			/* read file back in */
			char		line[1024];

			resetPQExpBuffer(query_buf);
1592
			while (fgets(line, sizeof(line), stream) != NULL)
1593 1594
				appendPQExpBufferStr(query_buf, line);

1595 1596 1597 1598 1599
			if (ferror(stream))
			{
				psql_error("%s: %s\n", fname, strerror(errno));
				error = true;
			}
Bruce Momjian's avatar
Bruce Momjian committed
1600 1601

			fclose(stream);
1602 1603
		}

1604
	}
Bruce Momjian's avatar
Bruce Momjian committed
1605

1606
	/* remove temp file */
Bruce Momjian's avatar
Bruce Momjian committed
1607
	if (!filename_arg)
1608 1609 1610 1611 1612 1613 1614
	{
		if (remove(fname) == -1)
		{
			psql_error("%s: %s\n", fname, strerror(errno));
			error = true;
		}
	}
1615

Bruce Momjian's avatar
Bruce Momjian committed
1616
	return !error;
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
}



/*
 * process_file
 *
 * Read commands from filename and then them to the main processing loop
 * Handler for \i, but can be used for other things as well.
 */
1627
int
Peter Eisentraut's avatar
Peter Eisentraut committed
1628
process_file(char *filename)
1629
{
Bruce Momjian's avatar
Bruce Momjian committed
1630 1631
	FILE	   *fd;
	int			result;
1632
	char	   *oldfilename;
1633

Bruce Momjian's avatar
Bruce Momjian committed
1634 1635
	if (!filename)
		return false;
1636

Bruce Momjian's avatar
Bruce Momjian committed
1637
	fd = fopen(filename, "r");
1638

Bruce Momjian's avatar
Bruce Momjian committed
1639 1640
	if (!fd)
	{
1641
		psql_error("%s: %s\n", filename, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1642 1643
		return false;
	}
1644

1645 1646
	oldfilename = pset.inputfile;
	pset.inputfile = filename;
Peter Eisentraut's avatar
Peter Eisentraut committed
1647
	result = MainLoop(fd);
Bruce Momjian's avatar
Bruce Momjian committed
1648
	fclose(fd);
Peter Eisentraut's avatar
Peter Eisentraut committed
1649
	pset.inputfile = oldfilename;
1650
	return result;
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
}



/*
 * do_pset
 *
 */
static const char *
_align2string(enum printFormat in)
{
Bruce Momjian's avatar
Bruce Momjian committed
1662 1663
	switch (in)
	{
1664
		case PRINT_NOTHING:
Bruce Momjian's avatar
Bruce Momjian committed
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
			return "nothing";
			break;
		case PRINT_UNALIGNED:
			return "unaligned";
			break;
		case PRINT_ALIGNED:
			return "aligned";
			break;
		case PRINT_HTML:
			return "html";
			break;
		case PRINT_LATEX:
			return "latex";
			break;
	}
	return "unknown";
1681 1682 1683 1684
}


bool
1685
do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
1686
{
Bruce Momjian's avatar
Bruce Momjian committed
1687 1688
	size_t		vallen = 0;

1689
#ifdef USE_ASSERT_CHECKING
Bruce Momjian's avatar
Bruce Momjian committed
1690
	assert(param);
1691
#else
Bruce Momjian's avatar
Bruce Momjian committed
1692 1693
	if (!param)
		return false;
1694 1695
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713
	if (value)
		vallen = strlen(value);

	/* set format */
	if (strcmp(param, "format") == 0)
	{
		if (!value)
			;
		else if (strncasecmp("unaligned", value, vallen) == 0)
			popt->topt.format = PRINT_UNALIGNED;
		else if (strncasecmp("aligned", value, vallen) == 0)
			popt->topt.format = PRINT_ALIGNED;
		else if (strncasecmp("html", value, vallen) == 0)
			popt->topt.format = PRINT_HTML;
		else if (strncasecmp("latex", value, vallen) == 0)
			popt->topt.format = PRINT_LATEX;
		else
		{
Peter Eisentraut's avatar
Peter Eisentraut committed
1714
			psql_error("\\pset: allowed formats are unaligned, aligned, html, latex\n");
Bruce Momjian's avatar
Bruce Momjian committed
1715 1716 1717 1718
			return false;
		}

		if (!quiet)
1719
			printf(gettext("Output format is %s.\n"), _align2string(popt->topt.format));
1720 1721
	}

Bruce Momjian's avatar
Bruce Momjian committed
1722 1723 1724 1725 1726
	/* set border style/width */
	else if (strcmp(param, "border") == 0)
	{
		if (value)
			popt->topt.border = atoi(value);
1727

Bruce Momjian's avatar
Bruce Momjian committed
1728
		if (!quiet)
1729
			printf(gettext("Border style is %d.\n"), popt->topt.border);
1730
	}
Bruce Momjian's avatar
Bruce Momjian committed
1731 1732 1733 1734 1735 1736

	/* set expanded/vertical mode */
	else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0)
	{
		popt->topt.expanded = !popt->topt.expanded;
		if (!quiet)
1737 1738 1739
			printf(popt->topt.expanded
				   ? gettext("Expanded display is on.\n")
				   : gettext("Expanded display is off.\n"));
1740 1741
	}

Bruce Momjian's avatar
Bruce Momjian committed
1742 1743 1744 1745 1746 1747 1748 1749 1750
	/* null display */
	else if (strcmp(param, "null") == 0)
	{
		if (value)
		{
			free(popt->nullPrint);
			popt->nullPrint = xstrdup(value);
		}
		if (!quiet)
1751
			printf(gettext("Null display is '%s'.\n"), popt->nullPrint ? popt->nullPrint : "");
1752 1753
	}

Bruce Momjian's avatar
Bruce Momjian committed
1754 1755 1756 1757 1758 1759 1760 1761 1762
	/* field separator for unaligned text */
	else if (strcmp(param, "fieldsep") == 0)
	{
		if (value)
		{
			free(popt->topt.fieldSep);
			popt->topt.fieldSep = xstrdup(value);
		}
		if (!quiet)
1763
			printf(gettext("Field separator is '%s'.\n"), popt->topt.fieldSep);
Peter Eisentraut's avatar
Peter Eisentraut committed
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773
	}

	/* record separator for unaligned text */
	else if (strcmp(param, "recordsep") == 0)
	{
		if (value)
		{
			free(popt->topt.recordSep);
			popt->topt.recordSep = xstrdup(value);
		}
1774 1775 1776
		if (!quiet)
		{
			if (strcmp(popt->topt.recordSep, "\n") == 0)
1777
				printf(gettext("Record separator is <newline>."));
1778
			else
1779
				printf(gettext("Record separator is '%s'.\n"), popt->topt.recordSep);
1780
		}
Bruce Momjian's avatar
Bruce Momjian committed
1781
	}
1782

Bruce Momjian's avatar
Bruce Momjian committed
1783 1784 1785 1786 1787 1788 1789
	/* toggle between full and barebones format */
	else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
	{
		popt->topt.tuples_only = !popt->topt.tuples_only;
		if (!quiet)
		{
			if (popt->topt.tuples_only)
1790
				puts(gettext("Showing only tuples."));
Bruce Momjian's avatar
Bruce Momjian committed
1791
			else
1792
				puts(gettext("Tuples only is off."));
Bruce Momjian's avatar
Bruce Momjian committed
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807
		}
	}

	/* set title override */
	else if (strcmp(param, "title") == 0)
	{
		free(popt->title);
		if (!value)
			popt->title = NULL;
		else
			popt->title = xstrdup(value);

		if (!quiet)
		{
			if (popt->title)
1808
				printf(gettext("Title is \"%s\".\n"), popt->title);
Bruce Momjian's avatar
Bruce Momjian committed
1809
			else
1810
				printf(gettext("Title is unset.\n"));
Bruce Momjian's avatar
Bruce Momjian committed
1811
		}
1812
	}
Bruce Momjian's avatar
Bruce Momjian committed
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825

	/* set HTML table tag options */
	else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0)
	{
		free(popt->topt.tableAttr);
		if (!value)
			popt->topt.tableAttr = NULL;
		else
			popt->topt.tableAttr = xstrdup(value);

		if (!quiet)
		{
			if (popt->topt.tableAttr)
1826
				printf(gettext("Table attribute is \"%s\".\n"), popt->topt.tableAttr);
Bruce Momjian's avatar
Bruce Momjian committed
1827
			else
1828
				printf(gettext("Table attributes unset.\n"));
Bruce Momjian's avatar
Bruce Momjian committed
1829
		}
1830 1831
	}

Bruce Momjian's avatar
Bruce Momjian committed
1832 1833 1834 1835 1836 1837 1838
	/* toggle use of pager */
	else if (strcmp(param, "pager") == 0)
	{
		popt->topt.pager = !popt->topt.pager;
		if (!quiet)
		{
			if (popt->topt.pager)
1839
				puts(gettext("Using pager is on."));
Bruce Momjian's avatar
Bruce Momjian committed
1840
			else
1841
				puts(gettext("Using pager is off."));
Bruce Momjian's avatar
Bruce Momjian committed
1842 1843
		}
	}
1844

1845 1846 1847 1848 1849 1850 1851
	/* disable "(x rows)" footer */
	else if (strcmp(param, "footer") == 0)
	{
		popt->default_footer = !popt->default_footer;
		if (!quiet)
		{
			if (popt->default_footer)
1852
				puts(gettext("Default footer is on."));
1853
			else
1854
				puts(gettext("Default footer is off."));
1855 1856
		}
	}
Bruce Momjian's avatar
Bruce Momjian committed
1857 1858 1859

	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1860
		psql_error("\\pset: unknown option: %s\n", param);
Bruce Momjian's avatar
Bruce Momjian committed
1861 1862 1863 1864
		return false;
	}

	return true;
1865 1866 1867 1868
}



1869
#define DEFAULT_SHELL "/bin/sh"
1870 1871 1872 1873

static bool
do_shell(const char *command)
{
Bruce Momjian's avatar
Bruce Momjian committed
1874
	int			result;
1875

Bruce Momjian's avatar
Bruce Momjian committed
1876 1877 1878
	if (!command)
	{
		char	   *sys;
1879
		const char *shellName;
Bruce Momjian's avatar
Bruce Momjian committed
1880 1881 1882 1883 1884 1885

		shellName = getenv("SHELL");
		if (shellName == NULL)
			shellName = DEFAULT_SHELL;

		sys = malloc(strlen(shellName) + 16);
1886 1887 1888 1889 1890 1891 1892 1893
		if (!sys)
		{
			psql_error("out of memory\n");
			if (pset.cur_cmd_interactive)
				return false;
			else
				exit(EXIT_FAILURE);
		}
Bruce Momjian's avatar
Bruce Momjian committed
1894 1895 1896 1897 1898 1899
		sprintf(sys, "exec %s", shellName);
		result = system(sys);
		free(sys);
	}
	else
		result = system(command);
1900

Bruce Momjian's avatar
Bruce Momjian committed
1901 1902
	if (result == 127 || result == -1)
	{
1903
		psql_error("\\!: failed\n");
Bruce Momjian's avatar
Bruce Momjian committed
1904 1905 1906
		return false;
	}
	return true;
1907
}