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