[Please CC any replies to me, thanks. Also, the actual discussion took
place on -hackers, so check there first]

As anyone who has been following -hackers knows, there's been a bit of
discussion about how psql should deal with ^C and pagers. Attached is a
patch that deals with all the memory leak and descriptor leak issues.
With this patch, even valgrind can't find any leaked memory (except a
few things from program startup). With this patch, I find psql a
pleasure to use, it doesn't bug me at all anymore.

As for the apparent complexity of the patch, I'll quote the new comment
from common.c.

 * About ctrlc_abort_jmp and ctrlc_abort_active:
 *
 * In the past the SIGINT handler, when it couldn't find a query to cancel,
 * would simply longjmp() back to the main loop. Very simple but
 * unfortunatly it leaked memory like crazy and file descriptors too if you
 * were using a pager. Hence, the handler has been changed to just set a
 * flag in those cases so the code would notice and gracefully quit.
 *
 * However, PostgreSQL uses signals with BSD semantics, which means the
 * system calls get restarted across a signal. Hence when sitting at the
 * prompt pressing Ctrl-C appeared to do nothing because the kernel would
 * keep restarting the read(). The only way to avoid this is to have the
 * signal handler longjmp().
 *
 * Hence the rules are as follows: If you are reading from a descriptor
 * which may block indefinitly and you want to be able to respond to Ctrl-C
 * you should setup with sigsetjmp() the jump buffer ctrlc_abort_jmp. And
 * when you are about to read something set ctrlc_abort_active to true. 
 * After the read set it back to false. If while the flag is true the user
 * presses Ctrl-C, control will pass to the sigsetjmp() block which allows
 * you to clean up gracefully and return. See gets_basic() and
 * gets_readline() in input.c for examples.

Yes, SysV semantics for signals would simplify the code a bit, but I
have no idea whether that's supported or not. Certainly there isn't any
support for it now in the postgres codebase.

There are minor cosmetic changes, like a message when you interrupted
the processing of a query. If you interrupt a pager which dies on ^C
(like more), psql stops generating output straight away instead of
continuing.

I've tested as much as I can think of and it handles everything nicely.
The question of ignoring SIGINT while the pager is active is
orthoginal, this patch doesn't address that at all.

Any comments on coding style and/or side-effects of this patch, don't
hesitate to forward to me.

Also available at:
http://svana.org/kleptog/pgsql/psql-ctrlc.patch

Have a nice day,
-- 
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.
Index: src/bin/psql/common.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/common.c,v
retrieving revision 1.108
diff -c -r1.108 common.c
*** src/bin/psql/common.c       15 Oct 2005 02:49:40 -0000      1.108
--- src/bin/psql/common.c       22 Oct 2005 19:11:41 -0000
***************
*** 230,240 ****
--- 230,267 ----
   * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
   * to protect the PGcancel structure against being changed while the other
   * thread is using it.
+  *
+  * * *
+  * About ctrlc_abort_jmp and ctrlc_abort_active:
+  *
+  * In the past the SIGINT handler, when it couldn't find a query to cancel,
+  * would simply longjmp() back to the main loop. Very simple but
+  * unfortunatly it leaked memory like crazy and file descriptors too if you
+  * were using a pager. Hence, the handler has been changed to just set a
+  * flag in those cases so the code would notice and gracefully quit.
+  *
+  * However, PostgreSQL uses signals with BSD semantics, which means the
+  * system calls get restarted across a signal. Hence when sitting at the
+  * prompt pressing Ctrl-C appeared to do nothing because the kernel would
+  * keep restarting the read(). The only way to avoid this is to have the
+  * signal handler longjmp().
+  *
+  * Hence the rules are as follows: If you are reading from a descriptor
+  * which may block indefinitly and you want to be able to respond to Ctrl-C
+  * you should setup with sigsetjmp() the jump buffer ctrlc_abort_jmp. And
+  * when you are about to read something set ctrlc_abort_active to true. 
+  * After the read set it back to false. If while the flag is true the user
+  * presses Ctrl-C, control will pass to the sigsetjmp() block which allows
+  * you to clean up gracefully and return. See gets_basic() and
+  * gets_readline() in input.c for examples.
   */
  static PGcancel *cancelConn = NULL;
  
  #ifdef WIN32
  static CRITICAL_SECTION cancelConnLock;
+ #else
+ extern sigjmp_buf     ctrlc_abort_jmp;
+ extern volatile bool  ctrlc_abort_active;
  #endif
  
  volatile bool cancel_pressed = false;
***************
*** 254,264 ****
        if (prompt_state)
                return;
  
!       if (cancelConn == NULL)
!               siglongjmp(main_loop_jmp, 1);
! 
        cancel_pressed = true;
  
        if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
                write_stderr("Cancel request sent\n");
        else
--- 281,309 ----
        if (prompt_state)
                return;
  
!       /* PostgreSQL uses BSD signal semantics, which means that system
!        * calls restart across signals. Hence just setting a flag here
!        * won't do squat if we're waiting for user input. The *only* way to
!        * abort a read/write is to siglongjump to somewhere up the stack.
!        *
!        * To this end, the input routines setup the jump buffer and a flag
!        * to indicate when it is valid. If this flag is set we jump out.
!        * Otherwise, we just set the flag and psql will notice in due
!        * course */
!        
!       if( ctrlc_abort_active )
!       {
!               ctrlc_abort_active = false;
!               siglongjmp( ctrlc_abort_jmp, 1 );
!       }
!       
!       /* We only now set the cancel flag, since in the previous cases the
!        * program deals with it directly. */
        cancel_pressed = true;
  
+       if (cancelConn == NULL)  /* Just return if no active query, we've set 
the flag */
+               return;
+ 
        if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
                write_stderr("Cancel request sent\n");
        else
Index: src/bin/psql/input.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/input.c,v
retrieving revision 1.46
diff -c -r1.46 input.c
*** src/bin/psql/input.c        15 Oct 2005 02:49:40 -0000      1.46
--- src/bin/psql/input.c        22 Oct 2005 19:11:41 -0000
***************
*** 14,19 ****
--- 14,25 ----
  #include "common.h"
  
  #ifndef WIN32
+ #include <setjmp.h>
+ sigjmp_buf      ctrlc_abort_jmp;
+ volatile bool ctrlc_abort_active;
+ #endif
+ 
+ #ifndef WIN32
  #define PSQLHISTORY ".psql_history"
  #else
  #define PSQLHISTORY "psql_history"
***************
*** 77,105 ****
        return gets_fromFile(stdin);
  }
  
- 
- /*
-  * gets_interactive()
-  *
-  * Gets a line of interactive input, using readline of desired.
-  * The result is malloc'ed.
-  */
- char *
- gets_interactive(const char *prompt)
- {
  #ifdef USE_READLINE
        char       *s;
  
!       static char *prev_hist = NULL;
  
!       if (useReadline)
!               /* On some platforms, readline is declared as readline(char *) 
*/
!               s = readline((char *) prompt);
!       else
!               s = gets_basic(prompt);
  
        if (useHistory && s && s[0])
        {
                enum histcontrol HC;
  
                HC = GetHistControlConfig();
--- 83,114 ----
        return gets_fromFile(stdin);
  }
  
  #ifdef USE_READLINE
+ static char *
+ gets_readline(const char prompt[])
+ {
        char       *s;
  
! #ifndef WIN32
!       /* Setup Ctrl-C handler, see common.c for details */
!       if (sigsetjmp(ctrlc_abort_jmp, 1) != 0)
!       {
!               fputc( '\n', stdout );
!               return strdup("\\r");  /* Clear query buffer */
!       }
!       ctrlc_abort_active = true;
! #endif
!       /* On some platforms, readline is declared as readline(char *) */
!       s = readline((char *) prompt);
  
! #ifndef WIN32
!       ctrlc_abort_active = false;
! #endif
  
        if (useHistory && s && s[0])
        {
+               static char *prev_hist = NULL;
+ 
                enum histcontrol HC;
  
                HC = GetHistControlConfig();
***************
*** 118,128 ****
        }
  
        return s;
- #else
-       return gets_basic(prompt);
- #endif
  }
  
  
  
  /*
--- 127,150 ----
        }
  
        return s;
  }
+ #endif
  
+ /*
+  * gets_interactive()
+  *
+  * Gets a line of interactive input, using readline of desired.
+  * The result is malloc'ed.
+  */
+ char *
+ gets_interactive(const char *prompt)
+ {
+ #ifdef USE_READLINE
+       if( useReadline )
+               return gets_readline( prompt );
+ #endif
+       return gets_basic(prompt);
+ }
  
  
  /*
***************
*** 138,145 ****
  
        initPQExpBuffer(&buffer);
  
!       while (fgets(line, sizeof(line), source) != NULL)
        {
                appendPQExpBufferStr(&buffer, line);
                if (buffer.data[buffer.len - 1] == '\n')
                {
--- 160,196 ----
  
        initPQExpBuffer(&buffer);
  
! #ifndef WIN32
!       /* Setup Ctrl-C handler, see common.c for details */
!       if (sigsetjmp(ctrlc_abort_jmp, 1) != 0)
!       {
!               fputc( '\n', stdout );
!               termPQExpBuffer(&buffer);
!               return strdup("\\r");  /* Clear query buffer */
!       }
! #endif
! 
!       /* The reason for this funny looking loop is to handle Ctrl-C
!        * properly. See common.c for full details, but the gist of it is
!        * that the user pressing Ctrl-C will not make the fgets return. 
!        * Hence, the ctrlc_abort_active flag is set so that *only* in the
!        * case that we are waiting in fgets does the little bit of code
!        * above get executed.  Otherwise you cause a race condition because
!        * appendPQExpBufferStr is not safe to longjmp out of. */
!       
!       while( !cancel_pressed )
        {
+               char *s;
+ #ifndef WIN32
+               ctrlc_abort_active = true;
+ #endif
+               s = fgets(line, sizeof(line), source);
+ #ifndef WIN32
+               ctrlc_abort_active = false;
+ #endif
+               if (s == NULL)
+                       break;
+                       
                appendPQExpBufferStr(&buffer, line);
                if (buffer.data[buffer.len - 1] == '\n')
                {
Index: src/bin/psql/mainloop.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.c,v
retrieving revision 1.68
diff -c -r1.68 mainloop.c
*** src/bin/psql/mainloop.c     15 Oct 2005 02:49:40 -0000      1.68
--- src/bin/psql/mainloop.c     22 Oct 2005 19:11:41 -0000
***************
*** 17,27 ****
  #include "psqlscan.h"
  #include "settings.h"
  
- #ifndef WIN32
- #include <setjmp.h>
- sigjmp_buf    main_loop_jmp;
- #endif
- 
  
  /*
   * Main processing loop for reading lines of input
--- 17,22 ----
***************
*** 84,124 ****
                        {
                                /*
                                 * 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;
                }
  
- #ifndef WIN32
-               if (sigsetjmp(main_loop_jmp, 1) != 0)
-               {
-                       /* got here with longjmp */
- 
-                       /* reset parsing state */
-                       resetPQExpBuffer(query_buf);
-                       psql_scan_finish(scan_state);
-                       psql_scan_reset(scan_state);
-                       count_eof = 0;
-                       slashCmdStatus = CMD_UNKNOWN;
-                       prompt_status = PROMPT_READY;
- 
-                       if (pset.cur_cmd_interactive)
-                               putc('\n', stdout);
-                       else
-                       {
-                               successResult = EXIT_USER;
-                               break;
-                       }
-               }
- 
                /*
!                * establish the control-C handler only after main_loop_jmp is 
ready
                 */
                pqsignal(SIGINT, handle_sigint);                /* control-C => 
cancel */
  #else                                                 /* WIN32 */
                setup_cancel_handler();
--- 79,98 ----
                        {
                                /*
                                 * You get here if you stopped a script with 
Ctrl-C and a
!                                * query cancel was issued. 
                                 */
                                successResult = EXIT_USER;
                                break;
                        }
!                       /* Only print this when interactive */
!                       fprintf( stderr, "Interrupted\n" );
                        cancel_pressed = false;
                }
  
                /*
!                * establish the control-C handler
                 */
+ #ifndef WIN32
                pqsignal(SIGINT, handle_sigint);                /* control-C => 
cancel */
  #else                                                 /* WIN32 */
                setup_cancel_handler();
***************
*** 286,291 ****
--- 260,268 ----
                        if (scan_result == PSCAN_INCOMPLETE ||
                                scan_result == PSCAN_EOL)
                                break;
+                               
+                       if (cancel_pressed)
+                               break;
                }
  
                psql_scan_finish(scan_state);
***************
*** 321,336 ****
                        successResult = EXIT_BADCONN;
        }
  
-       /*
-        * 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(previous_buf);
  
--- 298,303 ----
Index: src/bin/psql/mainloop.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.h,v
retrieving revision 1.17
diff -c -r1.17 mainloop.h
*** src/bin/psql/mainloop.h     1 Jan 2005 05:43:08 -0000       1.17
--- src/bin/psql/mainloop.h     22 Oct 2005 19:11:41 -0000
***************
*** 10,20 ****
  
  #include "postgres_fe.h"
  #include <stdio.h>
- #ifndef WIN32
- #include <setjmp.h>
- 
- extern sigjmp_buf main_loop_jmp;
- #endif
  
  int                   MainLoop(FILE *source);
  
--- 10,15 ----
Index: src/bin/psql/print.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.78
diff -c -r1.78 print.c
*** src/bin/psql/print.c        15 Oct 2005 02:49:40 -0000      1.78
--- src/bin/psql/print.c        22 Oct 2005 19:11:41 -0000
***************
*** 184,190 ****
  
        /* print cells */
        i = 0;
!       for (ptr = cells; *ptr; ptr++)
        {
                if (need_recordsep)
                {
--- 184,190 ----
  
        /* print cells */
        i = 0;
!       for (ptr = cells; *ptr && !cancel_pressed; ptr++)
        {
                if (need_recordsep)
                {
***************
*** 211,217 ****
        /* print footers */
  
        if (!opt_tuples_only && footers)
!               for (ptr = footers; *ptr; ptr++)
                {
                        if (need_recordsep)
                        {
--- 211,217 ----
        /* print footers */
  
        if (!opt_tuples_only && footers)
!               for (ptr = footers; *ptr && !cancel_pressed; ptr++)
                {
                        if (need_recordsep)
                        {
***************
*** 254,260 ****
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (i != 0 || (!opt_tuples_only && title))
                {
--- 254,260 ----
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (i != 0 || (!opt_tuples_only && title))
                {
***************
*** 277,283 ****
        }
  
        /* print footers */
!       if (!opt_tuples_only && footers && *footers)
        {
                fputs(opt_recordsep, fout);
                for (ptr = footers; *ptr; ptr++)
--- 277,283 ----
        }
  
        /* print footers */
!       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs(opt_recordsep, fout);
                for (ptr = footers; *ptr; ptr++)
***************
*** 393,399 ****
                cell_w = NULL;
  
        /* calc column widths */
!       for (i = 0; i < col_count; i++)
        {
                tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding);
                if (tmp > widths[i])
--- 393,399 ----
                cell_w = NULL;
  
        /* calc column widths */
!       for (i = 0; i < col_count && !cancel_pressed; i++)
        {
                tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding);
                if (tmp > widths[i])
***************
*** 401,407 ****
                head_w[i] = tmp;
        }
  
!       for (i = 0, ptr = cells; *ptr; ptr++, i++)
        {
                int                     add_numeric_locale_len;
  
--- 401,407 ----
                head_w[i] = tmp;
        }
  
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; ptr++, i++)
        {
                int                     add_numeric_locale_len;
  
***************
*** 447,453 ****
                else if (opt_border == 1)
                        fputc(' ', fout);
  
!               for (i = 0; i < col_count; i++)
                {
                        unsigned int nbspace;
  
--- 447,453 ----
                else if (opt_border == 1)
                        fputc(' ', fout);
  
!               for (i = 0; i < col_count && !cancel_pressed; i++)
                {
                        unsigned int nbspace;
  
***************
*** 476,482 ****
        }
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                /* beginning of line */
                if (i % col_count == 0)
--- 476,482 ----
        }
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                /* beginning of line */
                if (i % col_count == 0)
***************
*** 530,536 ****
                _print_horizontal_line(col_count, widths, opt_border, fout);
  
        /* print footers */
!       if (footers && !opt_tuples_only)
                for (ptr = footers; *ptr; ptr++)
                        fprintf(fout, "%s\n", *ptr);
  
--- 530,536 ----
                _print_horizontal_line(col_count, widths, opt_border, fout);
  
        /* print footers */
!       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                        fprintf(fout, "%s\n", *ptr);
  
***************
*** 599,605 ****
        }
  
        /* Count cells, find their lengths */
!       for (ptr = cells; *ptr; ptr++)
                cell_count++;
  
        if (cell_count > 0)
--- 599,605 ----
        }
  
        /* Count cells, find their lengths */
!       for (ptr = cells; *ptr && !cancel_pressed; ptr++)
                cell_count++;
  
        if (cell_count > 0)
***************
*** 615,621 ****
                cell_w = NULL;
  
        /* find longest data cell */
!       for (i = 0, ptr = cells; *ptr; ptr++, i++)
        {
                int                     add_numeric_locale_len;
  
--- 615,621 ----
                cell_w = NULL;
  
        /* find longest data cell */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; ptr++, i++)
        {
                int                     add_numeric_locale_len;
  
***************
*** 651,657 ****
                strcat(divider, "-+");
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (i % col_count == 0)
                {
--- 651,657 ----
                strcat(divider, "-+");
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (i % col_count == 0)
                {
***************
*** 722,728 ****
  
        /* print footers */
  
!       if (!opt_tuples_only && footers && *footers)
        {
                if (opt_border < 2)
                        fputc('\n', fout);
--- 722,728 ----
  
        /* print footers */
  
!       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                if (opt_border < 2)
                        fputc('\n', fout);
***************
*** 832,838 ****
                fputs("  </tr>\n", fout);
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (i % col_count == 0)
                        fputs("  <tr valign=\"top\">\n", fout);
--- 832,838 ----
                fputs("  </tr>\n", fout);
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (i % col_count == 0)
                        fputs("  <tr valign=\"top\">\n", fout);
***************
*** 861,867 ****
  
        /* print footers */
  
!       if (!opt_tuples_only && footers && *footers)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
--- 861,867 ----
  
        /* print footers */
  
!       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
***************
*** 906,912 ****
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (i % col_count == 0)
                {
--- 906,912 ----
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (i % col_count == 0)
                {
***************
*** 940,946 ****
        fputs("</table>\n", fout);
  
        /* print footers */
!       if (!opt_tuples_only && footers && *footers)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
--- 940,946 ----
        fputs("</table>\n", fout);
  
        /* print footers */
!       if (!opt_tuples_only && footers && *footers && !cancel_pressed)
        {
                fputs("<p>", fout);
                for (ptr = footers; *ptr; ptr++)
***************
*** 1043,1049 ****
                fputs("\\hline\n", fout);
  
        /* print headers and count columns */
!       for (i = 0, ptr = headers; i < col_count; i++, ptr++)
        {
                if (!opt_tuples_only)
                {
--- 1043,1049 ----
                fputs("\\hline\n", fout);
  
        /* print headers and count columns */
!       for (i = 0, ptr = headers; i < col_count && !cancel_pressed; i++, ptr++)
        {
                if (!opt_tuples_only)
                {
***************
*** 1062,1068 ****
        }
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (opt_numeric_locale)
                {
--- 1062,1068 ----
        }
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (opt_numeric_locale)
                {
***************
*** 1088,1094 ****
  
        /* print footers */
  
!       if (footers && !opt_tuples_only)
                for (ptr = footers; *ptr; ptr++)
                {
                        latex_escaped_print(*ptr, fout);
--- 1088,1094 ----
  
        /* print footers */
  
!       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        latex_escaped_print(*ptr, fout);
***************
*** 1139,1145 ****
  
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                /* new record */
                if (i % col_count == 0)
--- 1139,1145 ----
  
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                /* new record */
                if (i % col_count == 0)
***************
*** 1172,1178 ****
  
        /* print footers */
  
!       if (footers && !opt_tuples_only)
                for (ptr = footers; *ptr; ptr++)
                {
                        if (opt_numeric_locale)
--- 1172,1178 ----
  
        /* print footers */
  
!       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        if (opt_numeric_locale)
***************
*** 1255,1261 ****
        fputs(".\n", fout);
  
        /* print headers and count columns */
!       for (i = 0, ptr = headers; i < col_count; i++, ptr++)
        {
                if (!opt_tuples_only)
                {
--- 1255,1261 ----
        fputs(".\n", fout);
  
        /* print headers and count columns */
!       for (i = 0, ptr = headers; i < col_count && !cancel_pressed; i++, ptr++)
        {
                if (!opt_tuples_only)
                {
***************
*** 1271,1277 ****
                fputs("\n_\n", fout);
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                if (opt_numeric_locale)
                {
--- 1271,1277 ----
                fputs("\n_\n", fout);
  
        /* print cells */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                if (opt_numeric_locale)
                {
***************
*** 1294,1300 ****
  
        /* print footers */
  
!       if (footers && !opt_tuples_only)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
--- 1294,1300 ----
  
        /* print footers */
  
!       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
***************
*** 1345,1351 ****
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr; i++, ptr++)
        {
                /* new record */
                if (i % col_count == 0)
--- 1345,1351 ----
                col_count++;
  
        /* print records */
!       for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++)
        {
                /* new record */
                if (i % col_count == 0)
***************
*** 1401,1407 ****
  
        /* print footers */
  
!       if (footers && !opt_tuples_only)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
--- 1401,1407 ----
  
        /* print footers */
  
!       if (footers && !opt_tuples_only && !cancel_pressed)
                for (ptr = footers; *ptr; ptr++)
                {
                        troff_ms_escaped_print(*ptr, fout);
***************
*** 1479,1484 ****
--- 1479,1487 ----
  
        if (opt->format == PRINT_NOTHING)
                return;
+       
+       if( cancel_pressed )
+               return;
  
        if (!footers)
                footers = default_footer;
***************
*** 1590,1595 ****
--- 1593,1607 ----
        /* Only close if we used the pager */
        if (fout == stdout && output != stdout)
        {
+               /* Some pagers like less use Ctrl-C as part of their command
+                * set. Even so, we abort our processing and warn the user
+                * what we did.  If the pager quit as a result of the
+                * SIGINT, this message won't go anywhere but they'll still
+                * see the other message from the main loop. */
+               
+               if( cancel_pressed )
+                       fprintf( output, "Interrupted\n" );
+                       
                pclose(output);
  #ifndef WIN32
                pqsignal(SIGPIPE, SIG_DFL);
***************
*** 1610,1615 ****
--- 1622,1630 ----
        char       *align;
        int                     i;
  
+       if( cancel_pressed )
+               return;
+               
        /* extract headers */
        nfields = PQnfields(result);
  
***************
*** 1632,1638 ****
                exit(EXIT_FAILURE);
        }
  
!       for (i = 0; i < ncells; i++)
        {
                if (PQgetisnull(result, i / nfields, i % nfields))
                        cells[i] = opt->nullPrint ? opt->nullPrint : "";
--- 1647,1653 ----
                exit(EXIT_FAILURE);
        }
  
!       for (i = 0; i < ncells && !cancel_pressed; i++)
        {
                if (PQgetisnull(result, i / nfields, i % nfields))
                        cells[i] = opt->nullPrint ? opt->nullPrint : "";
***************
*** 1670,1676 ****
                exit(EXIT_FAILURE);
        }
  
!       for (i = 0; i < nfields; i++)
        {
                Oid                     ftype = PQftype(result, i);
  
--- 1685,1691 ----
                exit(EXIT_FAILURE);
        }
  
!       for (i = 0; i < nfields && !cancel_pressed; i++)
        {
                Oid                     ftype = PQftype(result, i);
  
***************
*** 1689,1697 ****
        }
  
        /* call table printer */
!       printTable(opt->title, headers, cells,
!                          (const char *const *) footers,
!                          align, &opt->topt, fout, flog);
  
        free(headers);
        free(cells);
--- 1704,1713 ----
        }
  
        /* call table printer */
!       if( !cancel_pressed )
!               printTable(opt->title, headers, cells,
!                                  (const char *const *) footers,
!                                  align, &opt->topt, fout, flog);
  
        free(headers);
        free(cells);
Index: src/bin/scripts/common.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/scripts/common.c,v
retrieving revision 1.19
diff -c -r1.19 common.c
*** src/bin/scripts/common.c    15 Oct 2005 02:49:41 -0000      1.19
--- src/bin/scripts/common.c    22 Oct 2005 19:11:41 -0000
***************
*** 19,24 ****
--- 19,27 ----
  
  #include "common.h"
  
+ /* This variable is not used in ../scripts, but is required because print.c 
needs it */
+ volatile bool cancel_pressed;
+ 
  #ifndef HAVE_INT_OPTRESET
  int                   optreset;
  #endif
Index: src/bin/scripts/common.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/scripts/common.h,v
retrieving revision 1.12
diff -c -r1.12 common.h
*** src/bin/scripts/common.h    15 Oct 2005 02:49:41 -0000      1.12
--- src/bin/scripts/common.h    22 Oct 2005 19:11:41 -0000
***************
*** 17,22 ****
--- 17,25 ----
  extern int    optreset;
  #endif
  
+ /* This variable is not used in ../scripts, but is required because print.c 
needs it */
+ extern volatile bool cancel_pressed;
+ 
  typedef void (*help_handler) (const char *progname);
  
  extern const char *get_user_name(const char *progname);

Attachment: pgpBTMbKSI385.pgp
Description: PGP signature

Reply via email to