aboutsummaryrefslogtreecommitdiff
path: root/src/bin/psql
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/bin/psql
downloadpostgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz
postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/bin/psql')
-rw-r--r--src/bin/psql/Makefile65
-rw-r--r--src/bin/psql/psql.c1230
-rw-r--r--src/bin/psql/psqlHelp.h168
-rw-r--r--src/bin/psql/rlstubs.c41
-rw-r--r--src/bin/psql/stringutils.c104
-rw-r--r--src/bin/psql/stringutils.h51
6 files changed, 1659 insertions, 0 deletions
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
new file mode 100644
index 00000000000..8e6ab5bb4f1
--- /dev/null
+++ b/src/bin/psql/Makefile
@@ -0,0 +1,65 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+# Makefile for bin/psql
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/bin/psql/Makefile,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+PROG= psql
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+#
+#USE_READLINE is set in Makefile.global
+#
+
+ifeq ($(USE_READLINE), true)
+ CFLAGS += -I$(READLINE_INCDIR) -I$(HISTORY_INCDIR)
+
+# if you are using an older readline that uses #include "readline.h" instead
+# of #include <readline/readline.h>,
+# uncomment this
+# CFLAGS += -DOLD_READLINE
+
+ LIBCURSES= -lcurses
+ LD_ADD += -L$(READLINE_LIBDIR) -L$(HISTORY_LIBDIR) -lreadline -lhistory $(LIBCURSES)
+# use the following if your readline has no separate history lib
+# LD_ADD += -L$(READLINE_LIBDIR) -lreadline $(LIBCURSES)
+
+ ifeq ($(PORTNAME), ultrix4)
+ LD_ADD += -ltermcap
+ else
+ ifeq ($(PORTNAME), sparc)
+ LD_ADD += -ltermcap
+ else
+ ifeq ($(PORTNAME), linux)
+ LD_ADD += -ltermcap
+ endif
+ ifeq ($(PORTNAME), next)
+ LD_ADD += -ltermcap
+ endif
+ endif
+ endif
+else
+ CFLAGS += -DNOREADLINE
+endif
+
+SRCS= psql.c stringutils.c
+
+ifneq ($(USE_READLINE), true)
+SRCS+= rlstubs.c
+endif
+
+include $(MKDIR)/postgres.prog.mk
+
+
+
+
diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c
new file mode 100644
index 00000000000..d5dbfcea6fd
--- /dev/null
+++ b/src/bin/psql/psql.c
@@ -0,0 +1,1230 @@
+/*-------------------------------------------------------------------------
+ *
+ * psql.c--
+ * an interactive front-end to postgres95
+ *
+ * Copyright (c) 1996, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "libpq-fe.h"
+#include "stringutils.h"
+
+#include "psqlHelp.h"
+
+#ifdef NOREADLINE
+extern char *readline(char *); /* in rlstubs.c */
+#else
+/* from the GNU readline library */
+#ifdef OLD_READLINE
+#include "readline.h"
+#include "history.h"
+#else
+#include <readline/readline.h>
+#include <history.h>
+#endif
+#endif
+
+#define MAX_QUERY_BUFFER 20000
+#define MAX_FIELD_SEP_LENGTH 40
+
+#define COPYBUFSIZ 8192
+
+#define DEFAULT_FIELD_SEP " "
+#define DEFAULT_EDITOR "vi"
+#define DEFAULT_SHELL "/bin/sh"
+
+typedef struct _psqlSettings {
+ int echoQuery; /* if 1, echo the query before sending it */
+ int quiet; /* run quietly, no messages, no promt */
+ int singleStep; /* if 1, prompt for each query */
+ int singleLineMode; /* if 1, query terminated by newline */
+ int useReadline; /* use the readline routines or not */
+ int printHeader; /* print output field headers or not */
+ int fillAlign; /* fill align the fields */
+ FILE *queryFout; /* where to send the query results */
+ char fieldSep[MAX_FIELD_SEP_LENGTH]; /* field separator */
+} PsqlSettings;
+
+/* declarations for functions in this file */
+static void usage(char* progname);
+static void slashUsage();
+static void handleCopyOut(PGresult *res, int quiet);
+static void handleCopyIn(PGresult *res, int quiet);
+static int tableList(PGconn* conn, int deep_tablelist);
+static int tableDesc(PGconn* conn, char* table);
+
+char* gets_noreadline(char* prompt, FILE* source);
+char* gets_readline(char* prompt, FILE* source);
+char* gets_fromFile(char* prompt, FILE* source);
+int listAllDbs(PGconn *db, PsqlSettings *settings);
+int SendQuery(PGconn* db, char* query, PsqlSettings *settings);
+int HandleSlashCmds(PGconn** db_ptr,
+ char *line,
+ char** prompt_ptr,
+ char *query,
+ PsqlSettings *settings);
+int MainLoop(PGconn** db_ptr, FILE *source, PsqlSettings *settings);
+FILE* setFout(char *fname);
+
+
+/*
+ * usage
+ * print out usage for command line arguments
+ */
+
+static void
+usage(char* progname)
+{
+ fprintf(stderr,"Usage: %s [options] [dbname]\n",progname);
+ fprintf(stderr,"\t -a authsvc set authentication service\n");
+ fprintf(stderr,"\t -A turn off fill-justification when printing out attributes\n");
+ fprintf(stderr,"\t -c query run single query (slash commands too)\n");
+ fprintf(stderr,"\t -d dbName specify database name\n");
+ fprintf(stderr,"\t -e echo the query sent to the backend\n");
+ fprintf(stderr,"\t -f filename use file as a source of queries\n");
+ fprintf(stderr,"\t -F sep set the field separator (default is " ")\n");
+ fprintf(stderr,"\t -h help information\n");
+ fprintf(stderr,"\t -H host set database server host\n");
+ fprintf(stderr,"\t -l list available databases\n");
+ fprintf(stderr,"\t -n don't use readline library\n");
+ fprintf(stderr,"\t -o filename send output to filename\n");
+ fprintf(stderr,"\t -p port set port number\n");
+ fprintf(stderr,"\t -q run quietly (no messages, no prompts)\n");
+ 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 attribute names\n");
+ exit(1);
+}
+
+/*
+ * slashUsage
+ * print out usage for the backslash commands
+ */
+
+static void
+slashUsage()
+{
+ fprintf(stderr,"\t \\a -- toggle fill-justification of display of attributes\n");
+ fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>\n");
+ fprintf(stderr,"\t \\d * -- list tables in database and columns in all tables\n");
+ fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>\n");
+ fprintf(stderr,"\t \\f <sep> -- change field separator\n");
+ fprintf(stderr,"\t \\g -- query to backend\n");
+ fprintf(stderr,"\t \\h <command> -- help on syntax of sql commands\n");
+ fprintf(stderr,"\t \\h * -- complete description of all sql commands\n");
+ fprintf(stderr,"\t \\g -- send query to backend\n");
+ fprintf(stderr,"\t \\i <fname> -- read queries from filename\n");
+ fprintf(stderr,"\t \\l -- list all databases\n");
+ fprintf(stderr,"\t \\o [<fname>] -- send query results file named <fname> or stdout\n");
+ fprintf(stderr,"\t \\p -- print the current query buffer\n");
+ fprintf(stderr,"\t \\q -- quit\n");
+ fprintf(stderr,"\t \\s [<fname>] -- save or print history\n");
+ fprintf(stderr,"\t \\t -- toggle output field headers (defaults to on)\n");
+ fprintf(stderr,"\t \\! [<cmd>] -- shell escape\n");
+ fprintf(stderr,"\t \\? -- help\n");
+}
+
+/*
+ * listAllDbs
+ *
+ * list all the databases in the system
+ * returns 0 if all went well
+ *
+ *
+ */
+int
+listAllDbs(PGconn *db, PsqlSettings *settings)
+{
+ PGresult *results;
+ char* query = "select * from pg_database;";
+
+ results = PQexec(db, query);
+ if (results == NULL) {
+ fprintf(stderr,"%s", PQerrorMessage(db));
+ return 1;
+ }
+
+ if (PQresultStatus(results) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr,"Unexpected error from executing: %s\n", query);
+ return 2;
+ }
+ else
+ {
+ PQdisplayTuples(results,
+ settings->queryFout,
+ settings->fillAlign,
+ settings->fieldSep,
+ settings->printHeader,
+ settings->quiet);
+ PQclear(results);
+ return 0;
+ }
+}
+
+/*
+ * tableList (PGconn* conn)
+ *
+ * List The Database Tables
+ * returns 0 if all went well
+ *
+ */
+int
+tableList (PGconn* conn, int deep_tablelist)
+{
+ char listbuf[256];
+ int nColumns;
+ int i;
+ char* ru;
+ char* rk;
+ char* rr;
+
+ PGresult* res;
+
+ listbuf[0] = '\0';
+ strcat(listbuf,"SELECT usename, relname, relkind, relhasrules");
+ strcat(listbuf," FROM pg_class, pg_user ");
+ strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') ");
+ strcat(listbuf," and relname !~ '^pg_'");
+ strcat(listbuf," and relname !~ '^Inv'");
+/* the usesysid = relowner won't work on stock 1.0 dbs, need to
+ add in the int4oideq function */
+ strcat(listbuf," and usesysid = relowner");
+ strcat(listbuf," ORDER BY relname ");
+ res = PQexec(conn,listbuf);
+ if (res == NULL) {
+ fprintf(stderr,"%s", PQerrorMessage(conn));
+ return (-1);
+ }
+
+ if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
+ fprintf(stderr,"No tables found in database %s.\n", PQdb(conn));
+ PQclear(res);
+ return (-1);
+ }
+
+ /* first, print out the attribute names */
+ nColumns = PQntuples(res);
+ if (nColumns > 0)
+ {
+ if ( deep_tablelist ) {
+ /* describe everything here */
+ char **table;
+ table = (char**)malloc(nColumns * sizeof(char*));
+ if ( table == NULL )
+ perror("malloc");
+
+ /* load table table*/
+ for (i=0; i < nColumns; i++) {
+ table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
+ if ( table[i] == NULL )
+ perror("malloc");
+ strcpy(table[i],PQgetvalue(res,i,1));
+ }
+
+ PQclear(res);
+ for (i=0; i < nColumns; i++) {
+ tableDesc(conn,table[i]);
+ }
+ free(table);
+ }
+ else {
+ /* Display the information */
+
+ printf ("\nDatabase = %s\n", PQdb(conn));
+ printf (" +------------------+----------------------------------+----------+\n");
+ printf (" | Owner | Relation | Type |\n");
+ printf (" +------------------+----------------------------------+----------+\n");
+
+ /* next, print out the instances */
+ for (i=0; i < PQntuples(res); i++) {
+ printf (" | %-16.16s", PQgetvalue(res,i,0));
+ printf (" | %-32.32s | ", PQgetvalue(res,i,1));
+ rk = PQgetvalue(res,i,2);
+ rr = PQgetvalue(res,i,3);
+ if (strcmp(rk, "r") == 0)
+ printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
+ else
+ printf ("%-8.8s |", "index");
+ printf("\n");
+ }
+ printf (" +------------------+----------------------------------+----------+\n");
+ PQclear(res);
+ }
+ return (0);
+
+ } else {
+ fprintf (stderr, "Couldn't find any tables!\n");
+ return (-1);
+ }
+}
+
+/*
+ * Describe a table (PGconn* conn, char* table)
+ *
+ * Describe the columns in a database table.
+ * returns 0 if all went well
+ *
+ *
+ */
+int
+tableDesc (PGconn* conn, char* table)
+{
+ char descbuf[256];
+ int nColumns;
+ char *rtype;
+ int i;
+ int rsize;
+
+ PGresult* res;
+
+ /* Build the query */
+
+ descbuf[0] = '\0';
+ strcat(descbuf,"SELECT a.attnum, a.attname, t.typname, a.attlen");
+ strcat(descbuf," FROM pg_class c, pg_attribute a, pg_type t ");
+ strcat(descbuf," WHERE c.relname = '");
+ strcat(descbuf,table);
+ strcat(descbuf,"'");
+ strcat(descbuf," and a.attnum > 0 ");
+ strcat(descbuf," and a.attrelid = c.oid ");
+ strcat(descbuf," and a.atttypid = t.oid ");
+ strcat(descbuf," ORDER BY attnum ");
+ res = PQexec(conn,descbuf);
+ if (res == NULL) {
+ fprintf(stderr,"%s", PQerrorMessage(conn));
+ return (-1);
+ }
+ if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
+ fprintf(stderr,"Couldn't find table %s!\n", table);
+ PQclear(res);
+ return (-1);
+ }
+ /* first, print out the attribute names */
+ nColumns = PQntuples(res);
+ if (nColumns > 0)
+ {
+ /*
+ ** Display the information
+ */
+
+ printf ("\nTable = %s\n", table);
+ printf ("+----------------------------------+----------------------------------+-------+\n");
+ printf ("| Field | Type | Length|\n");
+ printf ("+----------------------------------+----------------------------------+-------+\n");
+
+ /* next, print out the instances */
+ for (i=0; i < PQntuples(res); i++) {
+ printf ("| %-32.32s | ", PQgetvalue(res,i,1));
+ rtype = PQgetvalue(res,i,2);
+ rsize = atoi(PQgetvalue(res,i,3));
+ if (strcmp(rtype, "text") == 0) {
+ printf ("%-32.32s |", rtype);
+ printf (" %-6s |", "var" );
+ }
+ else if (strcmp(rtype, "bpchar") == 0) {
+ printf ("%-32.32s |", "char");
+ printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+ }
+ else if (strcmp(rtype, "varchar") == 0) {
+ printf ("%-32.32s |", rtype);
+ printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+ }
+ else {
+ /* array types start with an underscore */
+ if (rtype[0] != '_')
+ printf ("%-32.32s |", rtype);
+ else {
+ char *newname;
+ newname = malloc(strlen(rtype) + 2);
+ strcpy(newname, rtype+1);
+ strcat(newname, "[]");
+ printf ("%-32.32s |", newname);
+ free(newname);
+ }
+ if (rsize > 0)
+ printf ("%-6i |", rsize);
+ else
+ printf ("%-6s |", "var");
+ }
+ printf("\n");
+ }
+ printf ("+----------------------------------+----------------------------------+-------+\n");
+
+ PQclear(res);
+ return (0);
+
+ } else {
+ fprintf (stderr, "Couldn't find table %s!\n", table);
+ return (-1);
+ }
+}
+
+typedef char* (*READ_ROUTINE)(char* prompt, FILE* source);
+
+/* gets_noreadline prompt source
+ gets a line of input without calling readline, the source is ignored
+*/
+char*
+gets_noreadline(char* prompt, FILE* source)
+{
+ fputs(prompt, stdout);
+ fflush(stdout);
+ return(gets_fromFile(prompt,stdin));
+}
+
+/*
+ * gets_readline prompt source
+ * the routine to get input from GNU readline(), the source is ignored
+ * the prompt argument is used as the prompting string
+ */
+char*
+gets_readline(char* prompt, FILE* source)
+{
+ return (readline(prompt));
+}
+
+
+/*
+ * gets_fromFile prompt source
+ *
+ * the routine to read from a file, the prompt argument is ignored
+ * the source argument is a FILE*
+ */
+char*
+gets_fromFile(char* prompt, FILE* source)
+{
+ char* line;
+ int len;
+
+ line = malloc(MAX_QUERY_BUFFER+1);
+
+ /* read up to MAX_QUERY_BUFFER characters */
+ if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
+ return NULL;
+
+ line[MAX_QUERY_BUFFER-1] = '\0';
+ len = strlen(line);
+ if (len == MAX_QUERY_BUFFER)
+ {
+ fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n", MAX_QUERY_BUFFER);
+ }
+
+ return line;
+}
+
+/*
+ * SendQuery:
+ SendQuery: send the query string to the backend
+ *
+ * return 0 if the query executed successfully
+ * returns 1 otherwise
+ */
+int
+SendQuery(PGconn* db, char* query, PsqlSettings *settings)
+{
+ PGresult* results;
+ PGnotify* notify;
+ int status = 0;
+
+ if (settings->singleStep)
+ fprintf(stdout, "\n*******************************************************************************\n");
+
+ if (settings->echoQuery || settings->singleStep) {
+ fprintf(stderr,"QUERY: %s\n",query);
+ fflush(stderr);
+ }
+
+ if (settings->singleStep) {
+ fprintf(stdout, "\n*******************************************************************************\n");
+ fflush(stdout);
+ printf("\npress return to continue ..\n");
+ gets_fromFile("",stdin);
+ }
+
+ results = PQexec(db, query);
+ if (results == NULL) {
+ fprintf(stderr,"%s",PQerrorMessage(db));
+ return 1;
+ }
+
+ switch (PQresultStatus(results)) {
+ case PGRES_TUPLES_OK:
+ PQdisplayTuples(results,
+ settings->queryFout,
+ settings->fillAlign,
+ settings->fieldSep,
+ settings->printHeader,
+ settings->quiet);
+ PQclear(results);
+ break;
+ case PGRES_EMPTY_QUERY:
+ /* do nothing */
+ break;
+ case PGRES_COMMAND_OK:
+ if (!settings->quiet)
+ fprintf(stdout,"%s\n",PQcmdStatus(results));
+ break;
+ case PGRES_COPY_OUT:
+ handleCopyOut(results, settings->quiet);
+ break;
+ case PGRES_COPY_IN:
+ handleCopyIn(results, settings->quiet);
+ break;
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ case PGRES_BAD_RESPONSE:
+ status = 1;
+ fprintf(stderr,"%s",PQerrorMessage(db));
+ break;
+
+ }
+
+ /* check for asynchronous returns */
+ notify = PQnotifies(db);
+ if (notify) {
+ fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+ notify->relname, notify->be_pid);
+ free(notify);
+ }
+
+ return status;
+
+}
+
+/*
+ HandleSlashCmds:
+
+ Handles all the different commands that start with \
+ db_ptr is a pointer to the TgDb* structure
+ line is the current input line
+ prompt_ptr is a pointer to the prompt string,
+ a pointer is used because the prompt can be used with
+ a connection to a new database
+ returns a status:
+ 0 - send currently constructed query to backend (i.e. we got a \g)
+ 1 - skip processing of this line, continue building up query
+ 2 - terminate processing of this query entirely
+*/
+int
+HandleSlashCmds(PGconn** db_ptr,
+ char* line,
+ char** prompt_ptr,
+ char *query,
+ PsqlSettings *settings)
+{
+ int status = 0;
+ PGconn* db = *db_ptr;
+ char* dbname = PQdb(db);
+ char *optarg = NULL;
+ int len;
+
+ len = strlen(line);
+ if (len > 2)
+ optarg = leftTrim(line+2);
+ switch (line[1])
+ {
+ case 'a': /* toggles to fill fields on output */
+ if (settings->fillAlign)
+ settings->fillAlign = 0;
+ else
+ settings->fillAlign = 1;
+ if (!settings->quiet)
+ fprintf(stderr,"turning %s fill-justification\n",
+ (settings->fillAlign) ? "on" : "off" );
+ break;
+ case 'c': /* \c means connect to new database */
+ {
+ if (!optarg) {
+ fprintf(stderr,"\\c must be followed by a database name\n");
+ status = 1;
+ break;
+ }
+ if (strcmp(optarg, dbname) == 0) {
+ fprintf(stderr,"already connected to %s\n", dbname);
+ status = 1;
+ break;
+ }
+ else {
+ PGconn *olddb;
+
+ printf("closing connection to database:%s\n", dbname);
+ olddb = db;
+ db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
+ *db_ptr = db;
+ printf("connecting to new database: %s\n", optarg);
+ if (PQstatus(db) == CONNECTION_BAD) {
+ fprintf(stderr,"%s\n", PQerrorMessage(db));
+ printf("reconnecting to %s\n", dbname);
+ db = PQsetdb(PQhost(olddb), PQport(olddb),
+ NULL, NULL, dbname);
+ *db_ptr = db;
+ if (PQstatus(db) == CONNECTION_BAD) {
+ fprintf(stderr,
+ "could not reconnect to %s. exiting\n", dbname);
+ exit(2);
+ }
+ status = 1;
+ break;
+ }
+ PQfinish(olddb);
+ free(*prompt_ptr);
+ *prompt_ptr = malloc(strlen(optarg) + 10);
+ sprintf(*prompt_ptr,"%s=> ", optarg);
+ status = 1;
+ break;
+ }
+ }
+ break;
+ case 'd': /* \d describe tables or columns in a table */
+ {
+ if (!optarg) {
+ tableList(db,0);
+ status = 1;
+ break;
+ }
+ if ( strcmp(optarg,"*") == 0 ) {
+ tableList(db, 0);
+ tableList(db, 1);
+ }
+ else {
+ tableDesc(db,optarg);
+ }
+ status = 1;
+ break;
+ }
+ case 'e':
+ {
+ char s[256];
+ int fd;
+ int ql = strlen(query);
+ int f_arg = 0;
+ int cc;
+ if (optarg)
+ {
+ f_arg = 1;
+ strcpy(s, optarg);
+ }
+ else
+ {
+ sprintf(s, "/tmp/psql.%d.%d", getuid(), getpid());
+ unlink(s);
+ if (ql)
+ {
+ if ((fd=open(s, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
+ {
+ perror(s);
+ break;
+ }
+ if (query[ql-1]!='\n')
+ strcat(query, "\n");
+ if (write(fd, query, ql)!=ql)
+ {
+ perror(s);
+ close(fd);
+ unlink(s);
+ break;
+ }
+ close(fd);
+ }
+ }
+ {
+ char sys[256];
+ char *editorName;
+ editorName = getenv("EDITOR");
+ if (editorName == NULL)
+ editorName = DEFAULT_EDITOR;
+ sprintf(sys, "exec %s %s", editorName, s);
+ system(sys);
+ }
+ if ((fd=open(s, O_RDONLY))==-1)
+ {
+ if (!f_arg)
+ unlink(s);
+ break;
+ }
+ if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
+ {
+ perror(s);
+ close(fd);
+ if (!f_arg)
+ unlink(s);
+ break;
+ }
+ query[cc]='\0';
+ close(fd);
+ if (!f_arg)
+ unlink(s);
+ rightTrim(query);
+ if (query[strlen(query)-1]==';')
+ return 0;
+ break;
+ }
+ case 'f':
+ if (optarg)
+ strcpy(settings->fieldSep,optarg);
+ else
+ strcpy(settings->fieldSep,DEFAULT_FIELD_SEP);
+ break;
+ case 'g': /* \g means send query */
+ status = 0;
+ break;
+ case 'i': /* \i is include file */
+ {
+ FILE* fd;
+
+ if (!optarg) {
+ fprintf(stderr,"\\i must be followed by a file name\n");
+ status = 1;
+ break;
+ }
+
+ if ( (fd = fopen(optarg, "r")) == NULL)
+ {
+ fprintf(stderr,"file named %s could not be opened\n",optarg);
+ status = 1;
+ break;
+ }
+ MainLoop(&db, fd, settings);
+ fclose(fd);
+ status = 1;
+ break;
+ }
+ case 'h':
+ {
+ char* cmd;
+ int i, numCmds;
+ int all_help = 0;
+
+ if (!optarg) {
+ printf("type \\h <cmd> where <cmd> is one of the following:\n");
+ i = 0;
+ while (QL_HELP[i].cmd != NULL)
+ {
+ printf("\t%s\n", QL_HELP[i].cmd);
+ i++;
+ }
+ printf("type \\h * for a complete description of all commands\n");
+ }
+ else
+ {
+ cmd = optarg;
+
+ numCmds = 0;
+ while (QL_HELP[numCmds++].cmd != NULL);
+
+ numCmds = numCmds - 1;
+
+ if ( strcmp(cmd,"*") == 0 ) {
+ all_help=1;
+ }
+
+ for (i=0; i<numCmds;i++) {
+ if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help) {
+ printf("Command: %s\n",QL_HELP[i].cmd);
+ printf("Description: %s\n", QL_HELP[i].help);
+ printf("Syntax:\n");
+ printf("%s\n", QL_HELP[i].syntax);
+ if ( all_help ) {
+ printf("\n");
+ }
+ else {
+ break;
+ }
+ }
+ }
+ if (i == numCmds && ! all_help)
+ printf("command not found, try \\h with no arguments to see available help\n");
+ }
+ status = 1;
+ break;
+ }
+ case 'l': /* \l is list database */
+ listAllDbs(db,settings);
+ status = 1;
+ break;
+ case 'o':
+ settings->queryFout = setFout(optarg);
+ break;
+ case 'p':
+ if (query) {
+ fputs(query, stdout);
+ fputc('\n', stdout);
+ }
+ break;
+ case 'q': /* \q is quit */
+ status = 2;
+ break;
+ case 's': /* \s is save history to a file */
+ {
+ char* fname;
+
+ if (!optarg) {
+ fprintf(stderr,"\\s must be followed by a file name\n");
+ status = 1;
+ break;
+ }
+
+ fname = optarg;
+ if (write_history(fname) != 0)
+ {
+ fprintf(stderr,"cannot write history to %s\n",fname);
+ }
+ status = 1;
+ break;
+ }
+ case 't':
+ if ( settings->printHeader )
+ settings->printHeader = 0;
+ else
+ settings->printHeader = 1;
+ if (!settings->quiet)
+ fprintf(stderr,"turning %s printing of field headers\n",
+ (settings->printHeader) ? "on" : "off" );
+ break;
+ case '!':
+ if (!optarg) {
+ char sys[256];
+ char *shellName;
+ shellName = getenv("SHELL");
+ if (shellName == NULL)
+ shellName = DEFAULT_SHELL;
+ sprintf(sys,"exec %s", shellName);
+ system(sys);
+ }
+ else
+ system(optarg);
+ break;
+ default:
+ case '?': /* \? is help */
+ slashUsage();
+ status = 1;
+ break;
+ }
+ return status;
+}
+
+/*
+ MainLoop: main processing loop for reading lines of input
+ and sending them to the backend
+
+ this loop is re-entrant. May be called by \i command
+ which reads input from a file
+
+ *db_ptr must be initialized and set
+*/
+int
+MainLoop(PGconn** db_ptr,
+ FILE* source,
+ PsqlSettings *settings)
+{
+ char* prompt; /* readline prompt */
+ char* line; /* line of input*/
+ int len; /* length of the line */
+ char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
+ PGconn* db = *db_ptr;
+ char* dbname = PQdb(db);
+ int exitStatus = 0;
+
+ int slashCmdStatus = 0;
+ /* slashCmdStatus can be:
+ 0 - send currently constructed query to backend (i.e. we got a \g)
+ 1 - skip processing of this line, continue building up query
+ 2 - terminate processing of this query entirely
+ */
+
+ int send_query = 0;
+ int interactive;
+ READ_ROUTINE GetNextLine;
+
+ interactive = (source == stdin);
+
+ if (interactive) {
+ prompt = malloc(strlen(dbname) + 10);
+ if (settings->quiet)
+ prompt[0] = '\0';
+ else
+ sprintf(prompt,"%s=> ", dbname);
+ if (settings->useReadline) {
+ using_history();
+ GetNextLine = gets_readline;
+ } else
+ GetNextLine = gets_noreadline;
+
+ }
+ else
+ GetNextLine = gets_fromFile;
+
+ query[0] = '\0';
+
+ /* main loop for getting queries and executing them */
+ while ((line = GetNextLine(prompt, source)) != NULL)
+ {
+ exitStatus = 0;
+ line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
+
+ if (line[0] == '\0') {
+ free(line);
+ continue;
+ }
+
+ /* filter out comment lines that begin with --,
+ this could be incorrect if -- is part of a quoted string.
+ But we won't go through the trouble of detecting that. If you have
+ -- in your quoted string, be careful and don't start a line with it*/
+ if (line[0] == '-' && line[1] == '-') {
+ if (settings->singleStep) /* in single step mode, show comments */
+ fprintf(stdout,"%s\n",line);
+ free(line);
+ continue;
+ }
+
+ len = strlen(line);
+
+ if (interactive && settings->useReadline)
+ add_history(line); /* save non-empty lines in history */
+
+ /* do the query immediately if we are doing single line queries
+ or if the last character is a semicolon */
+ send_query = settings->singleLineMode || (line[len-1] == ';') ;
+
+ /* normally, \ commands have to be start the line,
+ but for backwards compatibility with monitor,
+ check for \g at the end of line */
+ if (len > 2 && !send_query)
+ {
+ if (line[len-1]=='g' && line[len-2]=='\\')
+ {
+ send_query = 1;
+ line[len-2]='\0';
+ }
+ }
+
+ /* slash commands have to be on their own line */
+ if (line[0] == '\\') {
+ slashCmdStatus = HandleSlashCmds(db_ptr,
+ line,
+ &prompt,
+ query,
+ settings);
+ db = *db_ptr; /* in case \c changed the database */
+ if (slashCmdStatus == 1)
+ continue;
+ if (slashCmdStatus == 2)
+ break;
+ if (slashCmdStatus == 0)
+ send_query = 1;
+ }
+ else
+ if (strlen(query) + len > MAX_QUERY_BUFFER)
+ {
+ fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
+ fprintf(stderr,"query line ignored\n");
+ }
+ else
+ if (query[0]!='\0') {
+ strcat(query,"\n");
+ strcat(query,line);
+ }
+ else
+ strcpy(query,line);
+
+ if (send_query && query[0] != '\0')
+ {
+ /* echo the line read from the file,
+ unless we are in single_step mode, because single_step mode
+ will echo anyway */
+ if (!interactive && !settings->singleStep)
+ fprintf(stderr,"%s\n",query);
+
+ exitStatus = SendQuery(db, query, settings);
+ query[0] = '\0';
+ }
+
+ free(line); /* free storage malloc'd by GetNextLine */
+ } /* while */
+ return exitStatus;
+}
+
+int
+main(int argc, char** argv)
+{
+ extern char* optarg;
+ extern int optind, opterr;
+
+ PGconn *db;
+ char* dbname = NULL;
+ char* host = NULL;
+ char* port = NULL;
+ char* qfilename = NULL;
+ char errbuf[ERROR_MSG_LENGTH];
+
+ PsqlSettings settings;
+
+ char* singleQuery = NULL;
+
+ int listDatabases = 0 ;
+ int exitStatus = 0;
+ int singleSlashCmd = 0;
+ int c;
+
+
+#ifdef NOREADLINE
+ settings.useReadline = 0;
+#else
+ settings.useReadline = 1;
+#endif
+
+ settings.quiet = 0;
+ settings.fillAlign = 1;
+ settings.printHeader = 1;
+ settings.echoQuery = 0;
+ settings.singleStep = 0;
+ settings.singleLineMode = 0;
+ settings.queryFout = stdout;
+ strcpy(settings.fieldSep, DEFAULT_FIELD_SEP);
+
+ while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qST")) != EOF) {
+ switch (c) {
+ case 'A':
+ settings.fillAlign = 0;
+ break;
+ case 'a':
+ fe_setauthsvc(optarg, errbuf);
+ break;
+ case 'c':
+ singleQuery = optarg;
+ if ( singleQuery[0] == '\\' ) {
+ singleSlashCmd=1;
+ }
+ break;
+ case 'd':
+ dbname = optarg;
+ break;
+ case 'e':
+ settings.echoQuery = 1;
+ break;
+ case 'f':
+ qfilename = optarg;
+ break;
+ case 'F':
+ strncpy(settings.fieldSep,optarg,MAX_FIELD_SEP_LENGTH);
+ break;
+ case 'l':
+ listDatabases = 1;
+ break;
+ case 'H':
+ host = optarg;
+ break;
+ case 'n':
+ settings.useReadline = 0;
+ break;
+ case 'o':
+ settings.queryFout = setFout(optarg);
+ break;
+ case 'p':
+ port = optarg;
+ break;
+ case 'q':
+ settings.quiet = 1;
+ break;
+ case 's':
+ settings.singleStep = 1;
+ break;
+ case 'S':
+ settings.singleLineMode = 1;
+ break;
+ case 'T':
+ settings.printHeader = 0;
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ /* if we still have an argument, use it as the database name */
+ if (argc - optind == 1)
+ dbname = argv[optind];
+
+ if (listDatabases)
+ dbname = "template1";
+
+ db = PQsetdb(host, port, NULL, NULL, dbname);
+ dbname = PQdb(db);
+
+ if (PQstatus(db) == CONNECTION_BAD) {
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
+ fprintf(stderr,"%s",PQerrorMessage(db));
+ exit(1);
+ }
+ if (listDatabases) {
+ exit(listAllDbs(db,&settings));
+ }
+
+ if (!settings.quiet && !singleQuery && !qfilename) {
+ printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
+ printf(" Please read the file COPYRIGHT for copyright terms of POSTGRES95\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");
+ printf(" You are currently connected to the database: %s\n\n", dbname);
+ }
+
+ if (qfilename || singleSlashCmd) {
+ /* read in a file full of queries instead of reading in queries
+ interactively */
+ char *line;
+ char prompt[100];
+
+ if ( singleSlashCmd ) {
+ /* Not really a query, but "Do what I mean, not what I say." */
+ line = singleQuery;
+ }
+ else {
+ line = malloc(strlen(qfilename) + 5);
+ sprintf(line,"\\i %s", qfilename);
+ }
+ HandleSlashCmds(&db, line, (char**)prompt, "", &settings);
+
+ } else {
+ if (singleQuery) {
+ exitStatus = SendQuery(db, singleQuery, &settings);
+ }
+ else
+ exitStatus = MainLoop(&db, stdin, &settings);
+ }
+
+ PQfinish(db);
+
+ return exitStatus;
+}
+
+
+static void
+handleCopyOut(PGresult *res, int quiet)
+{
+ bool copydone = false;
+ char copybuf[COPYBUFSIZ];
+ int ret;
+
+ if (!quiet)
+ fprintf(stdout, "Copy command returns...\n");
+
+ while (!copydone) {
+ ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
+
+ if (copybuf[0] == '.' && copybuf[1] =='\0') {
+ copydone = true; /* don't print this... */
+ } else {
+ fputs(copybuf, stdout);
+ switch (ret) {
+ case EOF:
+ copydone = true;
+ /*FALLTHROUGH*/
+ case 0:
+ fputc('\n', stdout);
+ break;
+ case 1:
+ break;
+ }
+ }
+ }
+ fflush(stdout);
+ PQendcopy(res->conn);
+}
+
+
+static void
+handleCopyIn(PGresult *res, int quiet)
+{
+ bool copydone = false;
+ bool firstload;
+ bool linedone;
+ char copybuf[COPYBUFSIZ];
+ char *s;
+ int buflen;
+ int c;
+
+ if (!quiet) {
+ fputs("Enter info followed by a newline\n", stdout);
+ fputs("End with a dot on a line by itself.\n", stdout);
+ }
+
+ /*
+ * eat extra newline still in input buffer
+ *
+ */
+ fflush(stdin);
+ if ((c = getc(stdin)) != '\n' && c != EOF) {
+ (void) ungetc(c, stdin);
+ }
+
+ while (!copydone) { /* for each input line ... */
+ if (!quiet) {
+ fputs(">> ", stdout);
+ fflush(stdout);
+ }
+ firstload = true;
+ linedone = false;
+ while (!linedone) { /* for each buffer ... */
+ s = copybuf;
+ buflen = COPYBUFSIZ;
+ for (; buflen > 1 &&
+ !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
+ --buflen) {
+ *s++ = c;
+ }
+ if (c == EOF) {
+ /* reading from stdin, but from a file */
+ PQputline(res->conn, ".");
+ copydone = true;
+ break;
+ }
+ *s = '\0';
+ PQputline(res->conn, copybuf);
+ if (firstload) {
+ if (!strcmp(copybuf, ".")) {
+ copydone = true;
+ }
+ firstload = false;
+ }
+ }
+ PQputline(res->conn, "\n");
+ }
+ PQendcopy(res->conn);
+}
+
+
+/* try to open fname and return a FILE*,
+ if it fails, use stdout, instead */
+FILE*
+setFout(char *fname)
+{
+ FILE *queryFout;
+
+ if (!fname)
+ queryFout = stdout;
+ else {
+ queryFout = fopen(fname, "w");
+ if (!queryFout) {
+ perror(fname);
+ queryFout = stdout;
+ }
+ }
+
+ return queryFout;
+}
+
diff --git a/src/bin/psql/psqlHelp.h b/src/bin/psql/psqlHelp.h
new file mode 100644
index 00000000000..e0d5077bc3b
--- /dev/null
+++ b/src/bin/psql/psqlHelp.h
@@ -0,0 +1,168 @@
+/*-------------------------------------------------------------------------
+ *
+ * psqlHelp.h--
+ * Help for query language syntax
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: psqlHelp.h,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+struct _helpStruct {
+ char* cmd; /* the command name */
+ char* help; /* the help associated with it */
+ char* syntax; /* the syntax associated with it */
+} ;
+
+static struct _helpStruct QL_HELP[] = {
+ { "abort",
+ "abort the current transaction",
+ "abort [transaction];"},
+ { "abort transaction",
+ "abort the current transaction",
+ "abort [transaction];"},
+ { "alter table",
+ "add/rename attributes, rename tables",
+ "alter table <relname> [*] add column <attr> <type>;\n\talter table <relname> [*] rename [column] <attr1> to <attr2>;\n\talter table <relname1> rename to <relname2>"},
+ { "begin",
+ "begin a new transaction",
+ "begin [transaction|work];"},
+ { "begin transaction",
+ "begin a new transaction",
+ "begin [transaction|work];"},
+ { "begin work",
+ "begin a new transaction",
+ "begin [transaction|work];"},
+ { "cluster",
+ "create a clustered index (from an existing index)",
+ "cluster <index_name> on <relation_name>"},
+ { "close",
+ "close an existing cursor (portal)",
+ "close <portalname>;"},
+ { "commit",
+ "commit a transaction",
+ "commit [work]"},
+ { "commit work",
+ "commit a transaction",
+ "commit [work]"},
+ { "copy",
+ "copy data to and from a table",
+ "copy [binary] [nonulls] <relname>\n\t{to|from} {<filename>|stdin|stdout} [using delimiters <delim>];"},
+ { "create",
+ "Please more be specific:",
+ "\tcreate aggregate\n\tcreate database\n\tcreate function\n\tcreate index\n\tcreate operator\n\tcreate rule\n\tcreate table\n\tcreate type\n\tcreate view"},
+ { "create aggregate",
+ "define an aggregate function",
+ "create aggregate <agg_name> [as] (basetype = <data_type>, \n\t[sfunc1 = <sfunc_1>, stype1 = <sfunc1_return_type>]\n\t[sfunc2 = <sfunc_2>, stype2 = <sfunc2_return_type>]\n\t[,finalfunc = <final-function>]\n\t[,initcond1 = <initial-cond1>][,initcond2 = <initial-cond2>]);"},
+ { "create database",
+ "create a database",
+ "create database <dbname>"},
+ { "create function",
+ "create a user-defined function",
+ "create function <function_name> ([<type1>,...<typeN>]) returns <return_type>\n\tas '<object_filename>'|'<sql-queries>'\n\tlanguage 'c'|'sql'|'internal';"},
+ { "create index",
+ "construct an index",
+ "create index <indexname> on <relname> using <access_method> (<attr1>|<funcname>(<attr1>,...) <type_class1>);"},
+ { "create operator",
+ "create a user-defined operator",
+ "create operator <operator_name> (\n\t[leftarg = <type1>][,rightarg = <type2>]\n\t,procedure = <func_name>,\n\t[,commutator = <com_op>][,negator = <neg_op>]\n\t[,restrict = <res_proc>][,hashes]\n\t[,join = <join_proc>][,sort = <sort_op1>...<sort_opN>]);"},
+ { "create rule",
+ "define a new rule",
+ "create rule <rule_name> as on\n\t[select|update|delete|insert]\n\tto <object> [where <qual>]\n\tdo [instead] [<action>|nothing| [<actions>]];"},
+ { "create table",
+ "create a new table",
+ "create table <relname> ( <attr1> <type1>,... <attrN> <typeN>)\n\t[inherits (<relname1>,...<relnameN>\n\tarchive=<archive_mode>\n\tstore=<smgr_name>\n\tarch_store=<smgr_name>];"},
+ { "create type",
+ "create a new user-defined base data type",
+ "create type <typename> (\n\tinternallength = (<number> | variable),\n\t[externallength = (<number>|variable),]\n\tinput=<input_function>, output = <output_function>\n\t[,element = <typename>][,delimiter=<character>][,default=\'<string>\']\n\t[,send = <send_function>][,receive = <receive_function>][,passedbyvalue]);"},
+ { "create view",
+ "create a view",
+ "create view <view_name> as select <expr1>[as <attr1>][,... <exprN>[as <attrN>]] [from <from_list>] [where <qual>];"},
+ { "declare",
+ "set up a cursor (portal)",
+ "declare <portalname> [binary] cursor for\n\tselect [distinct]\n\t<expr1> [as <attr1>],...<exprN> [as <attrN>]\n\t[from <from_list>] [where <qual>]\n\t[order by <attr1> [using <op1>],... <attrN> [using <opN>]];"},
+ { "delete",
+ "delete tuples",
+ "delete from <relname> [where <qual>];"},
+ { "drop",
+ "Please more be specific:",
+ "\tdrop aggregate\n\tdrop database\n\tdrop function\n\tdrop index\n\tdrop operator\n\tdrop rule\n\tdrop table\n\tdrop type\n\tdrop view"},
+ { "drop aggregate",
+ "remove an aggregate function",
+ "drop aggregate <agg_name>;"},
+ { "drop database",
+ "remove a database",
+ "drop database <dbname>"},
+ { "drop function",
+ "remove a user-defined function",
+ "drop function <funcname> ([<type1>,....<typeN>]);"},
+ { "drop index",
+ "remove an existing index",
+ "drop index <indexname>;"},
+ { "drop operator",
+ "remove a user-defined operator",
+ "drop operator <operator_name> ([<ltype>|none],[<rtype>|none]);"},
+ { "drop rule",
+ "remove a rule",
+ "drop rule <rulename>;"},
+ { "drop table",
+ "remove a table",
+ "drop table <relname>[,...<relnameN];"},
+ { "drop type",
+ "remove a user-defined base type",
+ "drop type <typename>;"},
+ { "drop view",
+ "remove a view",
+ "drop view <view_name>"},
+ { "end",
+ "end the current transaction",
+ "end [transaction];"},
+ { "end transaction",
+ "end the current transaction",
+ "end [transaction];"},
+ { "explain",
+ "explain the query execution plan",
+ "explain [with {cost|full_plan}] <query>"},
+ { "extend index",
+ "extend a partial index",
+ "extend index <indexname> [where <qual>];"},
+ { "fetch",
+ "retrieve tuples from a cursor (portal)",
+ "fetch [forward|backward] [<number>|all] [in <portalname>];"},
+ { "grant",
+ "grant access control to a user or group",
+ "grant <privilege[,privilege,...]> on <rel1>[,...<reln>] to \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"},
+ { "insert",
+ "insert tuples",
+ "insert into <relname> [(<attr1>...<attrN>)]\n\t[values (<expr1>...<exprN>); |\n\tselect <expr1>,...<exprN> [from <from_clause>] [where <qual>];"},
+ { "listen",
+ "listen for notification on a relation",
+ "listen <relname>"},
+ { "load",
+ "dynamically load a module",
+ "load <filename>;"},
+ { "notify",
+ "signal all frontends and backends listening on a relation",
+ "notify <relname>"},
+ { "purge",
+ "purge historical data",
+ "purge <relname> [before <abstime>] [after <reltime>];"},
+ { "revoke",
+ "revoke access control from a user or group",
+ "revoke <privilege[,privilege,...]> on <rel1>[,...<reln>] from \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"},
+ { "rollback",
+ "abort a transaction",
+ "rollback [work]"},
+ { "select",
+ "retrieve tuples",
+ "select [distinct on <attr>] <expr1> [as <attr1>], ... <exprN> [as <attrN>]\n\t[into table <relname>] [from <from_list>]\n\t[where <qual>]\n\t[order by <attr1>\n\t\t[using <op1>],..<attrN> [[using <opN>] | ASC | DESC]];" },
+ { "update",
+ "update tuples",
+ "update <relname> set <attr1>=<expr1>,...<attrN>=<exprN> [from <from_clause>] [where <qual>];"},
+ { "vacuum",
+ "vacuum the database, i.e. cleans out deleted records, updates statistics",
+ "vacuum;"},
+ { NULL, NULL, NULL} /* important to keep a NULL terminator here! */
+};
diff --git a/src/bin/psql/rlstubs.c b/src/bin/psql/rlstubs.c
new file mode 100644
index 00000000000..2640d3ce094
--- /dev/null
+++ b/src/bin/psql/rlstubs.c
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * rlstubs.c--
+ * stub routines when compiled without readline and history libraries
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/bin/psql/Attic/rlstubs.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+char *
+readline(char *prompt)
+{
+ static char buf[500];
+
+ printf("%s");
+ return fgets(buf, 500, stdin);
+}
+
+int
+write_history()
+{
+ return 0;
+}
+
+int
+using_history()
+{
+ return 0;
+}
+
+int
+add_history()
+{
+ return 0;
+}
diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c
new file mode 100644
index 00000000000..0a9043c6337
--- /dev/null
+++ b/src/bin/psql/stringutils.c
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringutils.c--
+ * simple string manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "stringutils.h"
+
+/* all routines assume null-terminated strings! */
+
+/* removes whitespaces from the left, right and both sides of a string */
+/* MODIFIES the string passed in and returns the head of it */
+char* leftTrim(char* s)
+{
+ char* s2 = s;
+ int shift=0;
+ int j=0;
+
+ while (isspace(*s))
+ { s++; shift++;}
+ if (shift > 0)
+ {
+ while ( (s2[j] = s2[j+shift]) !='\0')
+ j++;
+ }
+
+ return s2;
+}
+
+char* rightTrim(char* s)
+{
+ char* sEnd;
+ sEnd = s+strlen(s)-1;
+ while (isspace(*sEnd))
+ sEnd--;
+ if (sEnd < s)
+ s[0]='\0';
+ else
+ s[sEnd-s+1]='\0';
+ return s;
+}
+
+char* doubleTrim(char* s)
+{
+ strcpy(s,leftTrim(rightTrim(s)));
+ return s;
+}
+
+/* dupstr : copies a string, while allocating space for it.
+ the CALLER is responsible for freeing the space
+ returns NULL if the argument is NULL*/
+char* dupstr(char *s)
+{
+ char* result;
+
+ if (s == NULL)
+ return NULL;
+
+ result = (char*)malloc(strlen(s)+1);
+ strcpy(result, s);
+ return result;
+}
+
+
+#ifdef STRINGUTILS_TEST
+void testStringUtils()
+{
+ static char* tests[] = {" goodbye \n", /* space on both ends */
+ "hello world", /* no spaces to trim */
+ "", /* empty string */
+ "a", /* string with one char*/
+ " ", /* string with one whitespace*/
+ NULL_STR};
+
+ int i=0;
+ while (tests[i]!=NULL_STR)
+ {
+ char* t;
+ t = dupstr(tests[i]);
+ printf("leftTrim(%s) = ",t);
+ printf("%sEND\n", leftTrim(t));
+ t = dupstr(tests[i]);
+ printf("rightTrim(%s) = ",t);
+ printf("%sEND\n", rightTrim(t));
+ t = dupstr(tests[i]);
+ printf("doubleTrim(%s) = ",t);
+ printf("%sEND\n", doubleTrim(t));
+ i++;
+ }
+
+}
+
+#endif
diff --git a/src/bin/psql/stringutils.h b/src/bin/psql/stringutils.h
new file mode 100644
index 00000000000..d8564a02d08
--- /dev/null
+++ b/src/bin/psql/stringutils.h
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringutils.h--
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: stringutils.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STRINGUTILS_H
+#define STRINGUTILS_H
+
+/* use this for memory checking of alloc and free using Tcl's memory check
+ package*/
+#ifdef TCL_MEM_DEBUG
+#include <tcl.h>
+#define malloc(x) ckalloc(x)
+#define free(x) ckfree(x)
+#define realloc(x,y) ckrealloc(x,y)
+#endif
+
+/* string fiddling utilties */
+
+/* all routines assume null-terminated strings! as arguments */
+
+/* removes whitespaces from the left, right and both sides of a string */
+/* MODIFIES the string passed in and returns the head of it */
+extern char* leftTrim(char* s);
+extern char* rightTrim(char* s);
+extern char* doubleTrim(char* s);
+
+/* dupstr : copies a string, while making room for it */
+/* the CALLER is responsible for freeing the space */
+/* returns NULL if the argument is NULL */
+extern char* dupstr(char *s);
+
+#ifdef STRINGUTILS_TEST
+extern void testStringUtils();
+#endif
+
+#ifndef NULL_STR
+#define NULL_STR (char*)0
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#endif /* STRINGUTILS_H */