2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2025, PostgreSQL Global Development Group
6 * src/bin/psql/prompt.c
8 #include "postgres_fe.h"
16 #include "common/string.h"
18 #include "libpq/pqcomm.h"
22 /*--------------------------
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)
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]"
34 * %> - database server port number
35 * %n - database user name
37 * %/ - current database
38 * %~ - like %/ but "~" when database name equals user name
39 * %w - whitespace of the same width as the most recent output of PROMPT1
40 * %# - "#" if superuser, ">" otherwise
41 * %R - in prompt1 normally =, or ^ if single line mode,
42 * or a ! if session is not connected to a database;
43 * in prompt2 -, *, ', or ";
45 * %x - transaction status: empty, *, !, ? (unknown or no connection)
46 * %l - The line number inside the current statement, starting from 1.
47 * %? - the error code of the last query (not yet implemented)
50 * %[0-9] - the character with the given decimal code
51 * %0[0-7] - the character with the given octal code
52 * %0x[0-9A-Fa-f] - the character with the given hexadecimal code
54 * %`command` - The result of executing command in /bin/sh with trailing
56 * %:name: - The value of the psql variable 'name'
57 * (those will not be rescanned for more escape sequences!)
59 * %[ ... %] - tell readline that the contained text is invisible
61 * If the application-wide prompts become NULL somehow, the returned string
62 * will be empty (not NULL!).
63 *--------------------------
67 get_prompt(promptStatus_t status
, ConditionalStack cstack
)
69 #define MAX_PROMPT_SIZE 256
70 static char destination
[MAX_PROMPT_SIZE
+ 1];
71 char buf
[MAX_PROMPT_SIZE
+ 1];
74 const char *prompt_string
= "? ";
75 static size_t last_prompt1_width
= 0;
80 prompt_string
= pset
.prompt1
;
84 case PROMPT_SINGLEQUOTE
:
85 case PROMPT_DOUBLEQUOTE
:
86 case PROMPT_DOLLARQUOTE
:
89 prompt_string
= pset
.prompt2
;
93 prompt_string
= pset
.prompt3
;
97 destination
[0] = '\0';
99 for (p
= prompt_string
;
100 *p
&& strlen(destination
) < sizeof(destination
) - 1;
103 memset(buf
, 0, sizeof(buf
));
108 /* Current database */
111 strlcpy(buf
, PQdb(pset
.db
), sizeof(buf
));
118 if (strcmp(PQdb(pset
.db
), PQuser(pset
.db
)) == 0 ||
119 ((var
= getenv("PGDATABASE")) && strcmp(var
, PQdb(pset
.db
)) == 0))
120 strlcpy(buf
, "~", sizeof(buf
));
122 strlcpy(buf
, PQdb(pset
.db
), sizeof(buf
));
126 /* Whitespace of the same width as the last PROMPT1 */
130 Min(last_prompt1_width
, sizeof(buf
) - 1));
133 /* DB server hostname (long/short) */
138 const char *host
= PQhost(pset
.db
);
141 if (host
&& host
[0] && !is_unixsock_path(host
))
143 strlcpy(buf
, host
, sizeof(buf
));
145 buf
[strcspn(buf
, ".")] = '\0';
151 || strcmp(host
, DEFAULT_PGSOCKET_DIR
) == 0
153 strlcpy(buf
, "[local]", sizeof(buf
));
155 snprintf(buf
, sizeof(buf
), "[local:%s]", host
);
159 /* DB server port number */
161 if (pset
.db
&& PQport(pset
.db
))
162 strlcpy(buf
, PQport(pset
.db
), sizeof(buf
));
164 /* DB server user name */
167 strlcpy(buf
, session_username(), sizeof(buf
));
171 if (pset
.db
&& PQservice(pset
.db
))
172 strlcpy(buf
, PQservice(pset
.db
), sizeof(buf
));
178 int pid
= PQbackendPID(pset
.db
);
181 snprintf(buf
, sizeof(buf
), "%d", pid
);
193 *buf
= (char) strtol(p
, unconstify(char **, &p
), 8);
200 if (cstack
!= NULL
&& !conditional_active(cstack
))
204 else if (!pset
.singleline
)
209 case PROMPT_CONTINUE
:
212 case PROMPT_SINGLEQUOTE
:
215 case PROMPT_DOUBLEQUOTE
:
218 case PROMPT_DOLLARQUOTE
:
237 switch (PQtransactionStatus(pset
.db
))
243 case PQTRANS_INTRANS
:
246 case PQTRANS_INERROR
:
256 snprintf(buf
, sizeof(buf
), UINT64_FORMAT
, pset
.stmt_lineno
);
270 /* execute command */
273 int cmdend
= strcspn(p
+ 1, "`");
274 char *file
= pnstrdup(p
+ 1, cmdend
);
278 fd
= popen(file
, "r");
281 if (fgets(buf
, sizeof(buf
), fd
) == NULL
)
286 /* strip trailing newline and carriage return */
287 (void) pg_strip_crlf(buf
);
294 /* interpolate variable */
297 int nameend
= strcspn(p
+ 1, ":");
298 char *name
= pnstrdup(p
+ 1, nameend
);
301 val
= GetVariable(pset
.vars
, name
);
303 strlcpy(buf
, val
, sizeof(buf
));
311 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
314 * readline >=4.0 undocumented feature: non-printing
315 * characters in prompt strings must be marked as such, in
316 * order to properly display the line during editing.
318 buf
[0] = (*p
== '[') ? RL_PROMPT_START_IGNORE
: RL_PROMPT_END_IGNORE
;
320 #endif /* USE_READLINE */
340 strlcat(destination
, buf
, sizeof(destination
));
343 /* Compute the visible width of PROMPT1, for PROMPT2's %w */
344 if (prompt_string
== pset
.prompt1
)
346 char *p
= destination
;
347 char *end
= p
+ strlen(p
);
350 last_prompt1_width
= 0;
353 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
354 if (*p
== RL_PROMPT_START_IGNORE
)
359 else if (*p
== RL_PROMPT_END_IGNORE
)
370 chlen
= PQmblen(p
, pset
.encoding
);
372 break; /* Invalid string */
376 chwidth
= PQdsplen(p
, pset
.encoding
);
379 last_prompt1_width
= 0;
380 else if (chwidth
> 0)
381 last_prompt1_width
+= chwidth
;