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