PostgreSQL Source Code  git master
prompt.c
Go to the documentation of this file.
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2022, PostgreSQL Global Development Group
5  *
6  * src/bin/psql/prompt.c
7  */
8 #include "postgres_fe.h"
9 
10 #ifdef WIN32
11 #include <io.h>
12 #include <win32.h>
13 #endif
14 
15 #include "common.h"
16 #include "common/string.h"
17 #include "input.h"
18 #include "libpq/pqcomm.h"
19 #include "prompt.h"
20 #include "settings.h"
21 
22 /*--------------------------
23  * get_prompt
24  *
25  * Returns a statically allocated prompt made by interpolating certain
26  * tcsh style escape sequences into pset.vars "PROMPT1|2|3".
27  * (might not be completely multibyte safe)
28  *
29  * Defined interpolations are:
30  * %M - database server "hostname.domainname", "[local]" for AF_UNIX
31  * sockets, "[local:/dir/name]" if not default
32  * %m - like %M, but hostname only (before first dot), or always "[local]"
33  * %p - backend pid
34  * %> - database server port number
35  * %n - database user name
36  * %/ - current database
37  * %~ - like %/ but "~" when database name equals user name
38  * %w - whitespace of the same width as the most recent output of PROMPT1
39  * %# - "#" if superuser, ">" otherwise
40  * %R - in prompt1 normally =, or ^ if single line mode,
41  * or a ! if session is not connected to a database;
42  * in prompt2 -, *, ', or ";
43  * in prompt3 nothing
44  * %x - transaction status: empty, *, !, ? (unknown or no connection)
45  * %l - The line number inside the current statement, starting from 1.
46  * %? - the error code of the last query (not yet implemented)
47  * %% - a percent sign
48  *
49  * %[0-9] - the character with the given decimal code
50  * %0[0-7] - the character with the given octal code
51  * %0x[0-9A-Fa-f] - the character with the given hexadecimal code
52  *
53  * %`command` - The result of executing command in /bin/sh with trailing
54  * newline stripped.
55  * %:name: - The value of the psql variable 'name'
56  * (those will not be rescanned for more escape sequences!)
57  *
58  * %[ ... %] - tell readline that the contained text is invisible
59  *
60  * If the application-wide prompts become NULL somehow, the returned string
61  * will be empty (not NULL!).
62  *--------------------------
63  */
64 
65 char *
67 {
68 #define MAX_PROMPT_SIZE 256
69  static char destination[MAX_PROMPT_SIZE + 1];
70  char buf[MAX_PROMPT_SIZE + 1];
71  bool esc = false;
72  const char *p;
73  const char *prompt_string = "? ";
74  static size_t last_prompt1_width = 0;
75 
76  switch (status)
77  {
78  case PROMPT_READY:
79  prompt_string = pset.prompt1;
80  break;
81 
82  case PROMPT_CONTINUE:
83  case PROMPT_SINGLEQUOTE:
84  case PROMPT_DOUBLEQUOTE:
85  case PROMPT_DOLLARQUOTE:
86  case PROMPT_COMMENT:
87  case PROMPT_PAREN:
88  prompt_string = pset.prompt2;
89  break;
90 
91  case PROMPT_COPY:
92  prompt_string = pset.prompt3;
93  break;
94  }
95 
96  destination[0] = '\0';
97 
98  for (p = prompt_string;
99  *p && strlen(destination) < sizeof(destination) - 1;
100  p++)
101  {
102  memset(buf, 0, sizeof(buf));
103  if (esc)
104  {
105  switch (*p)
106  {
107  /* Current database */
108  case '/':
109  if (pset.db)
110  strlcpy(buf, PQdb(pset.db), sizeof(buf));
111  break;
112  case '~':
113  if (pset.db)
114  {
115  const char *var;
116 
117  if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
118  ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
119  strlcpy(buf, "~", sizeof(buf));
120  else
121  strlcpy(buf, PQdb(pset.db), sizeof(buf));
122  }
123  break;
124 
125  /* Whitespace of the same width as the last PROMPT1 */
126  case 'w':
127  if (pset.db)
128  memset(buf, ' ',
129  Min(last_prompt1_width, sizeof(buf) - 1));
130  break;
131 
132  /* DB server hostname (long/short) */
133  case 'M':
134  case 'm':
135  if (pset.db)
136  {
137  const char *host = PQhost(pset.db);
138 
139  /* INET socket */
140  if (host && host[0] && !is_unixsock_path(host))
141  {
142  strlcpy(buf, host, sizeof(buf));
143  if (*p == 'm')
144  buf[strcspn(buf, ".")] = '\0';
145  }
146  /* UNIX socket */
147  else
148  {
149  if (!host
150  || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
151  || *p == 'm')
152  strlcpy(buf, "[local]", sizeof(buf));
153  else
154  snprintf(buf, sizeof(buf), "[local:%s]", host);
155  }
156  }
157  break;
158  /* DB server port number */
159  case '>':
160  if (pset.db && PQport(pset.db))
161  strlcpy(buf, PQport(pset.db), sizeof(buf));
162  break;
163  /* DB server user name */
164  case 'n':
165  if (pset.db)
166  strlcpy(buf, session_username(), sizeof(buf));
167  break;
168  /* backend pid */
169  case 'p':
170  if (pset.db)
171  {
172  int pid = PQbackendPID(pset.db);
173 
174  if (pid)
175  snprintf(buf, sizeof(buf), "%d", pid);
176  }
177  break;
178 
179  case '0':
180  case '1':
181  case '2':
182  case '3':
183  case '4':
184  case '5':
185  case '6':
186  case '7':
187  *buf = (char) strtol(p, unconstify(char **, &p), 8);
188  --p;
189  break;
190  case 'R':
191  switch (status)
192  {
193  case PROMPT_READY:
194  if (cstack != NULL && !conditional_active(cstack))
195  buf[0] = '@';
196  else if (!pset.db)
197  buf[0] = '!';
198  else if (!pset.singleline)
199  buf[0] = '=';
200  else
201  buf[0] = '^';
202  break;
203  case PROMPT_CONTINUE:
204  buf[0] = '-';
205  break;
206  case PROMPT_SINGLEQUOTE:
207  buf[0] = '\'';
208  break;
209  case PROMPT_DOUBLEQUOTE:
210  buf[0] = '"';
211  break;
212  case PROMPT_DOLLARQUOTE:
213  buf[0] = '$';
214  break;
215  case PROMPT_COMMENT:
216  buf[0] = '*';
217  break;
218  case PROMPT_PAREN:
219  buf[0] = '(';
220  break;
221  default:
222  buf[0] = '\0';
223  break;
224  }
225  break;
226 
227  case 'x':
228  if (!pset.db)
229  buf[0] = '?';
230  else
231  switch (PQtransactionStatus(pset.db))
232  {
233  case PQTRANS_IDLE:
234  buf[0] = '\0';
235  break;
236  case PQTRANS_ACTIVE:
237  case PQTRANS_INTRANS:
238  buf[0] = '*';
239  break;
240  case PQTRANS_INERROR:
241  buf[0] = '!';
242  break;
243  default:
244  buf[0] = '?';
245  break;
246  }
247  break;
248 
249  case 'l':
251  break;
252 
253  case '?':
254  /* not here yet */
255  break;
256 
257  case '#':
258  if (is_superuser())
259  buf[0] = '#';
260  else
261  buf[0] = '>';
262  break;
263 
264  /* execute command */
265  case '`':
266  {
267  int cmdend = strcspn(p + 1, "`");
268  char *file = pnstrdup(p + 1, cmdend);
269  FILE *fd = popen(file, "r");
270 
271  if (fd)
272  {
273  if (fgets(buf, sizeof(buf), fd) == NULL)
274  buf[0] = '\0';
275  pclose(fd);
276  }
277 
278  /* strip trailing newline and carriage return */
279  (void) pg_strip_crlf(buf);
280 
281  free(file);
282  p += cmdend + 1;
283  break;
284  }
285 
286  /* interpolate variable */
287  case ':':
288  {
289  int nameend = strcspn(p + 1, ":");
290  char *name = pnstrdup(p + 1, nameend);
291  const char *val;
292 
294  if (val)
295  strlcpy(buf, val, sizeof(buf));
296  free(name);
297  p += nameend + 1;
298  break;
299  }
300 
301  case '[':
302  case ']':
303 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
304 
305  /*
306  * readline >=4.0 undocumented feature: non-printing
307  * characters in prompt strings must be marked as such, in
308  * order to properly display the line during editing.
309  */
310  buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
311  buf[1] = '\0';
312 #endif /* USE_READLINE */
313  break;
314 
315  default:
316  buf[0] = *p;
317  buf[1] = '\0';
318  break;
319  }
320  esc = false;
321  }
322  else if (*p == '%')
323  esc = true;
324  else
325  {
326  buf[0] = *p;
327  buf[1] = '\0';
328  esc = false;
329  }
330 
331  if (!esc)
332  strlcat(destination, buf, sizeof(destination));
333  }
334 
335  /* Compute the visible width of PROMPT1, for PROMPT2's %w */
336  if (prompt_string == pset.prompt1)
337  {
338  char *p = destination;
339  char *end = p + strlen(p);
340  bool visible = true;
341 
342  last_prompt1_width = 0;
343  while (*p)
344  {
345 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
346  if (*p == RL_PROMPT_START_IGNORE)
347  {
348  visible = false;
349  ++p;
350  }
351  else if (*p == RL_PROMPT_END_IGNORE)
352  {
353  visible = true;
354  ++p;
355  }
356  else
357 #endif
358  {
359  int chlen,
360  chwidth;
361 
362  chlen = PQmblen(p, pset.encoding);
363  if (p + chlen > end)
364  break; /* Invalid string */
365 
366  if (visible)
367  {
368  chwidth = PQdsplen(p, pset.encoding);
369 
370  if (*p == '\n')
371  last_prompt1_width = 0;
372  else if (chwidth > 0)
373  last_prompt1_width += chwidth;
374  }
375 
376  p += chlen;
377  }
378  }
379  }
380 
381  return destination;
382 }
const char * session_username(void)
Definition: common.c:2246
#define unconstify(underlying_type, expr)
Definition: c.h:1251
#define Min(x, y)
Definition: c.h:997
#define UINT64_FORMAT
Definition: c.h:495
bool conditional_active(ConditionalStack cstack)
Definition: conditional.c:140
const char * name
Definition: encode.c:561
char * PQhost(const PGconn *conn)
Definition: fe-connect.c:6714
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:6681
PGTransactionStatusType PQtransactionStatus(const PGconn *conn)
Definition: fe-connect.c:6790
char * PQuser(const PGconn *conn)
Definition: fe-connect.c:6689
int PQbackendPID(const PGconn *conn)
Definition: fe-connect.c:6869
char * PQport(const PGconn *conn)
Definition: fe-connect.c:6750
int PQmblen(const char *s, int encoding)
Definition: fe-misc.c:1185
int PQdsplen(const char *s, int encoding)
Definition: fe-misc.c:1205
#define free(a)
Definition: header.h:65
long val
Definition: informix.c:664
@ PQTRANS_INTRANS
Definition: libpq-fe.h:120
@ PQTRANS_IDLE
Definition: libpq-fe.h:118
@ PQTRANS_ACTIVE
Definition: libpq-fe.h:119
@ PQTRANS_INERROR
Definition: libpq-fe.h:121
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1316
#define DEFAULT_PGSOCKET_DIR
static bool is_superuser(Archive *fout)
Definition: pg_dump.c:4377
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:229
static char * buf
Definition: pg_test_fsync.c:67
#define snprintf
Definition: port.h:225
size_t strlcat(char *dst, const char *src, size_t siz)
Definition: strlcat.c:33
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static bool is_unixsock_path(const char *path)
Definition: pqcomm.h:93
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define MAX_PROMPT_SIZE
char * get_prompt(promptStatus_t status, ConditionalStack cstack)
Definition: prompt.c:66
enum _promptStatus promptStatus_t
@ PROMPT_READY
Definition: psqlscan.h:41
@ PROMPT_COPY
Definition: psqlscan.h:48
@ PROMPT_PAREN
Definition: psqlscan.h:47
@ PROMPT_COMMENT
Definition: psqlscan.h:43
@ PROMPT_CONTINUE
Definition: psqlscan.h:42
@ PROMPT_SINGLEQUOTE
Definition: psqlscan.h:44
@ PROMPT_DOLLARQUOTE
Definition: psqlscan.h:46
@ PROMPT_DOUBLEQUOTE
Definition: psqlscan.h:45
PsqlSettings pset
Definition: startup.c:32
int pg_strip_crlf(char *str)
Definition: string.c:121
VariableSpace vars
Definition: settings.h:118
int encoding
Definition: settings.h:83
const char * prompt2
Definition: settings.h:148
const char * prompt3
Definition: settings.h:149
PGconn * db
Definition: settings.h:82
uint64 stmt_lineno
Definition: settings.h:112
bool singleline
Definition: settings.h:135
const char * prompt1
Definition: settings.h:147
const char * GetVariable(VariableSpace space, const char *name)
Definition: variables.c:71