postgresql/src/bin/psql/prompt.c

265 lines
5.8 KiB
C
Raw Normal View History

#include <config.h>
#include <c.h>
#include "prompt.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
#include "settings.h"
#include "common.h"
#ifdef WIN32
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#endif
/*--------------------------
* get_prompt
*
* Returns a statically allocated prompt made by interpolating certain
* tcsh style escape sequences into pset->vars "prompt1|2|3".
* (might not be completely multibyte safe)
*
* Defined interpolations are:
* %M - database server hostname (or "." if not TCP/IP)
* %m - like %M but hostname truncated after first dot
* %> - database server port number (or "." if not TCP/IP)
* %n - database user name
* %/ - current database
* %~ - like %/ but "~" when database name equals user name
* %# - "#" if the username is postgres, ">" otherwise
* %R - in prompt1 normally =, or ^ if single line mode,
1999-11-05 00:14:30 +01:00
* or a ! if session is not connected to a database;
* in prompt2 -, *, ', or ";
* in prompt3 nothing
* %? - the error code of the last query (not yet implemented)
* %% - a percent sign
*
1999-11-05 00:14:30 +01:00
* %[0-9] - the character with the given decimal code
* %0[0-7] - the character with the given octal code
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
*
1999-11-05 00:14:30 +01:00
* %`command` - The result of executing command in /bin/sh with trailing
* newline stripped.
* %$name$ - The value of the psql/environment/magic varible 'name'
* (same rules as for, e.g., \echo $foo)
* (those will not be rescanned for more escape sequences!)
*
*
* If the application-wide prompts became NULL somehow, the returned string
* will be empty (not NULL!).
*--------------------------
*/
const char *
get_prompt(PsqlSettings *pset, promptStatus_t status)
{
#define MAX_PROMPT_SIZE 256
1999-11-05 00:14:30 +01:00
static char destination[MAX_PROMPT_SIZE + 1];
char buf[MAX_PROMPT_SIZE + 1];
bool esc = false;
const char *p;
const char *prompt_string;
1999-11-05 00:14:30 +01:00
if (status == PROMPT_READY)
prompt_string = GetVariable(pset->vars, "prompt1");
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT)
prompt_string = GetVariable(pset->vars, "prompt2");
else if (status == PROMPT_COPY)
prompt_string = GetVariable(pset->vars, "prompt3");
else
prompt_string = "? ";
1999-11-05 00:14:30 +01:00
destination[0] = '\0';
1999-11-05 00:14:30 +01:00
for (p = prompt_string;
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
p++)
{
memset(buf, 0, MAX_PROMPT_SIZE + 1);
1999-11-05 00:14:30 +01:00
if (esc)
{
switch (*p)
{
case '%':
strcpy(buf, "%");
break;
1999-11-05 00:14:30 +01:00
/* Current database */
case '/':
if (pset->db)
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
break;
case '~':
{
const char *var;
1999-11-05 00:14:30 +01:00
if (pset->db)
{
if (strcmp(PQdb(pset->db), PQuser(pset->db)) == 0 ||
((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db)) == 0))
strcpy(buf, "~");
else
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
}
break;
}
/* DB server hostname (long/short) */
case 'M':
case 'm':
if (pset->db)
{
if (PQhost(pset->db))
{
strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE);
if (*p == 'm')
buf[strcspn(buf, ".")] = '\0';
}
else
buf[0] = '.';
}
break;
/* DB server port number */
case '>':
if (pset->db && PQport(pset->db))
strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE);
1999-11-05 00:14:30 +01:00
break;
/* DB server user name */
case 'n':
if (pset->db)
strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE);
break;
1999-11-05 00:14:30 +01:00
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
long int l;
char *end;
1999-11-05 00:14:30 +01:00
l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char) l);
p = end - 1;
break;
}
1999-11-05 00:14:30 +01:00
case 'R':
switch (status)
{
case PROMPT_READY:
if (!pset->db)
buf[0] = '!';
else if (!GetVariableBool(pset->vars, "singleline"))
buf[0] = '=';
else
buf[0] = '^';
break;
case PROMPT_CONTINUE:
buf[0] = '-';
break;
case PROMPT_SINGLEQUOTE:
buf[0] = '\'';
break;
case PROMPT_DOUBLEQUOTE:
buf[0] = '"';
break;
case PROMPT_COMMENT:
buf[0] = '*';
break;
default:
buf[0] = '\0';
break;
}
1999-11-05 00:14:30 +01:00
case '?':
/* not here yet */
break;
1999-11-05 00:14:30 +01:00
case '#':
{
if (pset->db && strcmp(PQuser(pset->db), "postgres") == 0)
buf[0] = '#';
else
buf[0] = '>';
1999-11-05 00:14:30 +01:00
break;
}
1999-11-05 00:14:30 +01:00
/* execute command */
case '`':
{
FILE *fd = NULL;
char *file = strdup(p + 1);
int cmdend;
1999-11-05 00:14:30 +01:00
cmdend = strcspn(file, "`");
file[cmdend] = '\0';
if (file)
fd = popen(file, "r");
if (fd)
{
fgets(buf, MAX_PROMPT_SIZE - 1, fd);
pclose(fd);
}
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
free(file);
p += cmdend + 1;
break;
}
/* interpolate variable */
case '$':
{
char *name;
const char *val;
int nameend;
name = strdup(p + 1);
nameend = strcspn(name, "$");
name[nameend] = '\0';
val = interpolate_var(name, pset);
if (val)
strncpy(buf, val, MAX_PROMPT_SIZE);
free(name);
p += nameend + 1;
break;
}
default:
buf[0] = *p;
buf[1] = '\0';
}
esc = false;
}
else if (*p == '%')
esc = true;
else
{
buf[0] = *p;
buf[1] = '\0';
esc = false;
}
if (!esc)
strncat(destination, buf, MAX_PROMPT_SIZE - strlen(destination));
}
1999-11-05 00:14:30 +01:00
destination[MAX_PROMPT_SIZE] = '\0';
return destination;
}