Re: Patch for psql History Display on MacOSX

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Patch for psql History Display on MacOSX
Дата
Msg-id 27815.1409626565@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Patch for psql History Display on MacOSX  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I've confirmed that the attached patches work as expected in both the
oldest and newest readline and libedit versions available to me.
Barring further objections, I plan to commit and back-patch these
changes.

            regards, tom lane

diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 74d4618..6033dfd 100644
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** EOF
*** 277,283 ****
        <term><option>--no-readline</></term>
        <listitem>
        <para>
!        Do not use <application>readline</application> for line editing and do not use the history.
         This can be useful to turn off tab expansion when cutting and pasting.
        </para>
        </listitem>
--- 277,284 ----
        <term><option>--no-readline</></term>
        <listitem>
        <para>
!        Do not use <application>Readline</application> for line editing and do
!        not use the command history.
         This can be useful to turn off tab expansion when cutting and pasting.
        </para>
        </listitem>
*************** lo_import 152801
*** 2357,2368 ****
          <term><literal>\s [ <replaceable class="parameter">filename</replaceable> ]</literal></term>
          <listitem>
          <para>
!         Print or save the command line history to <replaceable
!         class="parameter">filename</replaceable>. If <replaceable
!         class="parameter">filename</replaceable> is omitted, the history
!         is written to the standard output. This option is only available
!         if <application>psql</application> is configured to use the
!         <acronym>GNU</acronym> <application>Readline</application> library.
          </para>
          </listitem>
        </varlistentry>
--- 2358,2370 ----
          <term><literal>\s [ <replaceable class="parameter">filename</replaceable> ]</literal></term>
          <listitem>
          <para>
!         Print <application>psql</application>'s command line history
!         to <replaceable class="parameter">filename</replaceable>.
!         If <replaceable class="parameter">filename</replaceable> is omitted,
!         the history is written to the standard output (using the pager if
!         appropriate).  This command is not available
!         if <application>psql</application> was built
!         without <application>Readline</application> support.
          </para>
          </listitem>
        </varlistentry>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index e16b4d5..e1949d8 100644
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 1088,1107 ****
          char       *fname = psql_scan_slash_option(scan_state,
                                                     OT_NORMAL, NULL, true);

- #if defined(WIN32) && !defined(__CYGWIN__)
-
-         /*
-          * XXX This does not work for all terminal environments or for output
-          * containing non-ASCII characters; see comments in simple_prompt().
-          */
- #define DEVTTY    "con"
- #else
- #define DEVTTY    "/dev/tty"
- #endif
-
          expand_tilde(&fname);
!         /* This scrolls off the screen when using /dev/tty */
!         success = saveHistory(fname ? fname : DEVTTY, -1, false, false);
          if (success && !pset.quiet && fname)
              printf(_("Wrote history to file \"%s\".\n"), fname);
          if (!fname)
--- 1088,1095 ----
          char       *fname = psql_scan_slash_option(scan_state,
                                                     OT_NORMAL, NULL, true);

          expand_tilde(&fname);
!         success = printHistory(fname, pset.popt.topt.pager);
          if (success && !pset.quiet && fname)
              printf(_("Wrote history to file \"%s\".\n"), fname);
          if (!fname)
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index aa32a3f..2e01eb1 100644
*** a/src/bin/psql/input.c
--- b/src/bin/psql/input.c
***************
*** 11,16 ****
--- 11,17 ----
  #include <unistd.h>
  #endif
  #include <fcntl.h>
+ #include <limits.h>

  #include "input.h"
  #include "settings.h"
*************** initializeInput(int flags)
*** 319,340 ****


  /*
!  * This function saves the readline history when user
!  * runs \s command or when psql exits.
   *
   * fname: pathname of history file.  (Should really be "const char *",
   * but some ancient versions of readline omit the const-decoration.)
   *
   * max_lines: if >= 0, limit history file to that many entries.
-  *
-  * appendFlag: if true, try to append just our new lines to the file.
-  * If false, write the whole available history.
-  *
-  * encodeFlag: whether to encode \n as \x01.  For \s calls we don't wish
-  * to do that, but must do so when saving the final history file.
   */
! bool
! saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag)
  {
  #ifdef USE_READLINE

--- 320,334 ----


  /*
!  * This function saves the readline history when psql exits.
   *
   * fname: pathname of history file.  (Should really be "const char *",
   * but some ancient versions of readline omit the const-decoration.)
   *
   * max_lines: if >= 0, limit history file to that many entries.
   */
! static bool
! saveHistory(char *fname, int max_lines)
  {
  #ifdef USE_READLINE

*************** saveHistory(char *fname, int max_lines,
*** 344,354 ****
       * where write_history will fail because it tries to chmod the target
       * file.
       */
!     if (useHistory && fname &&
!         strcmp(fname, DEVNULL) != 0)
      {
!         if (encodeFlag)
!             encode_history();

          /*
           * On newer versions of libreadline, truncate the history file as
--- 338,352 ----
       * where write_history will fail because it tries to chmod the target
       * file.
       */
!     if (strcmp(fname, DEVNULL) != 0)
      {
!         /*
!          * Encode \n, since otherwise readline will reload multiline history
!          * entries as separate lines.  (libedit doesn't really need this, but
!          * we do it anyway since it's too hard to tell which implementation we
!          * are using.)
!          */
!         encode_history();

          /*
           * On newer versions of libreadline, truncate the history file as
*************** saveHistory(char *fname, int max_lines,
*** 362,368 ****
           * see if the write failed.  Similarly for append_history.
           */
  #if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
-         if (appendFlag)
          {
              int            nlines;
              int            fd;
--- 360,365 ----
*************** saveHistory(char *fname, int max_lines,
*** 387,394 ****
              if (errno == 0)
                  return true;
          }
!         else
! #endif
          {
              /* truncate what we have ... */
              if (max_lines >= 0)
--- 384,390 ----
              if (errno == 0)
                  return true;
          }
! #else                            /* don't have append support */
          {
              /* truncate what we have ... */
              if (max_lines >= 0)
*************** saveHistory(char *fname, int max_lines,
*** 399,417 ****
              if (errno == 0)
                  return true;
          }

          psql_error("could not save history to file \"%s\": %s\n",
                     fname, strerror(errno));
      }
- #else
-     /* only get here in \s case, so complain */
-     psql_error("history is not supported by this installation\n");
  #endif

      return false;
  }


  static void
  finishInput(void)
  {
--- 395,468 ----
              if (errno == 0)
                  return true;
          }
+ #endif

          psql_error("could not save history to file \"%s\": %s\n",
                     fname, strerror(errno));
      }
  #endif

      return false;
  }


+ /*
+  * Print history to the specified file, or to the console if fname is NULL
+  * (psql \s command)
+  *
+  * We used to use saveHistory() for this purpose, but that doesn't permit
+  * use of a pager; moreover libedit's implementation behaves incompatibly
+  * (preferring to encode its output) and may fail outright when the target
+  * file is specified as /dev/tty.
+  */
+ bool
+ printHistory(const char *fname, unsigned short int pager)
+ {
+ #ifdef USE_READLINE
+     FILE       *output;
+     bool        is_pager;
+     int            i;
+     HIST_ENTRY *cur_hist;
+
+     if (!useHistory)
+         return false;
+
+     if (fname == NULL)
+     {
+         /* use pager, if enabled, when printing to console */
+         output = PageOutput(INT_MAX, pager);
+         is_pager = true;
+     }
+     else
+     {
+         output = fopen(fname, "w");
+         if (output == NULL)
+         {
+             psql_error("could not save history to file \"%s\": %s\n",
+                        fname, strerror(errno));
+             return false;
+         }
+         is_pager = false;
+     }
+
+     for (i = history_base; (cur_hist = history_get(i)) != NULL; i++)
+     {
+         fprintf(output, "%s\n", cur_hist->line);
+     }
+
+     if (is_pager)
+         ClosePager(output);
+     else
+         fclose(output);
+
+     return true;
+ #else
+     psql_error("history is not supported by this installation\n");
+     return false;
+ #endif
+ }
+
+
  static void
  finishInput(void)
  {
*************** finishInput(void)
*** 421,427 ****
          int            hist_size;

          hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
!         saveHistory(psql_history, hist_size, true, true);
          free(psql_history);
          psql_history = NULL;
      }
--- 472,478 ----
          int            hist_size;

          hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
!         (void) saveHistory(psql_history, hist_size);
          free(psql_history);
          psql_history = NULL;
      }
diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h
index 1d10a22..1b22399 100644
*** a/src/bin/psql/input.h
--- b/src/bin/psql/input.h
*************** char       *gets_interactive(const char *pr
*** 42,48 ****
  char       *gets_fromFile(FILE *source);

  void        initializeInput(int flags);
! bool        saveHistory(char *fname, int max_lines, bool appendFlag, bool encodeFlag);

  void        pg_append_history(const char *s, PQExpBuffer history_buf);
  void        pg_send_history(PQExpBuffer history_buf);
--- 42,49 ----
  char       *gets_fromFile(FILE *source);

  void        initializeInput(int flags);
!
! bool        printHistory(const char *fname, unsigned short int pager);

  void        pg_append_history(const char *s, PQExpBuffer history_buf);
  void        pg_send_history(PQExpBuffer history_buf);
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 2e01eb1..f3193a2 100644
*** a/src/bin/psql/input.c
--- b/src/bin/psql/input.c
*************** gets_fromFile(FILE *source)
*** 229,244 ****
  static void
  encode_history(void)
  {
      HIST_ENTRY *cur_hist;
      char       *cur_ptr;

!     history_set_pos(0);
!     for (cur_hist = current_history(); cur_hist; cur_hist = next_history())
      {
          /* some platforms declare HIST_ENTRY.line as const char * */
          for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
              if (*cur_ptr == '\n')
                  *cur_ptr = NL_IN_HISTORY;
      }
  }

--- 229,246 ----
  static void
  encode_history(void)
  {
+     int            i;
      HIST_ENTRY *cur_hist;
      char       *cur_ptr;

!     for (i = history_base; (cur_hist = history_get(i)) != NULL; i++)
      {
          /* some platforms declare HIST_ENTRY.line as const char * */
          for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
+         {
              if (*cur_ptr == '\n')
                  *cur_ptr = NL_IN_HISTORY;
+         }
      }
  }

*************** encode_history(void)
*** 248,263 ****
  static void
  decode_history(void)
  {
      HIST_ENTRY *cur_hist;
      char       *cur_ptr;

!     history_set_pos(0);
!     for (cur_hist = current_history(); cur_hist; cur_hist = next_history())
      {
          /* some platforms declare HIST_ENTRY.line as const char * */
          for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
              if (*cur_ptr == NL_IN_HISTORY)
                  *cur_ptr = '\n';
      }
  }
  #endif   /* USE_READLINE */
--- 250,267 ----
  static void
  decode_history(void)
  {
+     int            i;
      HIST_ENTRY *cur_hist;
      char       *cur_ptr;

!     for (i = history_base; (cur_hist = history_get(i)) != NULL; i++)
      {
          /* some platforms declare HIST_ENTRY.line as const char * */
          for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
+         {
              if (*cur_ptr == NL_IN_HISTORY)
                  *cur_ptr = '\n';
+         }
      }
  }
  #endif   /* USE_READLINE */

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Dobes Vandermeer
Дата:
Сообщение: Re: Tips/advice for implementing integrated RESTful HTTP API
Следующее
От: Sawada Masahiko
Дата:
Сообщение: Re: Concurrently option for reindexdb