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