OSDN Git Service

Another pgindent run. Fixes enum indenting, and improves #endif
[pg-rex/syncrep.git] / src / bin / psql / mainloop.c
index 2f38ffb..3de6671 100644 (file)
@@ -1,12 +1,14 @@
-#include <config.h>
-#include <c.h>
+/*
+ * psql - the PostgreSQL interactive terminal
+ *
+ * Copyright 2000 by PostgreSQL Global Development Group
+ *
+ * $Header: /cvsroot/pgsql/src/bin/psql/mainloop.c,v 1.42 2001/10/28 06:25:58 momjian Exp $
+ */
+#include "postgres_fe.h"
 #include "mainloop.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <pqexpbuffer.h>
+#include "pqexpbuffer.h"
 
 #include "settings.h"
 #include "prompt.h"
 #include "common.h"
 #include "command.h"
 
+#ifndef WIN32
+#include <setjmp.h>
+sigjmp_buf     main_loop_jmp;
+#endif
 
 
-/* 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.
+ *
+ * FIXME: rewrite this whole thing with flex
  */
 int
-MainLoop(PsqlSettings *pset, FILE *source)
+MainLoop(FILE *source)
 {
-    PQExpBuffer        query_buf;              /* buffer for query being accumulated */
-    char       *line;                  /* current line of input */
-    char       *xcomment;              /* start of extended comment */
-    int                len;                    /* length of the line */
-    int                successResult = EXIT_SUCCESS;
-    backslashResult slashCmdStatus;
-
-    bool       eof = false;    /* end of our command input? */
-    bool       success;
-    char       in_quote;               /* == 0 for no in_quote */
-    bool       was_bslash;             /* backslash */
-    int                paren_level;
-    unsigned int query_start;
-
-    int i, prevlen, thislen;
-
-    /* Save the prior command source */
-    FILE       *prev_cmd_source;
-    bool       prev_cmd_interactive;
-
-    bool die_on_error;
-    const char *interpol_char;
-
-
-    /* Save old settings */
-    prev_cmd_source = pset->cur_cmd_source;
-    prev_cmd_interactive = pset->cur_cmd_interactive;
-
-    /* Establish new source */
-    pset->cur_cmd_source = source;
-    pset->cur_cmd_interactive = ((source == stdin) && !pset->notty);
-
-
-    query_buf = createPQExpBuffer();
-    if (!query_buf) {
-       perror("createPQExpBuffer");
-       exit(EXIT_FAILURE);
-    }
-
-    xcomment = NULL;
-    in_quote = 0;
-    paren_level = 0;
-    slashCmdStatus = CMD_UNKNOWN;              /* set default */
-
-
-    /* main loop to get queries and execute them */
-    while (!eof)
-    {
-       if (slashCmdStatus == CMD_NEWEDIT)
+       PQExpBuffer query_buf;          /* buffer for query being accumulated */
+       PQExpBuffer previous_buf;       /* if there isn't anything in the new
+                                                                * buffer yet, use this one for \e, etc. */
+       char       *line;                       /* current line of input */
+       int                     len;                    /* length of the line */
+       volatile int successResult = EXIT_SUCCESS;
+       volatile backslashResult slashCmdStatus;
+
+       bool            success;
+       volatile char in_quote;         /* == 0 for no in_quote */
+       volatile bool in_xcomment;      /* in extended comment */
+       volatile int xcdepth;
+       volatile int paren_level;
+       unsigned int query_start;
+       volatile int count_eof = 0;
+       const char *var;
+       volatile unsigned int bslash_count = 0;
+
+       int                     i,
+                               prevlen,
+                               thislen;
+
+       /* Save the prior command source */
+       FILE       *prev_cmd_source;
+       bool            prev_cmd_interactive;
+
+       unsigned int prev_lineno;
+       volatile bool die_on_error = false;
+
+
+       /* Save old settings */
+       prev_cmd_source = pset.cur_cmd_source;
+       prev_cmd_interactive = pset.cur_cmd_interactive;
+
+       /* Establish new source */
+       pset.cur_cmd_source = source;
+       pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
+
+
+       query_buf = createPQExpBuffer();
+       previous_buf = createPQExpBuffer();
+       if (!query_buf || !previous_buf)
        {
-           /*
-            * just returned from editing the line? then just copy to the
-            * input buffer
-            */
-           line = strdup(query_buf->data);
-           resetPQExpBuffer(query_buf);
-           /* reset parsing state since we are rescanning whole query */
-           xcomment = NULL;
-           in_quote = 0;
-           paren_level = 0;
-       }
-       else
-       {
-           /*
-            * otherwise, set interactive prompt if necessary
-            * and get another line
-            */
-           if (pset->cur_cmd_interactive)
-           {
-               int prompt_status;
-               
-               if (in_quote && in_quote == '\'')
-                   prompt_status = PROMPT_SINGLEQUOTE;
-               else if (in_quote && in_quote == '"')
-                   prompt_status= PROMPT_DOUBLEQUOTE;
-               else if (xcomment != NULL)
-                   prompt_status = PROMPT_COMMENT;
-               else if (query_buf->len > 0)
-                   prompt_status = PROMPT_CONTINUE;
-               else
-                   prompt_status = PROMPT_READY;
-
-               line = gets_interactive(get_prompt(pset, prompt_status));
-           }
-           else
-               line = gets_fromFile(source);
-       }
-
-
-       /* Setting these will not have effect until next line */
-       die_on_error = GetVariableBool(pset->vars, "die_on_error");
-       interpol_char = GetVariable(pset->vars, "sql_interpol");;
-
-
-       /*
-        * query_buf holds query already accumulated.  line is the malloc'd
-        * new line of input (note it must be freed before looping around!)
-        * query_start is the next command start location within the line.
-        */
-
-       /* No more input.  Time to quit, or \i done */
-       if (line == NULL || (!pset->cur_cmd_interactive && *line == '\0'))
-       {                                               
-           if (GetVariableBool(pset->vars, "echo") && !GetVariableBool(pset->vars, "quiet"))
-               puts("EOF");
-           eof = true;
-           continue;
+               psql_error("out of memory\n");
+               exit(EXIT_FAILURE);
        }
 
-       /* not currently inside an extended comment? */
-       if (xcomment)
-           xcomment = line;
+       in_xcomment = false;
+       in_quote = 0;
+       paren_level = 0;
+       slashCmdStatus = CMD_UNKNOWN;           /* set default */
+       prev_lineno = pset.lineno;
+       pset.lineno = 0;
 
 
-       /* strip trailing backslashes, they don't have a clear meaning */
-       while (1) {
-           char * cp = strrchr(line, '\\');
-           if (cp && (*(cp + 1) == '\0'))
-               *cp = '\0';
-           else
-               break;
-       }
-
-               
-       /* echo back if input is from file and flag is set */
-       if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
-           fprintf(stderr, "%s\n", line);
-
-
-       /* interpolate variables into SQL */
-       len = strlen(line);
-       thislen = PQmblen(line);
-
-       for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])) ) {
-           if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i]) {
-               size_t in_length, out_length;
-               const char * value;
-               char * new;
-               bool closer; /* did we have a closing delimiter or just an end of line? */
-
-               in_length = strcspn(&line[i+thislen], interpol_char);
-               closer = line[i + thislen + in_length] == line[i];
-               line[i + thislen + in_length] = '\0';
-               value = interpolate_var(&line[i + thislen], pset);
-               out_length = strlen(value);
-
-               new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
-               if (!new) {
-                   perror("malloc");
-                   exit(EXIT_FAILURE);
+       /* main loop to get queries and execute them */
+       while (1)
+       {
+#ifndef WIN32
+
+               /*
+                * Welcome code for Control-C
+                */
+               if (cancel_pressed)
+               {
+                       if (!pset.cur_cmd_interactive)
+                       {
+                               /*
+                                * You get here if you stopped a script with Ctrl-C and a
+                                * query cancel was issued. In that case we don't do the
+                                * longjmp, so the query routine can finish nicely.
+                                */
+                               successResult = EXIT_USER;
+                               break;
+                       }
+
+                       cancel_pressed = false;
                }
 
-               new[0] = '\0';
-               strncat(new, line, i);
-               strcat(new, value);
-               if (closer)
-                   strcat(new, line + i + 2 + in_length);
-
-               free(line);
-               line = new;
-               i += out_length;
-           }
-       }
-
-       /* nothing left on line? then ignore */
-       if (line[0] == '\0') {
-           free(line);
-           continue;
-       }
-
-       slashCmdStatus = CMD_UNKNOWN;
-
-       len = strlen(line);
-       query_start = 0;
+               if (sigsetjmp(main_loop_jmp, 1) != 0)
+               {
+                       /* got here with longjmp */
+
+                       if (pset.cur_cmd_interactive)
+                       {
+                               fputc('\n', stdout);
+                               resetPQExpBuffer(query_buf);
+
+                               /* reset parsing state */
+                               in_xcomment = false;
+                               in_quote = 0;
+                               paren_level = 0;
+                               count_eof = 0;
+                               slashCmdStatus = CMD_UNKNOWN;
+                       }
+                       else
+                       {
+                               successResult = EXIT_USER;
+                               break;
+                       }
+               }
 
-       /*
-        * Parse line, looking for command separators.
-        *
-        * The current character is at line[i], the prior character at
-        * line[i - prevlen], the next character at line[i + thislen].
-        */
-       prevlen = 0;
-        thislen = (len > 0) ? PQmblen(line) : 0;
-
-#define ADVANCE_1  (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
-
-       success = true;
-       for (i = 0; i < len; ADVANCE_1) {
-           if (!success && die_on_error)
-               break;
-
-       
-           /* was the previous character a backslash? */
-           if (i > 0 && line[i - prevlen] == '\\')
-               was_bslash = true;
-           else
-               was_bslash = false;
-
-    
-           /* in quote? */
-           if (in_quote) {
-               /* end of quote */
-               if (line[i] == in_quote && !was_bslash)
-                   in_quote = '\0';
-           }
-
-           /* start of quote */
-           else if (line[i] == '\'' || line[i] == '"')
-               in_quote = line[i];
-
-           /* in extended comment? */
-           else if (xcomment != NULL) {
-               if (line[i] == '*' && line[i + thislen] == '/') {
-                   xcomment = NULL;
-                   ADVANCE_1;
+               /*
+                * establish the control-C handler only after main_loop_jmp is
+                * ready
+                */
+               pqsignal(SIGINT, handle_sigint);                /* control-C => cancel */
+#endif  /* not WIN32 */
+
+               if (slashCmdStatus == CMD_NEWEDIT)
+               {
+                       /*
+                        * just returned from editing the line? then just copy to the
+                        * input buffer
+                        */
+                       line = xstrdup(query_buf->data);
+                       resetPQExpBuffer(query_buf);
+                       /* reset parsing state since we are rescanning whole line */
+                       in_xcomment = false;
+                       in_quote = 0;
+                       paren_level = 0;
+                       slashCmdStatus = CMD_UNKNOWN;
                }
-           }
-   
-           /* start of extended comment? */
-           else if (line[i] == '/' && line[i + thislen] == '*') {
-               xcomment = &line[i];
-               ADVANCE_1;
-           }
-
-           /* single-line comment? truncate line */
-           else if ((line[i] == '-' && line[i + thislen] == '-') ||
-                    (line[i] == '/' && line[i + thislen] == '/'))
-           {
-               line[i] = '\0';         /* remove comment */
-               break;
-           }
-
-           /* count nested parentheses */
-           else if (line[i] == '(')
-               paren_level++;
-
-           else if (line[i] == ')' && paren_level > 0)
-               paren_level--;
-
-           /* semicolon? then send query */
-           else if (line[i] == ';' && !was_bslash && paren_level==0) {
-               line[i] = '\0';
-               /* is there anything else on the line? */
-               if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
-                   /* insert a cosmetic newline, if this is not the first line in the buffer */
-                   if (query_buf->len > 0)
-                       appendPQExpBufferChar(query_buf, '\n');
-                   /* append the line to the query buffer */
-                   appendPQExpBufferStr(query_buf, line + query_start);
+               else
+               {
+                       fflush(stdout);
+
+                       /*
+                        * otherwise, set interactive prompt if necessary and get
+                        * another line
+                        */
+                       if (pset.cur_cmd_interactive)
+                       {
+                               int                     prompt_status;
+
+                               if (in_quote && in_quote == '\'')
+                                       prompt_status = PROMPT_SINGLEQUOTE;
+                               else if (in_quote && in_quote == '"')
+                                       prompt_status = PROMPT_DOUBLEQUOTE;
+                               else if (in_xcomment)
+                                       prompt_status = PROMPT_COMMENT;
+                               else if (paren_level)
+                                       prompt_status = PROMPT_PAREN;
+                               else if (query_buf->len > 0)
+                                       prompt_status = PROMPT_CONTINUE;
+                               else
+                                       prompt_status = PROMPT_READY;
+
+                               line = gets_interactive(get_prompt(prompt_status));
+                       }
+                       else
+                               line = gets_fromFile(source);
                }
 
-               /* execute query */
-               success = SendQuery(pset, query_buf->data);
-
-               resetPQExpBuffer(query_buf);
-               query_start = i + thislen;
-           }
 
-           /* backslash command */
-           else if (was_bslash) {
-               const char * end_of_cmd = NULL;
+               /* Setting this will not have effect until next line. */
+               die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP");
+
+               /*
+                * query_buf holds query already accumulated.  line is the
+                * malloc'd new line of input (note it must be freed before
+                * looping around!) query_start is the next command start location
+                * within the line.
+                */
+
+               /* No more input.  Time to quit, or \i done */
+               if (line == NULL)
+               {
+                       if (pset.cur_cmd_interactive)
+                       {
+                               bool            getout = true;
+
+                               /* This tries to mimic bash's IGNOREEOF feature. */
+                               const char *val = GetVariable(pset.vars, "IGNOREEOF");
+
+                               if (val)
+                               {
+                                       long int        maxeof;
+                                       char       *endptr;
+
+                                       if (*val == '\0')
+                                               maxeof = 10;
+                                       else
+                                       {
+                                               maxeof = strtol(val, &endptr, 0);
+                                               if (*endptr != '\0')    /* string not valid as a
+                                                                                                * number */
+                                                       maxeof = 10;
+                                       }
+
+                                       if (count_eof++ != maxeof)
+                                               getout = false; /* not quite there yet */
+                               }
+
+                               if (getout)
+                               {
+                                       if (QUIET())
+                                               putc('\n', stdout);
+                                       else
+                                               puts("\\q");
+                                       break;
+                               }
+                               else
+                               {
+                                       if (!QUIET())
+                                               printf(gettext("Use \"\\q\" to leave %s.\n"), pset.progname);
+                                       continue;
+                               }
+                       }
+                       else
+/* not interactive */
+                               break;
+               }
+               else
+                       count_eof = 0;
 
-               line[i - prevlen] = '\0'; /* overwrites backslash */
+               pset.lineno++;
 
-               /* is there anything else on the line? */
-               if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
-                   /* insert a cosmetic newline, if this is not the first line in the buffer */
-                   if (query_buf->len > 0)
-                       appendPQExpBufferChar(query_buf, '\n');
-                   /* append the line to the query buffer */
-                   appendPQExpBufferStr(query_buf, line + query_start);
+               /* nothing left on line? then ignore */
+               if (line[0] == '\0' && !in_quote)
+               {
+                       free(line);
+                       continue;
                }
 
-               /* handle backslash command */
-
-               slashCmdStatus = HandleSlashCmds(pset, &line[i], query_buf, &end_of_cmd);
+               /* echo back if flag is set */
+               var = GetVariable(pset.vars, "ECHO");
+               if (!pset.cur_cmd_interactive && var && strcmp(var, "all") == 0)
+                       puts(line);
+               fflush(stdout);
+
+               len = strlen(line);
+               query_start = 0;
+
+               /*
+                * Parse line, looking for command separators.
+                *
+                * The current character is at line[i], the prior character at line[i
+                * - prevlen], the next character at line[i + thislen].
+                */
+#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i, pset.encoding))
+
+               success = true;
+               for (i = 0, prevlen = 0, thislen = (len > 0) ? PQmblen(line, pset.encoding) : 0;
+                        i < len;
+                        ADVANCE_1)
+               {
+                       /* was the previous character a backslash? */
+                       bool            was_bslash = (i > 0 && line[i - prevlen] == '\\');
+
+                       if (was_bslash)
+                               bslash_count++;
+                       else
+                               bslash_count = 0;
+
+       rescan:
+
+                       /*
+                        * It is important to place the in_* test routines before the
+                        * in_* detection routines. i.e. we have to test if we are in
+                        * a quote before testing for comments. bjm  2000-06-30
+                        */
+
+                       /* in quote? */
+                       if (in_quote)
+                       {
+                               /* end of quote */
+                               if (line[i] == in_quote && bslash_count % 2 == 0)
+                                       in_quote = '\0';
+                       }
+
+                       /* in extended comment? */
+                       else if (in_xcomment)
+                       {
+                               if (line[i] == '*' && line[i + thislen] == '/')
+                               {
+                                       if (xcdepth > 0)
+                                               xcdepth--;
+                                       else
+                                       {
+                                               in_xcomment = false;
+                                               ADVANCE_1;
+                                       }
+                               }
+                               else if (line[i] == '/' && line[i + thislen] == '*')
+                                       xcdepth++;
+                       }
+
+                       /* start of extended comment? */
+                       else if (line[i] == '/' && line[i + thislen] == '*')
+                       {
+                               xcdepth = 0;
+                               in_xcomment = true;
+                               ADVANCE_1;
+                       }
+
+                       /* start of quote */
+                       else if (!was_bslash &&
+                                        (line[i] == '\'' || line[i] == '"'))
+                               in_quote = line[i];
+
+
+                       /* single-line comment? truncate line */
+                       else if (line[i] == '-' && line[i + thislen] == '-')
+                       {
+                               line[i] = '\0'; /* remove comment */
+                               break;
+                       }
+
+                       /* count nested parentheses */
+                       else if (line[i] == '(')
+                               paren_level++;
+
+                       else if (line[i] == ')' && paren_level > 0)
+                               paren_level--;
+
+                       /* colon -> substitute variable */
+                       /* we need to be on the watch for the '::' operator */
+                       else if (line[i] == ':' && !was_bslash
+                                 && strspn(line + i + thislen, VALID_VARIABLE_CHARS) > 0
+                                        && !(prevlen > 0 && line[i - prevlen] == ':')
+                               )
+                       {
+                               size_t          in_length,
+                                                       out_length;
+                               const char *value;
+                               char       *new;
+                               char            after;          /* the character after the
+                                                                                * variable name will be
+                                                                                * temporarily overwritten */
+
+                               in_length = strspn(&line[i + thislen], VALID_VARIABLE_CHARS);
+                               after = line[i + thislen + in_length];
+                               line[i + thislen + in_length] = '\0';
+
+                               /*
+                                * if the variable doesn't exist we'll leave the string as
+                                * is
+                                */
+                               value = GetVariable(pset.vars, &line[i + thislen]);
+                               if (value)
+                               {
+                                       out_length = strlen(value);
+
+                                       new = malloc(len + out_length - (1 + in_length) + 1);
+                                       if (!new)
+                                       {
+                                               psql_error("out of memory\n");
+                                               exit(EXIT_FAILURE);
+                                       }
+
+                                       sprintf(new, "%.*s%s%c", i, line, value, after);
+                                       if (after)
+                                               strcat(new, line + i + 1 + in_length + 1);
+
+                                       free(line);
+                                       line = new;
+                                       len = strlen(new);
+
+                                       goto rescan;    /* reparse the just substituted */
+                               }
+                               else
+                               {
+                                       /* restore overwritten character */
+                                       line[i + thislen + in_length] = after;
+                                       /* move on ... */
+                               }
+                       }
+
+                       /* semicolon? then send query */
+                       else if (line[i] == ';' && !was_bslash && !paren_level)
+                       {
+                               line[i] = '\0';
+                               /* is there anything else on the line? */
+                               if (line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
+                               {
+                                       /*
+                                        * insert a cosmetic newline, if this is not the first
+                                        * line in the buffer
+                                        */
+                                       if (query_buf->len > 0)
+                                               appendPQExpBufferChar(query_buf, '\n');
+                                       /* append the line to the query buffer */
+                                       appendPQExpBufferStr(query_buf, line + query_start);
+                                       appendPQExpBufferChar(query_buf, ';');
+                               }
+
+                               /* execute query */
+                               success = SendQuery(query_buf->data);
+                               slashCmdStatus = success ? CMD_SEND : CMD_ERROR;
+
+                               resetPQExpBuffer(previous_buf);
+                               appendPQExpBufferStr(previous_buf, query_buf->data);
+                               resetPQExpBuffer(query_buf);
+                               query_start = i + thislen;
+                       }
+
+                       /*
+                        * if you have a burning need to send a semicolon or colon to
+                        * the backend ...
+                        */
+                       else if (was_bslash && (line[i] == ';' || line[i] == ':'))
+                       {
+                               /* remove the backslash */
+                               memmove(line + i - prevlen, line + i, len - i + 1);
+                               len--;
+                       }
+
+                       /* backslash command */
+                       else if (was_bslash)
+                       {
+                               const char *end_of_cmd = NULL;
+
+                               paren_level = 0;
+                               line[i - prevlen] = '\0';               /* overwrites backslash */
+
+                               /* is there anything else on the line for the command? */
+                               if (line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
+                               {
+                                       /*
+                                        * insert a cosmetic newline, if this is not the first
+                                        * line in the buffer
+                                        */
+                                       if (query_buf->len > 0)
+                                               appendPQExpBufferChar(query_buf, '\n');
+                                       /* append the line to the query buffer */
+                                       appendPQExpBufferStr(query_buf, line + query_start);
+                               }
+
+                               /* handle backslash command */
+                               slashCmdStatus = HandleSlashCmds(&line[i],
+                                                  query_buf->len > 0 ? query_buf : previous_buf,
+                                                                                                &end_of_cmd);
+
+                               success = slashCmdStatus != CMD_ERROR;
+
+                               if ((slashCmdStatus == CMD_SEND || slashCmdStatus == CMD_NEWEDIT) &&
+                                       query_buf->len == 0)
+                               {
+                                       /* copy previous buffer to current for for handling */
+                                       appendPQExpBufferStr(query_buf, previous_buf->data);
+                               }
+
+                               if (slashCmdStatus == CMD_SEND)
+                               {
+                                       success = SendQuery(query_buf->data);
+                                       query_start = i + thislen;
+
+                                       resetPQExpBuffer(previous_buf);
+                                       appendPQExpBufferStr(previous_buf, query_buf->data);
+                                       resetPQExpBuffer(query_buf);
+                               }
+
+                               /* process anything left after the backslash command */
+                               i += end_of_cmd - &line[i];
+                               query_start = i;
+                       }
+
+
+                       /* stop the script after error */
+                       if (!success && die_on_error)
+                               break;
+
+               }                                               /* for (line) */
+
+
+               if (slashCmdStatus == CMD_TERMINATE)
+               {
+                       successResult = EXIT_SUCCESS;
+                       break;
+               }
 
-               success = slashCmdStatus != CMD_ERROR;
 
-               if (slashCmdStatus == CMD_SEND) {
-                   success = SendQuery(pset, query_buf->data);
-                   resetPQExpBuffer(query_buf);
-                   query_start = i + thislen;
+               /* Put the rest of the line in the query buffer. */
+               if (in_quote || line[query_start + strspn(line + query_start, " \t\n\r")] != '\0')
+               {
+                       if (query_buf->len > 0)
+                               appendPQExpBufferChar(query_buf, '\n');
+                       appendPQExpBufferStr(query_buf, line + query_start);
                }
 
-               /* is there anything left after the backslash command? */
-               if (end_of_cmd) {
-                   i += end_of_cmd - &line[i];
-                   query_start = i;
-               }
-               else
-                   break;
-           }
-       }
-
+               free(line);
 
-       if (!success && die_on_error && !pset->cur_cmd_interactive) {
-           successResult = EXIT_USER;
-           break;
-       }
 
+               /* In single line mode, send off the query if any */
+               if (query_buf->data[0] != '\0' && GetVariableBool(pset.vars, "SINGLELINE"))
+               {
+                       success = SendQuery(query_buf->data);
+                       slashCmdStatus = success ? CMD_SEND : CMD_ERROR;
+                       resetPQExpBuffer(previous_buf);
+                       appendPQExpBufferStr(previous_buf, query_buf->data);
+                       resetPQExpBuffer(query_buf);
+               }
 
-       if (slashCmdStatus == CMD_TERMINATE) {
-           successResult = EXIT_SUCCESS;
-           break;
-       }
 
+               if (!success && die_on_error && !pset.cur_cmd_interactive)
+               {
+                       successResult = EXIT_USER;
+                       break;
+               }
 
-       /* Put the rest of the line in the query buffer. */
-       if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
-           if (query_buf->len > 0)
-               appendPQExpBufferChar(query_buf, '\n');
-           appendPQExpBufferStr(query_buf, line + query_start);
-       }
 
-       free(line);
+               /* Have we lost the db connection? */
+               if (pset.db == NULL && !pset.cur_cmd_interactive)
+               {
+                       successResult = EXIT_BADCONN;
+                       break;
+               }
+       }                                                       /* while !endoffile/session */
 
+       /*
+        * Process query at the end of file without a semicolon
+        */
+       if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
+               successResult == EXIT_SUCCESS)
+       {
+               success = SendQuery(query_buf->data);
 
-       /* In single line mode, send off the query if any */
-       if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline")) {
-           success = SendQuery(pset, query_buf->data);
-           resetPQExpBuffer(query_buf);
+               if (!success && die_on_error)
+                       successResult = EXIT_USER;
+               else if (pset.db == NULL)
+                       successResult = EXIT_BADCONN;
        }
-           
 
-       /* Have we lost the db connection? */
-       if (pset->db == NULL && !pset->cur_cmd_interactive) {
-           successResult = EXIT_BADCONN;
-           break;
-       }
-    }                                                  /* while */
+       /*
+        * Reset SIGINT handler because main_loop_jmp will be invalid as soon
+        * as we exit this routine.  If there is an outer MainLoop instance,
+        * it will re-enable ^C catching as soon as it gets back to the top of
+        * its loop and resets main_loop_jmp to point to itself.
+        */
+#ifndef WIN32
+       pqsignal(SIGINT, SIG_DFL);
+#endif
 
-    destroyPQExpBuffer(query_buf);
+       destroyPQExpBuffer(query_buf);
+       destroyPQExpBuffer(previous_buf);
 
-    pset->cur_cmd_source = prev_cmd_source;
-    pset->cur_cmd_interactive = prev_cmd_interactive;
+       pset.cur_cmd_source = prev_cmd_source;
+       pset.cur_cmd_interactive = prev_cmd_interactive;
+       pset.lineno = prev_lineno;
 
-    return successResult;
+       return successResult;
 }      /* MainLoop() */
-