mysql.cc 81.1 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17 18 19 20 21
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* mysql command tool
 * Commands compatible with mSQL by David J. Hughes
 *
 * Written by:
 *   Michael 'Monty' Widenius
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
22 23 24 25 26 27 28
 *   Andi Gutmans  <andi@zend.com>
 *   Zeev Suraski  <zeev@zend.com>
 *   Jani Tolonen  <jani@mysql.com>
 *   Matt Wagner   <matt@mysql.com>
 *   Jeremy Cole   <jcole@mysql.com>
 *   Tonu Samuel   <tonu@mysql.com>
 *   Harrison Fisk <hcfisk@buffalo.edu>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
29 30 31
 *
 **/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
32
#include "client_priv.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33
#include <m_ctype.h>
34
#include <stdarg.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
35 36
#include <my_dir.h>
#ifndef __GNU_LIBRARY__
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
37
#define __GNU_LIBRARY__		      // Skip warnings in getopt.h
bk@work.mysql.com's avatar
bk@work.mysql.com committed
38 39 40
#endif
#include "my_readline.h"
#include <signal.h>
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
41
#include <violite.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
42

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
43
const char *VER= "13.4";
44

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
45 46 47
/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH	     1024

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
48
gptr sql_alloc(unsigned size);	     // Don't use mysqld alloc for these
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62
void sql_element_free(void *ptr);
#include "sql_string.h"

extern "C" {
#if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
#include <curses.h>
#include <term.h>
#else
#if defined(HAVE_TERMIOS_H)
#include <termios.h>
#include <unistd.h>
#elif defined(HAVE_TERMBITS_H)
#include <termbits.h>
#elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
63
#include <asm/termbits.h>		// Standard linux
bk@work.mysql.com's avatar
bk@work.mysql.com committed
64 65 66 67 68 69 70 71
#endif
#undef VOID
#if defined(HAVE_TERMCAP_H)
#include <termcap.h>
#else
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
72
#undef SYSV				// hack to avoid syntax error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
73 74 75 76 77 78
#ifdef HAVE_TERM_H
#include <term.h>
#endif
#endif
#endif

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
79
#undef bcmp				// Fix problem with new readline
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
80
#if defined( __WIN__) || defined(OS2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
81
#include <conio.h>
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
82
#elif !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
83 84 85 86 87 88 89 90
#include <readline/readline.h>
#define HAVE_READLINE
#endif
  //int vidattr(long unsigned int attrs);	// Was missing in sun curses
}

#if !defined(HAVE_VIDATTR)
#undef vidattr
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
91
#define vidattr(A) {}			// Can't get this to work
bk@work.mysql.com's avatar
bk@work.mysql.com committed
92 93
#endif

94 95
#ifdef FN_NO_CASE_SENCE
#define cmp_database(A,B) my_strcasecmp(system_charset_info, (A), (B))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
96 97 98 99
#else
#define cmp_database(A,B) strcmp((A),(B))
#endif

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
100 101 102 103
#if !defined( __WIN__) && !defined( OS2) && !defined(__NETWARE__) && (!defined(HAVE_mit_thread) || !defined(THREAD))
#define USE_POPEN
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
104 105
#include "completion_hash.h"

jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
106 107
#define PROMPT_CHAR '\\'

bk@work.mysql.com's avatar
bk@work.mysql.com committed
108 109 110 111 112 113 114 115 116 117 118
typedef struct st_status
{
  int exit_status;
  ulong query_start_line;
  char *file_name;
  LINE_BUFFER *line_buff;
  bool batch,add_to_history;
} STATUS;


static HashTable ht;
119
static char **defaults_argv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
120 121 122 123 124

enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
typedef enum enum_info_type INFO_TYPE;

static MYSQL mysql;			/* The connection */
125 126
static my_bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0,
               connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
127
	       rehash=1,skip_updates=0,safe_updates=0,one_database=0,
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
128
	       opt_compress=0, using_opt_local_infile=0,
129 130
	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
               opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
131
               tty_password= 0, opt_nobeep=0;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
132
static uint verbose=0,opt_silent=0,opt_mysql_port=0, opt_local_infile=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
133 134 135
static my_string opt_mysql_unix_port=0;
static int connect_flag=CLIENT_INTERACTIVE;
static char *current_host,*current_db,*current_user=0,*opt_password=0,
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
136
            *current_prompt=0, *default_charset;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
137 138
static char *histfile;
static String glob_buffer,old_buffer;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
139 140
static String processed_prompt;
static char *full_username=0,*part_username=0,*default_prompt=0;
141
static int wait_time = 5;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
142
static STATUS status;
143
static ulong select_limit,max_join_size,opt_connect_timeout=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
144
static char mysql_charsets_dir[FN_REFLEN+1];
145
static const char *xmlmeta[] = {
146 147 148 149
  "&", "&amp;",
  "<", "&lt;",
  0, 0
};
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
150 151 152 153 154 155 156 157
static const char *day_names[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *month_names[]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
			    "Aug","Sep","Oct","Nov","Dec"};
static char default_pager[FN_REFLEN];
static char pager[FN_REFLEN], outfile[FN_REFLEN];
static FILE *PAGER, *OUTFILE;
static MEM_ROOT hash_mem_root;
static uint prompt_counter;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
158

159 160 161 162 163
#ifdef HAVE_SMEM
static char *shared_memory_base_name=0;
#endif
static uint opt_protocol=0;
			   
bk@work.mysql.com's avatar
bk@work.mysql.com committed
164 165 166 167 168 169
#include "sslopt-vars.h"

#ifndef DBUG_OFF
const char *default_dbug_option="d:t:o,/tmp/mysql.trace";
#endif

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
170 171 172
void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
173
void tee_putc(int c, FILE *file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
174 175 176 177
/* The names of functions that actually do the manipulation. */
static int get_options(int argc,char **argv);
static int com_quit(String *str,char*),
	   com_go(String *str,char*), com_ego(String *str,char*),
178
	   com_print(String *str,char*),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
179 180 181
	   com_help(String *str,char*), com_clear(String *str,char*),
	   com_connect(String *str,char*), com_status(String *str,char*),
	   com_use(String *str,char*), com_source(String *str, char*),
182
	   com_rehash(String *str, char*), com_tee(String *str, char*),
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
183
           com_notee(String *str, char*),
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
184
           com_prompt(String *str, char*);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
185

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
186
#ifdef USE_POPEN
187
static int com_nopager(String *str, char*), com_pager(String *str, char*),
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
188
           com_edit(String *str,char*), com_shell(String *str, char *);
189 190
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
191 192 193 194 195
static int read_lines(bool execute_commands);
static int sql_connect(char *host,char *database,char *user,char *password,
		       uint silent);
static int put_info(const char *str,INFO_TYPE info,uint error=0);
static void safe_put_field(const char *pos,ulong length);
196
static void xmlencode_print(const char *src, uint length);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
197 198
static void init_pager();
static void end_pager();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
199
static void init_tee(const char *);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
200
static void end_tee();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
201
static const char* construct_prompt();
202
static char *get_arg(char *line, my_bool get_next_arg);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
203 204
static void init_username();
static void add_int_to_prompt(int toadd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206 207 208 209 210 211 212 213 214 215 216 217

/* A structure which contains information on the commands this program
   can understand. */

typedef struct {
  const char *name;		/* User printable name of the function. */
  char cmd_char;		/* msql command character */
  int (*func)(String *str,char *); /* Function to call to do the job. */
  bool takes_params;		/* Max parameters for command */
  const char *doc;		/* Documentation for this function.  */
} COMMANDS;

static COMMANDS commands[] = {
218 219
  { "help",   'h', com_help,   1, "Display this help." },
  { "?",      '?', com_help,   1, "Synonym for `help'." },
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
220
  { "clear",  'c', com_clear,  0, "Clear command."},
221
  { "connect",'r', com_connect,1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
222
    "Reconnect to the server. Optional arguments are db and host." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
223
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
224
  { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
225
#endif
226
  { "ego",    'G', com_ego,    0,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
227 228 229
    "Send command to mysql server, display result vertically."},
  { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
  { "go",     'g', com_go,     0, "Send command to mysql server." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
230
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
231 232 233
  { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
#endif
  { "notee",  't', com_notee,  0, "Don't write into outfile." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
234
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
235 236 237 238
  { "pager",  'P', com_pager,  1, 
    "Set PAGER [to_pager]. Print the query results via PAGER." },
#endif
  { "print",  'p', com_print,  0, "Print current command." },
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
239
  { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
240 241
  { "quit",   'q', com_quit,   0, "Quit mysql." },
  { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
bk@work.mysql.com's avatar
bk@work.mysql.com committed
242
  { "source", '.', com_source, 1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
243 244
    "Execute a SQL script file. Takes a file name as an argument."},
  { "status", 's', com_status, 0, "Get status information from the server."},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
245
#ifdef USE_POPEN
246 247
  { "system", '!', com_shell,  1, "Execute a system shell command."},
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
248 249
  { "tee",    'T', com_tee,    1, 
    "Set outfile [to_outfile]. Append everything into given outfile." },
250
  { "use",    'u', com_use,    1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
251
    "Use another database. Takes database name as argument." },
bk@work.mysql.com's avatar
bk@work.mysql.com committed
252

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
253
  /* Get bash-like expansion for some commands */
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
  { "create table",     0, 0, 0, ""},
  { "create database",  0, 0, 0, ""},
  { "drop",             0, 0, 0, ""},
  { "select",           0, 0, 0, ""},
  { "insert",           0, 0, 0, ""},
  { "replace",          0, 0, 0, ""},
  { "update",           0, 0, 0, ""},
  { "delete",           0, 0, 0, ""},
  { "explain",          0, 0, 0, ""},
  { "show databases",   0, 0, 0, ""},
  { "show fields from", 0, 0, 0, ""},
  { "show keys from",   0, 0, 0, ""},
  { "show tables",      0, 0, 0, ""},
  { "load data from",   0, 0, 0, ""},
  { "alter table",      0, 0, 0, ""},
  { "set option",       0, 0, 0, ""},
  { "lock tables",      0, 0, 0, ""},
  { "unlock tables",    0, 0, 0, ""},
  { (char *)NULL,       0, 0, 0, ""}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
273 274 275
};

static const char *load_default_groups[]= { "mysql","client",0 };
276 277
static const char *server_default_groups[]=
{ "server", "embedded", "mysql_SERVER", 0 };
bk@work.mysql.com's avatar
bk@work.mysql.com committed
278 279

#ifdef HAVE_READLINE
280 281 282
extern "C" int add_history(const char *command); /* From readline directory */
extern "C" int read_history(const char *command);
extern "C" int write_history(const char *command);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
283 284 285 286
static void initialize_readline (char *name);
#endif

static COMMANDS *find_command (char *name,char cmd_name);
287 288
static bool add_line(String &buffer,char *line,char *in_string,
                     bool *ml_comment);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
289 290 291
static void remove_cntrl(String &buffer);
static void print_table_data(MYSQL_RES *result);
static void print_table_data_html(MYSQL_RES *result);
292
static void print_table_data_xml(MYSQL_RES *result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
static void print_tab_data(MYSQL_RES *result);
static void print_table_data_vertically(MYSQL_RES *result);
static ulong start_timer(void);
static void end_timer(ulong start_time,char *buff);
static void mysql_end_timer(ulong start_time,char *buff);
static void nice_time(double sec,char *buff,bool part_second);
static sig_handler mysql_end(int sig);


int main(int argc,char *argv[])
{
  char buff[80];

  MY_INIT(argv[0]);
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);

jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
310 311 312 313 314 315
  default_prompt = my_strdup(getenv("MYSQL_PS1") ? 
			     getenv("MYSQL_PS1") : 
			     "mysql> ",MYF(MY_WME));
  current_prompt = my_strdup(default_prompt,MYF(MY_WME));
  prompt_counter=0;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
316 317
  outfile[0]=0;			// no (default) outfile
  strmov(pager, "stdout");	// the default, if --pager wasn't given
318 319 320 321 322
  {
    char *tmp=getenv("PAGER");
    if (tmp)
      strmov(default_pager,tmp);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
323 324 325 326 327 328 329 330 331
  if (!isatty(0) || !isatty(1))
  {
    status.batch=1; opt_silent=1;
    ignore_errors=0;
  }
  else
    status.add_to_history=1;
  status.exit_status=1;
  load_defaults("my",load_default_groups,&argc,&argv);
332
  defaults_argv=argv;
333
  if (get_options(argc, (char **) argv))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
334
  {
335
    free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
336 337 338 339 340
    my_end(0);
    exit(1);
  }
  if (status.batch && !status.line_buff &&
      !(status.line_buff=batch_readline_init(max_allowed_packet+512,stdin)))
341 342
  {
    free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
343
    exit(1);
344
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
345
  glob_buffer.realloc(512);
346 347 348
  mysql_server_init(0, NULL, (char**) server_default_groups);
  completion_hash_init(&ht, 128);
  init_alloc_root(&hash_mem_root, 16384, 0);
349
  bzero((char*) &mysql, sizeof(mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
350 351 352
  if (sql_connect(current_host,current_db,current_user,opt_password,
		  opt_silent))
  {
353 354 355
    quick=1;					// Avoid history
    status.exit_status=1;
    mysql_end(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
356 357 358 359
  }
  if (!status.batch)
    ignore_errors=1;				// Don't abort monitor
  signal(SIGINT, mysql_end);			// Catch SIGINT to clean up
360
  signal(SIGQUIT, mysql_end);			// Catch SIGQUIT to clean up
bk@work.mysql.com's avatar
bk@work.mysql.com committed
361

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
362
  /*
363
    Run in interactive mode like the ingres/postgres monitor
bk@work.mysql.com's avatar
bk@work.mysql.com committed
364 365 366 367 368 369 370 371 372 373 374
  */

  put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
	   INFO_INFO);
  sprintf((char*) glob_buffer.ptr(),
	  "Your MySQL connection id is %ld to server version: %s\n",
	  mysql_thread_id(&mysql),mysql_get_server_info(&mysql));
  put_info((char*) glob_buffer.ptr(),INFO_INFO);

#ifdef HAVE_READLINE
  initialize_readline(my_progname);
375
  if (!status.batch && !quick && !opt_html && !opt_xml)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
376
  {
377
    /* read-history from file, default ~/.mysql_history*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
378 379 380 381
    if (getenv("MYSQL_HISTFILE"))
      histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
    else if (getenv("HOME"))
    {
382 383
      histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
				 + (uint) strlen("/.mysql_history")+2,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
384 385 386 387 388 389 390
				 MYF(MY_WME));
      if (histfile)
	sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
    }
    if (histfile)
    {
      if (verbose)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
391
	tee_fprintf(stdout, "Reading history-file %s\n",histfile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
392 393 394 395
      read_history(histfile);
    }
  }
#endif
396 397 398
  sprintf(buff, "%s",
	  "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n");
#ifdef NOT_YET
399
	  "Type 'help [[%]function name[%]]' to get help on usage of function.\n");
400
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
401 402
  put_info(buff,INFO_INFO);
  status.exit_status=read_lines(1);		// read lines and execute them
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
403 404
  if (opt_outfile)
    end_tee();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
405 406 407 408 409 410 411 412
  mysql_end(0);
#ifndef _lint
  DBUG_RETURN(0);				// Keep compiler happy
#endif
}

sig_handler mysql_end(int sig)
{
413
  mysql_close(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
414
#ifdef HAVE_READLINE
415
  if (!status.batch && !quick && !opt_html && !opt_xml)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
416 417 418
  {
    /* write-history */
    if (verbose)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
419
      tee_fprintf(stdout, "Writing history-file %s\n",histfile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
420 421 422 423
    write_history(histfile);
  }
  batch_readline_end(status.line_buff);
  completion_hash_free(&ht);
424 425
  free_root(&hash_mem_root,MYF(0));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
426
#endif
427 428
  if (sig >= 0)
    put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
429 430
  glob_buffer.free();
  old_buffer.free();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
431
  processed_prompt.free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
432 433 434 435 436 437
  my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
  my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR));
  my_free(histfile,MYF(MY_ALLOW_ZERO_PTR));
  my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
  my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
  my_free(current_user,MYF(MY_ALLOW_ZERO_PTR));
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
438 439 440
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(default_prompt,MYF(MY_ALLOW_ZERO_PTR));
441 442 443
#ifdef HAVE_SMEM
  my_free(shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR));
#endif
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
444
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
445
  mysql_server_end();
446
  free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
447 448 449 450
  my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
  exit(status.exit_status);
}

451 452

static struct my_option my_long_options[] =
bk@work.mysql.com's avatar
bk@work.mysql.com committed
453
{
454 455 456 457
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
  {"auto-rehash", OPT_AUTO_REHASH,
   "Enable automatic rehashing. One doesn't need to use 'rehash' to get table and field completion, but startup and reconnecting may take a longer time. Disable with --disable-auto-rehash.",
458
   (gptr*) &rehash, (gptr*) &rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
459
  {"no-auto-rehash", 'A',
460
   "No automatic rehashing. One has to use 'rehash' to get table and field completion. This gives a quicker start of mysql and disables rehashing on reconnect. WARNING: options deprecated; use --disable-auto-rehash instead.",
461 462 463 464 465 466
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"batch", 'B',
   "Print results with a tab as separator, each row on new line. Doesn't use history file.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"character-sets-dir", OPT_CHARSETS_DIR,
   "Directory where character sets are.", (gptr*) &charsets_dir,
   (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
467 468 469
  {"default-character-set", OPT_DEFAULT_CHARSET,
   "Set the default character set.", (gptr*) &default_charset,
   (gptr*) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
470 471 472
  {"compress", 'C', "Use compression in server/client protocol.",
   (gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
473
#ifndef DBUG_OFF
474 475 476
  {"debug", '#', "Output debug log.", (gptr*) &default_dbug_option,
   (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
477
  {"database", 'D', "Database to use.", (gptr*) &current_db,
478
   (gptr*) &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
479 480 481 482 483 484 485 486
  {"execute", 'e', "Execute command and quit. (Output like with --batch).", 0,
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"vertical", 'E', "Print the output of a query (rows) vertically.",
   (gptr*) &vertical, (gptr*) &vertical, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
   0},
  {"force", 'f', "Continue even if we get an sql error.",
   (gptr*) &ignore_errors, (gptr*) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
   0, 0, 0, 0},
487
  {"no-named-commands", 'g',
488
   "Named commands are disabled. Use \\* form only, or use named commands only in the beginning of a line ending with a semicolon (;) Since version 10.9 the client now starts with this option ENABLED by default! Disable with '-G'. Long format commands still work from the first line. WARNING: option deprecated; use --disable-named-commands instead.",
489 490
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"named-commands", 'G',
491
   "Enable named commands. Named commands mean this program's internal commands; see mysql> help . When enabled, the named commands can be used from any line of the query, otherwise only from the first line, before an enter. Disable with --disable-named-commands. This option is disabled by default.",
492 493
   (gptr*) &named_cmds, (gptr*) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
494
  {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0,
495
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
496
  {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
497 498
   (gptr*) &opt_local_infile,
   (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
499 500
  {"no-beep", 'b', "Turn off beep on error.", (gptr*) &opt_nobeep,
   (gptr*) &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, 
501
  {"host", 'h', "Connect to host.", (gptr*) &current_host,
502
   (gptr*) &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
503 504 505 506 507 508
  {"html", 'H', "Produce HTML output.", (gptr*) &opt_html, (gptr*) &opt_html,
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"xml", 'X', "Produce XML output", (gptr*) &opt_xml, (gptr*) &opt_xml, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"line-numbers", OPT_LINE_NUMBERS, "Write line numbers for errors.",
   (gptr*) &line_numbers, (gptr*) &line_numbers, 0, GET_BOOL,
509
   NO_ARG, 1, 0, 0, 0, 0, 0},  
510
  {"skip-line-numbers", 'L', "Don't write line number for errors. WARNING: -L is deprecated, use long version of this option instead.", 0, 0, 0, GET_NO_ARG,
511
   NO_ARG, 0, 0, 0, 0, 0, 0},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
512
#ifdef USE_POPEN
513
  {"no-pager", OPT_NOPAGER,
514
   "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.",
515
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
516
#endif
517
  {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG,
518 519 520 521 522
   NO_ARG, 0, 0, 0, 0, 0, 0},
  {"unbuffered", 'n', "Flush buffer after each query.", (gptr*) &unbuffered,
   (gptr*) &unbuffered, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"column-names", OPT_COLUMN_NAMES, "Write column names in results.",
   (gptr*) &column_names, (gptr*) &column_names, 0, GET_BOOL,
523
   NO_ARG, 1, 0, 0, 0, 0, 0},
524
  {"skip-column-names", 'N',
525
   "Don't write column names in results. WARNING: -N is deprecated, use long version of this options instead.",
526 527
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"set-variable", 'O',
528
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
529 530 531 532
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"one-database", 'o',
   "Only update the default database. This is useful for skipping updates to other database in the update log.",
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
533
#ifdef USE_POPEN
534 535 536
  {"pager", OPT_PAGER,
   "Pager to use to display results. If you don't supply an option the default pager is taken from your ENV variable PAGER. Valid pagers are less, more, cat [> filename], etc. See interactive help (\\h) also. This option does not work in batch mode.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
537
#endif
538 539 540
  {"password", 'p',
   "Password to use when connecting to server. If password is not given it's asked from the tty.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
541
#ifdef __WIN__
542 543
  {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
544
#endif
545 546 547
  {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
   (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0,
   0},
548
  {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
549
   (gptr*) &current_prompt, (gptr*) &current_prompt, 0, GET_STR_ALLOC,
550
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
551 552
  {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory)",
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
553 554 555 556 557 558 559 560
  {"quick", 'q',
   "Don't cache result, print it row by row. This may slow down the server if the output is suspended. Doesn't use history file. ",
   (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"raw", 'r', "Write fields without conversion. Used with --batch",
   (gptr*) &opt_raw_data, (gptr*) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
  {"silent", 's', "Be more silent.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0,
   0, 0},
561 562 563 564 565
#ifdef HAVE_SMEM
  {"shared_memory_base_name", OPT_SHARED_MEMORY_BASE_NAME,
   "Base name of shared memory", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name, 
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif
566
  {"socket", 'S', "Socket file to use for connection.",
567
   (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR_ALLOC,
568
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
569
#include "sslopt-longopts.h"
570 571 572 573 574 575 576
  {"table", 't', "Output in table format.", (gptr*) &output_tables,
   (gptr*) &output_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"debug-info", 'T', "Print some debug info at exit.", (gptr*) &info_flag,
   (gptr*) &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"tee", OPT_TEE,
   "Append everything into outfile. See interactive help (\\h) also. Does not work in batch mode.",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
577
#ifndef DONT_ALLOW_USER_CHANGE
578
  {"user", 'u', "User for login if not current user.", (gptr*) &current_user,
579
   (gptr*) &current_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
580
#endif
581 582 583
  {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0,
   0, 0, 0, 0},
584 585 586
  {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0,
   0, 0, 0, 0},
587 588 589 590 591 592 593
  {"verbose", 'v', "Write more. (-v -v -v gives the table output format)", 0,
   0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"version", 'V', "Output version information and exit.", 0, 0, 0,
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"wait", 'w', "Wait and retry if connection is down.", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
  {"connect_timeout", OPT_CONNECT_TIMEOUT, "", (gptr*) &opt_connect_timeout,
594
   (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0,
595 596
   0, 1},
  {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, "",
597
   (gptr*) &max_allowed_packet, (gptr*) &max_allowed_packet, 0, GET_ULONG,
598 599 600
   REQUIRED_ARG, 16 *1024L*1024L, 4096, 512*1024L*1024L, MALLOC_OVERHEAD,
   1024, 0},
  {"net_buffer_length", OPT_NET_BUFFER_LENGTH, "",
601
   (gptr*) &net_buffer_length, (gptr*) &net_buffer_length, 0, GET_ULONG,
602 603
   REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
  {"select_limit", OPT_SELECT_LIMIT, "", (gptr*) &select_limit,
604
   (gptr*) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ~0L, 0, 1, 0},
605
  {"max_join_size", OPT_MAX_JOIN_SIZE, "", (gptr*) &max_join_size,
606
   (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ~0L, 0, 1,
607 608
   0},
  { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
609 610 611 612 613
};


static void usage(int version)
{
614 615
  printf("%s  Ver %s Distrib %s, for %s (%s)\n",
	 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
616 617
  if (version)
    return;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
618
  printf("\
619
Copyright (C) 2002 MySQL AB\n\
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
620 621
This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\
and you are welcome to modify and redistribute it under the GPL license\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
622
  printf("Usage: %s [OPTIONS] [database]\n", my_progname);
623
  my_print_help(my_long_options);
624
  print_defaults("my", load_default_groups);
625
  my_print_variables(my_long_options);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
626 627
}

628 629 630
static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
	       char *argument)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
631
{
632
  switch(optid) {
633 634 635 636 637 638 639 640 641 642
  case OPT_CHARSETS_DIR:
    strmov(mysql_charsets_dir, argument);
    charsets_dir = mysql_charsets_dir;
    break;
  case OPT_LOCAL_INFILE:
    using_opt_local_infile=1;
    break;
  case OPT_TEE:
    if (argument == disabled_my_option)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
643 644
      if (opt_outfile)
	end_tee();
645 646
    }
    else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
647
      init_tee(argument);
648 649
    break;
  case OPT_NOTEE:
650
    printf("WARNING: option deprecated; use --disable-tee instead.\n");
651 652 653 654
    if (opt_outfile)
      end_tee();
    break;
  case OPT_PAGER:
655 656
    if (argument == disabled_my_option)
      opt_nopager= 1;
657
    else
658 659 660 661 662 663 664 665
    {
      opt_nopager= 0;
      if (argument)
	strmov(pager, argument);
      else
	strmov(pager, default_pager);
      strmov(default_pager, pager);
    }
666 667
    break;
  case OPT_NOPAGER:
668
    printf("WARNING: option deprecated; use --disable-pager instead.\n");
669
    opt_nopager= 1;
670 671
  case OPT_MYSQL_PROTOCOL:
  {
672 673
    if ((opt_protocol = find_type(argument, &sql_protocol_typelib,0)) ==
	~(ulong) 0)
674 675 676 677 678 679
    {
      fprintf(stderr, "Unknown option to protocol: %s\n", argument);
      exit(1);
    }
    break;
  }
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
    break;
  case 'A':
    rehash= 0;
    break;
  case 'N':
    column_names= 0;
    break;
  case 'e':
    status.batch= 1;
    status.add_to_history= 0;
    batch_readline_end(status.line_buff);	// If multiple -e
    if (!(status.line_buff= batch_readline_command(argument)))
      return 1;
    ignore_errors= 0;
    break;
  case 'o':
    if (argument == disabled_my_option)
      one_database= 0;
    else
      one_database= skip_updates= 1;
    break;
  case 'p':
    if (argument == disabled_my_option)
      argument= (char*) "";			// Don't require password
    if (argument)
    {
      char *start= argument;
      my_free(opt_password, MYF(MY_ALLOW_ZERO_PTR));
      opt_password= my_strdup(argument, MYF(MY_FAE));
      while (*argument) *argument++= 'x';		// Destroy argument
      if (*start)
	start[1]=0 ;
    }
    else
      tty_password= 1;
    break;
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    info_flag= 1;
    break;
  case 's':
    if (argument == disabled_my_option)
      opt_silent= 0;
    else
      opt_silent++;
    break;
  case 'v':
    if (argument == disabled_my_option)
      verbose= 0;
    else
      verbose++;
    break;
  case 'B':
    if (!status.batch)
    {
      status.batch= 1;
      status.add_to_history= 0;
      opt_silent++;				// more silent
    }
    break;
  case 'W':
bk@work.mysql.com's avatar
bk@work.mysql.com committed
741
#ifdef __WIN__
742
    opt_protocol = MYSQL_PROTOCOL_PIPE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
743
#endif
744
    break;
745
#include <sslopt-case.h>
746 747 748 749 750 751 752
  case 'V':
    usage(1);
    exit(0);
  case 'I':
  case '?':
    usage(0);
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
753
  }
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
  return 0;
}


static int get_options(int argc, char **argv)
{
  char *tmp, *pagpoint;
  int ho_error;

  tmp= (char *) getenv("MYSQL_HOST");
  if (tmp)
    current_host= my_strdup(tmp, MYF(MY_WME));

  pagpoint= getenv("PAGER");
  if (!((char*) (pagpoint)))
  {
    strmov(pager, "stdout");
    opt_nopager= 1;
  }
  else
    strmov(pager, pagpoint);
  strmov(default_pager, pager);

  if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
778
    exit(ho_error);
779

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
780 781 782 783
  if (status.batch) /* disable pager and outfile in this case */
  {
    strmov(default_pager, "stdout");
    strmov(pager, "stdout");
784 785
    opt_nopager= 1;
    opt_outfile= 0;
786
    connect_flag= 0; /* Not in interactive mode */
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
787
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
788 789 790 791 792 793 794 795 796 797 798 799
  if (default_charset)
  {
    if (set_default_charset_by_name(default_charset, MYF(MY_WME)))
      exit(1);
  }
  if (argc > 1)
  {
    usage(0);
    exit(1);
  }
  if (argc == 1)
  {
800 801
    my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
    current_db= my_strdup(*argv, MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
802 803
  }
  if (tty_password)
804
    opt_password= get_tty_password(NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
805 806 807 808 809
  return(0);
}

static int read_lines(bool execute_commands)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
810
#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
811 812 813 814 815
  char linebuffer[254];
#endif
  char	*line;
  char	in_string=0;
  ulong line_number=0;
816
  bool ml_comment= 0;  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
817 818
  COMMANDS *com;
  status.exit_status=1;
819
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
820 821 822 823 824 825 826 827 828 829
  for (;;)
  {
    if (status.batch || !execute_commands)
    {
      line=batch_readline(status.line_buff);
      line_number++;
      if (!glob_buffer.length())
	status.query_start_line=line_number;
    }
    else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
830
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
831 832 833 834
      char *prompt= (char*) (glob_buffer.is_empty() ? construct_prompt() :
			     !in_string ? "    -> " :
			     in_string == '\'' ?
			     "    '> " : "    \"> ");
835 836
      if (opt_outfile && glob_buffer.is_empty())
	fflush(OUTFILE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
837 838 839 840 841 842

#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
      tee_fputs(prompt, stdout);
#ifdef __NETWARE__
      line=fgets(linebuffer, sizeof(linebuffer)-1, stdin);
      /* Remove the '\n' */
843
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
844 845 846
        char *p = strrchr(line, '\n');
	if (p != NULL)
	  *p = '\0';
847
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
848 849 850 851 852 853 854 855 856 857
#else
      linebuffer[0]= (char) sizeof(linebuffer);
      line= _cgets(linebuffer);
#endif /* __NETWARE__ */
#else
      if (opt_outfile)
	fputs(prompt, OUTFILE);
      line= readline(prompt);
#endif /* defined( __WIN__) || defined(OS2) || defined(__NETWARE__) */

858 859
      if (opt_outfile)
	fprintf(OUTFILE, "%s\n", line);
860
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
861 862 863 864 865 866 867 868
    if (!line)					// End of file
    {
      status.exit_status=0;
      break;
    }
    if (!in_string && (line[0] == '#' ||
		       (line[0] == '-' && line[1] == '-') ||
		       line[0] == 0))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
869
      continue;					// Skip comment lines
bk@work.mysql.com's avatar
bk@work.mysql.com committed
870

871 872 873 874
    /*
      Check if line is a mysql command line
      (We want to allow help, print and clear anywhere at line start
    */
875
    if (execute_commands && (named_cmds || glob_buffer.is_empty()) 
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
876
	&& !in_string && (com=find_command(line,0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
877 878 879 880 881 882 883 884 885 886 887
    {
      if ((*com->func)(&glob_buffer,line) > 0)
	break;
      if (glob_buffer.is_empty())		// If buffer was emptied
	in_string=0;
#ifdef HAVE_READLINE
      if (status.add_to_history)
	add_history(line);
#endif
      continue;
    }
888
    if (add_line(glob_buffer,line,&in_string,&ml_comment))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
      break;
  }
  /* if in batch mode, send last query even if it doesn't end with \g or go */

  if ((status.batch || !execute_commands) && !status.exit_status)
  {
    remove_cntrl(glob_buffer);
    if (!glob_buffer.is_empty())
    {
      status.exit_status=1;
      if (com_go(&glob_buffer,line) <= 0)
	status.exit_status=0;
    }
  }
  return status.exit_status;
}


static COMMANDS *find_command (char *name,char cmd_char)
{
  uint len;
  char *end;

  if (!name)
  {
    len=0;
    end=0;
  }
  else
  {
919
    while (my_isspace(system_charset_info,*name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
920 921 922 923 924 925
      name++;
    if (strchr(name,';') || strstr(name,"\\g"))
      return ((COMMANDS *) 0);
    if ((end=strcont(name," \t")))
    {
      len=(uint) (end - name);
926
      while (my_isspace(system_charset_info,*end))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
927 928 929 930 931
	end++;
      if (!*end)
	end=0;					// no arguments to function
    }
    else
932
      len=(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
933 934 935 936 937
  }

  for (uint i= 0; commands[i].name; i++)
  {
    if (commands[i].func &&
938 939
	((name && 
	  !my_strncasecmp(system_charset_info,name,commands[i].name,len) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
940 941 942 943 944 945 946 947 948
	  !commands[i].name[len] &&
	  (!end || (end && commands[i].takes_params))) ||
	 !name && commands[i].cmd_char == cmd_char))
      return (&commands[i]);
  }
  return ((COMMANDS *) 0);
}


949 950
static bool add_line(String &buffer,char *line,char *in_string,
                     bool *ml_comment)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
951 952 953 954 955 956 957 958 959 960 961 962
{
  uchar inchar;
  char buff[80],*pos,*out;
  COMMANDS *com;

  if (!line[0] && buffer.is_empty())
    return 0;
#ifdef HAVE_READLINE
  if (status.add_to_history && line[0])
    add_history(line);
#endif
#ifdef USE_MB
963
  char *strend=line+(uint) strlen(line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
964 965 966 967
#endif

  for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
  {
968
    if (my_isspace(system_charset_info,inchar) && out == line && 
969
        buffer.is_empty())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
970 971 972
      continue;
#ifdef USE_MB
    int l;
973 974
    if (use_mb(system_charset_info) &&
        (l = my_ismbchar(system_charset_info, pos, strend))) {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
975 976 977 978 979 980
	while (l--)
	    *out++ = *pos++;
	pos--;
	continue;
    }
#endif
981
    if (!*ml_comment && inchar == '\\')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
982 983 984
    {					// mSQL or postgreSQL style command ?
      if (!(inchar = (uchar) *++pos))
	break;				// readline adds one '\'
985
      if (*in_string || inchar == 'N')	// \N is short for NULL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
986 987 988 989 990 991 992
      {					// Don't allow commands in string
	*out++='\\';
	*out++= (char) inchar;
	continue;
      }
      if ((com=find_command(NullS,(char) inchar)))
      {
993
	const String tmp(line,(uint) (out-line), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
	buffer.append(tmp);
	if ((*com->func)(&buffer,pos-1) > 0)
	  return 1;				// Quit
	if (com->takes_params)
	{
	  for (pos++ ; *pos && *pos != ';' ; pos++) ;	// Remove parameters
	  if (!*pos)
	    pos--;
	}
	out=line;
      }
      else
      {
	sprintf(buff,"Unknown command '\\%c'.",inchar);
	if (put_info(buff,INFO_ERROR) > 0)
	  return 1;
	*out++='\\';
	*out++=(char) inchar;
	continue;
      }
    }
1015
    else if (!*ml_comment && inchar == ';' && !*in_string)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
    {						// ';' is end of command
      if (out != line)
	buffer.append(line,(uint) (out-line));	// Add this line
      if ((com=find_command(buffer.c_ptr(),0)))
      {
	if ((*com->func)(&buffer,buffer.c_ptr()) > 0)
	  return 1;				// Quit
      }
      else
      {
	int error=com_go(&buffer,0);
	if (error)
	{
	  return error < 0 ? 0 : 1;		// < 0 is not fatal
	}
      }
      buffer.length(0);
      out=line;
    }
1035 1036 1037
    else if (!*ml_comment && (!*in_string && (inchar == '#' ||
			      inchar == '-' && pos[1] == '-' &&
			      my_isspace(system_charset_info,pos[2]))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1038
      break;					// comment to end of line
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
    else if (!*in_string && inchar == '/' && *(pos+1) == '*')
    {
      pos++;
      *ml_comment= 1;
      if (out != line)
      {
        buffer.append(line,(uint) (out-line));
        out=line;
      }
    }
    else if (*ml_comment && !*in_string && inchar == '*' && *(pos+1) == '/')
    {
      pos++;
      *ml_comment= 0;
    }      
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1054 1055 1056 1057 1058 1059
    else
    {						// Add found char to buffer
      if (inchar == *in_string)
	*in_string=0;
      else if (!*in_string && (inchar == '\'' || inchar == '"'))
	*in_string=(char) inchar;
1060 1061
      if (!(*ml_comment))
        *out++ = (char) inchar;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1062 1063 1064 1065 1066 1067 1068 1069
    }
  }
  if (out != line || !buffer.is_empty())
  {
    *out++='\n';
    uint length=(uint) (out-line);
    if (buffer.length() + length >= buffer.alloced_length())
      buffer.realloc(buffer.length()+length+IO_SIZE);
1070
    if (!(*ml_comment) && buffer.append(line,length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1071 1072 1073 1074 1075
      return 1;
  }
  return 0;
}

1076 1077 1078
/*****************************************************************
	    Interface to Readline Completion
******************************************************************/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1079 1080 1081

#ifdef HAVE_READLINE

1082 1083
static char *new_command_generator(const char *text, int);
static char **new_mysql_completion (const char *text, int start, int end);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1084

1085 1086 1087 1088 1089
/*
  Tell the GNU Readline library how to complete.  We want to try to complete
  on command names if this is the first word in the line, or on filenames
  if not.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1090

1091 1092 1093 1094 1095
#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE)
char *no_completion(const char*,int)
#else
int no_completion()
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
{
  return 0;					/* No filename completion */
}

static void initialize_readline (char *name)
{
  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = name;

  /* Tell the completer that we want a crack first. */
1106 1107 1108 1109 1110 1111
#if defined(USE_NEW_READLINE_INTERFACE)
  rl_attempted_completion_function= (rl_completion_func_t*)&new_mysql_completion;
  rl_completion_entry_function= (rl_compentry_func_t*)&no_completion;
#elif defined(USE_LIBEDIT_INTERFACE)
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
  rl_completion_entry_function= (CPFunction*)&no_completion;
1112
#else
1113 1114
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
  rl_completion_entry_function= (Function*)&no_completion;
1115
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1116 1117
}

1118 1119 1120 1121 1122 1123
/*
  Attempt to complete on the contents of TEXT.  START and END show the
  region of TEXT that contains the word to complete.  We can use the
  entire line in case we want to do some simple parsing.  Return the
  array of matches, or NULL if there aren't any.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1124

1125
static char **new_mysql_completion (const char *text,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1126 1127 1128 1129
				    int start __attribute__((unused)),
				    int end __attribute__((unused)))
{
  if (!status.batch && !quick)
1130
#if defined(USE_NEW_READLINE_INTERFACE)
1131
    return rl_completion_matches(text, new_command_generator);
1132 1133 1134
#else
    return completion_matches((char *)text, (CPFunction *)new_command_generator);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1135 1136 1137 1138
  else
    return (char**) 0;
}

1139
static char *new_command_generator(const char *text,int state)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1140 1141 1142 1143 1144 1145 1146
{
  static int textlen;
  char *ptr;
  static Bucket *b;
  static entry *e;
  static uint i;

1147
  if (!state)
1148
    textlen=(uint) strlen(text);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1149

1150 1151 1152 1153
  if (textlen>0)
  {						/* lookup in the hash */
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1154 1155
      uint len;

1156
      b = find_all_matches(&ht,text,(uint) strlen(text),&len);
1157
      if (!b)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1158 1159 1160 1161
	return NullS;
      e = b->pData;
    }

1162 1163
    if (e)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1164 1165 1166 1167
      ptr= strdup(e->str);
      e = e->pNext;
      return ptr;
    }
1168 1169 1170
  }
  else
  { /* traverse the entire hash, ugly but works */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1171

1172 1173
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1174
      /* find the first used bucket */
1175 1176 1177 1178
      for (i=0 ; i < ht.nTableSize ; i++)
      {
	if (ht.arBuckets[i])
	{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1179 1180 1181 1182 1183 1184 1185
	  b = ht.arBuckets[i];
	  e = b->pData;
	  break;
	}
      }
    }
    ptr= NullS;
1186 1187 1188
    while (e && !ptr)
    {					/* find valid entry in bucket */
      if ((uint) strlen(e->str) == b->nKeyLength)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1189 1190 1191
	ptr = strdup(e->str);
      /* find the next used entry */
      e = e->pNext;
1192 1193
      if (!e)
      { /* find the next used bucket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1194
	b = b->pNext;
1195 1196 1197 1198 1199 1200
	if (!b)
	{
	  for (i++ ; i<ht.nTableSize; i++)
	  {
	    if (ht.arBuckets[i])
	    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1201 1202 1203 1204 1205 1206
	      b = ht.arBuckets[i];
	      e = b->pData;
	      break;
	    }
	  }
	}
1207 1208
	else
	  e = b->pData;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1209 1210
      }
    }
1211
    if (ptr)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1212 1213 1214 1215 1216 1217 1218 1219
      return ptr;
  }
  return NullS;
}


/* Build up the completion hash */

1220
static void build_completion_hash(bool rehash, bool write_info)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222
{
  COMMANDS *cmd=commands;
1223 1224
  MYSQL_RES *databases=0,*tables=0;
  MYSQL_RES *fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1225 1226 1227 1228 1229 1230 1231
  static char ***field_names= 0;
  MYSQL_ROW database_row,table_row;
  MYSQL_FIELD *sql_field;
  char buf[NAME_LEN*2+2];		 // table name plus field name plus 2
  int i,j,num_fields;
  DBUG_ENTER("build_completion_hash");

1232
  if (status.batch || quick || !current_db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
    DBUG_VOID_RETURN;			// We don't need completion in batches

  if (tables)
  {
    mysql_free_result(tables);
    tables=0;
  }

  /* hash SQL commands */
  while (cmd->name) {
    add_word(&ht,(char*) cmd->name);
    cmd++;
  }
1246
  if (!rehash)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1247 1248
    DBUG_VOID_RETURN;

1249 1250 1251 1252 1253 1254
  /* Free old used memory */
  if (field_names)
    field_names=0;
  completion_hash_clean(&ht);
  free_root(&hash_mem_root,MYF(0));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1255 1256 1257
  /* hash MySQL functions (to be implemented) */

  /* hash all database names */
1258 1259
  if (mysql_query(&mysql,"show databases") == 0)
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1260 1261 1262 1263 1264
    if (!(databases = mysql_store_result(&mysql)))
      put_info(mysql_error(&mysql),INFO_INFO);
    else
    {
      while ((database_row=mysql_fetch_row(databases)))
1265 1266 1267 1268 1269 1270
      {
	char *str=strdup_root(&hash_mem_root, (char*) database_row[0]);
	if (str)
	  add_word(&ht,(char*) str);
      }
      mysql_free_result(databases);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
    }
  }
  /* hash all table names */
  if (mysql_query(&mysql,"show tables")==0)
  {
    if (!(tables = mysql_store_result(&mysql)))
      put_info(mysql_error(&mysql),INFO_INFO);
    else
    {
      if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
      {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1282
	tee_fprintf(stdout, "\
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1283 1284 1285 1286 1287
Reading table information for completion of table and column names\n\
You can turn off this feature to get a quicker startup with -A\n\n");
      }
      while ((table_row=mysql_fetch_row(tables)))
      {
1288 1289 1290 1291
	char *str=strdup_root(&hash_mem_root, (char*) table_row[0]);
	if (str &&
	    !completion_hash_exists(&ht,(char*) str, (uint) strlen(str)))
	  add_word(&ht,str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1292 1293 1294 1295 1296
      }
    }
  }

  /* hash all field names, both with the table prefix and without it */
1297 1298
  if (!tables)					/* no tables */
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1299 1300 1301
    DBUG_VOID_RETURN;
  }
  mysql_data_seek(tables,0);
1302 1303 1304 1305
  if (!(field_names= (char ***) alloc_root(&hash_mem_root,sizeof(char **) *
					   (uint) (mysql_num_rows(tables)+1))))
  {
    mysql_free_result(tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1306
    DBUG_VOID_RETURN;
1307
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1308 1309 1310 1311 1312 1313
  i=0;
  while ((table_row=mysql_fetch_row(tables)))
  {
    if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
    {
      num_fields=mysql_num_fields(fields);
1314 1315 1316 1317
      if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
						  sizeof(char *) *
						  (num_fields*2+1))))
	break;
1318
      field_names[i][num_fields*2]= '\0';
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1319 1320 1321 1322
      j=0;
      while ((sql_field=mysql_fetch_field(fields)))
      {
	sprintf(buf,"%s.%s",table_row[0],sql_field->name);
1323
	field_names[i][j] = strdup_root(&hash_mem_root,buf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1324
	add_word(&ht,field_names[i][j]);
1325 1326
	field_names[i][num_fields+j] = strdup_root(&hash_mem_root,
						   sql_field->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1327
	if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
1328
				    (uint) strlen(field_names[i][num_fields+j])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1329 1330 1331
	  add_word(&ht,field_names[i][num_fields+j]);
	j++;
      }
1332
      mysql_free_result(fields);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1333 1334
    }
    else
1335
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1336 1337
      tee_fprintf(stdout,
		  "Didn't find any fields in table '%s'\n",table_row[0]);
1338
      field_names[i]= 0;
1339
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1340 1341
    i++;
  }
1342
  mysql_free_result(tables);
1343
  field_names[i]=0;				// End pointer
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1344 1345 1346 1347 1348 1349 1350
  DBUG_VOID_RETURN;
}

	/* for gnu readline */

#ifndef HAVE_INDEX
extern "C" {
1351
extern char *index(const char *,int c),*rindex(const char *,int);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1352

1353
char *index(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1354 1355 1356 1357 1358 1359 1360 1361
{
  for (;;)
  {
     if (*s == (char) c) return (char*) s;
     if (!*s++) return NullS;
  }
}

1362
char *rindex(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373
{
  reg3 char *t;

  t = NullS;
  do if (*s == (char) c) t = (char*) s; while (*s++);
  return (char*) t;
}
}
#endif
#endif /* HAVE_READLINE */

1374

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1375 1376 1377 1378 1379 1380
static int reconnect(void)
{
  if (!status.batch)
  {
    put_info("No connection. Trying to reconnect...",INFO_INFO);
    (void) com_connect((String *) 0, 0);
1381 1382
    if (rehash)
      com_rehash(NULL, NULL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
  }
  if (!connected)
    return put_info("Can't connect to the server\n",INFO_ERROR);
  return 0;
}


/***************************************************************************
 The different commands
***************************************************************************/

1394 1395 1396 1397 1398 1399 1400
int mysql_real_query_for_lazy(const char *buf, int length)
{
  for (uint retry=0;; retry++)
  {
    if (!mysql_real_query(&mysql,buf,length))
      return 0;    
    uint error=put_info(mysql_error(&mysql),INFO_ERROR, mysql_errno(&mysql));
1401 1402
    if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
	status.batch)
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
      return error;
    if (reconnect())
      return error;
  }
}

int mysql_store_result_for_lazy(MYSQL_RES **result)
{
  if ((*result=mysql_store_result(&mysql)))
    return 0;

  if (mysql_error(&mysql)[0])
    return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));

  return 0;
}

1420

1421
static int com_server_help(String *buffer __attribute__((unused)),
1422
			   char *line __attribute__((unused)), char *help_arg)
1423 1424 1425 1426
{
  MYSQL_ROW cur;
  const char *server_cmd= buffer->ptr();
  char cmd_buf[100];
1427 1428
  MYSQL_RES *result;
  int error;
1429

1430
  if (help_arg[0] != '\'')
1431
  {
1432
    (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
    server_cmd= cmd_buf;
  }

  if (!status.batch)
  {
    old_buffer= *buffer;
    old_buffer.copy();
  }

  if (!connected && reconnect())
    return 1;

1445
  if ((error= mysql_real_query_for_lazy(server_cmd,strlen(server_cmd))))
1446
    return error;
1447
  if ((error= mysql_store_result_for_lazy(&result)))
1448 1449 1450 1451
    return error;

  if (result)
  {
1452
    ulonglong num_rows= mysql_num_rows(result);
1453
    if (num_rows == 1)
1454 1455
    {
      if (!(cur= mysql_fetch_row(result)))
1456 1457 1458 1459
      {
	error= -1;
	goto err;
      }
1460 1461

      init_pager();
1462
      if (cur[1][0] == 'Y')
1463
      {
1464
	tee_fprintf(PAGER, "Help topic \'%s\'\n", cur[0]);
1465
	tee_fprintf(PAGER, "%s\n", cur[2]);
1466
	tee_fprintf(PAGER, "For help on specific function please type 'help <function>'\nwhere function is one of next:\n%s\n", cur[3]);
1467 1468 1469
      }
      else
      {
1470 1471 1472 1473
	tee_fprintf(PAGER, "Name: \'%s\'\n\n", cur[0]);
	tee_fprintf(PAGER, "Description:\n%s\n\n", cur[2]);
	if (cur[3])
	  tee_fprintf(PAGER, "Examples:\n%s\n", cur[3]);
1474 1475 1476
      }
      end_pager();
    }
1477
    else if (num_rows > 1)
1478
    {
1479 1480
      put_info("Many help items for your request exist", INFO_INFO);
      put_info("For more specific request please type 'help <item>' where item is one of next:", INFO_INFO);
1481 1482 1483

      init_pager();
      char last_char= '_';
1484 1485 1486 1487
      while ((cur= mysql_fetch_row(result)))
      {
	if (cur[1][0]!=last_char)
	{
1488
	  put_info("-------------------------------------------", INFO_INFO);
1489
	  put_info(cur[1][0] == 'Y' ? 
1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
		   "categories:" : "functions:", INFO_INFO);
	  put_info("-------------------------------------------", INFO_INFO);
	}
	last_char= cur[1][0];
	tee_fprintf(PAGER, "%s\n", cur[0]);
      }
      tee_fprintf(PAGER, "\n");
      end_pager();
    }
    else
    {
      put_info("\nNothing found\n", INFO_INFO);
    }
  }

1505
err:
1506 1507 1508 1509
  mysql_free_result(result);
  return error;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1510
static int
1511 1512
com_help(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1513 1514
{
  reg1 int i;
1515
  char * help_arg= strchr(line,' ');
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1516

1517
  if (help_arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1518
  {
1519
    return com_server_help(buffer,line,help_arg+1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1520
  }
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535
  else
  {
    put_info("\nFor the complete MySQL Manual online visit:\n   http://www.mysql.com/documentation\n", INFO_INFO);
    put_info("For info on technical support from MySQL developers visit:\n   http://www.mysql.com/support\n", INFO_INFO);
    put_info("For info on MySQL books, utilities, consultants, etc. visit:\n   http://www.mysql.com/portal\n", INFO_INFO);
    put_info("List of all MySQL commands:", INFO_INFO);
    if (!named_cmds)
      put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
    for (i = 0; commands[i].name; i++)
    {
      if (commands[i].func)
        tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
		    commands[i].cmd_char, commands[i].doc);
    }
  }
1536 1537
  if (connected && mysql_get_server_version(&mysql) >= 40100)
    put_info("\nFor server side help, type 'help all'\n", INFO_INFO);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
  return 0;
}


	/* ARGSUSED */
static int
com_clear(String *buffer,char *line __attribute__((unused)))
{
  buffer->length(0);
  return 0;
}


/*
1552 1553 1554 1555
  Execute command
  Returns: 0  if ok
          -1 if not fatal error
	  1  if fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1556 1557 1558 1559 1560 1561
*/


static int
com_go(String *buffer,char *line __attribute__((unused)))
{
1562
  char		buff[200], time_buff[32], *pos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1563
  MYSQL_RES	*result;
1564
  ulong		timer, warnings;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1565 1566 1567 1568 1569 1570 1571 1572
  uint		error=0;

  if (!status.batch)
  {
    old_buffer= *buffer;			// Save for edit command
    old_buffer.copy();
  }

1573
  /* Remove garbage for nicer messages */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592
  LINT_INIT(buff[0]);
  remove_cntrl(*buffer);

  if (buffer->is_empty())
  {
    if (status.batch)				// Ignore empty quries
      return 0;
    return put_info("No query specified\n",INFO_ERROR);

  }
  if (!connected && reconnect())
  {
    buffer->length(0);				// Remove query on error
    return status.batch ? 1 : -1;		// Fatal error
  }
  if (verbose)
    (void) com_print(buffer,0);

  if (skip_updates &&
1593 1594 1595
      (buffer->length() < 4 || my_strnncoll(system_charset_info,
					    (const uchar*)buffer->ptr(),4,
					    (const uchar*)"SET ",4)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1596 1597 1598 1599 1600 1601
  {
    (void) put_info("Ignoring query to other database",INFO_INFO);
    return 0;
  }

  timer=start_timer();
1602 1603 1604

  error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
  if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1605
  {
1606 1607
    buffer->length(0); // Remove query on error
    return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1608
  }
1609

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
  error=0;
  buffer->length(0);

  if (quick)
  {
    if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
    {
      return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
    }
  }
  else
  {
1622 1623 1624
    error= mysql_store_result_for_lazy(&result);
    if (error)
      return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
  }

  if (verbose >= 3 || !opt_silent)
    mysql_end_timer(timer,time_buff);
  else
    time_buff[0]=0;
  if (result)
  {
    if (!mysql_num_rows(result) && ! quick)
    {
1635
      strmov(buff, "Empty set");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1636 1637 1638
    }
    else
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1639
      init_pager();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1640 1641
      if (opt_html)
	print_table_data_html(result);
1642 1643
      else if (opt_xml)
	print_table_data_xml(result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1644 1645 1646 1647 1648 1649
      else if (vertical)
	print_table_data_vertically(result);
      else if (opt_silent && verbose <= 2 && !output_tables)
	print_tab_data(result);
      else
	print_table_data(result);
1650
      sprintf(buff,"%ld %s in set",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1651
	      (long) mysql_num_rows(result),
1652
	      (long) mysql_num_rows(result) == 1 ? "row" : "rows");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1653
      end_pager();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1654 1655 1656
    }
  }
  else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
1657
    strmov(buff,"Query OK");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1658
  else
1659
    sprintf(buff,"Query OK, %ld %s affected",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1660
	    (long) mysql_affected_rows(&mysql),
1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
	    (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows");

  pos=strend(buff);
  if ((warnings= mysql_warning_count(&mysql)))
  {
    *pos++= ',';
    *pos++= ' ';
    pos=int2str(warnings, pos, 10);
    pos=strmov(pos, " warning");
    if (warnings != 1)
      *pos++= 's';
  }
  strmov(pos, time_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686
  put_info(buff,INFO_RESULT);
  if (mysql_info(&mysql))
    put_info(mysql_info(&mysql),INFO_RESULT);
  put_info("",INFO_RESULT);			// Empty row

  if (result && !mysql_eof(result))	/* Something wrong when using quick */
    error=put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  else if (unbuffered)
    fflush(stdout);
  mysql_free_result(result);
  return error;				/* New command follows */
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1687 1688 1689

static void init_pager()
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1690
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705
  if (!opt_nopager)
  {
    if (!(PAGER= popen(pager, "w")))
    {
      tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
      PAGER= stdout;
    }
  }
  else
#endif
    PAGER= stdout;
}

static void end_pager()
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1706
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1707 1708 1709 1710 1711
  if (!opt_nopager)
    pclose(PAGER);
#endif
}

1712

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1713
static void init_tee(const char *file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1714
{
1715
  FILE* new_outfile;
1716
  if (opt_outfile)
1717
    end_tee();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1718 1719 1720 1721 1722
  if (!(new_outfile= my_fopen(file_name, O_APPEND | O_WRONLY, MYF(MY_WME))))
  {
    tee_fprintf(stdout, "Error logging to file '%s'\n", file_name);
    return;
  }
1723
  OUTFILE = new_outfile;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1724 1725 1726 1727
  strmake(outfile, file_name, FN_REFLEN-1);
  tee_fprintf(stdout, "Logging to file '%s'\n", file_name);
  opt_outfile= 1;
  return;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1728 1729
}

1730

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1731 1732 1733
static void end_tee()
{
  my_fclose(OUTFILE, MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1734
  OUTFILE= 0;
1735
  opt_outfile= 0;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1736 1737 1738
  return;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
static int
com_ego(String *buffer,char *line)
{
  int result;
  bool oldvertical=vertical;
  vertical=1;
  result=com_go(buffer,line);
  vertical=oldvertical;
  return result;
}

1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765
static void
print_field_types(MYSQL_RES *result)
{
  MYSQL_FIELD	*field;  
  while ((field = mysql_fetch_field(result)))
  {
    tee_fprintf(PAGER,"%s '%s' %d %d %d %d %d\n",
		field->name,
		field->table ? "" : field->table,
		(int) field->type,
		field->length, field->max_length, 
		field->flags, field->decimals);
  }
  tee_puts("", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1766 1767 1768 1769 1770 1771 1772 1773

static void
print_table_data(MYSQL_RES *result)
{
  String separator(256);
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
  bool		*num_flag;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1774 1775 1776
#ifdef __NETWARE__
  uint		lines= 0;
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1777 1778

  num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
1779 1780 1781 1782 1783
  if (info_flag)
  {
    print_field_types(result);
    mysql_field_seek(result,0);
  }
1784
  separator.copy("+",1,system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1785 1786
  while ((field = mysql_fetch_field(result)))
  {
1787
    uint length= column_names ? field->name_length : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797
    if (quick)
      length=max(length,field->length);
    else
      length=max(length,field->max_length);
    if (length < 4 && !IS_NOT_NULL(field->flags))
      length=4;					// Room for "NULL"
    field->max_length=length+1;
    separator.fill(separator.length()+length+2,'-');
    separator.append('+');
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1798
  tee_puts(separator.c_ptr(), PAGER);
1799
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1800 1801
  {
    mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1802
    (void) tee_fputs("|", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1803 1804
    for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1805 1806
      tee_fprintf(PAGER, " %-*s|",min(field->max_length,MAX_COLUMN_LENGTH),
		  field->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1807 1808
      num_flag[off]= IS_NUM(field->type);
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1809 1810
    (void) tee_fputs("\n", PAGER);
    tee_puts(separator.c_ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1811 1812
  }

1813
  while ((cur= mysql_fetch_row(result)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1814
  {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1815
    (void) tee_fputs("|", PAGER);
1816 1817
    mysql_field_seek(result, 0);
    for (uint off= 0; off < mysql_num_fields(result); off++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1818
    {
1819 1820 1821
      const char *str= cur[off] ? cur[off] : "NULL";
      field= mysql_fetch_field(result);
      uint length= field->max_length;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1822 1823
      if (length > MAX_COLUMN_LENGTH)
      {
1824 1825
	tee_fputs(str, PAGER);
	tee_fputs(" |", PAGER);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1826 1827
      }
      else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1828
      tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1829
		  length, str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1830
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1831
    (void) tee_fputs("\n", PAGER);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1832 1833 1834 1835
#ifdef __NETWARE__
    // on a long result the screen could hog the cpu
    if ((lines++ & 1023) == 0) pthread_yield();
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1836
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1837
  tee_puts(separator.c_ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1838 1839 1840
  my_afree((gptr) num_flag);
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1841

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1842 1843 1844
static void
print_table_data_html(MYSQL_RES *result)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1845 1846 1847 1848 1849
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
#ifdef __NETWARE__
  uint		lines= 0;
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1850 1851

  mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1852
  (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
1853
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1854 1855 1856
  {
    while((field = mysql_fetch_field(result)))
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1857 1858 1859
      tee_fprintf(PAGER, "<TH>%s</TH>", (field->name ? 
					 (field->name[0] ? field->name : 
					  " &nbsp; ") : "NULL"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1860
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1861
    (void) tee_fputs("</TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1862 1863 1864
  }
  while ((cur = mysql_fetch_row(result)))
  {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1865
    (void) tee_fputs("<TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1866 1867 1868
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
      ulong *lengths=mysql_fetch_lengths(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1869
      (void) tee_fputs("<TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1870
      safe_put_field(cur[i],lengths[i]);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1871
      (void) tee_fputs("</TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1872
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1873
    (void) tee_fputs("</TR>", PAGER);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1874 1875 1876 1877
#ifdef __NETWARE__
    // on a long result the screen could hog the cpu
    if ((lines++ & 1023) == 0) pthread_yield();
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1878
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1879
  (void) tee_fputs("</TABLE>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1880 1881 1882
}


1883 1884 1885 1886 1887
static void
print_table_data_xml(MYSQL_RES *result)
{
  MYSQL_ROW   cur;
  MYSQL_FIELD *fields;
1888 1889 1890
#ifdef __NETWARE__
  uint		lines= 0;
#endif
1891 1892 1893

  mysql_field_seek(result,0);

1894
  tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
1895
  xmlencode_print(glob_buffer.ptr(), strlen(glob_buffer.ptr()));
1896
  tee_fputs("\">", PAGER);
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907

  fields = mysql_fetch_fields(result);
  while ((cur = mysql_fetch_row(result)))
  {
    (void) tee_fputs("\n  <row>\n", PAGER);
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
      ulong *lengths=mysql_fetch_lengths(result);
      tee_fprintf(PAGER, "\t<%s>", (fields[i].name ?
				  (fields[i].name[0] ? fields[i].name :
				   " &nbsp; ") : "NULL"));
1908
      xmlencode_print(cur[i], lengths[i]);
1909 1910 1911 1912 1913
      tee_fprintf(PAGER, "</%s>\n", (fields[i].name ?
				     (fields[i].name[0] ? fields[i].name :
				      " &nbsp; ") : "NULL"));
    }
    (void) tee_fputs("  </row>\n", PAGER);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1914 1915 1916 1917
#ifdef __NETWARE__
      // on a long result the screen could hog the cpu
    if ((lines++ & 1023) == 0) pthread_yield();
#endif
1918 1919 1920 1921
  }
  (void) tee_fputs("</resultset>\n", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931

static void
print_table_data_vertically(MYSQL_RES *result)
{
  MYSQL_ROW	cur;
  uint		max_length=0;
  MYSQL_FIELD	*field;

  while ((field = mysql_fetch_field(result)))
  {
1932
    uint length= field->name_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1933 1934 1935 1936 1937 1938 1939 1940 1941
    if (length > max_length)
      max_length= length;
    field->max_length=length;
  }

  mysql_field_seek(result,0);
  for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
  {
    mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1942 1943
    tee_fprintf(PAGER, 
		"*************************** %d. row ***************************\n", row_count);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1944 1945 1946
    for (uint off=0; off < mysql_num_fields(result); off++)
    {
      field= mysql_fetch_field(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1947 1948
      tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
      tee_fprintf(PAGER, "%s\n",cur[off] ? (char*) cur[off] : "NULL");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1949
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1950 1951 1952 1953
#ifdef __NETWARE__
      // on a long result the screen could hog the cpu
    if ((row_count & 1023) == 0) pthread_yield();
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1954 1955 1956
  }
}

1957

1958
static const char
1959 1960
*array_value(const char **array, char key)
{
1961
  int x;
1962 1963 1964
  for (x= 0; array[x]; x+= 2)
    if (*array[x] == key)
      return array[x + 1];
1965 1966 1967
  return 0;
}

1968

1969
static void
1970
xmlencode_print(const char *src, uint length)
1971
{
1972 1973 1974
  if (!src)
    tee_fputs("NULL", PAGER);
  else
1975
  {
1976 1977 1978 1979 1980 1981 1982 1983 1984
    for (const char *p = src; *p && length; *p++, length--)
    {
      const char *t;
      if ((t = array_value(xmlmeta, *p)))
	tee_fputs(t, PAGER);
      else
	tee_putc(*p, PAGER);
    }
  }
1985
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1986

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1987 1988 1989 1990 1991

static void
safe_put_field(const char *pos,ulong length)
{
  if (!pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1992
    tee_fputs("NULL", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1993 1994 1995
  else
  {
    if (opt_raw_data)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
1996
      tee_fputs(pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1997 1998 1999 2000
    else for (const char *end=pos+length ; pos != end ; pos++)
    {
#ifdef USE_MB
      int l;
2001
      if (use_mb(system_charset_info) &&
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2002 2003
          (l = my_ismbchar(system_charset_info, pos, end)))
      {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2004
	  while (l--)
2005
	    tee_putc(*pos++, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2006 2007 2008 2009 2010
	  pos--;
	  continue;
      }
#endif
      if (!*pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2011
	tee_fputs("\\0", PAGER); // This makes everything hard
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2012
      else if (*pos == '\t')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2013
	tee_fputs("\\t", PAGER); // This would destroy tab format
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2014
      else if (*pos == '\n')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2015
	tee_fputs("\\n", PAGER); // This too
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2016
      else if (*pos == '\\')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2017
	tee_fputs("\\\\", PAGER);
2018
	else
2019
	tee_putc(*pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
    }
  }
}


static void
print_tab_data(MYSQL_RES *result)
{
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
  ulong		*lengths;

2032
  if (opt_silent < 2 && column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2033 2034 2035 2036 2037
  {
    int first=0;
    while ((field = mysql_fetch_field(result)))
    {
      if (first++)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2038 2039
	(void) tee_fputs("\t", PAGER);
      (void) tee_fputs(field->name, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2040
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2041
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2042 2043 2044 2045 2046 2047 2048
  }
  while ((cur = mysql_fetch_row(result)))
  {
    lengths=mysql_fetch_lengths(result);
    safe_put_field(cur[0],lengths[0]);
    for (uint off=1 ; off < mysql_num_fields(result); off++)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2049
      (void) tee_fputs("\t", PAGER);
2050
      safe_put_field(cur[off], lengths[off]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2051
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2052
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2053 2054 2055
  }
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2056 2057 2058 2059 2060 2061 2062
static int
com_tee(String *buffer, char *line __attribute__((unused)))
{
  char file_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
2063
  while (my_isspace(system_charset_info,*line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2064 2065 2066 2067 2068
    line++;
  if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
  {
    if (!strlen(outfile))
    {
2069
      printf("No previous outfile available, you must give a filename!\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2070 2071
      return 0;
    }
2072 2073 2074 2075 2076 2077 2078
    else if (opt_outfile)
    {
      tee_fprintf(stdout, "Currently logging to file '%s'\n", outfile);
      return 0;
    }
    else
      param = outfile;			//resume using the old outfile
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2079
  }
2080 2081

  /* eliminate the spaces before the parameters */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2082
  while (my_isspace(system_charset_info,*param))
2083 2084 2085
    param++;
  end= strmake(file_name, param, sizeof(file_name) - 1);
  /* remove end space from command line */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2086 2087
  while (end > file_name && (my_isspace(system_charset_info,end[-1]) || 
			     my_iscntrl(system_charset_info,end[-1])))
2088 2089
    end--;
  end[0]= 0;
2090
  if (end == file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2091 2092 2093 2094
  {
    printf("No outfile specified!\n");
    return 0;
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2095
  init_tee(file_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2096 2097 2098
  return 0;
}

2099

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
static int
com_notee(String *buffer __attribute__((unused)),
	  char *line __attribute__((unused)))
{
  if (opt_outfile)
    end_tee();
  tee_fprintf(stdout, "Outfile disabled.\n");
  return 0;
}

2110
/*
2111
  Sorry, this command is not available in Windows.
2112 2113
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2114
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2115 2116 2117 2118 2119 2120 2121 2122
static int
com_pager(String *buffer, char *line __attribute__((unused)))
{
  char pager_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
  /* Skip space from file name */
2123
  while (my_isspace(system_charset_info,*line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2124
    line++;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2125
  if (!(param= strchr(line, ' '))) // if pager was not given, use the default
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2126
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2127
    if (!default_pager[0])
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2128
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2129
      tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2130 2131 2132 2133 2134 2135 2136 2137 2138
      opt_nopager=1;
      strmov(pager, "stdout");
      PAGER= stdout;
      return 0;
    }
    strmov(pager, default_pager);
  }
  else
  {
2139
    while (my_isspace(system_charset_info,*param))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2140 2141
      param++;
    end=strmake(pager_name, param, sizeof(pager_name)-1);
2142 2143
    while (end > pager_name && (my_isspace(system_charset_info,end[-1]) || 
                                my_iscntrl(system_charset_info,end[-1])))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2144 2145 2146
      end--;
    end[0]=0;
    strmov(pager, pager_name);
2147
    strmov(default_pager, pager_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2148 2149 2150 2151 2152 2153
  }
  opt_nopager=0;
  tee_fprintf(stdout, "PAGER set to %s\n", pager);
  return 0;
}

2154

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2155 2156 2157 2158 2159 2160 2161 2162 2163
static int
com_nopager(String *buffer __attribute__((unused)),
	    char *line __attribute__((unused)))
{
  strmov(pager, "stdout");
  opt_nopager=1;
  tee_fprintf(stdout, "PAGER set to stdout\n");
  return 0;
}
2164
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2165

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2166

2167
/*
2168
  Sorry, you can't send the result to an editor in Win32
2169 2170
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2171
#ifdef USE_POPEN
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2172 2173 2174
static int
com_edit(String *buffer,char *line __attribute__((unused)))
{
2175
  char	filename[FN_REFLEN],buff[160];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2176 2177 2178
  int	fd,tmp;
  const char *editor;

2179 2180
  if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY,
			   MYF(MY_WME))) < 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209
    goto err;
  if (buffer->is_empty() && !old_buffer.is_empty())
    (void) my_write(fd,(byte*) old_buffer.ptr(),old_buffer.length(),
		    MYF(MY_WME));
  else
    (void) my_write(fd,(byte*) buffer->ptr(),buffer->length(),MYF(MY_WME));
  (void) my_close(fd,MYF(0));

  if (!(editor = (char *)getenv("EDITOR")) &&
      !(editor = (char *)getenv("VISUAL")))
    editor = "vi";
  strxmov(buff,editor," ",filename,NullS);
  (void) system(buff);

  MY_STAT stat_arg;
  if (!my_stat(filename,&stat_arg,MYF(MY_WME)))
    goto err;
  if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
    goto err;
  (void) buffer->alloc((uint) stat_arg.st_size);
  if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L)
    buffer->length((uint) tmp);
  else
    buffer->length(0);
  (void) my_close(fd,MYF(0));
  (void) my_delete(filename,MYF(MY_WME));
err:
  return 0;
}
2210 2211
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2212 2213 2214 2215 2216 2217 2218

/* If arg is given, exit without errors. This happens on command 'quit' */

static int
com_quit(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2219 2220 2221 2222
#ifdef __NETWARE__
  // let the screen auto close on a normal shutdown
  setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2223 2224 2225 2226 2227 2228 2229 2230 2231
  status.exit_status=0;
  return 1;
}

static int
com_rehash(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
{
#ifdef HAVE_READLINE
2232
  build_completion_hash(1, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2233 2234 2235 2236
#endif
  return 0;
}

2237

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2238
#ifdef USE_POPEN
2239 2240 2241 2242 2243 2244 2245 2246 2247
static int
com_shell(String *buffer, char *line __attribute__((unused)))
{
  char *shell_cmd;
  if (!(shell_cmd = strchr(line, ' ')))
  {
    put_info("Usage: \\! shell-command", INFO_ERROR);
    return -1;
  }
2248 2249 2250 2251 2252
  /*
    The output of the shell command does not
    get directed to the pager or the outfile
  */
  if (system(shell_cmd) == -1)
2253 2254 2255 2256 2257 2258 2259 2260 2261
  {
    put_info(strerror(errno), INFO_ERROR, errno);
    return -1;
  }
  return 0;
}
#endif


bk@work.mysql.com's avatar
bk@work.mysql.com committed
2262 2263 2264
static int
com_print(String *buffer,char *line __attribute__((unused)))
{
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2265 2266
  tee_puts("--------------", stdout);
  (void) tee_fputs(buffer->c_ptr(), stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2267
  if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
2268
    tee_putc('\n', stdout);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2269
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2270 2271 2272 2273 2274 2275 2276
  return 0;					/* If empty buffer */
}

	/* ARGSUSED */
static int
com_connect(String *buffer, char *line)
{
2277
  char *tmp, buff[256];
2278
  bool save_rehash= rehash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2279 2280
  int error;

2281
  bzero(buff, sizeof(buff));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2282 2283
  if (buffer)
  {
2284 2285 2286
    strmov(buff, line);
    tmp= get_arg(buff, 0);
    if (tmp && *tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2287
    {
2288 2289 2290 2291
      my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
      current_db= my_strdup(tmp, MYF(MY_WME));
      tmp= get_arg(buff, 1);
      if (tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2292 2293 2294 2295 2296 2297
      {
	my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
	current_host=my_strdup(tmp,MYF(MY_WME));
      }
    }
    else
2298
      rehash= 0;				// Quick re-connect
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2299 2300 2301
    buffer->length(0);				// command used
  }
  else
2302
    rehash= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2303
  error=sql_connect(current_host,current_db,current_user,opt_password,0);
2304
  rehash= save_rehash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2305 2306 2307 2308 2309 2310

  if (connected)
  {
    sprintf(buff,"Connection id:    %ld",mysql_thread_id(&mysql));
    put_info(buff,INFO_INFO);
    sprintf(buff,"Current database: %s\n",
2311
	    current_db ? current_db : "*** NONE ***");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326
    put_info(buff,INFO_INFO);
  }
  return error;
}


static int com_source(String *buffer, char *line)
{
  char source_name[FN_REFLEN], *end, *param;
  LINE_BUFFER *line_buff;
  int error;
  STATUS old_status;
  FILE *sql_file;

  /* Skip space from file name */
2327
  while (my_isspace(system_charset_info,*line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2328
    line++;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2329 2330 2331
  if (!(param = strchr(line, ' ')))		// Skip command name
    return put_info("Usage: \\. <filename> | source <filename>", 
		    INFO_ERROR, 0);
2332
  while (my_isspace(system_charset_info,*param))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2333 2334
    param++;
  end=strmake(source_name,param,sizeof(source_name)-1);
2335 2336
  while (end > source_name && (my_isspace(system_charset_info,end[-1]) || 
                               my_iscntrl(system_charset_info,end[-1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2337 2338
    end--;
  end[0]=0;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2339
  unpack_filename(source_name,source_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2340
  /* open file name */
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2341
  if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376
  {
    char buff[FN_REFLEN+60];
    sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
    return put_info(buff, INFO_ERROR, 0);
  }

  if (!(line_buff=batch_readline_init(max_allowed_packet+512,sql_file)))
  {
    my_fclose(sql_file,MYF(0));
    return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
  }

  /* Save old status */
  old_status=status;
  bfill((char*) &status,sizeof(status),(char) 0);

  status.batch=old_status.batch;		// Run in batch mode
  status.line_buff=line_buff;
  status.file_name=source_name;
  glob_buffer.length(0);			// Empty command buffer
  error=read_lines(0);				// Read lines from file
  status=old_status;				// Continue as before
  my_fclose(sql_file,MYF(0));
  batch_readline_end(line_buff);
  return error;
}


	/* ARGSUSED */
static int
com_use(String *buffer __attribute__((unused)), char *line)
{
  char *tmp;
  char buff[256];

2377
  bzero(buff, sizeof(buff));
2378
  strmov(buff, line);
2379
  tmp= get_arg(buff, 0);
2380
  if (!tmp || !*tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2381
  {
2382
    put_info("USE must be followed by a database name", INFO_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2383 2384
    return 0;
  }
2385
  if (!current_db || cmp_database(current_db, tmp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2386 2387
  {
    if (one_database)
2388
      skip_updates= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406
    else
    {
      /*
	reconnect once if connection is down or if connection was found to
	be down during query
      */
      if (!connected && reconnect())
	return status.batch ? 1 : -1;			// Fatal error
      if (mysql_select_db(&mysql,tmp))
      {
	if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
	  return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));

	if (reconnect())
	  return status.batch ? 1 : -1;			// Fatal error
	if (mysql_select_db(&mysql,tmp))
	  return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
      }
2407 2408
      my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
      current_db=my_strdup(tmp,MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2409
#ifdef HAVE_READLINE
2410
      build_completion_hash(rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2411 2412 2413 2414
#endif
    }
  }
  else
2415
    skip_updates= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2416 2417 2418 2419 2420
  put_info("Database changed",INFO_INFO);
  return 0;
}


2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431
/*
  Gets argument from a command on the command line. If get_next_arg is
  not defined, skips the command and returns the first argument. The
  line is modified by adding zero to the end of the argument. If
  get_next_arg is defined, then the function searches for end of string
  first, after found, returns the next argument and adds zero to the
  end. If you ever wish to use this feature, remember to initialize all
  items in the array to zero first.
*/

char *get_arg(char *line, my_bool get_next_arg)
2432 2433 2434 2435
{
  char *ptr;
  my_bool quoted= 0, valid_arg= 0;
  uint count= 0;
2436
  char qtype= 0;
2437 2438

  ptr= line;
2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454
  if (get_next_arg)
  {
    for (; ptr && *ptr; ptr++);
    if ((ptr + 1) && *(ptr + 1))
      ptr++;
  }
  else
  {
    /* skip leading white spaces */
    while (my_isspace(system_charset_info, *ptr))
      ptr++;
    if (*ptr == '\\') // short command was used
      ptr+= 2;
    while (!my_isspace(system_charset_info, *ptr)) // skip command
      ptr++;
  }
2455 2456
  while (my_isspace(system_charset_info, *ptr))
    ptr++;
2457
  if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
2458
  {
2459
    qtype= *ptr;
2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475
    quoted= 1;
    ptr++;
  }
  for (; ptr && *ptr; ptr++, count++)
  {
    if (*ptr == '\\') // escaped character
    {
      // jump over the backslash
      char *tmp_ptr, tmp_buff[256];
      tmp_ptr= strmov(tmp_buff, (ptr - count));
      tmp_ptr-= (strlen(tmp_buff) - count);
      strmov(tmp_ptr, (ptr + 1));
      strmov(line, tmp_buff);
      ptr= line;
      ptr+= count;
    }
2476
    else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype))
2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488
    {
      *ptr= 0;
      break;
    }
  }
  for (ptr-= count; ptr && *ptr; ptr++)
    if (!my_isspace(system_charset_info, *ptr))
      valid_arg= 1;
  return valid_arg ? ptr - count : '\0';
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
2489 2490 2491 2492
static int
sql_real_connect(char *host,char *database,char *user,char *password,
		 uint silent)
{
2493 2494
  mysql_close(&mysql);
  connected= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2495
  mysql_init(&mysql);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2496
  if (opt_connect_timeout)
2497 2498
  {
    uint timeout=opt_connect_timeout;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2499
    mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
2500 2501
		  (char*) &timeout);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2502 2503
  if (opt_compress)
    mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2504 2505
  if (using_opt_local_infile)
    mysql_options(&mysql,MYSQL_OPT_LOCAL_INFILE, (char*) &opt_local_infile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2506 2507 2508
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
2509
		  opt_ssl_capath, opt_ssl_cipher);
2510 2511 2512 2513 2514 2515
#endif
  if (opt_protocol)
    mysql_options(&mysql,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
#ifdef HAVE_SMEM
  if (shared_memory_base_name)
    mysql_options(&mysql,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2516 2517 2518 2519 2520 2521 2522 2523 2524
#endif
  if (safe_updates)
  {
    char init_command[100];
    sprintf(init_command,
	    "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,SQL_MAX_JOIN_SIZE=%lu",
	    select_limit,max_join_size);
    mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
  }
2525 2526
  if (!mysql_real_connect(&mysql, host, user, password,
			  database, opt_mysql_port, opt_mysql_unix_port,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539
			  connect_flag))
  {
    if (!silent ||
	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
	 mysql_errno(&mysql) != CR_CONNECTION_ERROR))
    {
      put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
      (void) fflush(stdout);
      return ignore_errors ? -1 : 1;		// Abort
    }
    return -1;					// Retryable
  }
  connected=1;
2540
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2541
  mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens
2542
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2543
#ifdef HAVE_READLINE
2544
  build_completion_hash(rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561
#endif
  return 0;
}


static int
sql_connect(char *host,char *database,char *user,char *password,uint silent)
{
  bool message=0;
  uint count=0;
  int error;
  for (;;)
  {
    if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
    {
      if (count)
      {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2562
	tee_fputs("\n", stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2563 2564 2565 2566 2567 2568 2569 2570 2571
	(void) fflush(stderr);
      }
      return error;
    }
    if (!wait_flag)
      return ignore_errors ? -1 : 1;
    if (!message && !silent)
    {
      message=1;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2572
      tee_fputs("Waiting",stderr); (void) fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2573
    }
2574
    (void) sleep(wait_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588
    if (!silent)
    {
      putc('.',stderr); (void) fflush(stderr);
      count++;
    }
  }
}



static int
com_status(String *buffer __attribute__((unused)),
	   char *line __attribute__((unused)))
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2589
  const char *status;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2590
  tee_puts("--------------", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2591 2592 2593 2594 2595
  usage(1);					/* Print version */
  if (connected)
  {
    MYSQL_RES *result;
    LINT_INIT(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2596
    tee_fprintf(stdout, "\nConnection id:\t\t%ld\n",mysql_thread_id(&mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2597 2598 2599 2600
    if (!mysql_query(&mysql,"select DATABASE(),USER()") &&
	(result=mysql_use_result(&mysql)))
    {
      MYSQL_ROW cur=mysql_fetch_row(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2601 2602
      tee_fprintf(stdout, "Current database:\t%s\n",cur[0]);
      tee_fprintf(stdout, "Current user:\t\t%s\n",cur[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2603 2604
      (void) mysql_fetch_row(result);		// Read eof
    }
2605 2606
#ifdef HAVE_OPENSSL
    if (mysql.net.vio->ssl_ && SSL_get_cipher(mysql.net.vio->ssl_))
2607
      tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
2608 2609 2610
		  SSL_get_cipher(mysql.net.vio->ssl_));
    else
#endif /* HAVE_OPENSSL */
2611
      tee_puts("SSL:\t\t\tNot in use", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2612 2613 2614 2615
  }
  else
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2616
    tee_fprintf(stdout, "\nNo connection\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2617 2618 2619 2620 2621 2622
    vidattr(A_NORMAL);
    return 0;
  }
  if (skip_updates)
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2623
    tee_fprintf(stdout, "\nAll updates ignored to this database\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2624 2625
    vidattr(A_NORMAL);
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2626
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2627
  tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
2628
  tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2629 2630 2631 2632
#endif
  tee_fprintf(stdout, "Server version:\t\t%s\n", mysql_get_server_info(&mysql));
  tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
  tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
2633
  tee_fprintf(stdout, "Client characterset:\t%s\n",
2634
	      system_charset_info->name);
2635
  tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->name);
2636
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2637
  if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2638
    tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2639
  else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2640
    tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
2641 2642
  if (mysql.net.compress)
    tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
2643
#endif
2644

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2645 2646 2647
  if ((status=mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
  {
    ulong sec;
2648 2649 2650 2651
    char buff[40];
    const char *pos= strchr(status,' ');
    /* print label */
    tee_fprintf(stdout, "%.*s\t\t\t", (int) (pos-status), status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2652 2653 2654
    if ((status=str2int(pos,10,0,LONG_MAX,(long*) &sec)))
    {
      nice_time((double) sec,buff,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2655
      tee_puts(buff, stdout);			/* print nice time */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2656 2657 2658 2659
      while (*status == ' ') status++;		/* to next info */
    }
    if (status)
    {
2660
      tee_putc('\n', stdout);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2661
      tee_puts(status, stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2662 2663 2664 2665 2666
    }
  }
  if (safe_updates)
  {
    vidattr(A_BOLD);
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
2667
    tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2668
    vidattr(A_NORMAL);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2669
    tee_fprintf(stdout, "\
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
2670 2671 2672
UPDATEs and DELETEs that don't use a key in the WHERE clause are not allowed.\n\
(One can force an UPDATE/DELETE by adding LIMIT # at the end of the command.)\n\
SELECT has an automatic 'LIMIT %lu' if LIMIT is not used.\n\
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2673
Max number of examined row combination in a join is set to: %lu\n\n",
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
2674
select_limit, max_join_size);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2675
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2676
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2677 2678 2679 2680 2681 2682 2683 2684
  return 0;
}


static int
put_info(const char *str,INFO_TYPE info_type,uint error)
{
  static int inited=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2685

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2686 2687 2688 2689 2690 2691 2692 2693
  if (status.batch)
  {
    if (info_type == INFO_ERROR)
    {
      (void) fflush(stdout);
      fprintf(stderr,"ERROR");
      if (error)
	(void) fprintf(stderr," %d",error);
2694
      if (status.query_start_line && line_numbers)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705
      {
	(void) fprintf(stderr," at line %lu",status.query_start_line);
	if (status.file_name)
	  (void) fprintf(stderr," in file: '%s'", status.file_name);
      }
      (void) fprintf(stderr,": %s\n",str);
      (void) fflush(stderr);
      if (!ignore_errors)
	return 1;
    }
    else if (info_type == INFO_RESULT && verbose > 1)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2706
      tee_puts(str, stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721
    if (unbuffered)
      fflush(stdout);
    return info_type == INFO_ERROR ? -1 : 0;
  }
  if (!opt_silent || info_type == INFO_ERROR)
  {
    if (!inited)
    {
      inited=1;
#ifdef HAVE_SETUPTERM
      (void) setupterm((char *)0, 1, (int *) 0);
#endif
    }
    if (info_type == INFO_ERROR)
    {
2722
      if (!opt_nobeep)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2723
	putchar('\007');		      	/* This should make a bell */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2724 2725
      vidattr(A_STANDOUT);
      if (error)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2726
        (void) tee_fprintf(stderr, "ERROR %d: ", error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2727
      else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2728
        tee_puts("ERROR: ", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2729 2730 2731
    }
    else
      vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2732
    (void) tee_puts(str, stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2733 2734 2735 2736 2737 2738 2739
    vidattr(A_NORMAL);
  }
  if (unbuffered)
    fflush(stdout);
  return info_type == INFO_ERROR ? -1 : 0;
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2740

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2741 2742 2743 2744
static void remove_cntrl(String &buffer)
{
  char *start,*end;
  end=(start=(char*) buffer.ptr())+buffer.length();
2745
  while (start < end && !my_isgraph(system_charset_info,end[-1]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2746 2747 2748 2749 2750
    end--;
  buffer.length((uint) (end-start));
}


jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2751 2752 2753 2754 2755
void tee_fprintf(FILE *file, const char *fmt, ...)
{
  va_list args;

  va_start(args, fmt);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2756
  (void) vfprintf(file, fmt, args);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2757 2758 2759
#ifdef OS2
  fflush( file);
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2760
  if (opt_outfile)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2761
    (void) vfprintf(OUTFILE, fmt, args);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2762 2763 2764 2765 2766 2767 2768
  va_end(args);
}


void tee_fputs(const char *s, FILE *file)
{
  fputs(s, file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2769 2770 2771
#ifdef OS2
  fflush( file);
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2772 2773 2774 2775 2776 2777 2778 2779 2780
  if (opt_outfile)
    fputs(s, OUTFILE);
}


void tee_puts(const char *s, FILE *file)
{
  fputs(s, file);
  fputs("\n", file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2781 2782 2783
#ifdef OS2
  fflush( file);
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2784 2785 2786 2787 2788 2789 2790
  if (opt_outfile)
  {
    fputs(s, OUTFILE);
    fputs("\n", OUTFILE);
  }
}

2791 2792 2793
void tee_putc(int c, FILE *file)
{
  putc(c, file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2794 2795 2796
#ifdef OS2
  fflush( file);
#endif
2797 2798 2799 2800
  if (opt_outfile)
    putc(c, OUTFILE);
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2801
#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2802 2803 2804
#include <time.h>
#else
#include <sys/times.h>
2805
#ifdef _SC_CLK_TCK				// For mit-pthreads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2806 2807 2808
#undef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
#endif
2809
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2810 2811 2812

static ulong start_timer(void)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2813
#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867
 return clock();
#else
  struct tms tms_tmp;
  return times(&tms_tmp);
#endif
}


static void nice_time(double sec,char *buff,bool part_second)
{
  ulong tmp;
  if (sec >= 3600.0*24)
  {
    tmp=(ulong) floor(sec/(3600.0*24));
    sec-=3600.0*24*tmp;
    buff=int2str((long) tmp,buff,10);
    buff=strmov(buff,tmp > 1 ? " days " : " day ");
  }
  if (sec >= 3600.0)
  {
    tmp=(ulong) floor(sec/3600.0);
    sec-=3600.0*tmp;
    buff=int2str((long) tmp,buff,10);
    buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
  }
  if (sec >= 60.0)
  {
    tmp=(ulong) floor(sec/60.0);
    sec-=60.0*tmp;
    buff=int2str((long) tmp,buff,10);
    buff=strmov(buff," min ");
  }
  if (part_second)
    sprintf(buff,"%.2f sec",sec);
  else
    sprintf(buff,"%d sec",(int) sec);
}


static void end_timer(ulong start_time,char *buff)
{
  nice_time((double) (start_timer() - start_time) /
	    CLOCKS_PER_SEC,buff,1);
}


static void mysql_end_timer(ulong start_time,char *buff)
{
  buff[0]=' ';
  buff[1]='(';
  end_timer(start_time,buff+2);
  strmov(strend(buff),")");
}

2868 2869
static const char* construct_prompt()
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2870 2871 2872 2873 2874 2875
  //erase the old prompt
  processed_prompt.free();
  //get the date struct
  time_t  lclock = time(NULL);
  struct tm *t = localtime(&lclock);
  //parse thru the settings for the prompt
2876 2877 2878
  for (char *c = current_prompt; *c ; *c++)
  {
    if (*c != PROMPT_CHAR)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2879
	processed_prompt.append(*c);
2880 2881
    else
    {
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908
      switch (*++c) {
      case '\0':
	//stop it from going beyond if ends with %
	c--;
	break;
      case 'c':
	add_int_to_prompt(++prompt_counter);
	break;
      case 'v':
	processed_prompt.append(mysql_get_server_info(&mysql));
	break;
      case 'd':
	processed_prompt.append(current_db ? current_db : "(none)");
	break;
      case 'h':
      {
	const char *prompt=mysql_get_host_info(&mysql);
	if (strstr(prompt, "Localhost"))
	  processed_prompt.append("localhost");
	else
	{
	  const char *end=strcend(prompt,' ');
	  processed_prompt.append(prompt, (uint) (end-prompt));
	}
	break;
      }
      case 'p':
2909
#ifndef EMBEDDED_LIBRARY
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2910 2911 2912 2913
	if (strstr(mysql_get_host_info(&mysql),"TCP/IP") ||
	    ! mysql.unix_socket)
	  add_int_to_prompt(mysql.port);
	else
2914 2915
	{
	  char *pos=strrchr(mysql.unix_socket,'/');
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2916
 	  processed_prompt.append(pos ? pos+1 : mysql.unix_socket);
2917
	}
2918
#endif
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007
	break;
      case 'U':
	if (!full_username)
	  init_username();
	processed_prompt.append(full_username);
	break;
      case 'u':
	if (!full_username)
	  init_username();
	processed_prompt.append(part_username);
	break;
      case PROMPT_CHAR:
	processed_prompt.append(PROMPT_CHAR);
	break;
      case 'n':
	processed_prompt.append('\n');
	break;
      case ' ':
      case '_':
	processed_prompt.append(' ');
	break;
      case 'R':
	add_int_to_prompt(t->tm_hour);
	break;
      case 'r':
	int getHour;
	getHour = t->tm_hour % 12;
	if (getHour == 0)
	  getHour=12;
	add_int_to_prompt(getHour);
	break;
      case 'm':
	if (t->tm_min < 10)
	  processed_prompt.append('0');
	add_int_to_prompt(t->tm_min);
	break;
      case 'y':
	int getYear;
	getYear = t->tm_year % 100;
	if (getYear < 10)
	  processed_prompt.append('0');
	add_int_to_prompt(getYear);
	break;
      case 'Y':
	add_int_to_prompt(t->tm_year+1900);
	break;
      case 'D':
	char* dateTime;
	time_t lclock;
	lclock = time(NULL);
	dateTime = ctime(&lclock);
	processed_prompt.append(strtok(dateTime,"\n"));
	break;
      case 's':
	add_int_to_prompt(t->tm_sec);
	break;
      case 'w':
	processed_prompt.append(day_names[t->tm_wday]);
	break;
      case 'P':
	processed_prompt.append(t->tm_hour < 12 ? "am" : "pm");
	break;
      case 'o':
	add_int_to_prompt(t->tm_mon+1);
	break;
      case 'O':
	processed_prompt.append(month_names[t->tm_mon]);
	break;
      case '\'':
	processed_prompt.append("'");
	break;
      case '"':
	processed_prompt.append('"');
	break;
      case 'S':
	processed_prompt.append(';');
	break;
      case 't':
	processed_prompt.append('\t');
	break;
      default:
	processed_prompt.append(c);
      }
    }
  }
  processed_prompt.append('\0');
  return processed_prompt.ptr();
}

3008 3009 3010

static void add_int_to_prompt(int toadd)
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3011 3012 3013 3014 3015
  char buffer[16];
  int10_to_str(toadd,buffer,10);
  processed_prompt.append(buffer);
}

3016 3017
static void init_username()
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3018 3019 3020 3021 3022 3023 3024
  my_free(full_username,MYF(MY_ALLOW_ZERO_PTR));
  my_free(part_username,MYF(MY_ALLOW_ZERO_PTR));

  MYSQL_RES *result;
  LINT_INIT(result);
  if (!mysql_query(&mysql,"select USER()") &&
      (result=mysql_use_result(&mysql)))
3025 3026 3027 3028 3029 3030
  {
    MYSQL_ROW cur=mysql_fetch_row(result);
    full_username=my_strdup(cur[0],MYF(MY_WME));
    part_username=my_strdup(strtok(cur[0],"@"),MYF(MY_WME));
    (void) mysql_fetch_row(result);		// Read eof
  }
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3031 3032
}

3033 3034 3035
static int com_prompt(String *buffer, char *line)
{
  char *ptr=strchr(line, ' ');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3036 3037
  prompt_counter = 0;
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
3038 3039
  current_prompt=my_strdup(ptr ? ptr+1 : default_prompt,MYF(MY_WME));
  if (!ptr)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3040 3041 3042 3043 3044 3045
    tee_fprintf(stdout, "Returning to default PROMPT of %s\n", default_prompt);
  else
    tee_fprintf(stdout, "PROMPT set to '%s'\n", current_prompt);
  return 0;
}

3046
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057
/* Keep sql_string library happy */

gptr sql_alloc(unsigned int Size)
{
  return my_malloc(Size,MYF(MY_WME));
}

void sql_element_free(void *ptr)
{
  my_free((gptr) ptr,MYF(0));
}
3058
#endif /* EMBEDDED_LIBRARY */
3059