Commit 0365c51e authored by Bruce Momjian's avatar Bruce Momjian

Overhaul MainLoop input processing for quotes, comments, backslashes.

parent f64b8403
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.28 1996/11/11 05:55:30 momjian Exp $ * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.29 1996/11/14 16:08:03 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -58,117 +58,117 @@ typedef struct _psqlSettings { ...@@ -58,117 +58,117 @@ typedef struct _psqlSettings {
bool quiet; /* run quietly, no messages, no promt */ bool quiet; /* run quietly, no messages, no promt */
bool singleStep; /* prompt before for each query */ bool singleStep; /* prompt before for each query */
bool singleLineMode; /* query terminated by newline */ bool singleLineMode; /* query terminated by newline */
bool useReadline; /* use libreadline routines */ bool useReadline;/* use libreadline routines */
} PsqlSettings; } PsqlSettings;
/* declarations for functions in this file */ /* declarations for functions in this file */
static void usage(char *progname); static void usage(char *progname);
static void slashUsage(); static void slashUsage();
static void handleCopyOut(PGresult *res, bool quiet, FILE *copystream); static void handleCopyOut(PGresult * res, bool quiet, FILE * copystream);
static void handleCopyIn(PGresult *res, const bool mustprompt, static void
FILE *copystream); handleCopyIn(PGresult * res, const bool mustprompt,
static int tableList(PsqlSettings *ps, bool deep_tablelist); FILE * copystream);
static int tableDesc(PsqlSettings *ps, char *table); static int tableList(PsqlSettings * ps, bool deep_tablelist);
static int tableDesc(PsqlSettings * ps, char *table);
char *gets_noreadline(char *prompt, FILE *source);
char *gets_readline(char *prompt, FILE *source); char *gets_noreadline(char *prompt, FILE * source);
char *gets_fromFile(char *prompt, FILE *source); char *gets_readline(char *prompt, FILE * source);
int listAllDbs(PsqlSettings *settings); char *gets_fromFile(char *prompt, FILE * source);
void SendQuery(bool *success_p, PsqlSettings *settings, const char *query, int listAllDbs(PsqlSettings * settings);
const bool copy_in, const bool copy_out, FILE *copystream); void
int HandleSlashCmds(PsqlSettings *settings, SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
const bool copy_in, const bool copy_out, FILE * copystream);
int
HandleSlashCmds(PsqlSettings * settings,
char *line, char *line,
char *query); char *query);
int MainLoop(PsqlSettings *settings, FILE *source); int MainLoop(PsqlSettings * settings, FILE * source);
/* probably should move this into libpq */ /* probably should move this into libpq */
void PQprint(FILE *fp, void
PGresult *res, PQprint(FILE * fp,
PQprintOpt *po PGresult * res,
); PQprintOpt * po
);
FILE *setFout(PsqlSettings *ps, char *fname); FILE *setFout(PsqlSettings * ps, char *fname);
/* /*
* usage * usage print out usage for command line arguments
* print out usage for command line arguments
*/ */
static void static void
usage(char *progname) usage(char *progname)
{ {
fprintf(stderr,"Usage: %s [options] [dbname]\n",progname); fprintf(stderr, "Usage: %s [options] [dbname]\n", progname);
fprintf(stderr,"\t -a authsvc set authentication service\n"); fprintf(stderr, "\t -a authsvc set authentication service\n");
fprintf(stderr,"\t -A turn off alignment when printing out attributes\n"); fprintf(stderr, "\t -A turn off alignment when printing out attributes\n");
fprintf(stderr,"\t -c query run single query (slash commands too)\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 -d dbName specify database name\n");
fprintf(stderr,"\t -e echo the query sent to the backend\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 filename use file as a source of queries\n");
fprintf(stderr,"\t -F sep set the field separator (default is " ")\n"); fprintf(stderr, "\t -F sep set the field separator (default is " ")\n");
fprintf(stderr,"\t -h host set database server host\n"); fprintf(stderr, "\t -h host set database server host\n");
fprintf(stderr,"\t -H turn on html3.0 table output\n"); fprintf(stderr, "\t -H turn on html3.0 table output\n");
fprintf(stderr,"\t -l list available databases\n"); fprintf(stderr, "\t -l list available databases\n");
fprintf(stderr,"\t -n don't use readline library\n"); fprintf(stderr, "\t -n don't use readline library\n");
fprintf(stderr,"\t -o filename send output to filename or (|pipe)\n"); fprintf(stderr, "\t -o filename send output to filename or (|pipe)\n");
fprintf(stderr,"\t -p port set port number\n"); fprintf(stderr, "\t -p port set port number\n");
fprintf(stderr,"\t -q run quietly (no messages, no prompts)\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 step mode (prompts for each query)\n");
fprintf(stderr,"\t -S single line mode (i.e. query terminated by newline)\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 -t turn off printing of headings and row count\n");
fprintf(stderr,"\t -T html set html3.0 table command options (cf. -H)\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"); fprintf(stderr, "\t -x turn on expanded output (field names on left)\n");
exit(1); exit(1);
} }
/* /*
* slashUsage * slashUsage print out usage for the backslash commands
* print out usage for the backslash commands
*/ */
static char *on(bool f) static char *
on(bool f)
{ {
return f? "on": "off"; return f ? "on" : "off";
} }
static void static void
slashUsage(PsqlSettings *ps) slashUsage(PsqlSettings * ps)
{ {
fprintf(stderr,"\t \\? -- help\n"); fprintf(stderr, " \\? -- help\n");
fprintf(stderr,"\t \\a -- toggle field-alignment (currenty %s)\n", on(ps->opt.align)); fprintf(stderr, " \\a -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
fprintf(stderr,"\t \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption? ps->opt.caption: ""); fprintf(stderr, " \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption ? ps->opt.caption : "");
fprintf(stderr,"\t \\connect <dbname> -- connect to new database (currently '%s')\n", PQdb(ps->db)); fprintf(stderr, " \\connect <dbname> -- connect to new database (currently '%s')\n", PQdb(ps->db));
fprintf(stderr,"\t \\copy <dbname> -- copy table to/from a file\n"); fprintf(stderr, " \\copy <dbname> -- copy table to/from a file\n");
fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>,* for all\n"); fprintf(stderr, " \\d [<table>] -- list tables in database or columns in <table>, * for all\n");
fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>,\\E execute too\n"); fprintf(stderr, " \\e [<fname>] -- edit the current query buffer or <fname>, \\E execute too\n");
fprintf(stderr,"\t \\f [<sep>] -- change field separater (currently '%s')\n", ps->opt.fieldSep); fprintf(stderr, " \\f [<sep>] -- change field separater (currently '%s')\n", ps->opt.fieldSep);
fprintf(stderr,"\t \\g [<fname>] -- send query to backend [and place results in <fname>]\n"); fprintf(stderr, " \\g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]\n");
fprintf(stderr,"\t \\g |<cmd> -- send query to backend and pipe results into <cmd>\n"); fprintf(stderr, " \\h [<cmd>] -- help on syntax of sql commands, * for all commands\n");
fprintf(stderr,"\t \\h [<cmd>] -- help on syntax of sql commands, * for all commands\n"); fprintf(stderr, " \\H -- toggle html3 output (currently %s)\n", on(ps->opt.html3));
fprintf(stderr,"\t \\H -- toggle html3 output (currently %s)\n", on(ps->opt.html3)); fprintf(stderr, " \\i <fname> -- read and execute queries from filename\n");
fprintf(stderr,"\t \\i <fname> -- read and execute queries from filename\n"); fprintf(stderr, " \\l -- list all databases\n");
fprintf(stderr,"\t \\l -- list all databases\n"); fprintf(stderr, " \\m -- toggle monitor-like table display (currently %s)\n", on(ps->opt.standard));
fprintf(stderr,"\t \\m -- toggle monitor-like table display (currently %s)\n", on(ps->opt.standard)); fprintf(stderr, " \\o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe\n");
fprintf(stderr,"\t \\o [<fname>] -- send all query results to <fname> or stdout\n"); fprintf(stderr, " \\p -- print the current query buffer\n");
fprintf(stderr,"\t \\o |<cmd> -- pipe all query results through <cmd>\n"); fprintf(stderr, " \\q -- quit\n");
fprintf(stderr,"\t \\p -- print the current query buffer\n"); fprintf(stderr, " \\r -- reset(clear) the query buffer\n");
fprintf(stderr,"\t \\q -- quit\n"); fprintf(stderr, " \\s [<fname>] -- print history or save it in <fname>\n");
fprintf(stderr,"\t \\r -- reset(clear) the query buffer\n"); fprintf(stderr, " \\t -- toggle table headings and row count (currently %s)\n", on(ps->opt.header));
fprintf(stderr,"\t \\s [<fname>] -- print history or save it in <fname>\n"); fprintf(stderr, " \\T [<html>] -- set html3.0 <table ...> options (currently '%s')\n", ps->opt.tableOpt ? ps->opt.tableOpt : "");
fprintf(stderr,"\t \\t -- toggle table headings and row count (currently %s)\n", on(ps->opt.header)); fprintf(stderr, " \\x -- toggle expanded output (currently %s)\n", on(ps->opt.expanded));
fprintf(stderr,"\t \\T [<html>] -- set html3.0 <table ...> options (currently '%s')\n", ps->opt.tableOpt? ps->opt.tableOpt: ""); fprintf(stderr, " \\! [<cmd>] -- shell escape or command\n");
fprintf(stderr,"\t \\x -- toggle expanded output (currently %s)\n", on(ps->opt.expanded));
fprintf(stderr,"\t \\! [<cmd>] -- shell escape or command\n");
} }
static PGresult * static PGresult *
PSQLexec(PsqlSettings *ps, char *query) PSQLexec(PsqlSettings * ps, char *query)
{ {
PGresult *res = PQexec(ps->db, query); PGresult *res = PQexec(ps->db, query);
if (!res) if (!res)
fputs(PQerrorMessage(ps->db), stderr); fputs(PQerrorMessage(ps->db), stderr);
else else {
{ if (PQresultStatus(res) == PGRES_COMMAND_OK ||
if (PQresultStatus(res)==PGRES_COMMAND_OK || PQresultStatus(res) == PGRES_TUPLES_OK)
PQresultStatus(res)==PGRES_TUPLES_OK)
return res; return res;
if (!ps->quiet) if (!ps->quiet)
fputs(PQerrorMessage(ps->db), stderr); fputs(PQerrorMessage(ps->db), stderr);
...@@ -179,22 +179,20 @@ PSQLexec(PsqlSettings *ps, char *query) ...@@ -179,22 +179,20 @@ PSQLexec(PsqlSettings *ps, char *query)
/* /*
* listAllDbs * listAllDbs
* *
* list all the databases in the system * list all the databases in the system returns 0 if all went well
* returns 0 if all went well
* *
* *
*/ */
int int
listAllDbs(PsqlSettings *ps) listAllDbs(PsqlSettings * ps)
{ {
PGresult *results; PGresult *results;
char *query = "select * from pg_database;"; char *query = "select * from pg_database;";
if (!(results=PSQLexec(ps, query))) if (!(results = PSQLexec(ps, query)))
return 1; return 1;
else else {
{
PQprint(ps->queryFout, PQprint(ps->queryFout,
results, results,
&ps->opt); &ps->opt);
...@@ -204,13 +202,11 @@ listAllDbs(PsqlSettings *ps) ...@@ -204,13 +202,11 @@ listAllDbs(PsqlSettings *ps)
} }
/* /*
* * List The Database Tables returns 0 if all went well
* List The Database Tables
* returns 0 if all went well
* *
*/ */
int int
tableList (PsqlSettings *ps, bool deep_tablelist) tableList(PsqlSettings * ps, bool deep_tablelist)
{ {
char listbuf[256]; char listbuf[256];
int nColumns; int nColumns;
...@@ -221,70 +217,70 @@ tableList (PsqlSettings *ps, bool deep_tablelist) ...@@ -221,70 +217,70 @@ tableList (PsqlSettings *ps, bool deep_tablelist)
PGresult *res; PGresult *res;
listbuf[0] = '\0'; listbuf[0] = '\0';
strcat(listbuf,"SELECT usename, relname, relkind, relhasrules"); strcat(listbuf, "SELECT usename, relname, relkind, relhasrules");
strcat(listbuf," FROM pg_class, pg_user "); strcat(listbuf, " FROM pg_class, pg_user ");
strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') "); strcat(listbuf, "WHERE ( relkind = 'r' OR relkind = 'i') ");
strcat(listbuf," and relname !~ '^pg_'"); strcat(listbuf, " and relname !~ '^pg_'");
strcat(listbuf," and relname !~ '^Inv[0-9]+'"); strcat(listbuf, " and relname !~ '^Inv[0-9]+'");
/* the usesysid = relowner won't work on stock 1.0 dbs, need to /*
add in the int4oideq function */ * the usesysid = relowner won't work on stock 1.0 dbs, need to add in
strcat(listbuf," and usesysid = relowner"); * the int4oideq function
strcat(listbuf," ORDER BY relname "); */
if (!(res=PSQLexec(ps, listbuf))) strcat(listbuf, " and usesysid = relowner");
strcat(listbuf, " ORDER BY relname ");
if (!(res = PSQLexec(ps, listbuf)))
return -1; return -1;
/* first, print out the attribute names */ /* first, print out the attribute names */
nColumns = PQntuples(res); nColumns = PQntuples(res);
if (nColumns > 0) if (nColumns > 0) {
{ if (deep_tablelist) {
if ( deep_tablelist ) {
/* describe everything here */ /* describe everything here */
char **table; char **table;
table = (char**)malloc(nColumns * sizeof(char*)); table = (char **) malloc(nColumns * sizeof(char *));
if ( table == NULL ) if (table == NULL)
perror("malloc"); perror("malloc");
/* load table table */ /* load table table */
for (i=0; i < nColumns; i++) { for (i = 0; i < nColumns; i++) {
table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1); table[i] = (char *) malloc(PQgetlength(res, i, 1) * sizeof(char) + 1);
if ( table[i] == NULL ) if (table[i] == NULL)
perror("malloc"); perror("malloc");
strcpy(table[i],PQgetvalue(res,i,1)); strcpy(table[i], PQgetvalue(res, i, 1));
} }
PQclear(res); PQclear(res);
for (i=0; i < nColumns; i++) { for (i = 0; i < nColumns; i++) {
tableDesc(ps, table[i]); tableDesc(ps, table[i]);
} }
free(table); free(table);
} } else {
else {
/* Display the information */ /* Display the information */
printf ("\nDatabase = %s\n", PQdb(ps->db)); printf("\nDatabase = %s\n", PQdb(ps->db));
printf (" +------------------+----------------------------------+----------+\n"); printf(" +------------------+----------------------------------+----------+\n");
printf (" | Owner | Relation | Type |\n"); printf(" | Owner | Relation | Type |\n");
printf (" +------------------+----------------------------------+----------+\n"); printf(" +------------------+----------------------------------+----------+\n");
/* next, print out the instances */ /* next, print out the instances */
for (i=0; i < PQntuples(res); i++) { for (i = 0; i < PQntuples(res); i++) {
printf (" | %-16.16s", PQgetvalue(res,i,0)); printf(" | %-16.16s", PQgetvalue(res, i, 0));
printf (" | %-32.32s | ", PQgetvalue(res,i,1)); printf(" | %-32.32s | ", PQgetvalue(res, i, 1));
rk = PQgetvalue(res,i,2); rk = PQgetvalue(res, i, 2);
rr = PQgetvalue(res,i,3); rr = PQgetvalue(res, i, 3);
if (strcmp(rk, "r") == 0) if (strcmp(rk, "r") == 0)
printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" ); printf("%-8.8s |", (rr[0] == 't') ? "view?" : "table");
else else
printf ("%-8.8s |", "index"); printf("%-8.8s |", "index");
printf("\n"); printf("\n");
} }
printf (" +------------------+----------------------------------+----------+\n"); printf(" +------------------+----------------------------------+----------+\n");
PQclear(res); PQclear(res);
} }
return (0); return (0);
} else { } else {
fprintf (stderr, "Couldn't find any tables!\n"); fprintf(stderr, "Couldn't find any tables!\n");
return (-1); return (-1);
} }
} }
...@@ -292,13 +288,12 @@ tableList (PsqlSettings *ps, bool deep_tablelist) ...@@ -292,13 +288,12 @@ tableList (PsqlSettings *ps, bool deep_tablelist)
/* /*
* Describe a table * Describe a table
* *
* Describe the columns in a database table. * Describe the columns in a database table. returns 0 if all went well
* returns 0 if all went well
* *
* *
*/ */
int int
tableDesc (PsqlSettings *ps, char *table) tableDesc(PsqlSettings * ps, char *table)
{ {
char descbuf[256]; char descbuf[256];
int nColumns; int nColumns;
...@@ -311,97 +306,93 @@ tableDesc (PsqlSettings *ps, char *table) ...@@ -311,97 +306,93 @@ tableDesc (PsqlSettings *ps, char *table)
/* Build the query */ /* Build the query */
descbuf[0] = '\0'; descbuf[0] = '\0';
strcat(descbuf,"SELECT a.attnum, a.attname, t.typname, a.attlen"); 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, " FROM pg_class c, pg_attribute a, pg_type t ");
strcat(descbuf," WHERE c.relname = '"); strcat(descbuf, " WHERE c.relname = '");
strcat(descbuf,table); strcat(descbuf, table);
strcat(descbuf,"'"); strcat(descbuf, "'");
strcat(descbuf," and a.attnum > 0 "); strcat(descbuf, " and a.attnum > 0 ");
strcat(descbuf," and a.attrelid = c.oid "); strcat(descbuf, " and a.attrelid = c.oid ");
strcat(descbuf," and a.atttypid = t.oid "); strcat(descbuf, " and a.atttypid = t.oid ");
strcat(descbuf," ORDER BY attnum "); strcat(descbuf, " ORDER BY attnum ");
if (!(res = PSQLexec(ps, descbuf))) if (!(res = PSQLexec(ps, descbuf)))
return -1; return -1;
/* first, print out the attribute names */ /* first, print out the attribute names */
nColumns = PQntuples(res); nColumns = PQntuples(res);
if (nColumns > 0) if (nColumns > 0) {
{
/* /*
** Display the information * * Display the information
*/ */
printf ("\nTable = %s\n", table); printf("\nTable = %s\n", table);
printf ("+----------------------------------+----------------------------------+-------+\n"); printf("+----------------------------------+----------------------------------+-------+\n");
printf ("| Field | Type | Length|\n"); printf("| Field | Type | Length|\n");
printf ("+----------------------------------+----------------------------------+-------+\n"); printf("+----------------------------------+----------------------------------+-------+\n");
/* next, print out the instances */ /* next, print out the instances */
for (i=0; i < PQntuples(res); i++) { for (i = 0; i < PQntuples(res); i++) {
printf ("| %-32.32s | ", PQgetvalue(res,i,1)); printf("| %-32.32s | ", PQgetvalue(res, i, 1));
rtype = PQgetvalue(res,i,2); rtype = PQgetvalue(res, i, 2);
rsize = atoi(PQgetvalue(res,i,3)); rsize = atoi(PQgetvalue(res, i, 3));
if (strcmp(rtype, "text") == 0) { if (strcmp(rtype, "text") == 0) {
printf ("%-32.32s |", rtype); printf("%-32.32s |", rtype);
printf ("%6s |", "var" ); printf("%6s |", "var");
} } else if (strcmp(rtype, "bpchar") == 0) {
else if (strcmp(rtype, "bpchar") == 0) { printf("%-32.32s |", "(bp)char");
printf ("%-32.32s |", "(bp)char"); printf("%6i |", rsize > 0 ? rsize - 4 : 0);
printf ("%6i |", rsize > 0 ? rsize - 4 : 0 ); } else if (strcmp(rtype, "varchar") == 0) {
} printf("%-32.32s |", rtype);
else if (strcmp(rtype, "varchar") == 0) { printf("%6i |", rsize > 0 ? rsize - 4 : 0);
printf ("%-32.32s |", rtype); } else {
printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
}
else {
/* array types start with an underscore */ /* array types start with an underscore */
if (rtype[0] != '_') if (rtype[0] != '_')
printf ("%-32.32s |", rtype); printf("%-32.32s |", rtype);
else { else {
char *newname; char *newname;
newname = malloc(strlen(rtype) + 2); newname = malloc(strlen(rtype) + 2);
strcpy(newname, rtype+1); strcpy(newname, rtype + 1);
strcat(newname, "[]"); strcat(newname, "[]");
printf ("%-32.32s |", newname); printf("%-32.32s |", newname);
free(newname); free(newname);
} }
if (rsize > 0) if (rsize > 0)
printf ("%6i |", rsize); printf("%6i |", rsize);
else else
printf ("%6s |", "var"); printf("%6s |", "var");
} }
printf("\n"); printf("\n");
} }
printf ("+----------------------------------+----------------------------------+-------+\n"); printf("+----------------------------------+----------------------------------+-------+\n");
PQclear(res); PQclear(res);
return (0); return (0);
} else { } else {
fprintf (stderr, "Couldn't find table %s!\n", table); fprintf(stderr, "Couldn't find table %s!\n", table);
return (-1); return (-1);
} }
} }
typedef char *(*READ_ROUTINE)(char *prompt, FILE *source); typedef char *(*READ_ROUTINE) (char *prompt, FILE * source);
/* gets_noreadline prompt source /*
gets a line of input without calling readline, the source is ignored * gets_noreadline prompt source gets a line of input without calling
*/ * readline, the source is ignored
*/
char * char *
gets_noreadline(char *prompt, FILE *source) gets_noreadline(char *prompt, FILE * source)
{ {
fputs(prompt, stdout); fputs(prompt, stdout);
fflush(stdout); fflush(stdout);
return(gets_fromFile(prompt,stdin)); return (gets_fromFile(prompt, stdin));
} }
/* /*
* gets_readline prompt source * gets_readline prompt source the routine to get input from GNU readline(),
* the routine to get input from GNU readline(), the source is ignored * the source is ignored the prompt argument is used as the prompting string
* the prompt argument is used as the prompting string
*/ */
char * char *
gets_readline(char *prompt, FILE *source) gets_readline(char *prompt, FILE * source)
{ {
return (readline(prompt)); return (readline(prompt));
} }
...@@ -409,40 +400,38 @@ gets_readline(char *prompt, FILE *source) ...@@ -409,40 +400,38 @@ gets_readline(char *prompt, FILE *source)
/* /*
* gets_fromFile prompt source * gets_fromFile prompt source
* *
* the routine to read from a file, the prompt argument is ignored * the routine to read from a file, the prompt argument is ignored the source
* the source argument is a FILE * * argument is a FILE *
*/ */
char * char *
gets_fromFile(char *prompt, FILE *source) gets_fromFile(char *prompt, FILE * source)
{ {
char *line; char *line;
int len; int len;
line = malloc(MAX_QUERY_BUFFER+1); line = malloc(MAX_QUERY_BUFFER + 1);
/* read up to MAX_QUERY_BUFFER characters */ /* read up to MAX_QUERY_BUFFER characters */
if (fgets(line, MAX_QUERY_BUFFER, source) == NULL) if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
return NULL; return NULL;
line[MAX_QUERY_BUFFER-1] = '\0'; line[MAX_QUERY_BUFFER - 1] = '\0';
len = strlen(line); len = strlen(line);
if (len == MAX_QUERY_BUFFER) if (len == MAX_QUERY_BUFFER) {
{
fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n", fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n",
MAX_QUERY_BUFFER); MAX_QUERY_BUFFER);
} }
return line; return line;
} }
/* /*
* SendQuery: send the query string to the backend * SendQuery: send the query string to the backend return *success_p = 1 if
* return *success_p = 1 if the query executed successfully * the query executed successfully returns *success_p = 0 otherwise
* returns *success_p = 0 otherwise
*/ */
void void
SendQuery(bool *success_p, PsqlSettings *settings, const char *query, SendQuery(bool * success_p, PsqlSettings * settings, const char *query,
const bool copy_in, const bool copy_out, FILE *copystream) { const bool copy_in, const bool copy_out, FILE * copystream)
{
PGresult *results; PGresult *results;
PGnotify *notify; PGnotify *notify;
...@@ -452,34 +441,33 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query, ...@@ -452,34 +441,33 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
"*****************************************\n"); "*****************************************\n");
if (settings->echoQuery || settings->singleStep) { if (settings->echoQuery || settings->singleStep) {
fprintf(stderr,"QUERY: %s\n",query); fprintf(stderr, "QUERY: %s\n", query);
fflush(stderr); fflush(stderr);
} }
if (settings->singleStep) { if (settings->singleStep) {
fprintf(stdout, "\n**************************************" fprintf(stdout, "\n**************************************"
"*****************************************\n"); "*****************************************\n");
fflush(stdout); fflush(stdout);
printf("\npress return to continue ..\n"); printf("\npress return to continue ..\n");
gets_fromFile("",stdin); gets_fromFile("", stdin);
} }
results = PQexec(settings->db, query); results = PQexec(settings->db, query);
if (results == NULL) { if (results == NULL) {
fprintf(stderr,"%s",PQerrorMessage(settings->db)); fprintf(stderr, "%s", PQerrorMessage(settings->db));
*success_p = false; *success_p = false;
} else { } else {
switch (PQresultStatus(results)) { switch (PQresultStatus(results)) {
case PGRES_TUPLES_OK: case PGRES_TUPLES_OK:
if (settings->gfname) { if (settings->gfname) {
PsqlSettings ps=*settings; PsqlSettings ps = *settings;
FILE *fp; FILE *fp;
ps.queryFout=stdout; ps.queryFout = stdout;
fp=setFout(&ps, settings->gfname); fp = setFout(&ps, settings->gfname);
if (!fp || fp==stdout) { if (!fp || fp == stdout) {
*success_p = false; *success_p = false;
break; break;
} else *success_p = true; } else
*success_p = true;
PQprint(fp, PQprint(fp,
results, results,
&(settings->opt)); &(settings->opt));
...@@ -487,7 +475,7 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query, ...@@ -487,7 +475,7 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
pclose(fp); pclose(fp);
else else
fclose(fp); fclose(fp);
settings->gfname=NULL; settings->gfname = NULL;
break; break;
} else { } else {
*success_p = true; *success_p = true;
...@@ -504,7 +492,7 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query, ...@@ -504,7 +492,7 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
case PGRES_COMMAND_OK: case PGRES_COMMAND_OK:
*success_p = true; *success_p = true;
if (!settings->quiet) if (!settings->quiet)
fprintf(stdout,"%s\n", PQcmdStatus(results)); fprintf(stdout, "%s\n", PQcmdStatus(results));
break; break;
case PGRES_COPY_OUT: case PGRES_COPY_OUT:
*success_p = true; *success_p = true;
...@@ -537,10 +525,17 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query, ...@@ -537,10 +525,17 @@ SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE: case PGRES_BAD_RESPONSE:
*success_p = false; *success_p = false;
fprintf(stderr,"%s",PQerrorMessage(settings->db)); fprintf(stderr, "%s", PQerrorMessage(settings->db));
break; break;
} }
if (PQstatus(settings->db) == CONNECTION_BAD) {
fprintf(stderr,
"We have lost the connection to the backend, so "
"further processing is impossible. "
"Terminating.\n");
exit(2); /* we are out'ta here */
}
/* check for asynchronous returns */ /* check for asynchronous returns */
notify = PQnotifies(settings->db); notify = PQnotifies(settings->db);
if (notify) { if (notify) {
...@@ -562,9 +557,8 @@ editFile(char *fname) ...@@ -562,9 +557,8 @@ editFile(char *fname)
editorName = getenv("EDITOR"); editorName = getenv("EDITOR");
if (!editorName) if (!editorName)
editorName = DEFAULT_EDITOR; editorName = DEFAULT_EDITOR;
sys=malloc(strlen(editorName)+strlen(fname)+32+1); sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
if (!sys) if (!sys) {
{
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
...@@ -574,9 +568,9 @@ editFile(char *fname) ...@@ -574,9 +568,9 @@ editFile(char *fname)
} }
static bool static bool
toggle(PsqlSettings *settings, bool *sw, char *msg) toggle(PsqlSettings * settings, bool * sw, char *msg)
{ {
*sw= !*sw; *sw = !*sw;
if (!settings->quiet) if (!settings->quiet)
fprintf(stderr, "turned %s %s\n", on(*sw), msg); fprintf(stderr, "turned %s %s\n", on(*sw), msg);
return *sw; return *sw;
...@@ -585,20 +579,22 @@ toggle(PsqlSettings *settings, bool *sw, char *msg) ...@@ -585,20 +579,22 @@ toggle(PsqlSettings *settings, bool *sw, char *msg)
static void static void
unescape(char *dest, const char *source) { unescape(char *dest, const char *source)
/*----------------------------------------------------------------------------- {
/*-----------------------------------------------------------------------------
Return as the string <dest> the value of string <source> with escape Return as the string <dest> the value of string <source> with escape
sequences turned into the bytes they represent. sequences turned into the bytes they represent.
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
char *p; char *p;
bool esc; /* Last character we saw was the escape character (/) */ bool esc; /* Last character we saw was the escape
* character (/) */
esc = false; /* Haven't seen escape character yet */ esc = false; /* Haven't seen escape character yet */
for (p = (char *)source; *p; p++) { for (p = (char *) source; *p; p++) {
char c; /* Our output character */ char c; /* Our output character */
if (esc) { if (esc) {
switch(*p) { switch (*p) {
case 'n': case 'n':
c = '\n'; c = '\n';
break; break;
...@@ -618,15 +614,16 @@ unescape(char *dest, const char *source) { ...@@ -618,15 +614,16 @@ unescape(char *dest, const char *source) {
c = *p; c = *p;
} }
esc = false; esc = false;
} else } else if (*p == '\\') {
if (*p == '\\') {
esc = true; esc = true;
c = ' '; /* meaningless, but compiler doesn't know that */ c = ' '; /* meaningless, but compiler doesn't know
* that */
} else { } else {
c = *p; c = *p;
esc = false; esc = false;
} }
if (!esc) *dest ++= c; if (!esc)
*dest++ = c;
} }
*dest = '\0'; /* Terminating null character */ *dest = '\0'; /* Terminating null character */
} }
...@@ -636,16 +633,18 @@ unescape(char *dest, const char *source) { ...@@ -636,16 +633,18 @@ unescape(char *dest, const char *source) {
static void static void
parse_slash_copy(const char *args, char *table, const int table_len, parse_slash_copy(const char *args, char *table, const int table_len,
char *file, const int file_len, char *file, const int file_len,
bool *from_p, bool *error_p) { bool * from_p, bool * error_p)
{
char work_args[200]; char work_args[200];
/* A copy of the \copy command arguments, except that we modify it /*
as we parse to suit our parsing needs. * A copy of the \copy command arguments, except that we modify it as we
* parse to suit our parsing needs.
*/ */
char *table_tok, *fromto_tok; char *table_tok, *fromto_tok;
strncpy(work_args, args, sizeof(work_args)); strncpy(work_args, args, sizeof(work_args));
work_args[sizeof(work_args)-1] = '\0'; work_args[sizeof(work_args) - 1] = '\0';
*error_p = false; /* initial assumption */ *error_p = false; /* initial assumption */
...@@ -655,15 +654,17 @@ parse_slash_copy(const char *args, char *table, const int table_len, ...@@ -655,15 +654,17 @@ parse_slash_copy(const char *args, char *table, const int table_len,
*error_p = true; *error_p = true;
} else { } else {
strncpy(table, table_tok, table_len); strncpy(table, table_tok, table_len);
file[table_len-1] = '\0'; file[table_len - 1] = '\0';
fromto_tok = strtok(NULL, " "); fromto_tok = strtok(NULL, " ");
if (fromto_tok == NULL) { if (fromto_tok == NULL) {
fprintf(stderr, "'FROM' or 'TO' must follow table name.\n"); fprintf(stderr, "'FROM' or 'TO' must follow table name.\n");
*error_p = true; *error_p = true;
} else { } else {
if (strcasecmp(fromto_tok, "from") == 0) *from_p = true; if (strcasecmp(fromto_tok, "from") == 0)
else if (strcasecmp(fromto_tok, "to") == 0) *from_p = false; *from_p = true;
else if (strcasecmp(fromto_tok, "to") == 0)
*from_p = false;
else { else {
fprintf(stderr, fprintf(stderr,
"Unrecognized token found where " "Unrecognized token found where "
...@@ -681,7 +682,7 @@ parse_slash_copy(const char *args, char *table, const int table_len, ...@@ -681,7 +682,7 @@ parse_slash_copy(const char *args, char *table, const int table_len,
*error_p = true; *error_p = true;
} else { } else {
strncpy(file, file_tok, file_len); strncpy(file, file_tok, file_len);
file[file_len-1] = '\0'; file[file_len - 1] = '\0';
if (strtok(NULL, " ") != NULL) { if (strtok(NULL, " ") != NULL) {
fprintf(stderr, fprintf(stderr,
"You have extra tokens after the filename.\n"); "You have extra tokens after the filename.\n");
...@@ -696,8 +697,9 @@ parse_slash_copy(const char *args, char *table, const int table_len, ...@@ -696,8 +697,9 @@ parse_slash_copy(const char *args, char *table, const int table_len,
static void static void
do_copy(const char *args, PsqlSettings *settings) { do_copy(const char *args, PsqlSettings * settings)
/*--------------------------------------------------------------------------- {
/*---------------------------------------------------------------------------
Execute a \copy command (frontend copy). We have to open a file, then Execute a \copy command (frontend copy). We have to open a file, then
submit a COPY query to the backend and either feed it data from the submit a COPY query to the backend and either feed it data from the
file or route its response into the file. file or route its response into the file.
...@@ -705,14 +707,14 @@ do_copy(const char *args, PsqlSettings *settings) { ...@@ -705,14 +707,14 @@ do_copy(const char *args, PsqlSettings *settings) {
We do a text copy with default (tab) column delimiters. Some day, we We do a text copy with default (tab) column delimiters. Some day, we
should do all the things a backend copy can do. should do all the things a backend copy can do.
----------------------------------------------------------------------------*/ ----------------------------------------------------------------------------*/
char query[200]; char query[200];
/* The COPY command we send to the back end */ /* The COPY command we send to the back end */
bool from; bool from;
/* The direction of the copy is from a file to a table. */ /* The direction of the copy is from a file to a table. */
char file[MAXPATHLEN+1]; char file[MAXPATHLEN + 1];
/* The pathname of the file from/to which we copy */ /* The pathname of the file from/to which we copy */
char table[NAMEDATALEN+1]; char table[NAMEDATALEN + 1];
/* The name of the table from/to which we copy */ /* The name of the table from/to which we copy */
bool syntax_error; bool syntax_error;
/* The \c command has invalid syntax */ /* The \c command has invalid syntax */
...@@ -756,20 +758,21 @@ do_copy(const char *args, PsqlSettings *settings) { ...@@ -756,20 +758,21 @@ do_copy(const char *args, PsqlSettings *settings) {
static void static void
do_connect(const char *new_dbname, PsqlSettings *settings) { do_connect(const char *new_dbname, PsqlSettings * settings)
{
char *dbname=PQdb(settings->db); char *dbname = PQdb(settings->db);
if (!new_dbname) if (!new_dbname)
fprintf(stderr,"\\connect must be followed by a database name\n"); fprintf(stderr, "\\connect must be followed by a database name\n");
else { else {
PGconn *olddb=settings->db; PGconn *olddb = settings->db;
printf("closing connection to database: %s\n", dbname); printf("closing connection to database: %s\n", dbname);
settings->db = PQsetdb(PQhost(olddb), PQport(olddb), settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
NULL, NULL, new_dbname); NULL, NULL, new_dbname);
printf("connecting to new database: %s\n", new_dbname); printf("connecting to new database: %s\n", new_dbname);
if (PQstatus(settings->db) == CONNECTION_BAD) { if (PQstatus(settings->db) == CONNECTION_BAD) {
fprintf(stderr,"%s\n", PQerrorMessage(settings->db)); fprintf(stderr, "%s\n", PQerrorMessage(settings->db));
printf("reconnecting to %s\n", dbname); printf("reconnecting to %s\n", dbname);
settings->db = PQsetdb(PQhost(olddb), PQport(olddb), settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
NULL, NULL, dbname); NULL, NULL, dbname);
...@@ -782,14 +785,15 @@ do_connect(const char *new_dbname, PsqlSettings *settings) { ...@@ -782,14 +785,15 @@ do_connect(const char *new_dbname, PsqlSettings *settings) {
PQfinish(olddb); PQfinish(olddb);
free(settings->prompt); free(settings->prompt);
settings->prompt = malloc(strlen(PQdb(settings->db)) + 10); settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
sprintf(settings->prompt,"%s=> ", PQdb(settings->db)); sprintf(settings->prompt, "%s=> ", PQdb(settings->db));
} }
} }
} }
static void static void
do_edit(const char *filename_arg, char *query, int *retcode_p) { do_edit(const char *filename_arg, char *query, int *status_p)
{
int fd; int fd;
char tmp[64]; char tmp[64];
...@@ -799,53 +803,55 @@ do_edit(const char *filename_arg, char *query, int *retcode_p) { ...@@ -799,53 +803,55 @@ do_edit(const char *filename_arg, char *query, int *retcode_p) {
bool error; bool error;
if (filename_arg) { if (filename_arg) {
fname=(char *)filename_arg; fname = (char *) filename_arg;
error=false; error = false;
} else { } else {
sprintf(tmp, "/tmp/psql.%ld.%ld", (long)geteuid(), (long)getpid()); sprintf(tmp, "/tmp/psql.%ld.%ld", (long) geteuid(), (long) getpid());
fname=tmp; fname = tmp;
unlink(tmp); unlink(tmp);
if (ql > 0) { if (ql > 0) {
if ((fd=open(tmp, O_EXCL|O_CREAT|O_WRONLY, 0600)) == -1) { if ((fd = open(tmp, O_EXCL | O_CREAT | O_WRONLY, 0600)) == -1) {
perror(tmp); perror(tmp);
error=true; error = true;
} else { } else {
if (query[ql-1] != '\n') if (query[ql - 1] != '\n')
strcat(query, "\n"); strcat(query, "\n");
if (write(fd, query, ql) != ql) { if (write(fd, query, ql) != ql) {
perror(tmp); perror(tmp);
close(fd); close(fd);
unlink(tmp); unlink(tmp);
error=true; error = true;
} else error=false; } else
error = false;
close(fd); close(fd);
} }
} else error=false; } else
error = false;
} }
if (error) *retcode_p=1; if (error)
*status_p = 1;
else { else {
editFile(fname); editFile(fname);
if ((fd=open(fname, O_RDONLY))==-1) { if ((fd = open(fname, O_RDONLY)) == -1) {
perror(fname); perror(fname);
if (!filename_arg) if (!filename_arg)
unlink(fname); unlink(fname);
*retcode_p=1; *status_p = 1;
} else { } else {
if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1) { if ((cc = read(fd, query, MAX_QUERY_BUFFER)) == -1) {
perror(fname); perror(fname);
close(fd); close(fd);
if (!filename_arg) if (!filename_arg)
unlink(fname); unlink(fname);
*retcode_p=1; *status_p = 1;
} else { } else {
query[cc]='\0'; query[cc] = '\0';
close(fd); close(fd);
if (!filename_arg) if (!filename_arg)
unlink(fname); unlink(fname);
rightTrim(query); rightTrim(query);
if (query[strlen(query)-1]==';') *retcode_p=0; *status_p = 3;
else *retcode_p=1;
} }
} }
} }
...@@ -854,18 +860,20 @@ do_edit(const char *filename_arg, char *query, int *retcode_p) { ...@@ -854,18 +860,20 @@ do_edit(const char *filename_arg, char *query, int *retcode_p) {
static void static void
do_help(const char *topic) { do_help(const char *topic)
{
if (!topic) { if (!topic) {
char left_center_right; /* Which column we're displaying */ char left_center_right; /* Which column we're
* displaying */
int i; /* Index into QL_HELP[] */ int i; /* Index into QL_HELP[] */
printf("type \\h <cmd> where <cmd> is one of the following:\n"); printf("type \\h <cmd> where <cmd> is one of the following:\n");
left_center_right = 'L'; /* Start with left column */ left_center_right = 'L';/* Start with left column */
i = 0; i = 0;
while (QL_HELP[i].cmd != NULL) { while (QL_HELP[i].cmd != NULL) {
switch(left_center_right) { switch (left_center_right) {
case 'L': case 'L':
printf(" %-25s", QL_HELP[i].cmd); printf(" %-25s", QL_HELP[i].cmd);
left_center_right = 'C'; left_center_right = 'C';
...@@ -881,18 +889,19 @@ do_help(const char *topic) { ...@@ -881,18 +889,19 @@ do_help(const char *topic) {
}; };
i++; i++;
} }
if (left_center_right != 'L') puts("\n"); if (left_center_right != 'L')
puts("\n");
printf("type \\h * for a complete description of all commands\n"); printf("type \\h * for a complete description of all commands\n");
} else { } else {
int i; /* Index into QL_HELP[] */ int i; /* Index into QL_HELP[] */
bool help_found; /* We found the help he asked for */ bool help_found; /* We found the help he asked for */
help_found = false; /* Haven't found it yet */ help_found = false; /* Haven't found it yet */
for (i=0; QL_HELP[i].cmd; i++) { for (i = 0; QL_HELP[i].cmd; i++) {
if (strcmp(QL_HELP[i].cmd, topic) == 0 || if (strcmp(QL_HELP[i].cmd, topic) == 0 ||
strcmp(topic, "*") == 0 ) { strcmp(topic, "*") == 0) {
help_found = true; help_found = true;
printf("Command: %s\n",QL_HELP[i].cmd); printf("Command: %s\n", QL_HELP[i].cmd);
printf("Description: %s\n", QL_HELP[i].help); printf("Description: %s\n", QL_HELP[i].help);
printf("Syntax:\n"); printf("Syntax:\n");
printf("%s\n", QL_HELP[i].syntax); printf("%s\n", QL_HELP[i].syntax);
...@@ -908,7 +917,8 @@ do_help(const char *topic) { ...@@ -908,7 +917,8 @@ do_help(const char *topic) {
static void static void
do_shell(const char *command) { do_shell(const char *command)
{
if (!command) { if (!command) {
char *sys; char *sys;
...@@ -917,79 +927,83 @@ do_shell(const char *command) { ...@@ -917,79 +927,83 @@ do_shell(const char *command) {
shellName = getenv("SHELL"); shellName = getenv("SHELL");
if (shellName == NULL) if (shellName == NULL)
shellName = DEFAULT_SHELL; shellName = DEFAULT_SHELL;
sys = malloc(strlen(shellName)+16); sys = malloc(strlen(shellName) + 16);
if (!sys) { if (!sys) {
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
sprintf(sys,"exec %s", shellName); sprintf(sys, "exec %s", shellName);
system(sys); system(sys);
free(sys); free(sys);
} else system(command); } else
system(command);
} }
/* /*
HandleSlashCmds: * HandleSlashCmds:
*
Handles all the different commands that start with \ * Handles all the different commands that start with \ db_ptr is a pointer to
db_ptr is a pointer to the TgDb* structure * the TgDb* structure line is the current input line prompt_ptr is a pointer
line is the current input line * to the prompt string, a pointer is used because the prompt can be used
prompt_ptr is a pointer to the prompt string, * with a connection to a new database returns a status: 0 - send currently
a pointer is used because the prompt can be used with * constructed query to backend (i.e. we got a \g) 1 - skip processing of
a connection to a new database * this line, continue building up query 2 - terminate processing of this
returns a status: * query entirely, 3 - new query supplied by edit
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 int
HandleSlashCmds(PsqlSettings *settings, HandleSlashCmds(PsqlSettings * settings,
char *line, char *line,
char *query) char *query)
{ {
int status = 1; int status = 1;
char *optarg; char *optarg;
/* Pointer inside the <cmd> string to the argument of the slash /*
command, assuming it is a one-character slash command. If it's * Pointer inside the <cmd> string to the argument of the slash command,
not a one-character command, this is meaningless. * assuming it is a one-character slash command. If it's not a
* one-character command, this is meaningless.
*/ */
char *optarg2; char *optarg2;
/* Pointer inside the <cmd> string to the argument of the slash /*
command assuming it's not a one-character command. If it's a * Pointer inside the <cmd> string to the argument of the slash command
one-character command, this is meaningless. * assuming it's not a one-character command. If it's a one-character
* command, this is meaningless.
*/ */
char *cmd; char *cmd;
/* String: value of the slash command, less the slash and with escape /*
sequences decoded. * String: value of the slash command, less the slash and with escape
* sequences decoded.
*/ */
int blank_loc; int blank_loc;
/* Offset within <cmd> of first blank */ /* Offset within <cmd> of first blank */
cmd = malloc(strlen(line)); /* unescaping better not make string grow. */ cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
unescape(cmd, line+1); /* sets cmd string */ unescape(cmd, line + 1); /* sets cmd string */
/* Originally, there were just single character commands. Now, /*
we define some longer, friendly commands, but we have to keep * Originally, there were just single character commands. Now, we define
the old single character commands too. \c used to be what * some longer, friendly commands, but we have to keep the old single
\connect is now. Complicating matters is the fact that with * character commands too. \c used to be what \connect is now.
the single-character commands, you can start the argument * Complicating matters is the fact that with the single-character
right after the single character, so "\copy" would mean * commands, you can start the argument right after the single character,
"connect to database named 'opy'". * so "\copy" would mean "connect to database named 'opy'".
*/ */
if (strlen(cmd) > 1) optarg = cmd+1 + strspn(cmd+1, " \t"); if (strlen(cmd) > 1)
else optarg = NULL; optarg = cmd + 1 + strspn(cmd + 1, " \t");
else
optarg = NULL;
blank_loc = strcspn(cmd, " \t"); blank_loc = strcspn(cmd, " \t");
if (blank_loc == 0) optarg2 = NULL; if (blank_loc == 0)
else optarg2 = cmd + blank_loc + strspn(cmd+blank_loc, " \t"); optarg2 = NULL;
else
optarg2 = cmd + blank_loc + strspn(cmd + blank_loc, " \t");
switch (cmd[0]) switch (cmd[0]) {
{
case 'a': /* toggles to align fields on output */ case 'a': /* toggles to align fields on output */
toggle(settings, &settings->opt.align, "field alignment"); toggle(settings, &settings->opt.align, "field alignment");
break; break;
...@@ -997,15 +1011,13 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -997,15 +1011,13 @@ HandleSlashCmds(PsqlSettings *settings,
if (settings->opt.caption) if (settings->opt.caption)
free(settings->opt.caption); free(settings->opt.caption);
if (!optarg) if (!optarg)
settings->opt.caption=NULL; settings->opt.caption = NULL;
else else if (!(settings->opt.caption = strdup(optarg))) {
if (!(settings->opt.caption=strdup(optarg)))
{
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
break; break;
case 'c': { case 'c':{
if (strncmp(cmd, "copy ", strlen("copy ")) == 0) if (strncmp(cmd, "copy ", strlen("copy ")) == 0)
do_copy(optarg2, settings); do_copy(optarg2, settings);
else if (strncmp(cmd, "connect ", strlen("connect ")) == 0) else if (strncmp(cmd, "connect ", strlen("connect ")) == 0)
...@@ -1019,11 +1031,10 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1019,11 +1031,10 @@ HandleSlashCmds(PsqlSettings *settings,
tableList(settings, 0); tableList(settings, 0);
break; break;
} }
if (strcmp(optarg, "*") == 0 ) { if (strcmp(optarg, "*") == 0) {
tableList(settings, 0); tableList(settings, 0);
tableList(settings, 1); tableList(settings, 1);
} } else {
else {
tableDesc(settings, optarg); tableDesc(settings, optarg);
} }
break; break;
...@@ -1037,31 +1048,26 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1037,31 +1048,26 @@ HandleSlashCmds(PsqlSettings *settings,
FILE *fd; FILE *fd;
static char *lastfile; static char *lastfile;
struct stat st, st2; struct stat st, st2;
if (optarg) if (optarg) {
{
if (lastfile) if (lastfile)
free(lastfile); free(lastfile);
lastfile=malloc(strlen(optarg+1)); lastfile = malloc(strlen(optarg + 1));
if (!lastfile) if (!lastfile) {
{
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
strcpy(lastfile, optarg); strcpy(lastfile, optarg);
} else if (!lastfile) } else if (!lastfile) {
{ fprintf(stderr, "\\r must be followed by a file name initially\n");
fprintf(stderr,"\\r must be followed by a file name initially\n");
break; break;
} }
stat(lastfile, &st); stat(lastfile, &st);
editFile(lastfile); editFile(lastfile);
if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL)) if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL)) {
{
perror(lastfile); perror(lastfile);
break; break;
} }
if (st2.st_mtime==st.st_mtime) if (st2.st_mtime == st.st_mtime) {
{
if (!settings->quiet) if (!settings->quiet)
fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile); fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
fclose(fd); fclose(fd);
...@@ -1073,13 +1079,12 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1073,13 +1079,12 @@ HandleSlashCmds(PsqlSettings *settings,
} }
case 'f': case 'f':
{ {
char *fs=DEFAULT_FIELD_SEP; char *fs = DEFAULT_FIELD_SEP;
if (optarg) if (optarg)
fs=optarg; fs = optarg;
if (settings->opt.fieldSep); if (settings->opt.fieldSep);
free(settings->opt.fieldSep); free(settings->opt.fieldSep);
if (!(settings->opt.fieldSep=strdup(fs))) if (!(settings->opt.fieldSep = strdup(fs))) {
{
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
...@@ -1101,13 +1106,11 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1101,13 +1106,11 @@ HandleSlashCmds(PsqlSettings *settings,
FILE *fd; FILE *fd;
if (!optarg) { if (!optarg) {
fprintf(stderr,"\\i must be followed by a file name\n"); fprintf(stderr, "\\i must be followed by a file name\n");
break; break;
} }
if ((fd = fopen(optarg, "r")) == NULL) {
if ((fd = fopen(optarg, "r")) == NULL) fprintf(stderr, "file named %s could not be opened\n", optarg);
{
fprintf(stderr,"file named %s could not be opened\n",optarg);
break; break;
} }
MainLoop(settings, fd); MainLoop(settings, fd);
...@@ -1125,8 +1128,7 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1125,8 +1128,7 @@ HandleSlashCmds(PsqlSettings *settings,
setFout(settings, optarg); setFout(settings, optarg);
break; break;
case 'p': case 'p':
if (query) if (query) {
{
fputs(query, stdout); fputs(query, stdout);
fputc('\n', stdout); fputc('\n', stdout);
} }
...@@ -1135,29 +1137,27 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1135,29 +1137,27 @@ HandleSlashCmds(PsqlSettings *settings,
status = 2; status = 2;
break; break;
case 'r': /* reset(clear) the buffer */ case 'r': /* reset(clear) the buffer */
query[0]='\0'; query[0] = '\0';
if (!settings->quiet) if (!settings->quiet)
fprintf(stderr, "buffer reset(cleared)\n"); fprintf(stderr, "buffer reset(cleared)\n");
break; break;
case 's': /* \s is save history to a file */ case 's': /* \s is save history to a file */
if (!optarg) if (!optarg)
optarg="/dev/tty"; optarg = "/dev/tty";
if (write_history(optarg) != 0) if (write_history(optarg) != 0)
fprintf(stderr,"cannot write history to %s\n",optarg); fprintf(stderr, "cannot write history to %s\n", optarg);
break; break;
case 'm': /* monitor like type-setting */ case 'm': /* monitor like type-setting */
if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding")) if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding")) {
{
settings->opt.html3 = settings->opt.expanded = 0; settings->opt.html3 = settings->opt.expanded = 0;
settings->opt.align = settings->opt.header = 1; settings->opt.align = settings->opt.header = 1;
free(settings->opt.fieldSep); free(settings->opt.fieldSep);
settings->opt.fieldSep=strdup("|"); settings->opt.fieldSep = strdup("|");
if (!settings->quiet) if (!settings->quiet)
fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep); fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
} else } else {
{
free(settings->opt.fieldSep); free(settings->opt.fieldSep);
settings->opt.fieldSep=strdup(DEFAULT_FIELD_SEP); settings->opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
if (!settings->quiet) if (!settings->quiet)
fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep); fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
} }
...@@ -1169,10 +1169,8 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1169,10 +1169,8 @@ HandleSlashCmds(PsqlSettings *settings,
if (settings->opt.tableOpt) if (settings->opt.tableOpt)
free(settings->opt.tableOpt); free(settings->opt.tableOpt);
if (!optarg) if (!optarg)
settings->opt.tableOpt=NULL; settings->opt.tableOpt = NULL;
else else if (!(settings->opt.tableOpt = strdup(optarg))) {
if (!(settings->opt.tableOpt=strdup(optarg)))
{
perror("malloc"); perror("malloc");
exit(1); exit(1);
} }
...@@ -1193,37 +1191,41 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -1193,37 +1191,41 @@ HandleSlashCmds(PsqlSettings *settings,
} }
/* /*
MainLoop: main processing loop for reading lines of input * MainLoop: main processing loop for reading lines of input and sending them
and sending them to the backend * to the backend
*
this loop is re-entrant. May be called by \i command * this loop is re-entrant. May be called by \i command which reads input from
which reads input from a file * a file
*
*db_ptr must be initialized and set * db_ptr must be initialized and set
*/ */
int int
MainLoop(PsqlSettings *settings, FILE *source) MainLoop(PsqlSettings * settings, FILE * source)
{ {
char *line; /* line of input */ char *line; /* line of input */
int len; /* length of the line */ int len; /* length of the line */
char query[MAX_QUERY_BUFFER]; /* multi-line query storage */ char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
int exitStatus = 0; int successResult = 1;
int slashCmdStatus = 0; int slashCmdStatus = 0;
/* slashCmdStatus can be: /*
0 - send currently constructed query to backend (i.e. we got a \g) * slashCmdStatus can be: 0 - send currently constructed query to backend
1 - skip processing of this line, continue building up query * (i.e. we got a \g) 1 - skip processing of this line, continue building
2 - terminate processing of this query entirely * up query 2 - terminate processing of this query entirely 3 - new query
* supplied by edit
*/ */
bool sendQuery = 0; bool querySent = false;
bool querySent = 0;
bool interactive; bool interactive;
READ_ROUTINE GetNextLine; READ_ROUTINE GetNextLine;
bool connected = 1;
/* We are connected to the backend (last time we looked) */
bool eof = 0; bool eof = 0;
/* We've reached the end of our command input. */ /* We've reached the end of our command input. */
bool success;
bool in_quote;
bool was_bslash; /* backslash */
bool was_dash;
int paren_level;
char *query_start;
interactive = ((source == stdin) && !settings->notty); interactive = ((source == stdin) && !settings->notty);
#define PROMPT "=> " #define PROMPT "=> "
...@@ -1235,73 +1237,126 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1235,73 +1237,126 @@ MainLoop(PsqlSettings *settings, FILE *source)
if (settings->quiet) if (settings->quiet)
settings->prompt[0] = '\0'; settings->prompt[0] = '\0';
else else
sprintf(settings->prompt,"%s%s", PQdb(settings->db), PROMPT); sprintf(settings->prompt, "%s%s", PQdb(settings->db), PROMPT);
if (settings->useReadline) { if (settings->useReadline) {
using_history(); using_history();
GetNextLine = gets_readline; GetNextLine = gets_readline;
} else GetNextLine = gets_noreadline; } else
} else GetNextLine = gets_fromFile; GetNextLine = gets_noreadline;
} else
GetNextLine = gets_fromFile;
query[0] = '\0'; query[0] = '\0';
in_quote = false;
paren_level = 0;
slashCmdStatus = -1; /* set default */
/* main loop for getting queries and executing them */ /* main loop for getting queries and executing them */
while (connected && !eof) { while (!eof) {
if (slashCmdStatus == 3) {
line = strdup(query);
query[0] = '\0';
} else {
line = GetNextLine(settings->prompt, source); line = GetNextLine(settings->prompt, source);
if (interactive && settings->useReadline && line != NULL)
add_history(line); /* save non-empty lines in history */
}
query_start = line;
if (line == NULL) { /* No more input. Time to quit */ if (line == NULL) { /* No more input. Time to quit */
printf("EOF\n"); /* Goes on prompt line */ printf("EOF\n"); /* Goes on prompt line */
eof = true; eof = true;
} else { } else {
exitStatus = 0; if (!interactive && !settings->singleStep && !settings->quiet)
line = rightTrim(line); fprintf(stderr, "%s\n", line);
/* remove whitespaces on the right, incl. \n's */ /* remove whitespaces on the right, incl. \n's */
line = rightTrim(line);
if (line[0] == '\0') { if (line[0] == '\0') {
free(line); free(line);
continue; 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;
}
if (line[0] != '\\' && querySent) {
query[0]='\0';
querySent = 0;
}
len = strlen(line); len = strlen(line);
if (interactive && settings->useReadline) if (settings->singleLineMode) {
add_history(line); /* save non-empty lines in history */ SendQuery(&success, settings, line, false, false, 0);
successResult &= success;
querySent = true;
} else {
int i;
was_bslash = false;
was_dash = false;
/* do the query immediately if we are doing single line queries for (i = 0; i < len; i++) {
or if the last character is a semicolon if (!in_quote && line[i] == '\\') {
*/ char hold_char = line[i];
sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
/* normally, \ commands have to be start the line, line[i] = '\0';
but for backwards compatibility with monitor, if (query_start[0] != '\0') {
check for \g at the end of line */ if (query[0] != '\0') {
if (len > 2 && !sendQuery) { strcat(query, "\n");
if (line[len-1]=='g' && line[len-2]=='\\') { strcat(query, query_start);
sendQuery = 1; } else
line[len-2]='\0'; strcpy(query, query_start);
}
line[i] = hold_char;
query_start = line + i;
break; /* handle command */
} }
if (querySent && !isspace(line[i])) {
query[0] = '\0';
querySent = false;
} }
if (!in_quote && was_dash && line[i] == '-') {
/* print comment at top of query */
if (settings->singleStep)
fprintf(stdout, "%s\n", line + i - 1);
line[i - 1] = '\0'; /* remove comment */
break;
}
was_dash = false;
if (!in_quote && !paren_level &&
line[i] == ';') {
char hold_char = line[i + 1];
line[i + 1] = '\0';
if (query_start[0] != '\0') {
if (query[0] != '\0') {
strcat(query, "\n");
strcat(query, query_start);
} else
strcpy(query, query_start);
}
SendQuery(&success, settings, query, false, false, 0);
successResult &= success;
line[i + 1] = hold_char;
query_start = line + i + 1;
querySent = true;
}
if (was_bslash)
was_bslash = false;
else if (line[i] == '\\')
was_bslash = true;
else if (line[i] == '\'')
in_quote ^= 1;
else if (!in_quote && line[i] == '(')
paren_level++;
else if (!in_quote && paren_level && line[i] == ')')
paren_level--;
else if (!in_quote && line[i] == '-')
was_dash = true;
}
}
slashCmdStatus = -1;
/* slash commands have to be on their own line */ /* slash commands have to be on their own line */
if (line[0] == '\\') { if (!in_quote && query_start[0] == '\\') {
slashCmdStatus = HandleSlashCmds(settings, slashCmdStatus = HandleSlashCmds(settings,
line, query_start,
query); query);
if (slashCmdStatus == 1) { if (slashCmdStatus == 1) {
free(line); free(line);
...@@ -1311,45 +1366,32 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1311,45 +1366,32 @@ MainLoop(PsqlSettings *settings, FILE *source)
free(line); free(line);
break; break;
} }
if (slashCmdStatus == 0) } else if (strlen(query) + strlen(query_start) > MAX_QUERY_BUFFER) {
sendQuery = 1; fprintf(stderr, "query buffer max length of %d exceeded\n",
} else if (strlen(query) + len > MAX_QUERY_BUFFER) {
fprintf(stderr,"query buffer max length of %d exceeded\n",
MAX_QUERY_BUFFER); MAX_QUERY_BUFFER);
fprintf(stderr,"query line ignored\n"); fprintf(stderr, "query line ignored\n");
} else if (query[0]!='\0') { } else {
strcat(query,"\n"); if (query_start[0] != '\0') {
strcat(query,line); querySent = false;
} else strcpy(query,line); if (query[0] != '\0') {
if (sendQuery && query[0] != '\0') { strcat(query, "\n");
/* echo the line read from the file, strcat(query, query_start);
unless we are in single_step mode, because single_step mode } else
will echo anyway strcpy(query, query_start);
*/ }
bool success; /* The query succeeded at the backend */ }
if (!interactive && !settings->singleStep && !settings->quiet)
fprintf(stderr,"%s\n", query);
if (slashCmdStatus == 0) {
SendQuery(&success, settings, query, false, false, 0); SendQuery(&success, settings, query, false, false, 0);
exitStatus = success ? 0 : 1; successResult &= success;
querySent = 1; querySent = true;
if (PQstatus(settings->db) == CONNECTION_BAD) {
connected = 0;
fprintf(stderr,
"We have lost the connection to the backend, so "
"further processing is impossible. "
"Terminating.\n");
}
} }
free(line); /* free storage malloc'd by GetNextLine */ free(line); /* free storage malloc'd by GetNextLine */
} }
} /* while */ } /* while */
return exitStatus; return successResult;
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
...@@ -1366,8 +1408,8 @@ main(int argc, char **argv) ...@@ -1366,8 +1408,8 @@ main(int argc, char **argv)
char *singleQuery = NULL; char *singleQuery = NULL;
bool listDatabases = 0 ; bool listDatabases = 0;
int exitStatus = 0; int successResult = 1;
bool singleSlashCmd = 0; bool singleSlashCmd = 0;
int c; int c;
...@@ -1375,7 +1417,7 @@ main(int argc, char **argv) ...@@ -1375,7 +1417,7 @@ main(int argc, char **argv)
settings.opt.align = 1; settings.opt.align = 1;
settings.opt.header = 1; settings.opt.header = 1;
settings.queryFout = stdout; settings.queryFout = stdout;
settings.opt.fieldSep=strdup(DEFAULT_FIELD_SEP); settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
settings.opt.pager = 1; settings.opt.pager = 1;
if (!isatty(0) || !isatty(1)) if (!isatty(0) || !isatty(1))
settings.quiet = settings.notty = 1; settings.quiet = settings.notty = 1;
...@@ -1394,8 +1436,8 @@ main(int argc, char **argv) ...@@ -1394,8 +1436,8 @@ main(int argc, char **argv)
break; break;
case 'c': case 'c':
singleQuery = optarg; singleQuery = optarg;
if ( singleQuery[0] == '\\' ) { if (singleQuery[0] == '\\') {
singleSlashCmd=1; singleSlashCmd = 1;
} }
break; break;
case 'd': case 'd':
...@@ -1408,7 +1450,7 @@ main(int argc, char **argv) ...@@ -1408,7 +1450,7 @@ main(int argc, char **argv)
qfilename = optarg; qfilename = optarg;
break; break;
case 'F': case 'F':
settings.opt.fieldSep=optarg; settings.opt.fieldSep = optarg;
break; break;
case 'l': case 'l':
listDatabases = 1; listDatabases = 1;
...@@ -1462,14 +1504,13 @@ main(int argc, char **argv) ...@@ -1462,14 +1504,13 @@ main(int argc, char **argv)
dbname = PQdb(settings.db); dbname = PQdb(settings.db);
if (PQstatus(settings.db) == CONNECTION_BAD) { if (PQstatus(settings.db) == CONNECTION_BAD) {
fprintf(stderr,"Connection to database '%s' failed.\n", dbname); fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
fprintf(stderr,"%s",PQerrorMessage(settings.db)); fprintf(stderr, "%s", PQerrorMessage(settings.db));
exit(1); exit(1);
} }
if (listDatabases) { if (listDatabases) {
exit(listAllDbs(&settings)); exit(listAllDbs(&settings));
} }
if (!settings.quiet && !singleQuery && !qfilename) { if (!settings.quiet && !singleQuery && !qfilename) {
printf("Welcome to the POSTGRES95 interactive sql monitor:\n"); printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
printf(" Please read the file COPYRIGHT for copyright terms " printf(" Please read the file COPYRIGHT for copyright terms "
...@@ -1479,37 +1520,40 @@ main(int argc, char **argv) ...@@ -1479,37 +1520,40 @@ main(int argc, char **argv)
printf(" type \\g or terminate with semicolon to execute query\n"); printf(" type \\g or terminate with semicolon to execute query\n");
printf(" You are currently connected to the database: %s\n\n", dbname); printf(" You are currently connected to the database: %s\n\n", dbname);
} }
if (qfilename || singleSlashCmd) { if (qfilename || singleSlashCmd) {
/* read in a file full of queries instead of reading in queries /*
interactively */ * read in a file full of queries instead of reading in queries
* interactively
*/
char *line; char *line;
if ( singleSlashCmd ) { if (singleSlashCmd) {
/* Not really a query, but "Do what I mean, not what I say." */ /* Not really a query, but "Do what I mean, not what I say." */
line = singleQuery; line = singleQuery;
} else { } else {
line = malloc(strlen(qfilename) + 5); line = malloc(strlen(qfilename) + 5);
sprintf(line,"\\i %s", qfilename); sprintf(line, "\\i %s", qfilename);
} }
HandleSlashCmds(&settings, line, ""); HandleSlashCmds(&settings, line, "");
} else { } else {
if (singleQuery) { if (singleQuery) {
bool success; /* The query succeeded at the backend */ bool success; /* The query succeeded at the backend */
SendQuery(&success, &settings, singleQuery, false, false, 0); SendQuery(&success, &settings, singleQuery, false, false, 0);
exitStatus = success ? 0 : 1; successResult = success;
} else exitStatus = MainLoop(&settings, stdin); } else
successResult = MainLoop(&settings, stdin);
} }
PQfinish(settings.db); PQfinish(settings.db);
return exitStatus; return !successResult;
} }
#define COPYBUFSIZ 8192 #define COPYBUFSIZ 8192
static void static void
handleCopyOut(PGresult *res, bool quiet, FILE *copystream) { handleCopyOut(PGresult * res, bool quiet, FILE * copystream)
{
bool copydone; bool copydone;
char copybuf[COPYBUFSIZ]; char copybuf[COPYBUFSIZ];
int ret; int ret;
...@@ -1521,14 +1565,14 @@ handleCopyOut(PGresult *res, bool quiet, FILE *copystream) { ...@@ -1521,14 +1565,14 @@ handleCopyOut(PGresult *res, bool quiet, FILE *copystream) {
if (copybuf[0] == '\\' && if (copybuf[0] == '\\' &&
copybuf[1] == '.' && copybuf[1] == '.' &&
copybuf[2] =='\0') { copybuf[2] == '\0') {
copydone = true; /* don't print this... */ copydone = true; /* don't print this... */
} else { } else {
fputs(copybuf, copystream); fputs(copybuf, copystream);
switch (ret) { switch (ret) {
case EOF: case EOF:
copydone = true; copydone = true;
/*FALLTHROUGH*/ /* FALLTHROUGH */
case 0: case 0:
fputc('\n', copystream); fputc('\n', copystream);
break; break;
...@@ -1544,7 +1588,8 @@ handleCopyOut(PGresult *res, bool quiet, FILE *copystream) { ...@@ -1544,7 +1588,8 @@ handleCopyOut(PGresult *res, bool quiet, FILE *copystream) {
static void static void
handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) { handleCopyIn(PGresult * res, const bool mustprompt, FILE * copystream)
{
bool copydone = false; bool copydone = false;
bool firstload; bool firstload;
bool linedone; bool linedone;
...@@ -1558,7 +1603,6 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) { ...@@ -1558,7 +1603,6 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) {
fputs("End with a backslash and a " fputs("End with a backslash and a "
"period on a line by itself.\n", stdout); "period on a line by itself.\n", stdout);
} }
while (!copydone) { /* for each input line ... */ while (!copydone) { /* for each input line ... */
if (mustprompt) { if (mustprompt) {
fputs(">> ", stdout); fputs(">> ", stdout);
...@@ -1595,14 +1639,14 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) { ...@@ -1595,14 +1639,14 @@ handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) {
/* try to open fname and return a FILE *, /*
if it fails, use stdout, instead */ * try to open fname and return a FILE *, if it fails, use stdout, instead
*/
FILE * FILE *
setFout(PsqlSettings *ps, char *fname) setFout(PsqlSettings * ps, char *fname)
{ {
if (ps->queryFout && ps->queryFout != stdout) if (ps->queryFout && ps->queryFout != stdout) {
{
if (ps->pipe) if (ps->pipe)
pclose(ps->queryFout); pclose(ps->queryFout);
else else
...@@ -1610,16 +1654,12 @@ setFout(PsqlSettings *ps, char *fname) ...@@ -1610,16 +1654,12 @@ setFout(PsqlSettings *ps, char *fname)
} }
if (!fname) if (!fname)
ps->queryFout = stdout; ps->queryFout = stdout;
else else {
{ if (*fname == '|') {
if (*fname == '|')
{
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
ps->queryFout = popen(fname+1, "w"); ps->queryFout = popen(fname + 1, "w");
ps->pipe = 1; ps->pipe = 1;
} } else {
else
{
ps->queryFout = fopen(fname, "w"); ps->queryFout = fopen(fname, "w");
ps->pipe = 0; ps->pipe = 0;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.4 1996/09/16 06:06:17 scrappy Exp $ * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.5 1996/11/14 16:08:05 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,8 +19,11 @@ ...@@ -19,8 +19,11 @@
/* all routines assume null-terminated strings! */ /* all routines assume null-terminated strings! */
/* removes whitespaces from the left, right and both sides of a string */ /* The following routines remove whitespaces from the left, right
and both sides of a string */
/* MODIFIES the string passed in and returns the head of it */ /* MODIFIES the string passed in and returns the head of it */
char *leftTrim(char *s) char *leftTrim(char *s)
{ {
char *s2 = s; char *s2 = s;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment