Add error pattern checks for some TAP tests for non-existing objects
[pgsql.git] / src / bin / psql / prompt.c
blob08a14feb3c3fb0f5ff79c8d9a04890872620950e
1 /*
2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2025, PostgreSQL Global Development Group
6 * src/bin/psql/prompt.c
7 */
8 #include "postgres_fe.h"
10 #ifdef WIN32
11 #include <io.h>
12 #include <win32.h>
13 #endif
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"
22 /*--------------------------
23 * get_prompt
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]"
33 * %p - backend pid
34 * %> - database server port number
35 * %n - database user name
36 * %s - service
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 ";
44 * in prompt3 nothing
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)
48 * %% - a percent sign
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
55 * newline stripped.
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 *--------------------------
66 char *
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];
72 bool esc = false;
73 const char *p;
74 const char *prompt_string = "? ";
75 static size_t last_prompt1_width = 0;
77 switch (status)
79 case PROMPT_READY:
80 prompt_string = pset.prompt1;
81 break;
83 case PROMPT_CONTINUE:
84 case PROMPT_SINGLEQUOTE:
85 case PROMPT_DOUBLEQUOTE:
86 case PROMPT_DOLLARQUOTE:
87 case PROMPT_COMMENT:
88 case PROMPT_PAREN:
89 prompt_string = pset.prompt2;
90 break;
92 case PROMPT_COPY:
93 prompt_string = pset.prompt3;
94 break;
97 destination[0] = '\0';
99 for (p = prompt_string;
100 *p && strlen(destination) < sizeof(destination) - 1;
101 p++)
103 memset(buf, 0, sizeof(buf));
104 if (esc)
106 switch (*p)
108 /* Current database */
109 case '/':
110 if (pset.db)
111 strlcpy(buf, PQdb(pset.db), sizeof(buf));
112 break;
113 case '~':
114 if (pset.db)
116 const char *var;
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));
121 else
122 strlcpy(buf, PQdb(pset.db), sizeof(buf));
124 break;
126 /* Whitespace of the same width as the last PROMPT1 */
127 case 'w':
128 if (pset.db)
129 memset(buf, ' ',
130 Min(last_prompt1_width, sizeof(buf) - 1));
131 break;
133 /* DB server hostname (long/short) */
134 case 'M':
135 case 'm':
136 if (pset.db)
138 const char *host = PQhost(pset.db);
140 /* INET socket */
141 if (host && host[0] && !is_unixsock_path(host))
143 strlcpy(buf, host, sizeof(buf));
144 if (*p == 'm')
145 buf[strcspn(buf, ".")] = '\0';
147 /* UNIX socket */
148 else
150 if (!host
151 || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
152 || *p == 'm')
153 strlcpy(buf, "[local]", sizeof(buf));
154 else
155 snprintf(buf, sizeof(buf), "[local:%s]", host);
158 break;
159 /* DB server port number */
160 case '>':
161 if (pset.db && PQport(pset.db))
162 strlcpy(buf, PQport(pset.db), sizeof(buf));
163 break;
164 /* DB server user name */
165 case 'n':
166 if (pset.db)
167 strlcpy(buf, session_username(), sizeof(buf));
168 break;
169 /* service name */
170 case 's':
171 if (pset.db && PQservice(pset.db))
172 strlcpy(buf, PQservice(pset.db), sizeof(buf));
173 break;
174 /* backend pid */
175 case 'p':
176 if (pset.db)
178 int pid = PQbackendPID(pset.db);
180 if (pid)
181 snprintf(buf, sizeof(buf), "%d", pid);
183 break;
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)
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;
231 break;
233 case 'x':
234 if (!pset.db)
235 buf[0] = '?';
236 else
237 switch (PQtransactionStatus(pset.db))
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;
253 break;
255 case 'l':
256 snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno);
257 break;
259 case '?':
260 /* not here yet */
261 break;
263 case '#':
264 if (is_superuser())
265 buf[0] = '#';
266 else
267 buf[0] = '>';
268 break;
270 /* execute command */
271 case '`':
273 int cmdend = strcspn(p + 1, "`");
274 char *file = pnstrdup(p + 1, cmdend);
275 FILE *fd;
277 fflush(NULL);
278 fd = popen(file, "r");
279 if (fd)
281 if (fgets(buf, sizeof(buf), fd) == NULL)
282 buf[0] = '\0';
283 pclose(fd);
286 /* strip trailing newline and carriage return */
287 (void) pg_strip_crlf(buf);
289 free(file);
290 p += cmdend + 1;
291 break;
294 /* interpolate variable */
295 case ':':
297 int nameend = strcspn(p + 1, ":");
298 char *name = pnstrdup(p + 1, nameend);
299 const char *val;
301 val = GetVariable(pset.vars, name);
302 if (val)
303 strlcpy(buf, val, sizeof(buf));
304 free(name);
305 p += nameend + 1;
306 break;
309 case '[':
310 case ']':
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;
319 buf[1] = '\0';
320 #endif /* USE_READLINE */
321 break;
323 default:
324 buf[0] = *p;
325 buf[1] = '\0';
326 break;
328 esc = false;
330 else if (*p == '%')
331 esc = true;
332 else
334 buf[0] = *p;
335 buf[1] = '\0';
336 esc = false;
339 if (!esc)
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);
348 bool visible = true;
350 last_prompt1_width = 0;
351 while (*p)
353 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
354 if (*p == RL_PROMPT_START_IGNORE)
356 visible = false;
357 ++p;
359 else if (*p == RL_PROMPT_END_IGNORE)
361 visible = true;
362 ++p;
364 else
365 #endif
367 int chlen,
368 chwidth;
370 chlen = PQmblen(p, pset.encoding);
371 if (p + chlen > end)
372 break; /* Invalid string */
374 if (visible)
376 chwidth = PQdsplen(p, pset.encoding);
378 if (*p == '\n')
379 last_prompt1_width = 0;
380 else if (chwidth > 0)
381 last_prompt1_width += chwidth;
384 p += chlen;
389 return destination;