Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2017, 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 : #ifdef HAVE_UNIX_SOCKETS
16 : #include <unistd.h>
17 : #include <netdb.h>
18 : #endif
19 :
20 : #include "common.h"
21 : #include "input.h"
22 : #include "prompt.h"
23 : #include "settings.h"
24 :
25 :
26 : /*--------------------------
27 : * get_prompt
28 : *
29 : * Returns a statically allocated prompt made by interpolating certain
30 : * tcsh style escape sequences into pset.vars "PROMPT1|2|3".
31 : * (might not be completely multibyte safe)
32 : *
33 : * Defined interpolations are:
34 : * %M - database server "hostname.domainname", "[local]" for AF_UNIX
35 : * sockets, "[local:/dir/name]" if not default
36 : * %m - like %M, but hostname only (before first dot), or always "[local]"
37 : * %p - backend pid
38 : * %> - database server port number
39 : * %n - database user name
40 : * %/ - current database
41 : * %~ - like %/ but "~" when database name equals user name
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 :
68 : char *
69 0 : get_prompt(promptStatus_t status, ConditionalStack cstack)
70 : {
71 : #define MAX_PROMPT_SIZE 256
72 : static char destination[MAX_PROMPT_SIZE + 1];
73 : char buf[MAX_PROMPT_SIZE + 1];
74 0 : bool esc = false;
75 : const char *p;
76 0 : const char *prompt_string = "? ";
77 :
78 0 : switch (status)
79 : {
80 : case PROMPT_READY:
81 0 : prompt_string = pset.prompt1;
82 0 : break;
83 :
84 : case PROMPT_CONTINUE:
85 : case PROMPT_SINGLEQUOTE:
86 : case PROMPT_DOUBLEQUOTE:
87 : case PROMPT_DOLLARQUOTE:
88 : case PROMPT_COMMENT:
89 : case PROMPT_PAREN:
90 0 : prompt_string = pset.prompt2;
91 0 : break;
92 :
93 : case PROMPT_COPY:
94 0 : prompt_string = pset.prompt3;
95 0 : break;
96 : }
97 :
98 0 : destination[0] = '\0';
99 :
100 0 : for (p = prompt_string;
101 0 : *p && strlen(destination) < sizeof(destination) - 1;
102 0 : p++)
103 : {
104 0 : memset(buf, 0, sizeof(buf));
105 0 : if (esc)
106 : {
107 0 : switch (*p)
108 : {
109 : /* Current database */
110 : case '/':
111 0 : if (pset.db)
112 0 : strlcpy(buf, PQdb(pset.db), sizeof(buf));
113 0 : break;
114 : case '~':
115 0 : if (pset.db)
116 : {
117 : const char *var;
118 :
119 0 : if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
120 0 : ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
121 0 : strlcpy(buf, "~", sizeof(buf));
122 : else
123 0 : strlcpy(buf, PQdb(pset.db), sizeof(buf));
124 : }
125 0 : break;
126 :
127 : /* DB server hostname (long/short) */
128 : case 'M':
129 : case 'm':
130 0 : if (pset.db)
131 : {
132 0 : const char *host = PQhost(pset.db);
133 :
134 : /* INET socket */
135 0 : if (host && host[0] && !is_absolute_path(host))
136 : {
137 0 : strlcpy(buf, host, sizeof(buf));
138 0 : if (*p == 'm')
139 0 : buf[strcspn(buf, ".")] = '\0';
140 : }
141 : #ifdef HAVE_UNIX_SOCKETS
142 : /* UNIX socket */
143 : else
144 : {
145 0 : if (!host
146 0 : || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
147 0 : || *p == 'm')
148 0 : strlcpy(buf, "[local]", sizeof(buf));
149 : else
150 0 : snprintf(buf, sizeof(buf), "[local:%s]", host);
151 : }
152 : #endif
153 : }
154 0 : break;
155 : /* DB server port number */
156 : case '>':
157 0 : if (pset.db && PQport(pset.db))
158 0 : strlcpy(buf, PQport(pset.db), sizeof(buf));
159 0 : break;
160 : /* DB server user name */
161 : case 'n':
162 0 : if (pset.db)
163 0 : strlcpy(buf, session_username(), sizeof(buf));
164 0 : break;
165 : /* backend pid */
166 : case 'p':
167 0 : if (pset.db)
168 : {
169 0 : int pid = PQbackendPID(pset.db);
170 :
171 0 : if (pid)
172 0 : snprintf(buf, sizeof(buf), "%d", pid);
173 : }
174 0 : break;
175 :
176 : case '0':
177 : case '1':
178 : case '2':
179 : case '3':
180 : case '4':
181 : case '5':
182 : case '6':
183 : case '7':
184 0 : *buf = (char) strtol(p, (char **) &p, 8);
185 0 : --p;
186 0 : break;
187 : case 'R':
188 0 : switch (status)
189 : {
190 : case PROMPT_READY:
191 0 : if (cstack != NULL && !conditional_active(cstack))
192 0 : buf[0] = '@';
193 0 : else if (!pset.db)
194 0 : buf[0] = '!';
195 0 : else if (!pset.singleline)
196 0 : buf[0] = '=';
197 : else
198 0 : buf[0] = '^';
199 0 : break;
200 : case PROMPT_CONTINUE:
201 0 : buf[0] = '-';
202 0 : break;
203 : case PROMPT_SINGLEQUOTE:
204 0 : buf[0] = '\'';
205 0 : break;
206 : case PROMPT_DOUBLEQUOTE:
207 0 : buf[0] = '"';
208 0 : break;
209 : case PROMPT_DOLLARQUOTE:
210 0 : buf[0] = '$';
211 0 : break;
212 : case PROMPT_COMMENT:
213 0 : buf[0] = '*';
214 0 : break;
215 : case PROMPT_PAREN:
216 0 : buf[0] = '(';
217 0 : break;
218 : default:
219 0 : buf[0] = '\0';
220 0 : break;
221 : }
222 0 : break;
223 :
224 : case 'x':
225 0 : if (!pset.db)
226 0 : buf[0] = '?';
227 : else
228 0 : switch (PQtransactionStatus(pset.db))
229 : {
230 : case PQTRANS_IDLE:
231 0 : buf[0] = '\0';
232 0 : break;
233 : case PQTRANS_ACTIVE:
234 : case PQTRANS_INTRANS:
235 0 : buf[0] = '*';
236 0 : break;
237 : case PQTRANS_INERROR:
238 0 : buf[0] = '!';
239 0 : break;
240 : default:
241 0 : buf[0] = '?';
242 0 : break;
243 : }
244 0 : break;
245 :
246 : case 'l':
247 0 : snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno);
248 0 : break;
249 :
250 : case '?':
251 : /* not here yet */
252 0 : break;
253 :
254 : case '#':
255 0 : if (is_superuser())
256 0 : buf[0] = '#';
257 : else
258 0 : buf[0] = '>';
259 0 : break;
260 :
261 : /* execute command */
262 : case '`':
263 : {
264 : FILE *fd;
265 0 : char *file = pg_strdup(p + 1);
266 : int cmdend;
267 :
268 0 : cmdend = strcspn(file, "`");
269 0 : file[cmdend] = '\0';
270 0 : fd = popen(file, "r");
271 0 : if (fd)
272 : {
273 0 : if (fgets(buf, sizeof(buf), fd) == NULL)
274 0 : buf[0] = '\0';
275 0 : pclose(fd);
276 : }
277 0 : if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n')
278 0 : buf[strlen(buf) - 1] = '\0';
279 0 : free(file);
280 0 : p += cmdend + 1;
281 0 : break;
282 : }
283 :
284 : /* interpolate variable */
285 : case ':':
286 : {
287 : char *name;
288 : const char *val;
289 : int nameend;
290 :
291 0 : name = pg_strdup(p + 1);
292 0 : nameend = strcspn(name, ":");
293 0 : name[nameend] = '\0';
294 0 : val = GetVariable(pset.vars, name);
295 0 : if (val)
296 0 : strlcpy(buf, val, sizeof(buf));
297 0 : free(name);
298 0 : p += nameend + 1;
299 0 : break;
300 : }
301 :
302 : case '[':
303 : case ']':
304 : #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
305 :
306 : /*
307 : * readline >=4.0 undocumented feature: non-printing
308 : * characters in prompt strings must be marked as such, in
309 : * order to properly display the line during editing.
310 : */
311 0 : buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
312 0 : buf[1] = '\0';
313 : #endif /* USE_READLINE */
314 0 : break;
315 :
316 : default:
317 0 : buf[0] = *p;
318 0 : buf[1] = '\0';
319 0 : break;
320 :
321 : }
322 0 : esc = false;
323 : }
324 0 : else if (*p == '%')
325 0 : esc = true;
326 : else
327 : {
328 0 : buf[0] = *p;
329 0 : buf[1] = '\0';
330 0 : esc = false;
331 : }
332 :
333 0 : if (!esc)
334 0 : strlcat(destination, buf, sizeof(destination));
335 : }
336 :
337 0 : return destination;
338 : }
|