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-2025, 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 * %P - pipeline status: on, off or abort
35 * %> - database server port number
36 * %n - database user name
37 * %S - search_path
38 * %s - service
39 * %/ - current database
40 * %~ - like %/ but "~" when database name equals user name
41 * %w - whitespace of the same width as the most recent output of PROMPT1
42 * %# - "#" if superuser, ">" otherwise
43 * %R - in prompt1 normally =, or ^ if single line mode,
44 * or a ! if session is not connected to a database;
45 * in prompt2 -, *, ', or ";
46 * in prompt3 nothing
47 * %x - transaction status: empty, *, !, ? (unknown or no connection)
48 * %l - The line number inside the current statement, starting from 1.
49 * %? - the error code of the last query (not yet implemented)
50 * %% - a percent sign
51 *
52 * %[0-9] - the character with the given decimal code
53 * %0[0-7] - the character with the given octal code
54 * %0x[0-9A-Fa-f] - the character with the given hexadecimal code
55 *
56 * %`command` - The result of executing command in /bin/sh with trailing
57 * newline stripped.
58 * %:name: - The value of the psql variable 'name'
59 * (those will not be rescanned for more escape sequences!)
60 *
61 * %[ ... %] - tell readline that the contained text is invisible
62 *
63 * If the application-wide prompts become NULL somehow, the returned string
64 * will be empty (not NULL!).
65 *--------------------------
66 */
67
68char *
70{
71#define MAX_PROMPT_SIZE 256
72 static char destination[MAX_PROMPT_SIZE + 1];
73 char buf[MAX_PROMPT_SIZE + 1];
74 bool esc = false;
75 const char *p;
76 const char *prompt_string = "? ";
77 static size_t last_prompt1_width = 0;
78
79 switch (status)
80 {
81 case PROMPT_READY:
82 prompt_string = pset.prompt1;
83 break;
84
85 case PROMPT_CONTINUE:
89 case PROMPT_COMMENT:
90 case PROMPT_PAREN:
91 prompt_string = pset.prompt2;
92 break;
93
94 case PROMPT_COPY:
95 prompt_string = pset.prompt3;
96 break;
97 }
98
99 destination[0] = '\0';
100
101 for (p = prompt_string;
102 *p && strlen(destination) < sizeof(destination) - 1;
103 p++)
104 {
105 memset(buf, 0, sizeof(buf));
106 if (esc)
107 {
108 switch (*p)
109 {
110 /* Current database */
111 case '/':
112 if (pset.db)
113 strlcpy(buf, PQdb(pset.db), sizeof(buf));
114 break;
115 case '~':
116 if (pset.db)
117 {
118 const char *var;
119
120 if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
121 ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
122 strlcpy(buf, "~", sizeof(buf));
123 else
124 strlcpy(buf, PQdb(pset.db), sizeof(buf));
125 }
126 break;
127
128 /* Whitespace of the same width as the last PROMPT1 */
129 case 'w':
130 if (pset.db)
131 memset(buf, ' ',
132 Min(last_prompt1_width, sizeof(buf) - 1));
133 break;
134
135 /* DB server hostname (long/short) */
136 case 'M':
137 case 'm':
138 if (pset.db)
139 {
140 const char *host = PQhost(pset.db);
141
142 /* INET socket */
143 if (host && host[0] && !is_unixsock_path(host))
144 {
145 strlcpy(buf, host, sizeof(buf));
146 if (*p == 'm')
147 buf[strcspn(buf, ".")] = '\0';
148 }
149 /* UNIX socket */
150 else
151 {
152 if (!host
153 || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
154 || *p == 'm')
155 strlcpy(buf, "[local]", sizeof(buf));
156 else
157 snprintf(buf, sizeof(buf), "[local:%s]", host);
158 }
159 }
160 break;
161 /* DB server port number */
162 case '>':
163 if (pset.db && PQport(pset.db))
164 strlcpy(buf, PQport(pset.db), sizeof(buf));
165 break;
166 /* DB server user name */
167 case 'n':
168 if (pset.db)
169 strlcpy(buf, session_username(), sizeof(buf));
170 break;
171 /* search_path */
172 case 'S':
173 if (pset.db)
174 {
175 const char *sp = PQparameterStatus(pset.db, "search_path");
176
177 /* Use ? for versions that don't report search_path. */
178 strlcpy(buf, sp ? sp : "?", sizeof(buf));
179 }
180 break;
181 /* service name */
182 case 's':
183 {
184 const char *service_name = GetVariable(pset.vars, "SERVICE");
185
186 if (service_name)
187 strlcpy(buf, service_name, sizeof(buf));
188 }
189 break;
190 /* backend pid */
191 case 'p':
192 if (pset.db)
193 {
194 int pid = PQbackendPID(pset.db);
195
196 if (pid)
197 snprintf(buf, sizeof(buf), "%d", pid);
198 }
199 break;
200 /* pipeline status */
201 case 'P':
202 {
204
205 if (status == PQ_PIPELINE_ON)
206 strlcpy(buf, "on", sizeof(buf));
207 else if (status == PQ_PIPELINE_ABORTED)
208 strlcpy(buf, "abort", sizeof(buf));
209 else
210 strlcpy(buf, "off", sizeof(buf));
211 break;
212 }
213
214 case '0':
215 case '1':
216 case '2':
217 case '3':
218 case '4':
219 case '5':
220 case '6':
221 case '7':
222 *buf = (char) strtol(p, unconstify(char **, &p), 8);
223 --p;
224 break;
225 case 'R':
226 switch (status)
227 {
228 case PROMPT_READY:
229 if (cstack != NULL && !conditional_active(cstack))
230 buf[0] = '@';
231 else if (!pset.db)
232 buf[0] = '!';
233 else if (!pset.singleline)
234 buf[0] = '=';
235 else
236 buf[0] = '^';
237 break;
238 case PROMPT_CONTINUE:
239 buf[0] = '-';
240 break;
242 buf[0] = '\'';
243 break;
245 buf[0] = '"';
246 break;
248 buf[0] = '$';
249 break;
250 case PROMPT_COMMENT:
251 buf[0] = '*';
252 break;
253 case PROMPT_PAREN:
254 buf[0] = '(';
255 break;
256 default:
257 buf[0] = '\0';
258 break;
259 }
260 break;
261
262 case 'x':
263 if (!pset.db)
264 buf[0] = '?';
265 else
266 switch (PQtransactionStatus(pset.db))
267 {
268 case PQTRANS_IDLE:
269 buf[0] = '\0';
270 break;
271 case PQTRANS_ACTIVE:
272 case PQTRANS_INTRANS:
273 buf[0] = '*';
274 break;
275 case PQTRANS_INERROR:
276 buf[0] = '!';
277 break;
278 default:
279 buf[0] = '?';
280 break;
281 }
282 break;
283
284 case 'l':
286 break;
287
288 case '?':
289 /* not here yet */
290 break;
291
292 case '#':
293 if (is_superuser())
294 buf[0] = '#';
295 else
296 buf[0] = '>';
297 break;
298
299 /* execute command */
300 case '`':
301 {
302 int cmdend = strcspn(p + 1, "`");
303 char *file = pnstrdup(p + 1, cmdend);
304 FILE *fd;
305
306 fflush(NULL);
307 fd = popen(file, "r");
308 if (fd)
309 {
310 if (fgets(buf, sizeof(buf), fd) == NULL)
311 buf[0] = '\0';
312 pclose(fd);
313 }
314
315 /* strip trailing newline and carriage return */
316 (void) pg_strip_crlf(buf);
317
318 free(file);
319 p += cmdend + 1;
320 break;
321 }
322
323 /* interpolate variable */
324 case ':':
325 {
326 int nameend = strcspn(p + 1, ":");
327 char *name = pnstrdup(p + 1, nameend);
328 const char *val;
329
331 if (val)
332 strlcpy(buf, val, sizeof(buf));
333 free(name);
334 p += nameend + 1;
335 break;
336 }
337
338 case '[':
339 case ']':
340#if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
341
342 /*
343 * readline >=4.0 undocumented feature: non-printing
344 * characters in prompt strings must be marked as such, in
345 * order to properly display the line during editing.
346 */
347 buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
348 buf[1] = '\0';
349#endif /* USE_READLINE */
350 break;
351
352 default:
353 buf[0] = *p;
354 buf[1] = '\0';
355 break;
356 }
357 esc = false;
358 }
359 else if (*p == '%')
360 esc = true;
361 else
362 {
363 buf[0] = *p;
364 buf[1] = '\0';
365 esc = false;
366 }
367
368 if (!esc)
369 strlcat(destination, buf, sizeof(destination));
370 }
371
372 /* Compute the visible width of PROMPT1, for PROMPT2's %w */
373 if (prompt_string == pset.prompt1)
374 {
375 char *p = destination;
376 char *end = p + strlen(p);
377 bool visible = true;
378
379 last_prompt1_width = 0;
380 while (*p)
381 {
382#if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
383 if (*p == RL_PROMPT_START_IGNORE)
384 {
385 visible = false;
386 ++p;
387 }
388 else if (*p == RL_PROMPT_END_IGNORE)
389 {
390 visible = true;
391 ++p;
392 }
393 else
394#endif
395 {
396 int chlen,
397 chwidth;
398
399 chlen = PQmblen(p, pset.encoding);
400 if (p + chlen > end)
401 break; /* Invalid string */
402
403 if (visible)
404 {
405 chwidth = PQdsplen(p, pset.encoding);
406
407 if (*p == '\n')
408 last_prompt1_width = 0;
409 else if (chwidth > 0)
410 last_prompt1_width += chwidth;
411 }
412
413 p += chlen;
414 }
415 }
416 }
417
418 return destination;
419}
const char * session_username(void)
Definition: common.c:2520
#define unconstify(underlying_type, expr)
Definition: c.h:1245
#define Min(x, y)
Definition: c.h:1008
#define UINT64_FORMAT
Definition: c.h:562
bool conditional_active(ConditionalStack cstack)
Definition: conditional.c:140
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:7538
char * PQport(const PGconn *conn)
Definition: fe-connect.c:7607
PGTransactionStatusType PQtransactionStatus(const PGconn *conn)
Definition: fe-connect.c:7649
char * PQhost(const PGconn *conn)
Definition: fe-connect.c:7571
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
Definition: fe-connect.c:7659
int PQbackendPID(const PGconn *conn)
Definition: fe-connect.c:7740
char * PQuser(const PGconn *conn)
Definition: fe-connect.c:7546
PGpipelineStatus PQpipelineStatus(const PGconn *conn)
Definition: fe-connect.c:7748
int PQmblen(const char *s, int encoding)
Definition: fe-misc.c:1255
int PQdsplen(const char *s, int encoding)
Definition: fe-misc.c:1276
#define free(a)
Definition: header.h:65
long val
Definition: informix.c:689
@ PQTRANS_INTRANS
Definition: libpq-fe.h:149
@ PQTRANS_IDLE
Definition: libpq-fe.h:147
@ PQTRANS_ACTIVE
Definition: libpq-fe.h:148
@ PQTRANS_INERROR
Definition: libpq-fe.h:150
PGpipelineStatus
Definition: libpq-fe.h:186
@ PQ_PIPELINE_ABORTED
Definition: libpq-fe.h:189
@ PQ_PIPELINE_ON
Definition: libpq-fe.h:188
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1770
#define DEFAULT_PGSOCKET_DIR
static bool is_superuser(Archive *fout)
Definition: pg_dump.c:5053
static char * buf
Definition: pg_test_fsync.c:72
#define snprintf
Definition: port.h:239
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:67
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:69
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:154
VariableSpace vars
Definition: settings.h:151
const char * prompt2
Definition: settings.h:182
const char * prompt3
Definition: settings.h:183
PGconn * db
Definition: settings.h:103
uint64 stmt_lineno
Definition: settings.h:145
bool singleline
Definition: settings.h:168
const char * prompt1
Definition: settings.h:181
const char * GetVariable(VariableSpace space, const char *name)
Definition: variables.c:73
const char * name