psql: Add quit/help behavior/hint, for other tool portability
authorBruce Momjian <bruce@momjian.us>
Thu, 1 Feb 2018 13:30:43 +0000 (08:30 -0500)
committerBruce Momjian <bruce@momjian.us>
Thu, 1 Feb 2018 13:30:43 +0000 (08:30 -0500)
Issuing 'quit'/'exit' in an empty psql buffer exits psql.  Issuing
'quit'/'exit' in a non-empty psql buffer alone on a line with no prefix
whitespace issues a hint on how to exit.

Also add similar 'help' hints for 'help' in a non-empty psql buffer.

Reported-by: Everaldo Canuto
Discussion: https://postgr.es/m/flat/CALVFHFb-C_5_94hueWg6Dd0zu7TfbpT7hzsh9Zf0DEDOSaAnfA%40mail.gmail.com

Author: original author Robert Haas, modified by me

src/bin/psql/mainloop.c

index a8778a57aa79f6371f9b3ac887eecc320a83d700..a3ef15058fcb66c3968c3e6f3a77632dbef2d6fd 100644 (file)
@@ -216,21 +216,108 @@ MainLoop(FILE *source)
            continue;
        }
 
-       /* A request for help? Be friendly and give them some guidance */
-       if (pset.cur_cmd_interactive && query_buf->len == 0 &&
-           pg_strncasecmp(line, "help", 4) == 0 &&
-           (line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4])))
+       /* Recognize "help", "quit", "exit" only in interactive mode */
+       if (pset.cur_cmd_interactive)
        {
-           free(line);
-           puts(_("You are using psql, the command-line interface to PostgreSQL."));
-           printf(_("Type:  \\copyright for distribution terms\n"
-                    "       \\h for help with SQL commands\n"
-                    "       \\? for help with psql commands\n"
-                    "       \\g or terminate with semicolon to execute query\n"
-                    "       \\q to quit\n"));
+           char       *first_word = line;
+           char       *rest_of_line = NULL;
+           bool        found_help = false;
+           bool        found_exit_or_quit = false;
 
-           fflush(stdout);
-           continue;
+           /* Search for the words we recognize;  must be first word */
+           if (pg_strncasecmp(first_word, "help", 4) == 0)
+           {
+               rest_of_line = first_word + 4;
+               found_help = true;
+           }
+           else if (pg_strncasecmp(first_word, "exit", 4) == 0 ||
+                    pg_strncasecmp(first_word, "quit", 4) == 0)
+           {
+               rest_of_line = first_word + 4;
+               found_exit_or_quit = true;
+           }
+
+           /*
+            * If we found a command word, check whether the rest of the line
+            * contains only whitespace plus maybe one semicolon.  If not,
+            * ignore the command word after all.
+            */
+           if (rest_of_line != NULL)
+           {
+               /*
+                * Ignore unless rest of line is whitespace, plus maybe one
+                * semicolon
+                */
+               while (isspace((unsigned char) *rest_of_line))
+                   ++rest_of_line;
+               if (*rest_of_line == ';')
+                   ++rest_of_line;
+               while (isspace((unsigned char) *rest_of_line))
+                   ++rest_of_line;
+               if (*rest_of_line != '\0')
+               {
+                   found_help = false;
+                   found_exit_or_quit = false;
+               }
+           }
+
+           /*
+            * "help" is only a command when the query buffer is empty, but we
+            * emit a one-line message even when it isn't to help confused
+            * users.  The text is still added to the query buffer in that
+            * case.
+            */
+           if (found_help)
+           {
+               if (query_buf->len != 0)
+#ifndef WIN32
+                   puts(_("Use \\? for help or press control-C to clear the input buffer."));
+#else
+                   puts(_("Use \\? for help."));
+#endif
+               else
+               {
+                   puts(_("You are using psql, the command-line interface to PostgreSQL."));
+                   printf(_("Type:  \\copyright for distribution terms\n"
+                            "       \\h for help with SQL commands\n"
+                            "       \\? for help with psql commands\n"
+                            "       \\g or terminate with semicolon to execute query\n"
+                            "       \\q to quit\n"));
+                   free(line);
+                   fflush(stdout);
+                   continue;
+               }
+           }
+           /*
+            * "quit" and "exit" are only commands when the query buffer is
+            * empty, but we emit a one-line message even when it isn't to
+            * help confused users.  The text is still added to the query
+            * buffer in that case.
+            */
+           if (found_exit_or_quit)
+           {
+               if (query_buf->len != 0)
+               {
+                   if (prompt_status == PROMPT_READY ||
+                       prompt_status == PROMPT_CONTINUE ||
+                       prompt_status == PROMPT_PAREN)
+                       puts(_("Use \\q to quit."));
+                   else
+#ifndef WIN32
+                       puts(_("Use control-D to quit."));
+#else
+                       puts(_("Use control-C to quit."));
+#endif
+               }
+               else
+               {
+                   /* exit app */
+                   free(line);
+                   fflush(stdout);
+                   successResult = EXIT_SUCCESS;
+                   break;
+               }
+           }
        }
 
        /* echo back if flag is set, unless interactive */