postgresql/src/bin/psql/prompt.c

329 lines
6.9 KiB
C

/*
* psql - the PostgreSQL interactive terminal
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/prompt.c,v 1.17 2001/02/10 02:31:28 tgl Exp $
*/
#include "postgres_fe.h"
#include "prompt.h"
#include "libpq-fe.h"
#include "settings.h"
#include "common.h"
#include "variables.h"
#ifdef WIN32
#include <io.h>
#include <win32.h>
#endif
#ifdef HAVE_UNIX_SOCKETS
# include <unistd.h>
# include <netdb.h>
#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.domainname" (or "localhost" if this
* information is not available)
* %m - like %M, but hostname only (before first dot)
* %> - database server port number
* %n - database user name
* %/ - current database
* %~ - like %/ but "~" when database name equals user name
* %# - "#" if superuser, ">" otherwise
* %R - in prompt1 normally =, or ^ if single line mode,
* 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
*
* %[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
*
* %`command` - The result of executing command in /bin/sh with trailing
* newline stripped.
* %:name: - The value of the psql variable 'name'
* (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!).
*--------------------------
*/
/*
* We need hostname information, only if connection is via UNIX socket
*/
#ifdef HAVE_UNIX_SOCKETS
#define DOMAINNAME 1
#define HOSTNAME 2
/*
* Return full hostname for localhost.
* - informations are init only in firts time - not queries DNS or NIS
* for every localhost() call
*/
static char *
localhost(int type, char *buf, int siz)
{
static struct hostent *hp = NULL;
static int err = 0;
if (hp == NULL && err == 0)
{
char hname[256];
if (gethostname(hname, 256) == 0)
{
if (!(hp = gethostbyname(hname)))
err = 1;
}
else
err = 1;
}
if (hp == NULL)
return strncpy(buf, "localhost", siz);
strncpy(buf, hp->h_name, siz); /* full aaa.bbb.ccc */
if (type == HOSTNAME)
buf[strcspn(buf, ".")] = '\0';
return buf;
}
#endif /* HAVE_UNIX_SOCKETS */
char *
get_prompt(promptStatus_t status)
{
#define MAX_PROMPT_SIZE 256
static char destination[MAX_PROMPT_SIZE + 1];
char buf[MAX_PROMPT_SIZE + 1];
bool esc = false;
const char *p;
const char *prompt_string;
if (status == PROMPT_READY)
prompt_string = GetVariable(pset.vars, "PROMPT1");
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT || status == PROMPT_PAREN)
prompt_string = GetVariable(pset.vars, "PROMPT2");
else if (status == PROMPT_COPY)
prompt_string = GetVariable(pset.vars, "PROMPT3");
else
prompt_string = "? ";
destination[0] = '\0';
for (p = prompt_string;
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
p++)
{
memset(buf, 0, MAX_PROMPT_SIZE + 1);
if (esc)
{
switch (*p)
{
case '%':
strcpy(buf, "%");
break;
/* Current database */
case '/':
if (pset.db)
strncpy(buf, PQdb(pset.db), MAX_PROMPT_SIZE);
break;
case '~':
{
const char *var;
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)
{
/* INET socket */
if (PQhost(pset.db))
{
strncpy(buf, PQhost(pset.db), MAX_PROMPT_SIZE);
if (*p == 'm')
buf[strcspn(buf, ".")] = '\0';
}
/* UNIX socket */
#ifdef HAVE_UNIX_SOCKETS
else
{
if (*p == 'm')
localhost(HOSTNAME, buf, MAX_PROMPT_SIZE);
else
localhost(DOMAINNAME, buf, MAX_PROMPT_SIZE);
}
#endif /* HAVE_UNIX_SOCKETS */
}
break;
/* DB server port number */
case '>':
if (pset.db && PQport(pset.db))
strncpy(buf, PQport(pset.db), MAX_PROMPT_SIZE);
break;
/* DB server user name */
case 'n':
if (pset.db)
strncpy(buf, PQuser(pset.db), MAX_PROMPT_SIZE);
break;
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;
l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char) l);
p = end - 1;
break;
}
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;
case PROMPT_PAREN:
buf[0] = '(';
break;
default:
buf[0] = '\0';
break;
}
case '?':
/* not here yet */
break;
case '#':
{
if (pset.issuper)
buf[0] = '#';
else
buf[0] = '>';
break;
}
/* execute command */
case '`':
{
FILE *fd = NULL;
char *file = strdup(p + 1);
int cmdend;
cmdend = strcspn(file, "`");
file[cmdend] = '\0';
if (file)
fd = popen(file, "r");
if (fd)
{
fgets(buf, MAX_PROMPT_SIZE - 1, fd);
pclose(fd);
}
if (strlen(buf) > 0 && 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 = GetVariable(pset.vars, name);
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));
}
destination[MAX_PROMPT_SIZE] = '\0';
return destination;
}