]> granicus.if.org Git - postgresql/blobdiff - src/bin/psql/psql.c
From: Dan McGuirk <mcguirk@indirect.com>
[postgresql] / src / bin / psql / psql.c
index e7c13ba2a7f74524fc0256c4a72712af66780859..6fd299605b4b7141bd331b7f4c79aeb61ccb28bd 100644 (file)
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * psql.c--
- *    an interactive front-end to postgres95
+ *    an interactive front-end to postgreSQL
  *
  * Copyright (c) 1996, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.31 1996/11/22 04:43:48 bryanh Exp $
+ *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.58 1997/03/12 21:19:14 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <signal.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/param.h> /* for MAXPATHLEN */
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <ctype.h>
 #include "postgres.h"
 #include "libpq-fe.h"
+#include "pqsignal.h"
 #include "stringutils.h"
-
 #include "psqlHelp.h"
-
-#ifdef NOREADLINE
-#include "rlstubs.h"
-#else
-/* from the GNU readline library */
-#ifdef OLD_READLINE
-#include "readline.h"
-#include "history.h"
-#else
-#include <readline/readline.h>
-#include <readline/history.h>
+#ifndef HAVE_STRDUP
+#include "strdup.h"
 #endif
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
 #endif
 
+#ifdef HAVE_LIBREADLINE
+# ifdef HAVE_READLINE_H
+#  include <readline.h>
+#  if defined(HAVE_HISTORY) || defined(HAVE_LIBHISTORY)
+#   include <history.h>
+#  endif
+# else
+#  include <readline/readline.h>
+#  if defined(HAVE_HISTORY) || defined(HAVE_LIBHISTORY)
+#   include <readline/history.h>
+#  endif
+# endif
+#endif
+
+#define PROMPT "=> "
+
 #define MAX_QUERY_BUFFER 20000
 
 #define COPYBUFSIZ  8192
@@ -60,6 +70,7 @@ typedef struct _psqlSettings {
     bool            singleStep;        /* prompt before for each query */
     bool            singleLineMode;    /* query terminated by newline */
     bool            useReadline;/* use libreadline routines */
+    bool            getPassword;/* prompt the user for a username and password */
 }               PsqlSettings;
 
 /* declarations for functions in this file */
@@ -71,6 +82,9 @@ handleCopyIn(PGresult * res, const bool mustprompt,
             FILE * copystream);
 static int      tableList(PsqlSettings * ps, bool deep_tablelist);
 static int      tableDesc(PsqlSettings * ps, char *table);
+static void     prompt_for_password(char *username, char *password);
+static char *   make_connect_string(char *host, char *port, char *dbname,
+                                   char *username, char *password);
 
 char           *gets_noreadline(char *prompt, FILE * source);
 char           *gets_readline(char *prompt, FILE * source);
@@ -118,6 +132,7 @@ usage(char *progname)
     fprintf(stderr, "\t -s                      single step mode (prompts for each query)\n");
     fprintf(stderr, "\t -S                      single line mode (i.e. query terminated by newline)\n");
     fprintf(stderr, "\t -t                      turn off printing of headings and row count\n");
+    fprintf(stderr, "\t -u                      ask for a username and password for authentication\n");
     fprintf(stderr, "\t -T html                 set html3.0 table command options (cf. -H)\n");
     fprintf(stderr, "\t -x                      turn on expanded output (field names on left)\n");
     exit(1);
@@ -140,7 +155,7 @@ slashUsage(PsqlSettings * ps)
     fprintf(stderr, " \\a           -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
     fprintf(stderr, " \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption ? ps->opt.caption : "");
     fprintf(stderr, " \\connect <dbname>  -- connect to new database (currently '%s')\n", PQdb(ps->db));
-    fprintf(stderr, " \\copy <dbname>     -- copy table to/from a file\n");
+    fprintf(stderr, " \\copy table {from | to} <fname>\n");
     fprintf(stderr, " \\d [<table>] -- list tables in database or columns in <table>, * for all\n");
     fprintf(stderr, " \\e [<fname>] -- edit the current query buffer or <fname>, \\E execute too\n");
     fprintf(stderr, " \\f [<sep>]   -- change field separater (currently '%s')\n", ps->opt.fieldSep);
@@ -395,7 +410,17 @@ gets_noreadline(char *prompt, FILE * source)
 char           *
 gets_readline(char *prompt, FILE * source)
 {
-    return (readline(prompt));
+    char *s;
+#ifdef HAVE_LIBREADLINE
+    s = readline(prompt);
+#else
+    char buf[500];
+    printf("%s", prompt);
+    s = fgets(buf, 500, stdin);
+#endif
+    fputc('\r', stdout);
+    fflush(stdout);
+    return s;
 }
 
 /*
@@ -509,19 +534,10 @@ SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
            break;
        case PGRES_COPY_IN:
            *success_p = true;
-           if (copy_in) {
+           if (copy_in)
                handleCopyIn(results, false, copystream);
-           } else {
-               char            c;
-               /*
-                * eat extra newline still in input buffer
-                * 
-                */
-               fflush(stdin);
-               if ((c = getc(stdin)) != '\n' && c != EOF)
-                   (void) ungetc(c, stdin);
+           else
                handleCopyIn(results, !settings->quiet, stdin);
-           }
            break;
        case PGRES_NONFATAL_ERROR:
        case PGRES_FATAL_ERROR:
@@ -739,7 +755,7 @@ do_copy(const char *args, PsqlSettings * settings)
        } else {
            copystream = fopen(file, "w");
        }
-       if (copystream < 0)
+       if (copystream == NULL)
            fprintf(stderr,
                    "Unable to open file %s which to copy, errno = %s (%d).",
                    from ? "from" : "to", strerror(errno), errno);
@@ -787,7 +803,7 @@ do_connect(const char *new_dbname, PsqlSettings * settings)
            PQfinish(olddb);
            free(settings->prompt);
            settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
-           sprintf(settings->prompt, "%s=> ", PQdb(settings->db));
+           sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
        }
     }
 }
@@ -1088,7 +1104,7 @@ HandleSlashCmds(PsqlSettings * settings,
            char           *fs = DEFAULT_FIELD_SEP;
            if (optarg)
                fs = optarg;
-           if (settings->opt.fieldSep);
+           if (settings->opt.fieldSep)
                free(settings->opt.fieldSep);
            if (!(settings->opt.fieldSep = strdup(fs))) {
                perror("malloc");
@@ -1099,7 +1115,12 @@ HandleSlashCmds(PsqlSettings * settings,
            break;
        }
     case 'g':                  /* \g means send query */
-       settings->gfname = strdup(optarg);
+       if (!optarg)
+           settings->gfname = NULL;
+       else if (!(settings->gfname = strdup(optarg))) {
+           perror("malloc");
+           exit(1);
+       }
        status = 0;
        break;
     case 'h':                  /* help */
@@ -1150,8 +1171,10 @@ HandleSlashCmds(PsqlSettings * settings,
     case 's':                  /* \s is save history to a file */
        if (!optarg)
            optarg = "/dev/tty";
+#ifdef HAVE_HISTORY
        if (write_history(optarg) != 0)
            fprintf(stderr, "cannot write history to %s\n", optarg);
+#endif
        break;
     case 'm':                  /* monitor like type-setting */
        if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding")) {
@@ -1236,7 +1259,6 @@ MainLoop(PsqlSettings * settings, FILE * source)
     char           *query_start;
 
     interactive = ((source == stdin) && !settings->notty);
-#define PROMPT "=> "
     if (interactive) {
        if (settings->prompt)
            free(settings->prompt);
@@ -1247,7 +1269,9 @@ MainLoop(PsqlSettings * settings, FILE * source)
        else
            sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
        if (settings->useReadline) {
+#ifdef HAVE_HISTORY
            using_history();
+#endif
            GetNextLine = gets_readline;
        } else
            GetNextLine = gets_noreadline;
@@ -1262,26 +1286,38 @@ MainLoop(PsqlSettings * settings, FILE * source)
     /* main loop for getting queries and executing them */
     while (!eof) {
        if (slashCmdStatus == 3) {
+           paren_level = 0;
            line = strdup(query);
            query[0] = '\0';
        } else {
+           if (interactive && !settings->quiet) {
+               if (in_quote)
+                   settings->prompt[strlen(settings->prompt)-3] = '\'';
+               else if (query[0] != '\0' && !querySent)
+                   settings->prompt[strlen(settings->prompt)-3] = '-';
+               else
+                   settings->prompt[strlen(settings->prompt)-3] = '=';
+           }
            line = GetNextLine(settings->prompt, source);
+#ifdef HAVE_HISTORY
            if (interactive && settings->useReadline && line != NULL)
                add_history(line);      /* save non-empty lines in history */
+#endif
        }
 
        query_start = line;
 
        if (line == NULL) {     /* No more input.  Time to quit */
-           printf("EOF\n");    /* Goes on prompt line */
+           if (!settings->quiet)
+               printf("EOF\n");        /* Goes on prompt line */
            eof = true;
        } else {
-           if (!interactive && !settings->singleStep && !settings->quiet)
-               fprintf(stderr, "%s\n", line);
-
            /* remove whitespaces on the right, incl. \n's */
            line = rightTrim(line);
 
+           if (!interactive && !settings->singleStep && !settings->quiet)
+               fprintf(stderr, "%s\n", line);
+
            if (line[0] == '\0') {
                free(line);
                continue;
@@ -1367,6 +1403,8 @@ MainLoop(PsqlSettings * settings, FILE * source)
                                                 query_start,
                                                 query);
                if (slashCmdStatus == 1) {
+                   if (query[0] == '\0')
+                       paren_level = 0;
                    free(line);
                    continue;
                }
@@ -1429,12 +1467,17 @@ main(int argc, char **argv)
     settings.opt.pager = 1;
     if (!isatty(0) || !isatty(1))
        settings.quiet = settings.notty = 1;
-#ifndef NOREADLINE
+#ifdef HAVE_LIBREADLINE
     else
        settings.useReadline = 1;
 #endif
+#ifdef PSQL_ALWAYS_GET_PASSWORDS
+    settings.getPassword = 1;
+#else
+    settings.getPassword = 0;
+#endif
 
-    while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:x")) != EOF) {
+    while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:ux")) != EOF) {
        switch (c) {
        case 'A':
            settings.opt.align = 0;
@@ -1493,6 +1536,9 @@ main(int argc, char **argv)
        case 'T':
            settings.opt.tableOpt = optarg;
            break;
+       case 'u':
+           settings.getPassword = 1;
+           break;
        case 'x':
            settings.opt.expanded = 1;
            break;
@@ -1508,7 +1554,21 @@ main(int argc, char **argv)
     if (listDatabases)
        dbname = "template1";
 
-    settings.db = PQsetdb(host, port, NULL, NULL, dbname);
+    if(settings.getPassword) {
+       char username[9];
+       char password[9];
+       char *connect_string;
+
+       prompt_for_password(username, password);
+
+       /* now use PQconnectdb so we can pass these options */
+       connect_string = make_connect_string(host, port, dbname, username, password);
+       settings.db = PQconnectdb(connect_string);
+       free(connect_string);
+    } else {
+       settings.db = PQsetdb(host, port, NULL, NULL, dbname);
+    }
+
     dbname = PQdb(settings.db);
 
     if (PQstatus(settings.db) == CONNECTION_BAD) {
@@ -1520,9 +1580,9 @@ main(int argc, char **argv)
        exit(listAllDbs(&settings));
     }
     if (!settings.quiet && !singleQuery && !qfilename) {
-       printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
+       printf("Welcome to the POSTGRESQL interactive sql monitor:\n");
        printf("  Please read the file COPYRIGHT for copyright terms "
-              "of POSTGRES95\n\n");
+              "of POSTGRESQL\n\n");
        printf("   type \\? for help on slash commands\n");
        printf("   type \\q to quit\n");
        printf("   type \\g or terminate with semicolon to execute query\n");
@@ -1660,15 +1720,18 @@ setFout(PsqlSettings * ps, char *fname)
        else
            fclose(ps->queryFout);
     }
-    if (!fname)
+    if (!fname) {
        ps->queryFout = stdout;
+       pqsignal(SIGPIPE, SIG_DFL);
+    }
     else {
        if (*fname == '|') {
-           signal(SIGPIPE, SIG_IGN);
+           pqsignal(SIGPIPE, SIG_IGN);
            ps->queryFout = popen(fname + 1, "w");
            ps->pipe = 1;
        } else {
            ps->queryFout = fopen(fname, "w");
+           pqsignal(SIGPIPE, SIG_DFL);
            ps->pipe = 0;
        }
        if (!ps->queryFout) {
@@ -1678,3 +1741,87 @@ setFout(PsqlSettings * ps, char *fname)
     }
     return ps->queryFout;
 }
+
+static void prompt_for_password(char *username, char *password)
+{
+    int length;
+#ifdef HAVE_TERMIOS_H
+    struct termios t_orig, t;
+#endif
+
+    printf("Username: ");
+    fgets(username, 9, stdin);
+    length = strlen(username);
+    if(length > 0 && username[length-1] == '\n') username[length-1] = '\0';
+
+    printf("Password: ");
+#ifdef HAVE_TERMIOS_H
+    tcgetattr(0, &t);
+    t_orig = t;
+    t.c_lflag &= ~ECHO;
+    tcsetattr(0, TCSADRAIN, &t);
+#endif
+    fgets(password, 9, stdin);
+#ifdef HAVE_TERMIOS_H
+    tcsetattr(0, TCSADRAIN, &t_orig);
+#endif
+
+    length = strlen(password);
+    if(length > 0 && password[length-1] == '\n') password[length-1] = '\0';
+
+    printf("\n\n");
+}
+
+static char *make_connect_string(char *host, char *port, char *dbname,
+                                char *username, char *password)
+{
+    int connect_string_len = 0;
+    char *connect_string;
+    
+    if(host)
+       connect_string_len += 6 + strlen(host);       /* 6 == "host=" + " " */
+    if(username) 
+       connect_string_len += 6 + strlen(username);   /* 6 == "user=" + " " */
+    if(password)
+       connect_string_len += 10 + strlen(password);  /* 10 == "password=" + " " */
+    if(port)
+       connect_string_len += 6 + strlen(port);       /* 6 == "port=" + " " */
+    if(dbname)
+       connect_string_len += 8 + strlen(dbname);     /* 8 == "dbname=" + " " */
+    connect_string_len += 18;   /* "authtype=password" + null */
+    
+    connect_string = (char *)malloc(connect_string_len);
+    if(!connect_string) {
+       return 0;
+    }
+    connect_string[0] = '\0';
+    if(host) {
+       strcat(connect_string, "host=");
+       strcat(connect_string, host);
+       strcat(connect_string, " ");
+    }
+    if(username) {
+       strcat(connect_string, "user=");
+       strcat(connect_string, username);
+       strcat(connect_string, " ");
+    }
+    if(password) {
+       strcat(connect_string, "password=");
+       strcat(connect_string, password);
+       strcat(connect_string, " ");
+    }
+    if(port) {
+       strcat(connect_string, "port=");
+       strcat(connect_string, port);
+       strcat(connect_string, " ");
+    }
+    if(dbname) {
+       strcat(connect_string, "dbname=");
+       strcat(connect_string, dbname);
+       strcat(connect_string, " ");
+    }
+    strcat(connect_string, "authtype=password");
+
+    return connect_string;
+}
+