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