<commit_before>/*
 * psql - the PostgreSQL interactive terminal
 *
 * Copyright 2000 by PostgreSQL Global Development Group
 *
 * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.52 2001/05/09 17:29:10 momjian Exp $
 */
#include "postgres_fe.h"
#include "command.h"

#include <errno.h>
#include <assert.h>
#include <ctype.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifndef WIN32
#include <sys/types.h>			/* for umask() */
#include <sys/stat.h>			/* for stat() */
#include <fcntl.h>				/* open() flags */
#include <unistd.h>				/* for geteuid(), getpid(), stat() */
#else
#include <win32.h>
#include <io.h>
#include <fcntl.h>
#endif

#include "libpq-fe.h"
#include "pqexpbuffer.h"

#include "common.h"
#include "copy.h"
#include "describe.h"
#include "help.h"
#include "input.h"
#include "large_obj.h"
#include "mainloop.h"
#include "print.h"
#include "settings.h"
#include "variables.h"

#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#else
/* Grand unified hard-coded badness */
#define pg_encoding_to_char(x) "SQL_ASCII"
#endif


/* functions for use in this file */

static backslashResult exec_command(const char *cmd,
			 const char *options_string,
			 const char **continue_parse,
			 PQExpBuffer query_buf);

enum option_type
{
	OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
static char *scan_option(char **string, enum option_type type, char *quote);
static char *unescape(const unsigned char *source, size_t len);

static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
static bool do_connect(const char *new_dbname, const char *new_user);
static bool do_shell(const char *command);



/*----------
 * HandleSlashCmds:
 *
 * Handles all the different commands that start with '\',
 * ordinarily called by MainLoop().
 *
 * 'line' is the current input line, which should not start with a '\'
 * but with the actual command name
 * (that is taken care of by MainLoop)
 *
 * 'query_buf' contains the query-so-far, which may be modified by
 * execution of the backslash command (for example, \r clears it)
 * query_buf can be NULL if there is no query so far.
 *
 * Returns a status code indicating what action is desired, see command.h.
 *----------
 */

backslashResult
HandleSlashCmds(const char *line,
				PQExpBuffer query_buf,
				const char **end_of_cmd)
{
	backslashResult status = CMD_SKIP_LINE;
	char	   *my_line;
	char	   *options_string = NULL;
	size_t		blank_loc;
	const char *continue_parse = NULL;	/* tell the mainloop where the
										 * backslash command ended */

#ifdef USE_ASSERT_CHECKING
	assert(line);
#endif

	my_line = xstrdup(line);

	/*
	 * Find the first whitespace. line[blank_loc] will now be the
	 * whitespace character or the \0 at the end
	 *
	 * Also look for a backslash, so stuff like \p\g works.
	 */
	blank_loc = strcspn(my_line, " \t\n\r\\");

	if (my_line[blank_loc] == '\\')
	{
		continue_parse = &my_line[blank_loc];
		my_line[blank_loc] = '\0';
		/* If it's a double backslash, we skip it. */
		if (my_line[blank_loc + 1] == '\\')
			continue_parse += 2;
	}
	/* do we have an option string? */
	else if (my_line[blank_loc] != '\0')
	{
		options_string = &my_line[blank_loc + 1];
		my_line[blank_loc] = '\0';
	}

	status = exec_command(my_line, options_string, &continue_parse, query_buf);

	if (status == CMD_UNKNOWN)
	{

		/*
		 * If the command was not recognized, try inserting a space after
		 * the first letter and call again. The one letter commands allow
		 * arguments to start immediately after the command, but that is
		 * no longer encouraged.
		 */
		char		new_cmd[2];

		new_cmd[0] = my_line[0];
		new_cmd[1] = '\0';

		status = exec_command(new_cmd, line + 1, &continue_parse, query_buf);

		/* continue_parse must be relative to my_line for calculation below */
		continue_parse += my_line - line;

#if 0							/* turned out to be too annoying */<commit_msg>Add comment to explain need for bizarre-looking coding in HandleSlashCmds.<commit_after>/*
 * psql - the PostgreSQL interactive terminal
 *
 * Copyright 2000 by PostgreSQL Global Development Group
 *
 * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.53 2001/05/12 17:37:15 tgl Exp $
 */
#include "postgres_fe.h"
#include "command.h"

#include <errno.h>
#include <assert.h>
#include <ctype.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifndef WIN32
#include <sys/types.h>			/* for umask() */
#include <sys/stat.h>			/* for stat() */
#include <fcntl.h>				/* open() flags */
#include <unistd.h>				/* for geteuid(), getpid(), stat() */
#else
#include <win32.h>
#include <io.h>
#include <fcntl.h>
#endif

#include "libpq-fe.h"
#include "pqexpbuffer.h"

#include "common.h"
#include "copy.h"
#include "describe.h"
#include "help.h"
#include "input.h"
#include "large_obj.h"
#include "mainloop.h"
#include "print.h"
#include "settings.h"
#include "variables.h"

#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#else
/* Grand unified hard-coded badness */
#define pg_encoding_to_char(x) "SQL_ASCII"
#endif


/* functions for use in this file */

static backslashResult exec_command(const char *cmd,
			 const char *options_string,
			 const char **continue_parse,
			 PQExpBuffer query_buf);

enum option_type
{
	OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
static char *scan_option(char **string, enum option_type type, char *quote);
static char *unescape(const unsigned char *source, size_t len);

static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
static bool do_connect(const char *new_dbname, const char *new_user);
static bool do_shell(const char *command);



/*----------
 * HandleSlashCmds:
 *
 * Handles all the different commands that start with '\',
 * ordinarily called by MainLoop().
 *
 * 'line' is the current input line, which should not start with a '\'
 * but with the actual command name
 * (that is taken care of by MainLoop)
 *
 * 'query_buf' contains the query-so-far, which may be modified by
 * execution of the backslash command (for example, \r clears it)
 * query_buf can be NULL if there is no query so far.
 *
 * Returns a status code indicating what action is desired, see command.h.
 *----------
 */

backslashResult
HandleSlashCmds(const char *line,
				PQExpBuffer query_buf,
				const char **end_of_cmd)
{
	backslashResult status = CMD_SKIP_LINE;
	char	   *my_line;
	char	   *options_string = NULL;
	size_t		blank_loc;
	const char *continue_parse = NULL;	/* tell the mainloop where the
										 * backslash command ended */

#ifdef USE_ASSERT_CHECKING
	assert(line);
#endif

	my_line = xstrdup(line);

	/*
	 * Find the first whitespace. line[blank_loc] will now be the
	 * whitespace character or the \0 at the end
	 *
	 * Also look for a backslash, so stuff like \p\g works.
	 */
	blank_loc = strcspn(my_line, " \t\n\r\\");

	if (my_line[blank_loc] == '\\')
	{
		continue_parse = &my_line[blank_loc];
		my_line[blank_loc] = '\0';
		/* If it's a double backslash, we skip it. */
		if (my_line[blank_loc + 1] == '\\')
			continue_parse += 2;
	}
	/* do we have an option string? */
	else if (my_line[blank_loc] != '\0')
	{
		options_string = &my_line[blank_loc + 1];
		my_line[blank_loc] = '\0';
	}

	status = exec_command(my_line, options_string, &continue_parse, query_buf);

	if (status == CMD_UNKNOWN)
	{

		/*
		 * If the command was not recognized, try to parse it as a one-letter
		 * command with immediately following argument (a still-supported,
		 * but no longer encouraged, syntax).
		 */
		char		new_cmd[2];

		new_cmd[0] = my_line[0];
		new_cmd[1] = '\0';

		/* use line for options, because my_line was clobbered above */
		status = exec_command(new_cmd, line + 1, &continue_parse, query_buf);

		/* continue_parse must be relative to my_line for calculation below */
		continue_parse += my_line - line;

#if 0							/* turned out to be too annoying */
{'fraction_non_alphanumeric': 0.13055842812823165, 'fraction_numerical': 0.010082730093071354, 'mean_word_length': 3.7408088235294117, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '774936', 'n_tokens_mistral': 2912, 'n_tokens_neox': 2692, 'n_words': 840}