]> granicus.if.org Git - postgresql/blob - src/bin/psql/prompt.c
6bf86d1108c48bb865df4a501e4caa1f59eae62f
[postgresql] / src / bin / psql / prompt.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2006, PostgreSQL Global Development Group
5  *
6  * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.48 2006/08/29 15:19:51 tgl Exp $
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  * %> - database server port number
38  * %n - database user name
39  * %/ - current database
40  * %~ - like %/ but "~" when database name equals user name
41  * %# - "#" if superuser, ">" otherwise
42  * %R - in prompt1 normally =, or ^ if single line mode,
43  *                      or a ! if session is not connected to a database;
44  *              in prompt2 -, *, ', or ";
45  *              in prompt3 nothing
46  * %x - transaction status: empty, *, !, ? (unknown or no connection)
47  * %? - the error code of the last query (not yet implemented)
48  * %% - a percent sign
49  *
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
53  *
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!)
58  *
59  * %[ ... %]       - tell readline that the contained text is invisible
60  *
61  * If the application-wide prompts become NULL somehow, the returned string
62  * will be empty (not NULL!).
63  *--------------------------
64  */
65
66 char *
67 get_prompt(promptStatus_t status)
68 {
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
76         switch (status)
77         {
78                 case PROMPT_READY:
79                         prompt_string = pset.prompt1;
80                         break;
81
82                 case PROMPT_CONTINUE:
83                 case PROMPT_SINGLEQUOTE:
84                 case PROMPT_DOUBLEQUOTE:
85                 case PROMPT_DOLLARQUOTE:
86                 case PROMPT_COMMENT:
87                 case PROMPT_PAREN:
88                         prompt_string = pset.prompt2;
89                         break;
90
91                 case PROMPT_COPY:
92                         prompt_string = pset.prompt3;
93                         break;
94         }
95
96         destination[0] = '\0';
97
98         for (p = prompt_string;
99                  *p && strlen(destination) < MAX_PROMPT_SIZE;
100                  p++)
101         {
102                 memset(buf, 0, MAX_PROMPT_SIZE + 1);
103                 if (esc)
104                 {
105                         switch (*p)
106                         {
107                                         /* Current database */
108                                 case '/':
109                                         if (pset.db)
110                                                 strncpy(buf, PQdb(pset.db), MAX_PROMPT_SIZE);
111                                         break;
112                                 case '~':
113                                         if (pset.db)
114                                         {
115                                                 const char *var;
116
117                                                 if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
118                                                         ((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
119                                                         strcpy(buf, "~");
120                                                 else
121                                                         strncpy(buf, PQdb(pset.db), MAX_PROMPT_SIZE);
122                                         }
123                                         break;
124
125                                         /* DB server hostname (long/short) */
126                                 case 'M':
127                                 case 'm':
128                                         if (pset.db)
129                                         {
130                                                 const char *host = PQhost(pset.db);
131
132                                                 /* INET socket */
133                                                 if (host && host[0] && !is_absolute_path(host))
134                                                 {
135                                                         strncpy(buf, host, MAX_PROMPT_SIZE);
136                                                         if (*p == 'm')
137                                                                 buf[strcspn(buf, ".")] = '\0';
138                                                 }
139 #ifdef HAVE_UNIX_SOCKETS
140                                                 /* UNIX socket */
141                                                 else
142                                                 {
143                                                         if (!host
144                                                                 || strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
145                                                                 || *p == 'm')
146                                                                 strncpy(buf, "[local]", MAX_PROMPT_SIZE);
147                                                         else
148                                                                 snprintf(buf, MAX_PROMPT_SIZE, "[local:%s]", host);
149                                                 }
150 #endif
151                                         }
152                                         break;
153                                         /* DB server port number */
154                                 case '>':
155                                         if (pset.db && PQport(pset.db))
156                                                 strncpy(buf, PQport(pset.db), MAX_PROMPT_SIZE);
157                                         break;
158                                         /* DB server user name */
159                                 case 'n':
160                                         if (pset.db)
161                                                 strncpy(buf, session_username(), MAX_PROMPT_SIZE);
162                                         break;
163
164                                 case '0':
165                                 case '1':
166                                 case '2':
167                                 case '3':
168                                 case '4':
169                                 case '5':
170                                 case '6':
171                                 case '7':
172                                         *buf = (char) strtol(p, (char **) &p, 8);
173                                         --p;
174                                         break;
175                                 case 'R':
176                                         switch (status)
177                                         {
178                                                 case PROMPT_READY:
179                                                         if (!pset.db)
180                                                                 buf[0] = '!';
181                                                         else if (!pset.singleline)
182                                                                 buf[0] = '=';
183                                                         else
184                                                                 buf[0] = '^';
185                                                         break;
186                                                 case PROMPT_CONTINUE:
187                                                         buf[0] = '-';
188                                                         break;
189                                                 case PROMPT_SINGLEQUOTE:
190                                                         buf[0] = '\'';
191                                                         break;
192                                                 case PROMPT_DOUBLEQUOTE:
193                                                         buf[0] = '"';
194                                                         break;
195                                                 case PROMPT_DOLLARQUOTE:
196                                                         buf[0] = '$';
197                                                         break;
198                                                 case PROMPT_COMMENT:
199                                                         buf[0] = '*';
200                                                         break;
201                                                 case PROMPT_PAREN:
202                                                         buf[0] = '(';
203                                                         break;
204                                                 default:
205                                                         buf[0] = '\0';
206                                                         break;
207                                         }
208                                         break;
209
210                                 case 'x':
211                                         if (!pset.db)
212                                                 buf[0] = '?';
213                                         else
214                                                 switch (PQtransactionStatus(pset.db))
215                                                 {
216                                                         case PQTRANS_IDLE:
217                                                                 buf[0] = '\0';
218                                                                 break;
219                                                         case PQTRANS_ACTIVE:
220                                                         case PQTRANS_INTRANS:
221                                                                 buf[0] = '*';
222                                                                 break;
223                                                         case PQTRANS_INERROR:
224                                                                 buf[0] = '!';
225                                                                 break;
226                                                         default:
227                                                                 buf[0] = '?';
228                                                                 break;
229                                                 }
230                                         break;
231
232                                 case '?':
233                                         /* not here yet */
234                                         break;
235
236                                 case '#':
237                                         if (is_superuser())
238                                                 buf[0] = '#';
239                                         else
240                                                 buf[0] = '>';
241                                         break;
242
243                                         /* execute command */
244                                 case '`':
245                                         {
246                                                 FILE       *fd;
247                                                 char       *file = pg_strdup(p + 1);
248                                                 int                     cmdend;
249
250                                                 cmdend = strcspn(file, "`");
251                                                 file[cmdend] = '\0';
252                                                 fd = popen(file, "r");
253                                                 if (fd)
254                                                 {
255                                                         fgets(buf, MAX_PROMPT_SIZE - 1, fd);
256                                                         pclose(fd);
257                                                 }
258                                                 if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n')
259                                                         buf[strlen(buf) - 1] = '\0';
260                                                 free(file);
261                                                 p += cmdend + 1;
262                                                 break;
263                                         }
264
265                                         /* interpolate variable */
266                                 case ':':
267                                         {
268                                                 char       *name;
269                                                 const char *val;
270                                                 int                     nameend;
271
272                                                 name = pg_strdup(p + 1);
273                                                 nameend = strcspn(name, ":");
274                                                 name[nameend] = '\0';
275                                                 val = GetVariable(pset.vars, name);
276                                                 if (val)
277                                                         strncpy(buf, val, MAX_PROMPT_SIZE);
278                                                 free(name);
279                                                 p += nameend + 1;
280                                                 break;
281                                         }
282
283                                 case '[':
284                                 case ']':
285 #if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
286
287                                         /*
288                                          * readline >=4.0 undocumented feature: non-printing
289                                          * characters in prompt strings must be marked as such, in
290                                          * order to properly display the line during editing.
291                                          */
292                                         buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
293                                         buf[1] = '\0';
294 #endif   /* USE_READLINE */
295                                         break;
296
297                                 default:
298                                         buf[0] = *p;
299                                         buf[1] = '\0';
300                                         break;
301
302                         }
303                         esc = false;
304                 }
305                 else if (*p == '%')
306                         esc = true;
307                 else
308                 {
309                         buf[0] = *p;
310                         buf[1] = '\0';
311                         esc = false;
312                 }
313
314                 if (!esc)
315                         strncat(destination, buf, MAX_PROMPT_SIZE - strlen(destination));
316         }
317
318         destination[MAX_PROMPT_SIZE] = '\0';
319         return destination;
320 }