command.c 33.5 KB
Newer Older
Peter Eisentraut's avatar
Peter Eisentraut committed
1 2 3
/*
 * psql - the PostgreSQL interactive terminal
 *
Bruce Momjian's avatar
Bruce Momjian committed
4
 * Copyright (c) 2000-2004, PostgreSQL Global Development Group
Peter Eisentraut's avatar
Peter Eisentraut committed
5
 *
Peter Eisentraut's avatar
Peter Eisentraut committed
6
 * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.134 2004/11/09 14:39:43 petere 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 <ctype.h>
Peter Eisentraut's avatar
Peter Eisentraut committed
13 14 15
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
16
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
17
#include <sys/types.h>			/* for umask() */
18 19
#include <sys/stat.h>			/* for stat() */
#include <fcntl.h>				/* open() flags */
Bruce Momjian's avatar
Bruce Momjian committed
20
#include <unistd.h>				/* for geteuid(), getpid(), stat() */
21 22
#else
#include <win32.h>
23
#include <io.h>
24
#include <fcntl.h>
25
#include <direct.h>
26 27 28 29
#ifndef WIN32_CLIENT_ONLY
#include <sys/types.h>			/* for umask() */
#include <sys/stat.h>			/* for stat() */
#endif
30 31
#endif

32 33
#include "libpq-fe.h"
#include "pqexpbuffer.h"
34

35
#include "common.h"
36
#include "copy.h"
37
#include "describe.h"
38
#include "help.h"
39
#include "input.h"
40
#include "large_obj.h"
41
#include "mainloop.h"
42
#include "print.h"
43
#include "psqlscan.h"
44
#include "settings.h"
45
#include "variables.h"
46
#include "mb/pg_wchar.h"
47 48


49
/* functions for use in this file */
Bruce Momjian's avatar
Bruce Momjian committed
50
static backslashResult exec_command(const char *cmd,
Bruce Momjian's avatar
Bruce Momjian committed
51 52
			 PsqlScanState scan_state,
			 PQExpBuffer query_buf);
53 54
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
static bool do_connect(const char *new_dbname, const char *new_user);
55 56
static bool do_shell(const char *command);

57

58 59 60 61 62 63
/*----------
 * HandleSlashCmds:
 *
 * Handles all the different commands that start with '\',
 * ordinarily called by MainLoop().
 *
64 65 66
 * scan_state is a lexer working state that is set to continue scanning
 * just after the '\'.  The lexer is advanced past the command and all
 * arguments on return.
67 68
 *
 * 'query_buf' contains the query-so-far, which may be modified by
69
 * execution of the backslash command (for example, \r clears it).
70
 * query_buf can be NULL if there is no query so far.
71 72 73 74 75 76
 *
 * Returns a status code indicating what action is desired, see command.h.
 *----------
 */

backslashResult
77 78
HandleSlashCmds(PsqlScanState scan_state,
				PQExpBuffer query_buf)
79
{
Bruce Momjian's avatar
Bruce Momjian committed
80
	backslashResult status = CMD_SKIP_LINE;
81 82
	char	   *cmd;
	char	   *arg;
Bruce Momjian's avatar
Bruce Momjian committed
83

84
	psql_assert(scan_state);
Bruce Momjian's avatar
Bruce Momjian committed
85

86 87
	/* Parse off the command name */
	cmd = psql_scan_slash_command(scan_state);
Bruce Momjian's avatar
Bruce Momjian committed
88

89 90
	/* And try to execute it */
	status = exec_command(cmd, scan_state, query_buf);
91

92
	if (status == CMD_UNKNOWN && strlen(cmd) > 1)
Bruce Momjian's avatar
Bruce Momjian committed
93 94
	{
		/*
95 96 97
		 * 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
98
		 */
Bruce Momjian's avatar
Bruce Momjian committed
99
		char		new_cmd[2];
Bruce Momjian's avatar
Bruce Momjian committed
100

101 102
		/* don't change cmd until we know it's okay */
		new_cmd[0] = cmd[0];
Bruce Momjian's avatar
Bruce Momjian committed
103 104
		new_cmd[1] = '\0';

105
		psql_scan_slash_pushback(scan_state, cmd + 1);
106

107 108 109 110 111 112
		status = exec_command(new_cmd, scan_state, query_buf);

		if (status != CMD_UNKNOWN)
		{
			/* adjust cmd for possible messages below */
			cmd[1] = '\0';
113

114
#if 0							/* turned out to be too annoying */
115 116
			if (isalpha((unsigned char) cmd[0]))
				psql_error("Warning: This syntax is deprecated.\n");
Peter Eisentraut's avatar
Peter Eisentraut committed
117
#endif
118
		}
Bruce Momjian's avatar
Bruce Momjian committed
119
	}
120

Bruce Momjian's avatar
Bruce Momjian committed
121 122
	if (status == CMD_UNKNOWN)
	{
123
		if (pset.cur_cmd_interactive)
124
			fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), cmd);
125
		else
126
			psql_error("invalid command \\%s\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
127 128
		status = CMD_ERROR;
	}
129

130 131 132
	/* eat the rest of the options, if any */
	while ((arg = psql_scan_slash_option(scan_state,
										 OT_NORMAL, NULL, false)))
133
	{
134 135 136
		if (status != CMD_ERROR)
			psql_error("\\%s: extra argument \"%s\" ignored\n", cmd, arg);
		free(arg);
137
	}
138

139 140 141 142
	/* if there is a trailing \\, swallow it */
	psql_scan_slash_command_end(scan_state);

	free(cmd);
143

Bruce Momjian's avatar
Bruce Momjian committed
144
	return status;
145 146
}

147 148 149
/*
 * Subroutine to actually try to execute a backslash command.
 */
150
static backslashResult
Bruce Momjian's avatar
Bruce Momjian committed
151
exec_command(const char *cmd,
152 153
			 PsqlScanState scan_state,
			 PQExpBuffer query_buf)
154
{
Bruce Momjian's avatar
Bruce Momjian committed
155 156
	bool		success = true; /* indicate here if the command ran ok or
								 * failed */
157
	bool		quiet = QUIET();
Bruce Momjian's avatar
Bruce Momjian committed
158
	backslashResult status = CMD_SKIP_LINE;
159 160 161 162 163

	/*
	 * \a -- toggle field alignment This makes little sense but we keep it
	 * around.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
164 165
	if (strcmp(cmd, "a") == 0)
	{
166 167
		if (pset.popt.topt.format != PRINT_ALIGNED)
			success = do_pset("format", "aligned", &pset.popt, quiet);
Bruce Momjian's avatar
Bruce Momjian committed
168
		else
169
			success = do_pset("format", "unaligned", &pset.popt, quiet);
170
	}
Bruce Momjian's avatar
Bruce Momjian committed
171

172
	/* \C -- override table title (formerly change HTML caption) */
Bruce Momjian's avatar
Bruce Momjian committed
173
	else if (strcmp(cmd, "C") == 0)
174
	{
175 176
		char	   *opt = psql_scan_slash_option(scan_state,
												 OT_NORMAL, NULL, true);
177

178
		success = do_pset("title", opt, &pset.popt, quiet);
179 180
		free(opt);
	}
Bruce Momjian's avatar
Bruce Momjian committed
181

182
	/*----------
Bruce Momjian's avatar
Bruce Momjian committed
183 184
	 * \c or \connect -- connect to new database or as different user
	 *
185
	 * \c foo bar  connect to db "foo" as user "bar"
186 187 188 189
	 * \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
190 191 192
	 */
	else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
	{
193 194 195 196
		char	   *opt1,
				   *opt2;
		char		opt1q,
					opt2q;
197

198
		/*
Bruce Momjian's avatar
Bruce Momjian committed
199 200 201 202 203 204 205
		 * 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.
206
		 */
207 208 209 210
		opt1 = psql_scan_slash_option(scan_state,
									  OT_SQLIDHACK, &opt1q, true);
		opt2 = psql_scan_slash_option(scan_state,
									  OT_SQLIDHACK, &opt2q, true);
211 212

		if (opt2)
Bruce Momjian's avatar
Bruce Momjian committed
213
			/* gave username */
214 215
			success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1,
								 !opt2q && (strcmp(opt2, "-") == 0 || strcmp(opt2, "") == 0) ? "" : opt2);
216
		else if (opt1)
217 218 219 220 221 222 223 224
			/* 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);
225
	}
226

Peter Eisentraut's avatar
Peter Eisentraut committed
227 228 229
	/* \cd */
	else if (strcmp(cmd, "cd") == 0)
	{
230 231
		char	   *opt = psql_scan_slash_option(scan_state,
												 OT_NORMAL, NULL, true);
232
		char	   *dir;
Peter Eisentraut's avatar
Peter Eisentraut committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

		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;
248 249 250 251 252 253
#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
254
			dir = "/";
255
#endif   /* WIN32 */
Peter Eisentraut's avatar
Peter Eisentraut committed
256 257 258 259
		}

		if (chdir(dir) == -1)
		{
260
			psql_error("\\%s: could not change directory to \"%s\": %s\n",
Peter Eisentraut's avatar
Peter Eisentraut committed
261 262 263 264 265 266 267 268
					   cmd, dir, strerror(errno));
			success = false;
		}

		if (opt)
			free(opt);
	}

Bruce Momjian's avatar
Bruce Momjian committed
269
	/* \copy */
270
	else if (pg_strcasecmp(cmd, "copy") == 0)
271
	{
272
		char	   *opt = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
273
											 OT_WHOLE_LINE, NULL, false);
274 275 276

		success = do_copy(opt);
		free(opt);
277
	}
278

Bruce Momjian's avatar
Bruce Momjian committed
279 280 281
	/* \copyright */
	else if (strcmp(cmd, "copyright") == 0)
		print_copyright();
282

Bruce Momjian's avatar
Bruce Momjian committed
283 284 285
	/* \d* commands */
	else if (cmd[0] == 'd')
	{
286
		char	   *pattern;
287
		bool		show_verbose;
288

289
		/* We don't do SQLID reduction on the pattern yet */
290 291
		pattern = psql_scan_slash_option(scan_state,
										 OT_NORMAL, NULL, true);
292

293
		show_verbose = strchr(cmd, '+') ? true : false;
294

Bruce Momjian's avatar
Bruce Momjian committed
295 296 297
		switch (cmd[1])
		{
			case '\0':
298
			case '+':
299 300
				if (pattern)
					success = describeTableDetails(pattern, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
301
				else
302
					/* standard listing of interesting things */
303
					success = listTables("tvs", NULL, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
304 305
				break;
			case 'a':
306
				success = describeAggregates(pattern, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
307
				break;
308
			case 'b':
309
				success = describeTablespaces(pattern, show_verbose);
310
				break;
311 312 313 314 315 316
			case 'c':
				success = listConversions(pattern);
				break;
			case 'C':
				success = listCasts(pattern);
				break;
Bruce Momjian's avatar
Bruce Momjian committed
317
			case 'd':
318
				success = objectDescription(pattern);
Bruce Momjian's avatar
Bruce Momjian committed
319
				break;
320 321 322
			case 'D':
				success = listDomains(pattern);
				break;
Bruce Momjian's avatar
Bruce Momjian committed
323
			case 'f':
324
				success = describeFunctions(pattern, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
325
				break;
326 327 328
			case 'g':
				success = describeGroups(pattern);
				break;
Bruce Momjian's avatar
Bruce Momjian committed
329
			case 'l':
330
				success = do_lo_list();
Bruce Momjian's avatar
Bruce Momjian committed
331
				break;
332
			case 'n':
333
				success = listSchemas(pattern, show_verbose);
334
				break;
Bruce Momjian's avatar
Bruce Momjian committed
335
			case 'o':
336
				success = describeOperators(pattern);
Bruce Momjian's avatar
Bruce Momjian committed
337 338
				break;
			case 'p':
339
				success = permissionsList(pattern);
Bruce Momjian's avatar
Bruce Momjian committed
340 341
				break;
			case 'T':
342
				success = describeTypes(pattern, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
343 344 345 346 347 348
				break;
			case 't':
			case 'v':
			case 'i':
			case 's':
			case 'S':
349
				success = listTables(&cmd[1], pattern, show_verbose);
Bruce Momjian's avatar
Bruce Momjian committed
350
				break;
351
			case 'u':
352
				success = describeUsers(pattern);
353
				break;
354

Bruce Momjian's avatar
Bruce Momjian committed
355 356 357
			default:
				status = CMD_UNKNOWN;
		}
358 359 360

		if (pattern)
			free(pattern);
Bruce Momjian's avatar
Bruce Momjian committed
361
	}
362 363


Bruce Momjian's avatar
Bruce Momjian committed
364 365 366 367 368
	/*
	 * \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)
369 370 371 372 373 374 375 376 377 378
	{
		char	   *fname;

		if (!query_buf)
		{
			psql_error("no query buffer\n");
			status = CMD_ERROR;
		}
		else
		{
379 380
			fname = psql_scan_slash_option(scan_state,
										   OT_NORMAL, NULL, true);
381
			expand_tilde(&fname);
382 383
			if (fname)
				canonicalize_path(fname);
384 385 386 387
			status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
			free(fname);
		}
	}
388

389
	/* \echo and \qecho */
390
	else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
391
	{
392 393 394 395 396 397 398 399 400 401
		char	   *value;
		char		quoted;
		bool		no_newline = false;
		bool		first = true;
		FILE	   *fout;

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

403
		while ((value = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
404
											 OT_NORMAL, &quoted, false)))
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
		{
			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
420
	}
421

Tatsuo Ishii's avatar
Tatsuo Ishii committed
422
	/* \encoding -- set/show client side encoding */
423
	else if (strcmp(cmd, "encoding") == 0)
424
	{
425
		char	   *encoding = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
426
												 OT_NORMAL, NULL, false);
427

428
		if (!encoding)
429
		{
430
			/* show encoding */
Tatsuo Ishii's avatar
Tatsuo Ishii committed
431
			puts(pg_encoding_to_char(pset.encoding));
432
		}
433
		else
Tatsuo Ishii's avatar
Tatsuo Ishii committed
434 435 436
		{
			/* set encoding */
			if (PQsetClientEncoding(pset.db, encoding) == -1)
437
				psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding);
Tatsuo Ishii's avatar
Tatsuo Ishii committed
438 439 440 441
			else
			{
				/* save encoding info into psql internal data */
				pset.encoding = PQclientEncoding(pset.db);
442 443 444
				pset.popt.topt.encoding = pset.encoding;
				SetVariable(pset.vars, "ENCODING",
							pg_encoding_to_char(pset.encoding));
Tatsuo Ishii's avatar
Tatsuo Ishii committed
445
			}
446 447
			free(encoding);
		}
448 449
	}

450
	/* \f -- change field separator */
Bruce Momjian's avatar
Bruce Momjian committed
451
	else if (strcmp(cmd, "f") == 0)
452
	{
453
		char	   *fname = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
454
												 OT_NORMAL, NULL, false);
455

456
		success = do_pset("fieldsep", fname, &pset.popt, quiet);
457 458
		free(fname);
	}
459

Bruce Momjian's avatar
Bruce Momjian committed
460 461 462
	/* \g means send query */
	else if (strcmp(cmd, "g") == 0)
	{
463
		char	   *fname = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
464
											   OT_FILEPIPE, NULL, false);
465

466
		if (!fname)
467
			pset.gfname = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
468
		else
469 470
		{
			expand_tilde(&fname);
471
			pset.gfname = pg_strdup(fname);
472
		}
473
		free(fname);
Bruce Momjian's avatar
Bruce Momjian committed
474
		status = CMD_SEND;
475
	}
Bruce Momjian's avatar
Bruce Momjian committed
476 477 478

	/* help */
	else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
479
	{
480
		char	   *opt = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
481
											 OT_WHOLE_LINE, NULL, false);
482 483 484

		helpSQL(opt, pset.popt.topt.pager);
		free(opt);
485
	}
Bruce Momjian's avatar
Bruce Momjian committed
486 487 488

	/* HTML mode */
	else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
489
	{
490 491
		if (pset.popt.topt.format != PRINT_HTML)
			success = do_pset("format", "html", &pset.popt, quiet);
492
		else
493
			success = do_pset("format", "aligned", &pset.popt, quiet);
494
	}
Bruce Momjian's avatar
Bruce Momjian committed
495 496 497 498 499


	/* \i is include file */
	else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
	{
500 501
		char	   *fname = psql_scan_slash_option(scan_state,
												   OT_NORMAL, NULL, true);
502

503
		if (!fname)
504 505
		{
			psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
506 507 508
			success = false;
		}
		else
509
		{
510
			expand_tilde(&fname);
511
			success = (process_file(fname) == EXIT_SUCCESS);
512 513
			free(fname);
		}
514 515
	}

Bruce Momjian's avatar
Bruce Momjian committed
516 517
	/* \l is list databases */
	else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
518
		success = listAllDbs(false);
519
	else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
520
		success = listAllDbs(true);
Bruce Momjian's avatar
Bruce Momjian committed
521

522
	/*
523 524
	 * large object things
	 */
Bruce Momjian's avatar
Bruce Momjian committed
525 526
	else if (strncmp(cmd, "lo_", 3) == 0)
	{
527 528
		char	   *opt1,
				   *opt2;
529

530 531 532 533
		opt1 = psql_scan_slash_option(scan_state,
									  OT_NORMAL, NULL, true);
		opt2 = psql_scan_slash_option(scan_state,
									  OT_NORMAL, NULL, true);
534

Bruce Momjian's avatar
Bruce Momjian committed
535 536
		if (strcmp(cmd + 3, "export") == 0)
		{
537
			if (!opt2)
Bruce Momjian's avatar
Bruce Momjian committed
538
			{
539
				psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
540 541 542
				success = false;
			}
			else
543 544
			{
				expand_tilde(&opt2);
545
				success = do_lo_export(opt1, opt2);
546
			}
Bruce Momjian's avatar
Bruce Momjian committed
547 548 549 550
		}

		else if (strcmp(cmd + 3, "import") == 0)
		{
551
			if (!opt1)
Bruce Momjian's avatar
Bruce Momjian committed
552
			{
553
				psql_error("\\%s: missing required argument\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
554 555 556
				success = false;
			}
			else
557 558
			{
				expand_tilde(&opt1);
559
				success = do_lo_import(opt1, opt2);
560
			}
Bruce Momjian's avatar
Bruce Momjian committed
561 562 563
		}

		else if (strcmp(cmd + 3, "list") == 0)
564
			success = do_lo_list();
Bruce Momjian's avatar
Bruce Momjian committed
565 566 567

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

		else
			status = CMD_UNKNOWN;
579

580 581
		free(opt1);
		free(opt2);
582 583
	}

584

Bruce Momjian's avatar
Bruce Momjian committed
585 586
	/* \o -- set query output */
	else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
587
	{
588
		char	   *fname = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
589
												OT_FILEPIPE, NULL, true);
590

591
		expand_tilde(&fname);
592
		success = setQFout(fname);
593 594
		free(fname);
	}
Bruce Momjian's avatar
Bruce Momjian committed
595 596 597 598 599 600

	/* \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);
601
		else if (!quiet)
602
			puts(gettext("Query buffer is empty."));
603
		fflush(stdout);
604 605
	}

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

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

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

	/* \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);
634
		psql_scan_reset(scan_state);
Bruce Momjian's avatar
Bruce Momjian committed
635
		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 643
		char	   *fname = psql_scan_slash_option(scan_state,
												   OT_NORMAL, NULL, true);
Bruce Momjian's avatar
Bruce Momjian committed
644

645
		expand_tilde(&fname);
646
		/* This scrolls off the screen when using /dev/tty */
647
		success = saveHistory(fname ? fname : "/dev/tty");
648

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

654
	/* \set -- generalized set variable/option command */
Bruce Momjian's avatar
Bruce Momjian committed
655 656
	else if (strcmp(cmd, "set") == 0)
	{
657 658
		char	   *opt0 = psql_scan_slash_option(scan_state,
												  OT_NORMAL, NULL, false);
659 660

		if (!opt0)
Bruce Momjian's avatar
Bruce Momjian committed
661 662
		{
			/* list all variables */
663
			PrintVariables(pset.vars);
Bruce Momjian's avatar
Bruce Momjian committed
664 665 666 667
			success = true;
		}
		else
		{
668 669 670
			/*
			 * Set variable to the concatenation of the arguments.
			 */
671
			char	   *newval;
672 673
			char	   *opt;

674 675
			opt = psql_scan_slash_option(scan_state,
										 OT_NORMAL, NULL, false);
676
			newval = pg_strdup(opt ? opt : "");
677 678
			free(opt);

679 680
			while ((opt = psql_scan_slash_option(scan_state,
												 OT_NORMAL, NULL, false)))
681 682 683 684 685 686 687 688 689 690
			{
				newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
				if (!newval)
				{
					psql_error("out of memory\n");
					exit(EXIT_FAILURE);
				}
				strcat(newval, opt);
				free(opt);
			}
691

692 693 694
			if (SetVariable(pset.vars, opt0, newval))
			{
				/* Check for special variables */
695 696
				if (strcmp(opt0, "VERBOSITY") == 0)
					SyncVerbosityVariable();
697 698
			}
			else
Bruce Momjian's avatar
Bruce Momjian committed
699
			{
700
				psql_error("\\%s: error\n", cmd);
Bruce Momjian's avatar
Bruce Momjian committed
701 702
				success = false;
			}
703
			free(newval);
Bruce Momjian's avatar
Bruce Momjian committed
704
		}
705
		free(opt0);
706
	}
Bruce Momjian's avatar
Bruce Momjian committed
707 708 709

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


	/* \T -- define html <table ...> attributes */
	else if (strcmp(cmd, "T") == 0)
715
	{
716
		char	   *value = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
717
												 OT_NORMAL, NULL, false);
718

719
		success = do_pset("tableattr", value, &pset.popt, quiet);
720 721 722
		free(value);
	}

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

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

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

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

		if (!query_buf)
		{
			psql_error("no query buffer\n");
			status = CMD_ERROR;
		}
		else
		{
769 770
			fname = psql_scan_slash_option(scan_state,
										   OT_FILEPIPE, NULL, true);
771
			expand_tilde(&fname);
772 773 774 775 776 777 778 779 780 781 782 783 784 785

			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
786 787
				{
					canonicalize_path(fname);
788
					fd = fopen(fname, "w");
789
				}
790 791 792 793 794 795 796
				if (!fd)
				{
					psql_error("%s: %s\n", fname, strerror(errno));
					success = false;
				}
			}
		}
797

Bruce Momjian's avatar
Bruce Momjian committed
798 799 800
		if (fd)
		{
			int			result;
801

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

805
			if (is_pipe)
Bruce Momjian's avatar
Bruce Momjian committed
806 807 808
				result = pclose(fd);
			else
				result = fclose(fd);
809

Bruce Momjian's avatar
Bruce Momjian committed
810 811
			if (result == EOF)
			{
812
				psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
813 814 815
				success = false;
			}
		}
816

817
		free(fname);
818 819
	}

Bruce Momjian's avatar
Bruce Momjian committed
820 821
	/* \x -- toggle expanded table representation */
	else if (strcmp(cmd, "x") == 0)
822
		success = do_pset("expanded", NULL, &pset.popt, quiet);
823 824


825
	/* \z -- list table rights (equivalent to \dp) */
Bruce Momjian's avatar
Bruce Momjian committed
826
	else if (strcmp(cmd, "z") == 0)
827
	{
828
		char	   *pattern = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
829
												  OT_NORMAL, NULL, true);
830

831 832 833
		success = permissionsList(pattern);
		if (pattern)
			free(pattern);
834
	}
835

836
	/* \! -- shell escape */
Bruce Momjian's avatar
Bruce Momjian committed
837
	else if (strcmp(cmd, "!") == 0)
838
	{
839
		char	   *opt = psql_scan_slash_option(scan_state,
Bruce Momjian's avatar
Bruce Momjian committed
840
											 OT_WHOLE_LINE, NULL, false);
841 842 843

		success = do_shell(opt);
		free(opt);
844
	}
Bruce Momjian's avatar
Bruce Momjian committed
845

846
	/* \? -- slash command help */
Bruce Momjian's avatar
Bruce Momjian committed
847
	else if (strcmp(cmd, "?") == 0)
848
		slashUsage(pset.popt.topt.pager);
849

850
#if 0
851 852

	/*
Bruce Momjian's avatar
Bruce Momjian committed
853 854 855 856 857
	 * These commands don't do anything. I just use them to test the
	 * parser.
	 */
	else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0)
	{
858
		int			i = 0;
859
		char	   *value;
Bruce Momjian's avatar
Bruce Momjian committed
860

861 862
		while ((value = psql_scan_slash_option(scan_state,
											   OT_NORMAL, NULL, true)))
863
		{
864
			fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
865 866
			free(value);
		}
Bruce Momjian's avatar
Bruce Momjian committed
867
	}
868 869
#endif

Bruce Momjian's avatar
Bruce Momjian committed
870 871
	else
		status = CMD_UNKNOWN;
872

Bruce Momjian's avatar
Bruce Momjian committed
873 874
	if (!success)
		status = CMD_ERROR;
875

Bruce Momjian's avatar
Bruce Momjian committed
876
	return status;
877 878 879 880 881 882 883 884 885
}



/* 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.
886
 * (That is, the one currently in pset. But pset.db can also be NULL. A NULL
887
 * dbname is handled by libpq.)
888 889
 * 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.
890
 */
891
static bool
892
do_connect(const char *new_dbname, const char *new_user)
893
{
894
	PGconn	   *oldconn = pset.db;
Bruce Momjian's avatar
Bruce Momjian committed
895 896
	const char *dbparam = NULL;
	const char *userparam = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
897
	const char *pwparam = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
898 899 900 901
	char	   *prompted_password = NULL;
	bool		need_pass;
	bool		success = false;

902
	/* Delete variables (in case we fail before setting them anew) */
903
	UnsyncVariables();
904

905 906
	/* 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
907 908 909 910
		dbparam = PQdb(oldconn);
	else
		dbparam = new_dbname;

911
	/* If user is "" then use the old one */
912
	if (new_user && PQuser(oldconn) && strcmp(new_user, "") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
913 914 915 916 917
		userparam = PQuser(oldconn);
	else
		userparam = new_user;

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

	/*
Tom Lane's avatar
Tom Lane committed
922 923
	 * Use old password (if any) if no new one given and we are
	 * reconnecting as same user
Bruce Momjian's avatar
Bruce Momjian committed
924
	 */
925 926
	if (!pwparam && oldconn && PQuser(oldconn) && userparam &&
		strcmp(PQuser(oldconn), userparam) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
927
		pwparam = PQpass(oldconn);
928

Bruce Momjian's avatar
Bruce Momjian committed
929 930 931
	do
	{
		need_pass = false;
932
		pset.db = PQsetdbLogin(PQhost(oldconn), PQport(oldconn),
933
							   NULL, NULL, dbparam, userparam, pwparam);
Bruce Momjian's avatar
Bruce Momjian committed
934

935
		if (PQstatus(pset.db) == CONNECTION_BAD &&
936
			strcmp(PQerrorMessage(pset.db), PQnoPasswordSupplied) == 0 &&
937
			!feof(stdin))
Bruce Momjian's avatar
Bruce Momjian committed
938
		{
939
			PQfinish(pset.db);
Bruce Momjian's avatar
Bruce Momjian committed
940 941 942 943 944 945 946 947 948 949 950 951 952
			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.
	 */
953
	if (!pset.db || PQstatus(pset.db) == CONNECTION_BAD)
Bruce Momjian's avatar
Bruce Momjian committed
954
	{
955 956 957 958 959 960
		if (pset.cur_cmd_interactive)
		{
			psql_error("%s", PQerrorMessage(pset.db));
			PQfinish(pset.db);
			if (oldconn)
			{
961
				fputs(gettext("Previous connection kept\n"), stderr);
962 963 964 965 966 967 968 969 970 971 972 973 974
				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
975 976
			if (oldconn)
				PQfinish(oldconn);
977
			pset.db = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
978
		}
979
	}
Bruce Momjian's avatar
Bruce Momjian committed
980 981
	else
	{
982
		if (!QUIET())
Bruce Momjian's avatar
Bruce Momjian committed
983 984
		{
			if (userparam != new_user)	/* no new user */
985
				printf(gettext("You are now connected to database \"%s\".\n"), dbparam);
Bruce Momjian's avatar
Bruce Momjian committed
986
			else if (dbparam != new_dbname)		/* no new db */
987
				printf(gettext("You are now connected as new user \"%s\".\n"), new_user);
988
			else
989
				/* both new */
990
				printf(gettext("You are now connected to database \"%s\" as user \"%s\".\n"),
991
					   PQdb(pset.db), PQuser(pset.db));
Bruce Momjian's avatar
Bruce Momjian committed
992
		}
993

Bruce Momjian's avatar
Bruce Momjian committed
994 995
		if (oldconn)
			PQfinish(oldconn);
996

Bruce Momjian's avatar
Bruce Momjian committed
997 998
		success = true;
	}
999

1000
	PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
Peter Eisentraut's avatar
Peter Eisentraut committed
1001

1002
	/* Update variables */
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
	SyncVariables();

	return success;
}


/*
 * SyncVariables
 *
 * Make psql's internal variables agree with connection state upon
 * establishing a new connection.
 */
void
SyncVariables(void)
{
	/* get stuff from connection */
	pset.encoding = PQclientEncoding(pset.db);
	pset.popt.topt.encoding = pset.encoding;

1022 1023 1024 1025 1026
	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));
1027

1028
	/* send stuff to it, too */
1029
	SyncVerbosityVariable();
1030 1031
}

1032
/*
1033 1034 1035
 * UnsyncVariables
 *
 * Clear variables that should be not be set when there is no connection.
1036
 */
1037 1038
void
UnsyncVariables(void)
1039
{
1040 1041 1042 1043 1044
	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);
1045 1046
}

1047
/*
1048
 * Update connection state from VERBOSITY variable
1049 1050
 */
void
1051
SyncVerbosityVariable(void)
1052
{
1053
	switch (SwitchVariable(pset.vars, "VERBOSITY",
1054 1055
						   "default", "terse", "verbose", NULL))
	{
Bruce Momjian's avatar
Bruce Momjian committed
1056
		case 1:			/* default */
1057
			pset.verbosity = PQERRORS_DEFAULT;
1058
			break;
Bruce Momjian's avatar
Bruce Momjian committed
1059
		case 2:			/* terse */
1060
			pset.verbosity = PQERRORS_TERSE;
1061
			break;
Bruce Momjian's avatar
Bruce Momjian committed
1062
		case 3:			/* verbose */
1063
			pset.verbosity = PQERRORS_VERBOSE;
1064 1065
			break;
		default:				/* not set or unrecognized value */
1066
			pset.verbosity = PQERRORS_DEFAULT;
1067 1068
			break;
	}
1069 1070

	PQsetErrorVerbosity(pset.db, pset.verbosity);
1071
}
1072 1073


1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
/*
 * 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)
{
1084
	const char *editorName;
Bruce Momjian's avatar
Bruce Momjian committed
1085 1086
	char	   *sys;
	int			result;
1087

1088
	psql_assert(fname);
1089

Bruce Momjian's avatar
Bruce Momjian committed
1090 1091 1092 1093 1094 1095 1096 1097 1098
	/* Find an editor to use */
	editorName = getenv("PSQL_EDITOR");
	if (!editorName)
		editorName = getenv("EDITOR");
	if (!editorName)
		editorName = getenv("VISUAL");
	if (!editorName)
		editorName = DEFAULT_EDITOR;

1099
	sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
Bruce Momjian's avatar
Bruce Momjian committed
1100
	sprintf(sys,
1101
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
1102
			"exec "
1103
#endif
1104
			"%s\"%s\" \"%s\"%s", SYSTEMQUOTE, editorName, fname, SYSTEMQUOTE);
Bruce Momjian's avatar
Bruce Momjian committed
1105
	result = system(sys);
Peter Eisentraut's avatar
Peter Eisentraut committed
1106
	if (result == -1)
1107
		psql_error("could not start editor \"%s\"\n", editorName);
1108
	else if (result == 127)
Peter Eisentraut's avatar
Peter Eisentraut committed
1109
		psql_error("could not start /bin/sh\n");
Bruce Momjian's avatar
Bruce Momjian committed
1110 1111 1112
	free(sys);

	return result == 0;
1113 1114 1115 1116 1117 1118 1119
}


/* call this one */
static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf)
{
1120
	char		fnametmp[MAXPGPATH];
Peter Eisentraut's avatar
Peter Eisentraut committed
1121
	FILE	   *stream = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
1122 1123
	const char *fname;
	bool		error = false;
1124
	int			fd;
1125

1126
#ifndef WIN32_CLIENT_ONLY
Bruce Momjian's avatar
Bruce Momjian committed
1127 1128
	struct stat before,
				after;
1129 1130
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1131 1132 1133 1134 1135
	if (filename_arg)
		fname = filename_arg;
	else
	{
		/* make a temp file to edit */
1136
#ifndef WIN32
1137
		const char *tmpdir = getenv("TMPDIR");
1138

1139 1140 1141 1142 1143 1144 1145 1146 1147
		if (!tmpdir)
			tmpdir = "/tmp";
#else
		char tmpdir[MAXPGPATH];
		int ret;

		ret = GetTempPath(MAXPGPATH, tmpdir);
		if (ret == 0 || ret > MAXPGPATH)
		{
Peter Eisentraut's avatar
Peter Eisentraut committed
1148
			psql_error("cannot locate temporary directory: %s",
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
						!ret ? strerror(errno) : "");
			return false;
		}
		/*
		 *	No canonicalize_path() here.
		 *	EDIT.EXE run from CMD.EXE prepends the current directory to the
		 *	supplied path unless we use only backslashes, so we do that.
		 */
#endif
		snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d", tmpdir,
#ifndef WIN32
				"/",
1161
#else
1162
				"",	/* trailing separator already present */
1163
#endif
1164 1165
				(int)getpid());

Bruce Momjian's avatar
Bruce Momjian committed
1166
		fname = (const char *) fnametmp;
1167

1168
		fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
1169 1170
		if (fd != -1)
			stream = fdopen(fd, "w");
1171

1172
		if (fd == -1 || !stream)
Bruce Momjian's avatar
Bruce Momjian committed
1173
		{
1174
			psql_error("could not open temporary file \"%s\": %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
			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)
			{
1189
				psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1190 1191 1192 1193
				fclose(stream);
				remove(fname);
				error = true;
			}
1194 1195 1196 1197 1198 1199
			else if (fclose(stream) != 0)
			{
				psql_error("%s: %s\n", fname, strerror(errno));
				remove(fname);
				error = true;
			}
Bruce Momjian's avatar
Bruce Momjian committed
1200
		}
1201
	}
Bruce Momjian's avatar
Bruce Momjian committed
1202

1203
#ifndef WIN32_CLIENT_ONLY
Bruce Momjian's avatar
Bruce Momjian committed
1204 1205
	if (!error && stat(fname, &before) != 0)
	{
1206
		psql_error("%s: %s\n", fname, strerror(errno));
1207 1208 1209 1210
		error = true;
	}
#endif

Bruce Momjian's avatar
Bruce Momjian committed
1211 1212 1213
	/* call editor */
	if (!error)
		error = !editFile(fname);
1214

1215
#ifndef WIN32_CLIENT_ONLY
Bruce Momjian's avatar
Bruce Momjian committed
1216 1217
	if (!error && stat(fname, &after) != 0)
	{
1218
		psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1219 1220
		error = true;
	}
1221

Bruce Momjian's avatar
Bruce Momjian committed
1222 1223
	if (!error && before.st_mtime != after.st_mtime)
	{
1224
#else
Bruce Momjian's avatar
Bruce Momjian committed
1225 1226
	if (!error)
	{
1227
#endif
1228
		stream = fopen(fname, PG_BINARY_R);
Bruce Momjian's avatar
Bruce Momjian committed
1229 1230
		if (!stream)
		{
1231
			psql_error("%s: %s\n", fname, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1232 1233 1234 1235 1236 1237 1238 1239
			error = true;
		}
		else
		{
			/* read file back in */
			char		line[1024];

			resetPQExpBuffer(query_buf);
1240
			while (fgets(line, sizeof(line), stream) != NULL)
1241 1242
				appendPQExpBufferStr(query_buf, line);

1243 1244 1245 1246 1247
			if (ferror(stream))
			{
				psql_error("%s: %s\n", fname, strerror(errno));
				error = true;
			}
Bruce Momjian's avatar
Bruce Momjian committed
1248

1249
#ifdef USE_READLINE
1250 1251
#ifdef HAVE_REPLACE_HISTORY_ENTRY

Bruce Momjian's avatar
Bruce Momjian committed
1252
			replace_history_entry(where_history(), query_buf->data, NULL);
1253 1254 1255
#else
			add_history(query_buf->data);
#endif
1256
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1257
			fclose(stream);
1258 1259
		}

1260
	}
Bruce Momjian's avatar
Bruce Momjian committed
1261

1262
	/* remove temp file */
Bruce Momjian's avatar
Bruce Momjian committed
1263
	if (!filename_arg)
1264 1265 1266 1267 1268 1269 1270
	{
		if (remove(fname) == -1)
		{
			psql_error("%s: %s\n", fname, strerror(errno));
			error = true;
		}
	}
1271

Bruce Momjian's avatar
Bruce Momjian committed
1272
	return !error;
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
}



/*
 * 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.
 */
1283
int
Peter Eisentraut's avatar
Peter Eisentraut committed
1284
process_file(char *filename)
1285
{
Bruce Momjian's avatar
Bruce Momjian committed
1286 1287
	FILE	   *fd;
	int			result;
1288
	char	   *oldfilename;
1289

Bruce Momjian's avatar
Bruce Momjian committed
1290 1291
	if (!filename)
		return false;
1292

1293
	canonicalize_path(filename);
1294
	fd = fopen(filename, PG_BINARY_R);
1295

Bruce Momjian's avatar
Bruce Momjian committed
1296 1297
	if (!fd)
	{
1298
		psql_error("%s: %s\n", filename, strerror(errno));
Bruce Momjian's avatar
Bruce Momjian committed
1299 1300
		return false;
	}
1301

1302 1303
	oldfilename = pset.inputfile;
	pset.inputfile = filename;
Peter Eisentraut's avatar
Peter Eisentraut committed
1304
	result = MainLoop(fd);
Bruce Momjian's avatar
Bruce Momjian committed
1305
	fclose(fd);
Peter Eisentraut's avatar
Peter Eisentraut committed
1306
	pset.inputfile = oldfilename;
1307
	return result;
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
}



/*
 * do_pset
 *
 */
static const char *
_align2string(enum printFormat in)
{
Bruce Momjian's avatar
Bruce Momjian committed
1319 1320
	switch (in)
	{
1321
		case PRINT_NOTHING:
Bruce Momjian's avatar
Bruce Momjian committed
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
			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";
1338 1339 1340 1341
}


bool
1342
do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
1343
{
Bruce Momjian's avatar
Bruce Momjian committed
1344 1345
	size_t		vallen = 0;

1346
	psql_assert(param);
1347

Bruce Momjian's avatar
Bruce Momjian committed
1348 1349 1350 1351 1352 1353 1354 1355
	if (value)
		vallen = strlen(value);

	/* set format */
	if (strcmp(param, "format") == 0)
	{
		if (!value)
			;
1356
		else if (pg_strncasecmp("unaligned", value, vallen) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1357
			popt->topt.format = PRINT_UNALIGNED;
1358
		else if (pg_strncasecmp("aligned", value, vallen) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1359
			popt->topt.format = PRINT_ALIGNED;
1360
		else if (pg_strncasecmp("html", value, vallen) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1361
			popt->topt.format = PRINT_HTML;
1362
		else if (pg_strncasecmp("latex", value, vallen) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1363 1364 1365
			popt->topt.format = PRINT_LATEX;
		else
		{
Peter Eisentraut's avatar
Peter Eisentraut committed
1366
			psql_error("\\pset: allowed formats are unaligned, aligned, html, latex\n");
Bruce Momjian's avatar
Bruce Momjian committed
1367 1368 1369 1370
			return false;
		}

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

Bruce Momjian's avatar
Bruce Momjian committed
1374 1375 1376 1377 1378
	/* set border style/width */
	else if (strcmp(param, "border") == 0)
	{
		if (value)
			popt->topt.border = atoi(value);
1379

Bruce Momjian's avatar
Bruce Momjian committed
1380
		if (!quiet)
1381
			printf(gettext("Border style is %d.\n"), popt->topt.border);
1382
	}
Bruce Momjian's avatar
Bruce Momjian committed
1383 1384 1385 1386 1387 1388

	/* 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)
1389 1390 1391
			printf(popt->topt.expanded
				   ? gettext("Expanded display is on.\n")
				   : gettext("Expanded display is off.\n"));
1392 1393
	}

Bruce Momjian's avatar
Bruce Momjian committed
1394 1395 1396 1397 1398 1399
	/* null display */
	else if (strcmp(param, "null") == 0)
	{
		if (value)
		{
			free(popt->nullPrint);
1400
			popt->nullPrint = pg_strdup(value);
Bruce Momjian's avatar
Bruce Momjian committed
1401 1402
		}
		if (!quiet)
1403
			printf(gettext("Null display is \"%s\".\n"), popt->nullPrint ? popt->nullPrint : "");
1404 1405
	}

Bruce Momjian's avatar
Bruce Momjian committed
1406 1407 1408 1409 1410 1411
	/* field separator for unaligned text */
	else if (strcmp(param, "fieldsep") == 0)
	{
		if (value)
		{
			free(popt->topt.fieldSep);
1412
			popt->topt.fieldSep = pg_strdup(value);
Bruce Momjian's avatar
Bruce Momjian committed
1413 1414
		}
		if (!quiet)
1415
			printf(gettext("Field separator is \"%s\".\n"), popt->topt.fieldSep);
Peter Eisentraut's avatar
Peter Eisentraut committed
1416 1417 1418 1419 1420 1421 1422 1423
	}

	/* record separator for unaligned text */
	else if (strcmp(param, "recordsep") == 0)
	{
		if (value)
		{
			free(popt->topt.recordSep);
1424
			popt->topt.recordSep = pg_strdup(value);
Peter Eisentraut's avatar
Peter Eisentraut committed
1425
		}
1426 1427 1428
		if (!quiet)
		{
			if (strcmp(popt->topt.recordSep, "\n") == 0)
1429
				printf(gettext("Record separator is <newline>."));
1430
			else
1431
				printf(gettext("Record separator is \"%s\".\n"), popt->topt.recordSep);
1432
		}
Bruce Momjian's avatar
Bruce Momjian committed
1433
	}
1434

Bruce Momjian's avatar
Bruce Momjian committed
1435 1436 1437 1438 1439 1440 1441
	/* 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)
1442
				puts(gettext("Showing only tuples."));
Bruce Momjian's avatar
Bruce Momjian committed
1443
			else
1444
				puts(gettext("Tuples only is off."));
Bruce Momjian's avatar
Bruce Momjian committed
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
		}
	}

	/* set title override */
	else if (strcmp(param, "title") == 0)
	{
		free(popt->title);
		if (!value)
			popt->title = NULL;
		else
1455
			popt->title = pg_strdup(value);
Bruce Momjian's avatar
Bruce Momjian committed
1456 1457 1458 1459

		if (!quiet)
		{
			if (popt->title)
1460
				printf(gettext("Title is \"%s\".\n"), popt->title);
Bruce Momjian's avatar
Bruce Momjian committed
1461
			else
1462
				printf(gettext("Title is unset.\n"));
Bruce Momjian's avatar
Bruce Momjian committed
1463
		}
1464
	}
Bruce Momjian's avatar
Bruce Momjian committed
1465 1466 1467 1468 1469 1470 1471 1472

	/* 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
1473
			popt->topt.tableAttr = pg_strdup(value);
Bruce Momjian's avatar
Bruce Momjian committed
1474 1475 1476 1477

		if (!quiet)
		{
			if (popt->topt.tableAttr)
1478
				printf(gettext("Table attribute is \"%s\".\n"), popt->topt.tableAttr);
Bruce Momjian's avatar
Bruce Momjian committed
1479
			else
1480
				printf(gettext("Table attributes unset.\n"));
Bruce Momjian's avatar
Bruce Momjian committed
1481
		}
1482 1483
	}

Bruce Momjian's avatar
Bruce Momjian committed
1484 1485 1486
	/* toggle use of pager */
	else if (strcmp(param, "pager") == 0)
	{
1487
		if (value && pg_strcasecmp(value, "always") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
1488
			popt->topt.pager = 2;
1489
		else if (popt->topt.pager == 1)
Bruce Momjian's avatar
Bruce Momjian committed
1490
			popt->topt.pager = 0;
1491
		else
Bruce Momjian's avatar
Bruce Momjian committed
1492
			popt->topt.pager = 1;
Bruce Momjian's avatar
Bruce Momjian committed
1493 1494
		if (!quiet)
		{
1495
			if (popt->topt.pager == 1)
1496
				puts(gettext("Pager is used for long output."));
1497
			else if (popt->topt.pager == 2)
1498
				puts(gettext("Pager is always used."));
Bruce Momjian's avatar
Bruce Momjian committed
1499
			else
1500
				puts(gettext("Pager usage is off."));
Bruce Momjian's avatar
Bruce Momjian committed
1501 1502
		}
	}
1503

1504 1505 1506 1507 1508 1509 1510
	/* disable "(x rows)" footer */
	else if (strcmp(param, "footer") == 0)
	{
		popt->default_footer = !popt->default_footer;
		if (!quiet)
		{
			if (popt->default_footer)
1511
				puts(gettext("Default footer is on."));
1512
			else
1513
				puts(gettext("Default footer is off."));
1514 1515
		}
	}
Bruce Momjian's avatar
Bruce Momjian committed
1516 1517 1518

	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1519
		psql_error("\\pset: unknown option: %s\n", param);
Bruce Momjian's avatar
Bruce Momjian committed
1520 1521 1522 1523
		return false;
	}

	return true;
1524 1525 1526 1527
}



1528
#ifndef WIN32
1529
#define DEFAULT_SHELL "/bin/sh"
1530
#else
1531 1532 1533 1534 1535
/*
 *	CMD.EXE is in different places in different Win32 releases so we
 *	have to rely on the path to find it.
 */
#define DEFAULT_SHELL "cmd.exe"
1536
#endif
1537 1538 1539 1540

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

Bruce Momjian's avatar
Bruce Momjian committed
1543 1544 1545
	if (!command)
	{
		char	   *sys;
1546
		const char *shellName;
Bruce Momjian's avatar
Bruce Momjian committed
1547

1548
		shellName = getenv("SHELL");
1549 1550
#ifdef WIN32
		if (shellName == NULL)
1551 1552
			shellName = getenv("COMSPEC");
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1553 1554 1555
		if (shellName == NULL)
			shellName = DEFAULT_SHELL;

1556
		sys = pg_malloc(strlen(shellName) + 16);
Bruce Momjian's avatar
Bruce Momjian committed
1557
		sprintf(sys,
1558
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
1559
				"exec "
1560
#endif
1561
				"%s\"%s\"%s", SYSTEMQUOTE, shellName, SYSTEMQUOTE);
Bruce Momjian's avatar
Bruce Momjian committed
1562 1563 1564 1565 1566
		result = system(sys);
		free(sys);
	}
	else
		result = system(command);
1567

Bruce Momjian's avatar
Bruce Momjian committed
1568 1569
	if (result == 127 || result == -1)
	{
1570
		psql_error("\\!: failed\n");
Bruce Momjian's avatar
Bruce Momjian committed
1571 1572 1573
		return false;
	}
	return true;
1574
}