mysql.cc 100 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
 *   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>
28
 *   Harrison Fisk <harrison@mysql.com>
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

43 44 45 46
#if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
#include <locale.h>
#endif

monty@mishka.local's avatar
monty@mishka.local committed
47
const char *VER= "14.12";
48

monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
49 50 51
/* 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
52
gptr sql_alloc(unsigned size);	     // Don't use mysqld alloc for these
bk@work.mysql.com's avatar
bk@work.mysql.com committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66
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
67
#include <asm/termbits.h>		// Standard linux
bk@work.mysql.com's avatar
bk@work.mysql.com committed
68 69 70 71 72 73 74 75
#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
76
#undef SYSV				// hack to avoid syntax error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
77 78 79 80 81 82
#ifdef HAVE_TERM_H
#include <term.h>
#endif
#endif
#endif

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
83
#undef bcmp				// Fix problem with new readline
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
84
#if defined( __WIN__) || defined(OS2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
85
#include <conio.h>
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
86
#elif !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
87 88 89 90 91 92 93 94
#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
95
#define vidattr(A) {}			// Can't get this to work
bk@work.mysql.com's avatar
bk@work.mysql.com committed
96 97
#endif

98
#ifdef FN_NO_CASE_SENCE
99
#define cmp_database(cs,A,B) my_strcasecmp((cs), (A), (B))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
100
#else
101
#define cmp_database(cs,A,B) strcmp((A),(B))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
102 103
#endif

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
104 105 106 107
#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
108 109
#include "completion_hash.h"

jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
110
#define PROMPT_CHAR '\\'
111
#define DEFAULT_DELIMITER ";"
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
112

bk@work.mysql.com's avatar
bk@work.mysql.com committed
113 114 115 116 117 118 119 120 121 122 123
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;
124
static char **defaults_argv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
125 126 127 128 129

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

static MYSQL mysql;			/* The connection */
130 131
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,
132
	       rehash=1,skip_updates=0,safe_updates=0,one_database=0,
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
133
	       opt_compress=0, using_opt_local_infile=0,
134 135
	       vertical=0, line_numbers=1, column_names=1,opt_html=0,
               opt_xml=0,opt_nopager=1, opt_outfile=0, named_cmds= 0,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
136
	       tty_password= 0, opt_nobeep=0, opt_reconnect=1,
137
	       default_charset_used= 0, opt_secure_auth= 0,
138 139
               default_pager_set= 0, opt_sigint_ignore= 0,
         show_warnings = 0;
140
static ulong opt_max_allowed_packet, opt_net_buffer_length;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
141
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
142 143 144
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,
145
            *current_prompt=0, *delimiter_str= 0,
146
            *default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
147
static char *histfile;
148
static char *histfile_tmp;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
149
static String glob_buffer,old_buffer;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
150 151
static String processed_prompt;
static char *full_username=0,*part_username=0,*default_prompt=0;
152
static int wait_time = 5;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
153
static STATUS status;
154
static ulong select_limit,max_join_size,opt_connect_timeout=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
155
static char mysql_charsets_dir[FN_REFLEN+1];
156
static const char *xmlmeta[] = {
157 158
  "&", "&amp;",
  "<", "&lt;",
159 160
  ">", "&gt;",
  "\"", "&quot;",
161 162
  0, 0
};
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
163 164 165 166 167 168 169 170
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;
171 172
static char delimiter[16]= DEFAULT_DELIMITER;
static uint delimiter_length= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
173

174 175 176 177
#ifdef HAVE_SMEM
static char *shared_memory_base_name=0;
#endif
static uint opt_protocol=0;
178
static CHARSET_INFO *charset_info= &my_charset_latin1;
179

bk@work.mysql.com's avatar
bk@work.mysql.com committed
180 181 182 183
#include "sslopt-vars.h"

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

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
184 185 186
void tee_fprintf(FILE *file, const char *fmt, ...);
void tee_fputs(const char *s, FILE *file);
void tee_puts(const char *s, FILE *file);
187
void tee_putc(int c, FILE *file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
188 189 190 191
/* 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*),
192
	   com_print(String *str,char*),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
193 194 195
	   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*),
196
	   com_rehash(String *str, char*), com_tee(String *str, char*),
197
           com_notee(String *str, char*), com_charset(String *str,char*),
198 199
           com_prompt(String *str, char*), com_delimiter(String *str, char*),
     com_warnings(String *str, char*), com_nowarnings(String *str, char*);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
200

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
201
#ifdef USE_POPEN
202
static int com_nopager(String *str, char*), com_pager(String *str, char*),
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
203
           com_edit(String *str,char*), com_shell(String *str, char *);
204 205
#endif

206
static int read_and_execute(bool interactive);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
207 208
static int sql_connect(char *host,char *database,char *user,char *password,
		       uint silent);
209 210 211
static int put_info(const char *str,INFO_TYPE info,uint error=0,
		    const char *sql_state=0);
static int put_error(MYSQL *mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
212
static void safe_put_field(const char *pos,ulong length);
213
static void xmlencode_print(const char *src, uint length);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
214 215
static void init_pager();
static void end_pager();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
216
static void init_tee(const char *);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
217
static void end_tee();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
218
static const char* construct_prompt();
219
static char *get_arg(char *line, my_bool get_next_arg);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
220 221
static void init_username();
static void add_int_to_prompt(int toadd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
222 223 224 225 226 227 228 229 230 231 232 233 234

/* 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[] = {
235
  { "?",      '?', com_help,   1, "Synonym for `help'." },
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
236
  { "clear",  'c', com_clear,  0, "Clear command."},
237
  { "connect",'r', com_connect,1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
238
    "Reconnect to the server. Optional arguments are db and host." },
239
  { "delimiter", 'd', com_delimiter,    1,
240
    "Set statement delimiter. NOTE: Takes the rest of the line as new delimiter." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
241
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
242
  { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
243
#endif
244
  { "ego",    'G', com_ego,    0,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
245 246 247
    "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." },
248
  { "help",   'h', com_help,   1, "Display this help." },
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
249
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
250 251 252
  { "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
253
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
254 255 256 257
  { "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
258
  { "prompt", 'R', com_prompt, 1, "Change your mysql prompt."},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
259 260
  { "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
261
  { "source", '.', com_source, 1,
paul@snake-hub.snake.net's avatar
paul@snake-hub.snake.net committed
262
    "Execute an SQL script file. Takes a file name as an argument."},
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
263
  { "status", 's', com_status, 0, "Get status information from the server."},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
264
#ifdef USE_POPEN
265 266
  { "system", '!', com_shell,  1, "Execute a system shell command."},
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
267 268
  { "tee",    'T', com_tee,    1, 
    "Set outfile [to_outfile]. Append everything into given outfile." },
269
  { "use",    'u', com_use,    1,
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
270
    "Use another database. Takes database name as argument." },
271 272
  { "charset",    'C', com_charset,    1,
    "Switch to another charset. Might be needed for processing binlog with multi-byte charsets." },
273
  { "warnings", 'W', com_warnings,  0,
274
    "Show warnings after every statement." },
275
  { "nowarning", 'w', com_nowarnings, 0,
276
    "Don't show warnings after every statement." },
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
277
  /* Get bash-like expansion for some commands */
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
  { "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
297 298 299
};

static const char *load_default_groups[]= { "mysql","client",0 };
300 301
static const char *server_default_groups[]=
{ "server", "embedded", "mysql_SERVER", 0 };
bk@work.mysql.com's avatar
bk@work.mysql.com committed
302 303

#ifdef HAVE_READLINE
304 305 306 307
/*
 HIST_ENTRY is defined for libedit, but not for the real readline
 Need to redefine it for real readline to find it
*/
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
308
#if !defined(HAVE_HIST_ENTRY)
309 310 311 312 313 314
typedef struct _hist_entry {
  const char      *line;
  const char      *data;
} HIST_ENTRY; 
#endif

315 316 317
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);
318 319 320
extern "C" HIST_ENTRY *history_get(int num);
extern "C" int history_length;
static int not_in_history(const char *line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
321
static void initialize_readline (char *name);
322
static void fix_history(String *final_command);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
323 324
#endif

325
static COMMANDS *find_command(char *name,char cmd_name);
326 327
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
328 329 330
static void remove_cntrl(String &buffer);
static void print_table_data(MYSQL_RES *result);
static void print_table_data_html(MYSQL_RES *result);
331
static void print_table_data_xml(MYSQL_RES *result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
332 333
static void print_tab_data(MYSQL_RES *result);
static void print_table_data_vertically(MYSQL_RES *result);
334
static void print_warnings(void);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
335 336 337 338 339 340 341 342 343 344
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];
monty@mishka.local's avatar
monty@mishka.local committed
345 346 347
  char *defaults, *extra_defaults, *group_suffix;
  char *emb_argv[4];
  int emb_argc;
348

monty@mishka.local's avatar
monty@mishka.local committed
349 350 351 352 353
  /* Get --defaults-xxx args for mysql_server_init() */
  emb_argc= get_defaults_options(argc, argv, &defaults, &extra_defaults,
                                 &group_suffix)+1;
  memcpy((char*) emb_argv, (char*) argv, emb_argc * sizeof(*argv));
  emb_argv[emb_argc]= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
354 355 356 357

  MY_INIT(argv[0]);
  DBUG_ENTER("main");
  DBUG_PROCESS(argv[0]);
358 359
  
  delimiter_str= delimiter;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
360 361 362 363 364 365
  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
366 367
  outfile[0]=0;			// no (default) outfile
  strmov(pager, "stdout");	// the default, if --pager wasn't given
368 369
  {
    char *tmp=getenv("PAGER");
370 371 372 373 374
    if (tmp && strlen(tmp))
    {
      default_pager_set= 1;
      strmov(default_pager, tmp);
    }
375
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
376 377 378 379 380 381 382 383 384
  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);
385
  defaults_argv=argv;
386
  if (get_options(argc, (char **) argv))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
387
  {
388
    free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
389 390 391 392
    my_end(0);
    exit(1);
  }
  if (status.batch && !status.line_buff &&
393
      !(status.line_buff=batch_readline_init(opt_max_allowed_packet+512,stdin)))
394 395
  {
    free_defaults(defaults_argv);
396
    my_end(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
397
    exit(1);
398
  }
399
  if (mysql_server_init(emb_argc, emb_argv, (char**) server_default_groups))
400 401
  {
    free_defaults(defaults_argv);
402
    my_end(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
403
    exit(1);
404
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
405
  glob_buffer.realloc(512);
406 407
  completion_hash_init(&ht, 128);
  init_alloc_root(&hash_mem_root, 16384, 0);
408
  bzero((char*) &mysql, sizeof(mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
409 410 411
  if (sql_connect(current_host,current_db,current_user,opt_password,
		  opt_silent))
  {
412 413 414
    quick=1;					// Avoid history
    status.exit_status=1;
    mysql_end(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
415 416 417
  }
  if (!status.batch)
    ignore_errors=1;				// Don't abort monitor
418 419 420 421 422

  if (opt_sigint_ignore)
    signal(SIGINT, SIG_IGN);
  else
    signal(SIGINT, mysql_end);			// Catch SIGINT to clean up
423
  signal(SIGQUIT, mysql_end);			// Catch SIGQUIT to clean up
bk@work.mysql.com's avatar
bk@work.mysql.com committed
424

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
425
  /*
426
    Run in interactive mode like the ingres/postgres monitor
bk@work.mysql.com's avatar
bk@work.mysql.com committed
427 428 429 430 431
  */

  put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
	   INFO_INFO);
  sprintf((char*) glob_buffer.ptr(),
432
	  "Your MySQL connection id is %lu to server version: %s\n",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
433 434 435 436
	  mysql_thread_id(&mysql),mysql_get_server_info(&mysql));
  put_info((char*) glob_buffer.ptr(),INFO_INFO);

#ifdef HAVE_READLINE
437
  initialize_readline((char*) my_progname);
438
  if (!status.batch && !quick && !opt_html && !opt_xml)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
439
  {
440
    /* read-history from file, default ~/.mysql_history*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
441 442 443 444
    if (getenv("MYSQL_HISTFILE"))
      histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
    else if (getenv("HOME"))
    {
445 446
      histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
				 + (uint) strlen("/.mysql_history")+2,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
447 448 449 450 451 452 453
				 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
454
	tee_fprintf(stdout, "Reading history-file %s\n",histfile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
455
      read_history(histfile);
456 457 458 459 460 461 462
      if (!(histfile_tmp= (char*) my_malloc((uint) strlen(histfile) + 5,
					    MYF(MY_WME))))
      {
	fprintf(stderr, "Couldn't allocate memory for temp histfile!\n");
	exit(1);
      }
      sprintf(histfile_tmp, "%s.TMP", histfile);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
463 464 465
    }
  }
#endif
466
  sprintf(buff, "%s",
467
#ifndef NOT_YET
468
	  "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n");
469
#else
470
	  "Type 'help [[%]function name[%]]' to get help on usage of function.\n");
471
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
472
  put_info(buff,INFO_INFO);
473
  status.exit_status= read_and_execute(!status.batch);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
474 475
  if (opt_outfile)
    end_tee();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
476 477 478 479 480 481 482 483
  mysql_end(0);
#ifndef _lint
  DBUG_RETURN(0);				// Keep compiler happy
#endif
}

sig_handler mysql_end(int sig)
{
484
  mysql_close(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
485
#ifdef HAVE_READLINE
486
  if (!status.batch && !quick && !opt_html && !opt_xml)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
487 488 489
  {
    /* write-history */
    if (verbose)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
490
      tee_fprintf(stdout, "Writing history-file %s\n",histfile);
491 492
    if (!write_history(histfile_tmp))
      my_rename(histfile_tmp, histfile, MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
493 494 495
  }
  batch_readline_end(status.line_buff);
  completion_hash_free(&ht);
496 497
  free_root(&hash_mem_root,MYF(0));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
498
#endif
499 500
  if (sig >= 0)
    put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
501 502
  glob_buffer.free();
  old_buffer.free();
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
503
  processed_prompt.free();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
504 505 506
  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));
507
  my_free(histfile_tmp,MYF(MY_ALLOW_ZERO_PTR));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
508 509 510
  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
511 512 513
  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));
514 515 516
#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
517
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
518
  mysql_server_end();
519
  free_defaults(defaults_argv);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
520 521 522 523
  my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
  exit(status.exit_status);
}

524 525

static struct my_option my_long_options[] =
bk@work.mysql.com's avatar
bk@work.mysql.com committed
526
{
527 528
  {"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
529 530
  {"help", 'I', "Synonym for -?", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
   0, 0, 0, 0, 0},
531
#ifdef __NETWARE__
532
  {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
533 534
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#endif
535 536
  {"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.",
537
   (gptr*) &rehash, (gptr*) &rehash, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
538
  {"no-auto-rehash", 'A',
539
   "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.",
540 541
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"batch", 'B',
serg@serg.mylan's avatar
serg@serg.mylan committed
542
   "Don't use history file. Disable interactive behavior. (Enables --silent)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
543 544 545
  {"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},
546 547 548
  {"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},
549 550 551
  {"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},
552 553
#ifdef DBUG_OFF
  {"debug", '#', "This is a non-debug version. Catch this and exit",
serg@serg.mylan's avatar
serg@serg.mylan committed
554
   0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
555 556
#else
  {"debug", '#', "Output debug log", (gptr*) &default_dbug_option,
557 558
   (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
559
  {"database", 'D', "Database to use.", (gptr*) &current_db,
560
   (gptr*) &current_db, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
561 562
  {"delimiter", OPT_DELIMITER, "Delimiter to be used.", (gptr*) &delimiter_str,
   (gptr*) &delimiter_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
serg@serg.mylan's avatar
serg@serg.mylan committed
563
  {"execute", 'e', "Execute command and quit. (Disables --force and history file)", 0,
564 565 566 567 568 569 570
   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},
571
  {"no-named-commands", 'g',
572
   "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.",
573 574
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"named-commands", 'G',
575
   "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.",
576 577
   (gptr*) &named_cmds, (gptr*) &named_cmds, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
   0, 0},
578
  {"ignore-spaces", 'i', "Ignore space after function names.", 0, 0, 0,
579
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
580
  {"local-infile", OPT_LOCAL_INFILE, "Enable/disable LOAD DATA LOCAL INFILE.",
581 582
   (gptr*) &opt_local_infile,
   (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
583 584
  {"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}, 
585
  {"host", 'h', "Connect to host.", (gptr*) &current_host,
586
   (gptr*) &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
587 588 589 590 591 592
  {"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,
593
   NO_ARG, 1, 0, 0, 0, 0, 0},  
594
  {"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,
595
   NO_ARG, 0, 0, 0, 0, 0, 0},
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
596
#ifdef USE_POPEN
597
  {"no-pager", OPT_NOPAGER,
598
   "Disable pager and print to stdout. See interactive help (\\h) also. WARNING: option deprecated; use --disable-pager instead.",
599
   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
600
#endif
601
  {"no-tee", OPT_NOTEE, "Disable outfile. See interactive help (\\h) also. WARNING: option deprecated; use --disable-tee instead", 0, 0, 0, GET_NO_ARG,
602 603 604 605 606
   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,
607
   NO_ARG, 1, 0, 0, 0, 0, 0},
608
  {"skip-column-names", 'N',
609
   "Don't write column names in results. WARNING: -N is deprecated, use long version of this options instead.",
610 611
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"set-variable", 'O',
612
   "Change the value of a variable. Please note that this option is deprecated; you can set variables directly with --variable-name=value.",
613
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
614 615 616
  {"sigint-ignore", OPT_SIGINT_IGNORE, "Ignore SIGINT (CTRL-C)",
   (gptr*) &opt_sigint_ignore,  (gptr*) &opt_sigint_ignore, 0, GET_BOOL,
   NO_ARG, 0, 0, 0, 0, 0, 0},
617 618 619
  {"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
620
#ifdef USE_POPEN
621 622 623
  {"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
624
#endif
625 626 627
  {"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
628
#ifdef __WIN__
629 630
  {"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
631
#endif
632
  {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
633
   (gptr*) &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
634
   0},
635
  {"prompt", OPT_PROMPT, "Set the mysql prompt to this value.",
636
   (gptr*) &current_prompt, (gptr*) &current_prompt, 0, GET_STR_ALLOC,
637
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
638
  {"protocol", OPT_MYSQL_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
639
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
640
  {"quick", 'q',
641
   "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.",
642
   (gptr*) &quick, (gptr*) &quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
643
  {"raw", 'r', "Write fields without conversion. Used with --batch.",
644 645
   (gptr*) &opt_raw_data, (gptr*) &opt_raw_data, 0, GET_BOOL, NO_ARG, 0, 0, 0,
   0, 0, 0},
646 647
  {"reconnect", OPT_RECONNECT, "Reconnect if the connection is lost. Disable with --disable-reconnect. This option is enabled by default.", 
   (gptr*) &opt_reconnect, (gptr*) &opt_reconnect, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
serg@serg.mylan's avatar
serg@serg.mylan committed
648
  {"silent", 's', "Be more silent. Print results with a tab as separator, each row on new line.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0,
649
   0, 0},
650
#ifdef HAVE_SMEM
651
  {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
652
   "Base name of shared memory.", (gptr*) &shared_memory_base_name, (gptr*) &shared_memory_base_name, 
653 654
   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#endif
655
  {"socket", 'S', "Socket file to use for connection.",
656
   (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR_ALLOC,
657
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
bk@work.mysql.com's avatar
bk@work.mysql.com committed
658
#include "sslopt-longopts.h"
659 660 661 662 663 664 665
  {"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
666
#ifndef DONT_ALLOW_USER_CHANGE
667
  {"user", 'u', "User for login if not current user.", (gptr*) &current_user,
668
   (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
669
#endif
670
  {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.",
671
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
672
   0, 0, 0, 0},
673
  {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.",
674
   (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0,
675
   0, 0, 0, 0},
676
  {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0,
677 678 679 680 681
   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},
682 683 684
  {"connect_timeout", OPT_CONNECT_TIMEOUT,
   "Number of seconds before connection timeout.",
   (gptr*) &opt_connect_timeout,
685
   (gptr*) &opt_connect_timeout, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3600*12, 0,
686
   0, 1},
687 688
  {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
   "Max packet length to send to, or receive from server",
689
   (gptr*) &opt_max_allowed_packet, (gptr*) &opt_max_allowed_packet, 0, GET_ULONG,
690 691
   REQUIRED_ARG, 16 *1024L*1024L, 4096, (longlong) 2*1024L*1024L*1024L,
   MALLOC_OVERHEAD, 1024, 0},
692 693
  {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
   "Buffer for TCP/IP and socket communication",
694
   (gptr*) &opt_net_buffer_length, (gptr*) &opt_net_buffer_length, 0, GET_ULONG,
695
   REQUIRED_ARG, 16384, 1024, 512*1024*1024L, MALLOC_OVERHEAD, 1024, 0},
696 697 698
  {"select_limit", OPT_SELECT_LIMIT,
   "Automatic limit for SELECT when using --safe-updates",
   (gptr*) &select_limit,
699
   (gptr*) &select_limit, 0, GET_ULONG, REQUIRED_ARG, 1000L, 1, ~0L, 0, 1, 0},
700 701 702
  {"max_join_size", OPT_MAX_JOIN_SIZE,
   "Automatic limit for rows in a join when using --safe-updates",
   (gptr*) &max_join_size,
703
   (gptr*) &max_join_size, 0, GET_ULONG, REQUIRED_ARG, 1000000L, 1, ~0L, 0, 1,
704
   0},
705 706 707
  {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
    " uses old (pre-4.1.1) protocol", (gptr*) &opt_secure_auth,
    (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
708 709 710
  {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
    (gptr*) &show_warnings, (gptr*) &show_warnings, 0, GET_BOOL, NO_ARG, 
    0, 0, 0, 0, 0, 0},
711
  { 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
712 713 714 715 716
};


static void usage(int version)
{
monty@mysql.com's avatar
monty@mysql.com committed
717 718 719 720
  /* Divert all help information on NetWare to logger screen. */
#ifdef __NETWARE__
#define printf	consoleprintf
#endif
721 722 723 724 725 726 727

#if defined(USE_LIBEDIT_INTERFACE)
  const char* readline= "";
#else
  const char* readline= "readline";
#endif

728
#ifdef HAVE_READLINE
729 730 731
  printf("%s  Ver %s Distrib %s, for %s (%s) using %s %s\n",
	 my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,
         readline, rl_library_version);
732
#else
733
  printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, VER,
734 735 736
	MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
737 738
  if (version)
    return;
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
739
  printf("\
740
Copyright (C) 2002 MySQL AB\n\
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
741 742
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
743
  printf("Usage: %s [OPTIONS] [database]\n", my_progname);
744
  my_print_help(my_long_options);
745
  print_defaults("my", load_default_groups);
746
  my_print_variables(my_long_options);
monty@mysql.com's avatar
monty@mysql.com committed
747 748 749 750
  NETWARE_SET_SCREEN_MODE(1);
#ifdef __NETWARE__
#undef printf
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
751 752
}

monty@mysql.com's avatar
monty@mysql.com committed
753

754 755 756
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
757
{
758
  switch(optid) {
759 760 761 762 763
#ifdef __NETWARE__
  case OPT_AUTO_CLOSE:
    setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
    break;
#endif
764 765 766 767
  case OPT_CHARSETS_DIR:
    strmov(mysql_charsets_dir, argument);
    charsets_dir = mysql_charsets_dir;
    break;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
768 769 770
  case  OPT_DEFAULT_CHARSET:
    default_charset_used= 1;
    break;
771 772 773 774 775
  case OPT_DELIMITER:
    if (argument == disabled_my_option)
      strmov(delimiter, DEFAULT_DELIMITER);
    else
      strmake(delimiter, argument, sizeof(delimiter) - 1);
776
    delimiter_length= (uint)strlen(delimiter);
777 778
    delimiter_str= delimiter;
    break;
779 780 781 782 783 784
  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
785 786
      if (opt_outfile)
	end_tee();
787 788
    }
    else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
789
      init_tee(argument);
790 791
    break;
  case OPT_NOTEE:
792
    printf("WARNING: option deprecated; use --disable-tee instead.\n");
793 794 795 796
    if (opt_outfile)
      end_tee();
    break;
  case OPT_PAGER:
797 798
    if (argument == disabled_my_option)
      opt_nopager= 1;
799
    else
800 801
    {
      opt_nopager= 0;
802 803 804
      if (argument && strlen(argument))
      {
	default_pager_set= 1;
805
	strmov(pager, argument);
806 807 808
	strmov(default_pager, pager);
      }
      else if (default_pager_set)
809
	strmov(pager, default_pager);
810 811
      else
	opt_nopager= 1;
812
    }
813 814
    break;
  case OPT_NOPAGER:
815
    printf("WARNING: option deprecated; use --disable-pager instead.\n");
816
    opt_nopager= 1;
817 818
  case OPT_MYSQL_PROTOCOL:
  {
819
    if ((opt_protocol= find_type(argument, &sql_protocol_typelib,0)) <= 0)
820 821 822 823 824 825
    {
      fprintf(stderr, "Unknown option to protocol: %s\n", argument);
      exit(1);
    }
    break;
  }
826
  break;
827 828 829 830 831 832 833 834 835
  case 'A':
    rehash= 0;
    break;
  case 'N':
    column_names= 0;
    break;
  case 'e':
    status.batch= 1;
    status.add_to_history= 0;
serg@serg.mylan's avatar
serg@serg.mylan committed
836 837 838
    if (!status.line_buff)
      ignore_errors= 0;                         // do it for the first -e only
    if (!(status.line_buff= batch_readline_command(status.line_buff, argument)))
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
      return 1;
    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 ;
858
      tty_password= 0;
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
    }
    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':
serg@serg.mylan's avatar
serg@serg.mylan committed
880 881
    status.batch= 1;
    status.add_to_history= 0;
882
    set_if_bigger(opt_silent,1);                         // more silent
883 884
    break;
  case 'W':
bk@work.mysql.com's avatar
bk@work.mysql.com committed
885
#ifdef __WIN__
886
    opt_protocol = MYSQL_PROTOCOL_PIPE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
887
#endif
888
    break;
889
#include <sslopt-case.h>
890 891 892 893 894 895 896
  case 'V':
    usage(1);
    exit(0);
  case 'I':
  case '?':
    usage(0);
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
897
  }
898 899 900 901 902 903 904 905
  return 0;
}


static int get_options(int argc, char **argv)
{
  char *tmp, *pagpoint;
  int ho_error;
906
  MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921

  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);

922 923 924
  opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
  opt_net_buffer_length= *mysql_params->p_net_buffer_length;

925
  if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
926
    exit(ho_error);
927

928 929 930
  *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
  *mysql_params->p_net_buffer_length= opt_net_buffer_length;

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
931 932 933 934
  if (status.batch) /* disable pager and outfile in this case */
  {
    strmov(default_pager, "stdout");
    strmov(pager, "stdout");
935
    opt_nopager= 1;
936
    default_pager_set= 0;
937
    opt_outfile= 0;
938
    opt_reconnect= 0;
939
    connect_flag= 0; /* Not in interactive mode */
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
940
  }
941 942 943
  
  if (strcmp(default_charset, charset_info->csname) &&
      !(charset_info= get_charset_by_csname(default_charset, 
944
					    MY_CS_PRIMARY, MYF(MY_WME))))
945
    exit(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
946 947 948 949 950 951 952
  if (argc > 1)
  {
    usage(0);
    exit(1);
  }
  if (argc == 1)
  {
953
    skip_updates= 0;
954 955
    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
956 957
  }
  if (tty_password)
958
    opt_password= get_tty_password(NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
959 960 961
  return(0);
}

962
static int read_and_execute(bool interactive)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
963
{
964
#if defined(OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
965
  char linebuffer[254];
966
  String buffer;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
967
#endif
968 969 970 971 972
#if defined(__WIN__)
  String tmpbuf;
  String buffer;
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
973 974 975
  char	*line;
  char	in_string=0;
  ulong line_number=0;
976
  bool ml_comment= 0;  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
977 978
  COMMANDS *com;
  status.exit_status=1;
979
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
980 981
  for (;;)
  {
982
    if (!interactive)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
983 984 985 986 987 988 989
    {
      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
990
    {
991 992
      char *prompt= (char*) (ml_comment ? "   /*> " :
                             glob_buffer.is_empty() ?  construct_prompt() :
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
993 994
			     !in_string ? "    -> " :
			     in_string == '\'' ?
995 996 997
			     "    '> " : (in_string == '`' ?
			     "    `> " :
			     "    \"> "));
998 999
      if (opt_outfile && glob_buffer.is_empty())
	fflush(OUTFILE);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1000 1001 1002

#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
      tee_fputs(prompt, stdout);
1003
#if defined(__NETWARE__)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1004 1005
      line=fgets(linebuffer, sizeof(linebuffer)-1, stdin);
      /* Remove the '\n' */
monty@mysql.com's avatar
monty@mysql.com committed
1006
      if (line)
1007
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1008
        char *p = strrchr(line, '\n');
1009 1010
        if (p != NULL)
          *p = '\0';
1011
      }
1012 1013 1014
#elif defined(__WIN__)
      if (!tmpbuf.is_alloced())
        tmpbuf.alloc(65535);
1015
      tmpbuf.length(0);
1016 1017 1018 1019
      buffer.length(0);
      unsigned long clen;
      do
      {
1020
	line= my_cgets((char*)tmpbuf.ptr(), tmpbuf.alloced_length()-1, &clen);
1021 1022 1023 1024 1025
        buffer.append(line, clen);
        /* 
           if we got buffer fully filled than there is a chance that
           something else is still in console input buffer
        */
1026
      } while (tmpbuf.alloced_length() <= clen);
1027 1028
      line= buffer.c_ptr();
#else /* OS2 */
1029
      buffer.length(0);
1030 1031
      /* _cgets() expects the buffer size - 3 as the first byte */
      linebuffer[0]= (char) sizeof(linebuffer) - 3;
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
      do
      {
        line= _cgets(linebuffer);
        buffer.append(line, (unsigned char)linebuffer[1]);
      /*
        If _cgets() gets an input line that is linebuffer[0] bytes
        long, the next call to _cgets() will return immediately with
        linebuffer[1] == 0, and it does the same thing for input that
        is linebuffer[0]-1 bytes long. So it appears that even though
        _cgets() replaces the newline (which is two bytes on Window) with
        a nil, it still needs the space in the linebuffer for it. This is,
        naturally, undocumented.
       */
1045 1046
      } while ((unsigned char)linebuffer[0] <=
               (unsigned char)linebuffer[1] + 1);
1047
      line= buffer.c_ptr();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1048 1049 1050 1051 1052 1053 1054
#endif /* __NETWARE__ */
#else
      if (opt_outfile)
	fputs(prompt, OUTFILE);
      line= readline(prompt);
#endif /* defined( __WIN__) || defined(OS2) || defined(__NETWARE__) */

monty@mysql.com's avatar
monty@mysql.com committed
1055 1056 1057 1058 1059
      /*
        When Ctrl+d or Ctrl+z is pressed, the line may be NULL on some OS
        which may cause coredump.
      */
      if (opt_outfile && line)
1060
	fprintf(OUTFILE, "%s\n", line);
1061
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1062 1063 1064 1065 1066 1067 1068 1069
    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
1070
      continue;					// Skip comment lines
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1071

1072 1073 1074 1075
    /*
      Check if line is a mysql command line
      (We want to allow help, print and clear anywhere at line start
    */
1076
    if ((named_cmds || glob_buffer.is_empty())
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1077
	&& !in_string && (com=find_command(line,0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1078 1079 1080 1081 1082 1083
    {
      if ((*com->func)(&glob_buffer,line) > 0)
	break;
      if (glob_buffer.is_empty())		// If buffer was emptied
	in_string=0;
#ifdef HAVE_READLINE
1084
      if (interactive && status.add_to_history && not_in_history(line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1085 1086 1087 1088
	add_history(line);
#endif
      continue;
    }
1089
    if (add_line(glob_buffer,line,&in_string,&ml_comment))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1090 1091 1092 1093
      break;
  }
  /* if in batch mode, send last query even if it doesn't end with \g or go */

1094
  if (!interactive && !status.exit_status)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095 1096 1097 1098 1099 1100 1101 1102 1103
  {
    remove_cntrl(glob_buffer);
    if (!glob_buffer.is_empty())
    {
      status.exit_status=1;
      if (com_go(&glob_buffer,line) <= 0)
	status.exit_status=0;
    }
  }
1104

1105 1106 1107
#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
  buffer.free();
#endif
1108 1109 1110 1111
#if defined( __WIN__)
  tmpbuf.free();
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1112 1113 1114 1115
  return status.exit_status;
}


1116
static COMMANDS *find_command(char *name,char cmd_char)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1117 1118 1119
{
  uint len;
  char *end;
1120 1121
  DBUG_ENTER("find_command");
  DBUG_PRINT("enter",("name: '%s'  char: %d", name ? name : "NULL", cmd_char));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1122 1123 1124 1125 1126 1127 1128 1129

  if (!name)
  {
    len=0;
    end=0;
  }
  else
  {
1130
    while (my_isspace(charset_info,*name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1131
      name++;
1132
    /*
1133 1134 1135
      If there is an \\g in the row or if the row has a delimiter but
      this is not a delimiter command, let add_line() take care of
      parsing the row and calling find_command()
1136 1137
    */
    if (strstr(name, "\\g") || (strstr(name, delimiter) &&
1138 1139 1140 1141 1142
                                !(strlen(name) >= 9 &&
                                  !my_strnncoll(charset_info,
                                                (uchar*) name, 9,
                                                (const uchar*) "delimiter",
                                                9))))
1143
      DBUG_RETURN((COMMANDS *) 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1144 1145 1146
    if ((end=strcont(name," \t")))
    {
      len=(uint) (end - name);
1147
      while (my_isspace(charset_info,*end))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1148 1149 1150 1151 1152
	end++;
      if (!*end)
	end=0;					// no arguments to function
    }
    else
1153
      len=(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1154 1155 1156 1157 1158
  }

  for (uint i= 0; commands[i].name; i++)
  {
    if (commands[i].func &&
1159
	((name &&
1160 1161
	  !my_strnncoll(charset_info,(uchar*)name,len,
				     (uchar*)commands[i].name,len) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1162 1163 1164
	  !commands[i].name[len] &&
	  (!end || (end && commands[i].takes_params))) ||
	 !name && commands[i].cmd_char == cmd_char))
1165 1166 1167 1168
    {
      DBUG_PRINT("exit",("found command: %s", commands[i].name));
      DBUG_RETURN(&commands[i]);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1169
  }
1170
  DBUG_RETURN((COMMANDS *) 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1171 1172 1173
}


1174 1175
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
1176 1177
{
  uchar inchar;
1178
  char buff[80], *pos, *out;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1179
  COMMANDS *com;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1180
  bool need_space= 0;
1181
  DBUG_ENTER("add_line");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1182 1183

  if (!line[0] && buffer.is_empty())
1184
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1185
#ifdef HAVE_READLINE
1186
  if (status.add_to_history && line[0] && not_in_history(line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1187 1188 1189
    add_history(line);
#endif
#ifdef USE_MB
1190
  char *end_of_line=line+(uint) strlen(line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1191 1192 1193 1194
#endif

  for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
  {
1195
    if (my_isspace(charset_info,inchar) && out == line && 
1196
        buffer.is_empty())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1197 1198
      continue;
#ifdef USE_MB
1199
    int length;
1200
    if (use_mb(charset_info) &&
bar@mysql.com's avatar
bar@mysql.com committed
1201
        (length= my_ismbchar(charset_info, pos, end_of_line)))
1202
    {
1203 1204
      if (!*ml_comment)
      {
bar@mysql.com's avatar
bar@mysql.com committed
1205
        while (length--)
1206 1207 1208 1209
          *out++ = *pos++;
        pos--;
      }
      else
bar@mysql.com's avatar
bar@mysql.com committed
1210
        pos+= length - 1;
1211
      continue;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1212 1213
    }
#endif
1214
    if (!*ml_comment && inchar == '\\')
1215 1216 1217
    {
      // Found possbile one character command like \c

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1218 1219
      if (!(inchar = (uchar) *++pos))
	break;				// readline adds one '\'
1220
      if (*in_string || inchar == 'N')	// \N is short for NULL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222 1223 1224 1225 1226 1227
      {					// Don't allow commands in string
	*out++='\\';
	*out++= (char) inchar;
	continue;
      }
      if ((com=find_command(NullS,(char) inchar)))
      {
1228
	const String tmp(line,(uint) (out-line), charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1229 1230
	buffer.append(tmp);
	if ((*com->func)(&buffer,pos-1) > 0)
1231
	  DBUG_RETURN(1);                       // Quit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1232 1233
	if (com->takes_params)
	{
1234 1235 1236 1237
	  for (pos++ ;
	       *pos && (*pos != *delimiter ||
			!is_prefix(pos + 1, delimiter + 1)) ; pos++)
	    ;	// Remove parameters
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1238 1239
	  if (!*pos)
	    pos--;
1240 1241
	  else 
	    pos+= delimiter_length - 1; // Point at last delim char
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1242 1243 1244 1245 1246 1247 1248
	}
	out=line;
      }
      else
      {
	sprintf(buff,"Unknown command '\\%c'.",inchar);
	if (put_info(buff,INFO_ERROR) > 0)
1249
	  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1250 1251 1252 1253 1254
	*out++='\\';
	*out++=(char) inchar;
	continue;
      }
    }
1255 1256 1257 1258
    else if (!*ml_comment && !*in_string &&
             (*pos == *delimiter && is_prefix(pos + 1, delimiter + 1) ||
              buffer.length() == 0 && (out - line) >= 9 &&
              !my_strcasecmp(charset_info, line, "delimiter")))
1259 1260
    {					
      uint old_delimiter_length= delimiter_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1261
      if (out != line)
1262 1263
	buffer.append(line, (uint) (out - line));	// Add this line
      if ((com= find_command(buffer.c_ptr(), 0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1264
      {
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
        if (com->func == com_delimiter)
        {
          /*
            Delimiter wants the get rest of the given line as argument to
            allow one to change ';' to ';;' and back
          */
          char *end= strend(pos);
          buffer.append(pos, (uint) (end - pos));
          /* Ensure pos will point at \0 after the pos+= below */
          pos= end - old_delimiter_length + 1;
        }
1276
	if ((*com->func)(&buffer, buffer.c_ptr()) > 0)
1277
	  DBUG_RETURN(1);                       // Quit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1278 1279 1280
      }
      else
      {
serg@serg.mylan's avatar
serg@serg.mylan committed
1281
	if (com_go(&buffer, 0) > 0)             // < 0 is not fatal
1282
	  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1283 1284
      }
      buffer.length(0);
1285 1286
      out= line;
      pos+= old_delimiter_length - 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1287
    }
1288 1289
    else if (!*ml_comment && (!*in_string && (inchar == '#' ||
			      inchar == '-' && pos[1] == '-' &&
1290
			      my_isspace(charset_info,pos[2]))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1291
      break;					// comment to end of line
1292 1293
    else if (!*in_string && inchar == '/' && *(pos+1) == '*' &&
	     *(pos+2) != '!')
1294 1295 1296 1297 1298 1299 1300 1301 1302
    {
      pos++;
      *ml_comment= 1;
      if (out != line)
      {
        buffer.append(line,(uint) (out-line));
        out=line;
      }
    }
1303
    else if (*ml_comment && inchar == '*' && *(pos + 1) == '/')
1304 1305 1306
    {
      pos++;
      *ml_comment= 0;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1307
      need_space= 1;
1308
    }      
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1309 1310 1311
    else
    {						// Add found char to buffer
      if (inchar == *in_string)
1312
	*in_string= 0;
1313 1314 1315 1316
      else if (!*ml_comment && !*in_string &&
	       (inchar == '\'' || inchar == '"' || inchar == '`'))
	*in_string= (char) inchar;
      if (!*ml_comment)
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1317 1318 1319 1320 1321 1322
      {
        if (need_space && !my_isspace(charset_info, (char)inchar))
        {
          *out++= ' ';
          need_space= 0;
        }
1323
	*out++= (char) inchar;
antony@ltantony.mysql.com's avatar
antony@ltantony.mysql.com committed
1324
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1325 1326 1327 1328 1329 1330 1331 1332
    }
  }
  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);
1333
    if (!(*ml_comment) && buffer.append(line,length))
1334
      DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1335
  }
1336
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1337 1338
}

1339 1340 1341
/*****************************************************************
	    Interface to Readline Completion
******************************************************************/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1342 1343 1344

#ifdef HAVE_READLINE

1345 1346
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
1347

1348 1349 1350 1351 1352
/*
  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
1353

1354 1355 1356 1357 1358
#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
1359 1360 1361 1362
{
  return 0;					/* No filename completion */
}

1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
/*	glues pieces of history back together if in pieces   */
static void fix_history(String *final_command) 
{
  int total_lines = 1;
  char *ptr = final_command->c_ptr();
  String fixed_buffer; 	/* Converted buffer */
  char str_char = '\0';  /* Character if we are in a string or not */
  
  /* find out how many lines we have and remove newlines */
  while (*ptr != '\0') 
  {
    switch (*ptr) {
      /* string character */
    case '"':
    case '\'':
    case '`':
      if (str_char == '\0')	/* open string */
	str_char = *ptr;
      else if (str_char == *ptr)   /* close string */
	str_char = '\0';
      fixed_buffer.append(ptr,1);
      break;
    case '\n':
      /* 
	 not in string, change to space
	 if in string, leave it alone 
      */
      fixed_buffer.append(str_char == '\0' ? " " : "\n");
      total_lines++;
      break;
    case '\\':
      fixed_buffer.append('\\');
      /* need to see if the backslash is escaping anything */
      if (str_char) 
      {
	ptr++;
	/* special characters that need escaping */
	if (*ptr == '\'' || *ptr == '"' || *ptr == '\\')
	  fixed_buffer.append(ptr,1);
	else
	  ptr--;
      }
      break;
      
    default:
      fixed_buffer.append(ptr,1);
    }
    ptr++;
  }
  if (total_lines > 1)			
    add_history(fixed_buffer.ptr());
}

/*	
  returns 0 if line matches the previous history entry
  returns 1 if the line doesn't match the previous history entry
*/
static int not_in_history(const char *line) 
{
  HIST_ENTRY *oldhist = history_get(history_length);
  
  if (oldhist == 0)
    return 1;
  if (strcmp(oldhist->line,line) == 0)
    return 0;
  return 1;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1431 1432 1433 1434 1435 1436
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. */
1437 1438 1439 1440
#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)
1441 1442 1443
#ifdef HAVE_LOCALE_H
  setlocale(LC_ALL,""); /* so as libedit use isprint */
#endif
1444
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
1445
  rl_completion_entry_function= (Function*)&no_completion;
1446
#else
1447 1448
  rl_attempted_completion_function= (CPPFunction*)&new_mysql_completion;
  rl_completion_entry_function= (Function*)&no_completion;
1449
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1450 1451
}

1452 1453 1454 1455 1456 1457
/*
  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
1458

1459
static char **new_mysql_completion (const char *text,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1460 1461 1462 1463
				    int start __attribute__((unused)),
				    int end __attribute__((unused)))
{
  if (!status.batch && !quick)
1464
#if defined(USE_NEW_READLINE_INTERFACE)
1465
    return rl_completion_matches(text, new_command_generator);
1466 1467 1468
#else
    return completion_matches((char *)text, (CPFunction *)new_command_generator);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1469 1470 1471 1472
  else
    return (char**) 0;
}

1473
static char *new_command_generator(const char *text,int state)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1474 1475 1476 1477 1478 1479 1480
{
  static int textlen;
  char *ptr;
  static Bucket *b;
  static entry *e;
  static uint i;

1481
  if (!state)
1482
    textlen=(uint) strlen(text);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1483

1484 1485 1486 1487
  if (textlen>0)
  {						/* lookup in the hash */
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1488 1489
      uint len;

1490
      b = find_all_matches(&ht,text,(uint) strlen(text),&len);
1491
      if (!b)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1492 1493 1494 1495
	return NullS;
      e = b->pData;
    }

1496 1497
    if (e)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1498 1499 1500 1501
      ptr= strdup(e->str);
      e = e->pNext;
      return ptr;
    }
1502 1503 1504
  }
  else
  { /* traverse the entire hash, ugly but works */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1505

1506 1507
    if (!state)
    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1508
      /* find the first used bucket */
1509 1510 1511 1512
      for (i=0 ; i < ht.nTableSize ; i++)
      {
	if (ht.arBuckets[i])
	{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1513 1514 1515 1516 1517 1518 1519
	  b = ht.arBuckets[i];
	  e = b->pData;
	  break;
	}
      }
    }
    ptr= NullS;
1520 1521 1522
    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
1523 1524 1525
	ptr = strdup(e->str);
      /* find the next used entry */
      e = e->pNext;
1526 1527
      if (!e)
      { /* find the next used bucket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1528
	b = b->pNext;
1529 1530 1531 1532 1533 1534
	if (!b)
	{
	  for (i++ ; i<ht.nTableSize; i++)
	  {
	    if (ht.arBuckets[i])
	    {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1535 1536 1537 1538 1539 1540
	      b = ht.arBuckets[i];
	      e = b->pData;
	      break;
	    }
	  }
	}
1541 1542
	else
	  e = b->pData;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1543 1544
      }
    }
1545
    if (ptr)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1546 1547 1548 1549 1550 1551 1552 1553
      return ptr;
  }
  return NullS;
}


/* Build up the completion hash */

1554
static void build_completion_hash(bool rehash, bool write_info)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1555 1556
{
  COMMANDS *cmd=commands;
1557 1558
  MYSQL_RES *databases=0,*tables=0;
  MYSQL_RES *fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1559 1560 1561 1562 1563 1564 1565
  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");

1566
  if (status.batch || quick || !current_db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1567 1568 1569 1570 1571 1572 1573
    DBUG_VOID_RETURN;			// We don't need completion in batches

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

1577 1578 1579 1580 1581 1582
  /* 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
1583 1584 1585
  /* hash MySQL functions (to be implemented) */

  /* hash all database names */
1586 1587
  if (mysql_query(&mysql,"show databases") == 0)
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588 1589 1590 1591 1592
    if (!(databases = mysql_store_result(&mysql)))
      put_info(mysql_error(&mysql),INFO_INFO);
    else
    {
      while ((database_row=mysql_fetch_row(databases)))
1593 1594 1595 1596 1597 1598
      {
	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
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
    }
  }
  /* 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
1610
	tee_fprintf(stdout, "\
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1611 1612 1613 1614 1615
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)))
      {
1616 1617 1618 1619
	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
1620 1621 1622 1623 1624
      }
    }
  }

  /* hash all field names, both with the table prefix and without it */
1625 1626
  if (!tables)					/* no tables */
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1627 1628 1629
    DBUG_VOID_RETURN;
  }
  mysql_data_seek(tables,0);
1630 1631 1632 1633
  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
1634
    DBUG_VOID_RETURN;
1635
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1636 1637 1638 1639 1640 1641
  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);
1642 1643 1644
      if (!(field_names[i] = (char **) alloc_root(&hash_mem_root,
						  sizeof(char *) *
						  (num_fields*2+1))))
1645 1646 1647 1648
      {
        mysql_free_result(fields);
        break;
      }
1649
      field_names[i][num_fields*2]= '\0';
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1650 1651 1652
      j=0;
      while ((sql_field=mysql_fetch_field(fields)))
      {
1653
	sprintf(buf,"%.64s.%.64s",table_row[0],sql_field->name);
1654
	field_names[i][j] = strdup_root(&hash_mem_root,buf);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1655
	add_word(&ht,field_names[i][j]);
1656 1657
	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
1658
	if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
1659
				    (uint) strlen(field_names[i][num_fields+j])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1660 1661 1662
	  add_word(&ht,field_names[i][num_fields+j]);
	j++;
      }
1663
      mysql_free_result(fields);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1664 1665
    }
    else
1666
      field_names[i]= 0;
patg@krsna.patg.net's avatar
patg@krsna.patg.net committed
1667

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1668 1669
    i++;
  }
1670
  mysql_free_result(tables);
1671
  field_names[i]=0;				// End pointer
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1672 1673 1674 1675 1676 1677 1678
  DBUG_VOID_RETURN;
}

	/* for gnu readline */

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

1681
char *index(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682 1683 1684 1685 1686 1687 1688 1689
{
  for (;;)
  {
     if (*s == (char) c) return (char*) s;
     if (!*s++) return NullS;
  }
}

1690
char *rindex(const char *s,int c)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701
{
  reg3 char *t;

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

1702

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1703 1704
static int reconnect(void)
{
1705
  if (opt_reconnect)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1706 1707 1708
  {
    put_info("No connection. Trying to reconnect...",INFO_INFO);
    (void) com_connect((String *) 0, 0);
1709 1710
    if (rehash)
      com_rehash(NULL, NULL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1711 1712 1713 1714 1715 1716
  }
  if (!connected)
    return put_info("Can't connect to the server\n",INFO_ERROR);
  return 0;
}

1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732
static void get_current_db()
{
  MYSQL_RES *res;

  my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
  current_db= NULL;
  /* In case of error below current_db will be NULL */
  if (!mysql_query(&mysql, "SELECT DATABASE()") &&
      (res= mysql_use_result(&mysql)))
  {
    MYSQL_ROW row= mysql_fetch_row(res);
    if (row[0])
      current_db= my_strdup(row[0], MYF(MY_WME));
    mysql_free_result(res);
  }
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1733 1734 1735 1736 1737

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

1738 1739 1740 1741
int mysql_real_query_for_lazy(const char *buf, int length)
{
  for (uint retry=0;; retry++)
  {
1742
    int error;
1743
    if (!mysql_real_query(&mysql,buf,length))
1744
      return 0;
1745
    error= put_error(&mysql);
1746
    if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
1747
        !opt_reconnect)
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
      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])
1760
    return put_error(&mysql);
1761 1762 1763
  return 0;
}

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1764 1765 1766 1767 1768
static void print_help_item(MYSQL_ROW *cur, int num_name, int num_cat, char *last_char)
{
  char ccat= (*cur)[num_cat][0];
  if (*last_char != ccat)
  {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1769
    put_info(ccat == 'Y' ? "categories:" : "topics:", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1770 1771 1772 1773
    *last_char= ccat;
  }
  tee_fprintf(PAGER, "   %s\n", (*cur)[num_name]);
}
1774

1775

1776
static int com_server_help(String *buffer __attribute__((unused)),
1777
			   char *line __attribute__((unused)), char *help_arg)
1778 1779 1780 1781
{
  MYSQL_ROW cur;
  const char *server_cmd= buffer->ptr();
  char cmd_buf[100];
1782 1783
  MYSQL_RES *result;
  int error;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1784
  
1785
  if (help_arg[0] != '\'')
1786
  {
1787
    (void) strxnmov(cmd_buf, sizeof(cmd_buf), "help '", help_arg, "'", NullS);
1788 1789
    server_cmd= cmd_buf;
  }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1790
  
1791 1792 1793 1794 1795 1796 1797 1798 1799
  if (!status.batch)
  {
    old_buffer= *buffer;
    old_buffer.copy();
  }

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

1800
  if ((error= mysql_real_query_for_lazy(server_cmd,(int)strlen(server_cmd))) ||
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1801
      (error= mysql_store_result_for_lazy(&result)))
1802 1803 1804 1805
    return error;

  if (result)
  {
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1806 1807
    unsigned int num_fields= mysql_num_fields(result);
    my_ulonglong num_rows= mysql_num_rows(result);
monty@mysql.com's avatar
monty@mysql.com committed
1808
    mysql_fetch_fields(result);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1809
    if (num_fields==3 && num_rows==1)
1810 1811
    {
      if (!(cur= mysql_fetch_row(result)))
1812 1813 1814 1815
      {
	error= -1;
	goto err;
      }
1816 1817

      init_pager();
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1818 1819 1820 1821 1822
      tee_fprintf(PAGER,   "Name: \'%s\'\n", cur[0]);
      tee_fprintf(PAGER,   "Description:\n%s", cur[1]);
      if (cur[2] && *((char*)cur[2]))
	tee_fprintf(PAGER, "Examples:\n%s", cur[2]);
      tee_fprintf(PAGER,   "\n");
1823 1824
      end_pager();
    }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1825
    else if (num_fields >= 2 && num_rows)
1826 1827
    {
      init_pager();
1828 1829
      char last_char= 0;

1830
      int num_name= 0, num_cat= 0;
1831 1832 1833
      LINT_INIT(num_name);
      LINT_INIT(num_cat);

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1834
      if (num_fields == 2)
1835
      {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1836
	put_info("Many help items for your request exist.", INFO_INFO);
paul@frost.snake.net's avatar
paul@frost.snake.net committed
1837
	put_info("To make a more specific request, please type 'help <item>',\nwhere <item> is one of the following", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1838 1839
	num_name= 0;
	num_cat= 1;
1840
      }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1841 1842
      else if ((cur= mysql_fetch_row(result)))
      {
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1843
	tee_fprintf(PAGER, "You asked for help about help category: \"%s\"\n", cur[0]);
paul@frost.snake.net's avatar
paul@frost.snake.net committed
1844
	put_info("For more information, type 'help <item>', where <item> is one of the following", INFO_INFO);
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1845 1846 1847 1848
	num_name= 1;
	num_cat= 2;
	print_help_item(&cur,1,2,&last_char);
      }
1849

vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1850 1851
      while ((cur= mysql_fetch_row(result)))
	print_help_item(&cur,num_name,num_cat,&last_char);
1852 1853 1854 1855 1856
      tee_fprintf(PAGER, "\n");
      end_pager();
    }
    else
    {
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1857
      put_info("\nNothing found", INFO_INFO);
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
1858
      put_info("Please try to run 'help contents' for a list of all accessible topics\n", INFO_INFO);
1859 1860 1861
    }
  }

1862
err:
1863 1864 1865 1866
  mysql_free_result(result);
  return error;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1867
static int
1868 1869
com_help(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1870
{
1871 1872
  reg1 int i, j;
  char * help_arg= strchr(line,' '), buff[32], *end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1873

1874 1875
  if (help_arg)
    return com_server_help(buffer,line,help_arg+1);
1876

1877 1878 1879 1880 1881 1882 1883
  put_info("\nFor information about MySQL products and services, visit:\n"
           "   http://www.mysql.com/\n"
           "For developer information, including the MySQL Reference Manual, "
           "visit:\n"
           "   http://dev.mysql.com/\n"
           "To buy MySQL Network Support, training, or other products, visit:\n"
           "   https://shop.mysql.com/\n", INFO_INFO);
1884 1885 1886 1887
  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++)
1888
  {
1889
    end= strmov(buff, commands[i].name);
1890
    for (j= (int)strlen(commands[i].name); j < 10; j++)
1891
      end= strmov(end, " ");
1892
    if (commands[i].func)
1893
      tee_fprintf(stdout, "%s(\\%c) %s\n", buff,
1894
		  commands[i].cmd_char, commands[i].doc);
1895
  }
1896
  if (connected && mysql_get_server_version(&mysql) >= 40100)
serg@serg.mylan's avatar
serg@serg.mylan committed
1897
    put_info("\nFor server side help, type 'help contents'\n", INFO_INFO);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1898 1899 1900 1901 1902 1903 1904 1905
  return 0;
}


	/* ARGSUSED */
static int
com_clear(String *buffer,char *line __attribute__((unused)))
{
1906 1907 1908 1909
#ifdef HAVE_READLINE
  if (status.add_to_history)
    fix_history(buffer);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1910 1911 1912 1913
  buffer->length(0);
  return 0;
}

1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935
	/* ARGSUSED */
static int
com_charset(String *buffer __attribute__((unused)), char *line)
{
  char buff[256], *param;
  CHARSET_INFO * new_cs;
  strmake(buff, line, sizeof(buff) - 1);
  param= get_arg(buff, 0);
  if (!param || !*param)
  {
    return put_info("Usage: \\C char_setname | charset charset_name", 
		    INFO_ERROR, 0);
  }
  new_cs= get_charset_by_csname(param, MY_CS_PRIMARY, MYF(MY_WME));
  if (new_cs)
  {
    charset_info= new_cs;
    put_info("Charset changed", INFO_INFO);
  }
  else put_info("Charset is not found", INFO_INFO);
  return 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1936 1937

/*
1938 1939 1940 1941
  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
1942 1943 1944 1945 1946 1947
*/


static int
com_go(String *buffer,char *line __attribute__((unused)))
{
1948
  char		buff[200], time_buff[32], *pos;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1949
  MYSQL_RES	*result;
1950
  ulong		timer, warnings;
1951 1952
  uint		error= 0;
  int           err= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1953 1954 1955 1956 1957 1958 1959

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

1960
  /* Remove garbage for nicer messages */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973
  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
1974
    return opt_reconnect ? -1 : 1;          // Fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1975 1976 1977 1978 1979
  }
  if (verbose)
    (void) com_print(buffer,0);

  if (skip_updates &&
1980
      (buffer->length() < 4 || my_strnncoll(charset_info,
1981 1982
					    (const uchar*)buffer->ptr(),4,
					    (const uchar*)"SET ",4)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1983 1984 1985 1986 1987 1988
  {
    (void) put_info("Ignoring query to other database",INFO_INFO);
    return 0;
  }

  timer=start_timer();
1989 1990

  error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
1991 1992 1993 1994 1995 1996 1997 1998 1999 2000

#ifdef HAVE_READLINE
  if (status.add_to_history) 
  {  
    buffer->append(vertical ? "\\G" : delimiter);
    /* Append final command onto history */
    fix_history(buffer);
  }
#endif

2001
  if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2002
  {
2003 2004
    buffer->length(0); // Remove query on error
    return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2005 2006 2007 2008
  }
  error=0;
  buffer->length(0);

2009
  do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2010
  {
2011
    if (quick)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2012
    {
2013 2014
      if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
	return put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2015 2016 2017
    }
    else
    {
2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
      error= mysql_store_result_for_lazy(&result);
      if (error)
	return error;
    }

    if (verbose >= 3 || !opt_silent)
      mysql_end_timer(timer,time_buff);
    else
      time_buff[0]=0;
    if (result)
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
2029
      if (!mysql_num_rows(result) && ! quick && !info_flag)
2030 2031 2032
      {
	strmov(buff, "Empty set");
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2033
      else
2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050
      {
	init_pager();
	if (opt_html)
	  print_table_data_html(result);
	else if (opt_xml)
	  print_table_data_xml(result);
	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);
	sprintf(buff,"%ld %s in set",
		(long) mysql_num_rows(result),
		(long) mysql_num_rows(result) == 1 ? "row" : "rows");
	end_pager();
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2051
    }
2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063
    else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
      strmov(buff,"Query OK");
    else
      sprintf(buff,"Query OK, %ld %s affected",
	      (long) mysql_affected_rows(&mysql),
	      (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows");

    pos=strend(buff);
    if ((warnings= mysql_warning_count(&mysql)))
    {
      *pos++= ',';
      *pos++= ' ';
2064
      pos=int10_to_str(warnings, pos, 10);
2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079
      pos=strmov(pos, " warning");
      if (warnings != 1)
	*pos++= 's';
    }
    strmov(pos, time_buff);
    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_error(&mysql);
    else if (unbuffered)
      fflush(stdout);
    mysql_free_result(result);
2080 2081 2082
  } while (!(err= mysql_next_result(&mysql)));
  if (err >= 1)
    error= put_error(&mysql);
2083

2084
  if (show_warnings == 1 && warnings >= 1) /* Show warnings if any */
2085 2086 2087 2088 2089 2090
  {
    init_pager();
    print_warnings();
    end_pager();
  }

ramil@mysql.com's avatar
ramil@mysql.com committed
2091 2092
  if (!error && !status.batch && 
      (mysql.server_status & SERVER_STATUS_DB_DROPPED))
2093 2094
    get_current_db();

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2095 2096 2097
  return error;				/* New command follows */
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2098 2099 2100

static void init_pager()
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2101
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116
  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
2117
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2118 2119 2120 2121 2122
  if (!opt_nopager)
    pclose(PAGER);
#endif
}

2123

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2124
static void init_tee(const char *file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2125
{
2126
  FILE* new_outfile;
2127
  if (opt_outfile)
2128
    end_tee();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2129 2130 2131 2132 2133
  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;
  }
2134
  OUTFILE = new_outfile;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2135 2136 2137 2138
  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
2139 2140
}

2141

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2142 2143 2144
static void end_tee()
{
  my_fclose(OUTFILE, MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2145
  OUTFILE= 0;
2146
  opt_outfile= 0;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2147 2148 2149
  return;
}

monty@mishka.local's avatar
monty@mishka.local committed
2150

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161
static int
com_ego(String *buffer,char *line)
{
  int result;
  bool oldvertical=vertical;
  vertical=1;
  result=com_go(buffer,line);
  vertical=oldvertical;
  return result;
}

monty@mishka.local's avatar
monty@mishka.local committed
2162 2163 2164 2165

static const char *fieldtype2str(enum enum_field_types type)
{
  switch (type) {
serg@serg.mylan's avatar
serg@serg.mylan committed
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 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 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225
    case FIELD_TYPE_BIT:         return "BIT";
    case FIELD_TYPE_BLOB:        return "BLOB";
    case FIELD_TYPE_DATE:        return "DATE";
    case FIELD_TYPE_DATETIME:    return "DATETIME";
    case FIELD_TYPE_NEWDECIMAL:  return "NEWDECIMAL";
    case FIELD_TYPE_DECIMAL:     return "DECIMAL";
    case FIELD_TYPE_DOUBLE:      return "DOUBLE";
    case FIELD_TYPE_ENUM:        return "ENUM";
    case FIELD_TYPE_FLOAT:       return "FLOAT";
    case FIELD_TYPE_GEOMETRY:    return "GEOMETRY";
    case FIELD_TYPE_INT24:       return "INT24";
    case FIELD_TYPE_LONG:        return "LONG";
    case FIELD_TYPE_LONGLONG:    return "LONGLONG";
    case FIELD_TYPE_LONG_BLOB:   return "LONG_BLOB";
    case FIELD_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
    case FIELD_TYPE_NEWDATE:     return "NEWDATE";
    case FIELD_TYPE_NULL:        return "NULL";
    case FIELD_TYPE_SET:         return "SET";
    case FIELD_TYPE_SHORT:       return "SHORT";
    case FIELD_TYPE_STRING:      return "STRING";
    case FIELD_TYPE_TIME:        return "TIME";
    case FIELD_TYPE_TIMESTAMP:   return "TIMESTAMP";
    case FIELD_TYPE_TINY:        return "TINY";
    case FIELD_TYPE_TINY_BLOB:   return "TINY_BLOB";
    case FIELD_TYPE_VAR_STRING:  return "VAR_STRING";
    case FIELD_TYPE_YEAR:        return "YEAR";
    default:                     return "?-unknown-?";
  }
}

static char *fieldflags2str(uint f) {
  static char buf[1024];
  char *s=buf;
  *s=0;
#define ff2s_check_flag(X) \
                if (f & X ## _FLAG) { s=strmov(s, # X " "); f &= ~ X ## _FLAG; }
  ff2s_check_flag(NOT_NULL);
  ff2s_check_flag(PRI_KEY);
  ff2s_check_flag(UNIQUE_KEY);
  ff2s_check_flag(MULTIPLE_KEY);
  ff2s_check_flag(BLOB);
  ff2s_check_flag(UNSIGNED);
  ff2s_check_flag(ZEROFILL);
  ff2s_check_flag(BINARY);
  ff2s_check_flag(ENUM);
  ff2s_check_flag(AUTO_INCREMENT);
  ff2s_check_flag(TIMESTAMP);
  ff2s_check_flag(SET);
  ff2s_check_flag(NO_DEFAULT_VALUE);
  ff2s_check_flag(NUM);
  ff2s_check_flag(PART_KEY);
  ff2s_check_flag(GROUP);
  ff2s_check_flag(UNIQUE);
  ff2s_check_flag(BINCMP);
#undef ff2s_check_flag
  if (f)
    sprintf(s, " unknows=0x%04x", f);
  return buf;
}

2226 2227 2228
static void
print_field_types(MYSQL_RES *result)
{
serg@serg.mylan's avatar
serg@serg.mylan committed
2229 2230 2231
  MYSQL_FIELD   *field;
  uint i=0;

2232 2233
  while ((field = mysql_fetch_field(result)))
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250
    tee_fprintf(PAGER, "Field %3u:  `%s`\n"
                       "Catalog:    `%s`\n"
                       "Database:   `%s`\n"
                       "Table:      `%s`\n"
                       "Org_table:  `%s`\n"
                       "Type:       %s\n"
                       "Collation:  %s (%u)\n"
                       "Length:     %lu\n"
                       "Max_length: %lu\n"
                       "Decimals:   %u\n"
                       "Flags:      %s\n\n",
                ++i,
                field->name, field->catalog, field->db, field->table,
                field->org_table, fieldtype2str(field->type),
                get_charset_name(field->charsetnr), field->charsetnr,
                field->length, field->max_length, field->decimals,
                fieldflags2str(field->flags));
2251 2252 2253 2254
  }
  tee_puts("", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264

static void
print_table_data(MYSQL_RES *result)
{
  String separator(256);
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
  bool		*num_flag;

  num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
2265 2266 2267
  if (info_flag)
  {
    print_field_types(result);
serg@serg.mylan's avatar
serg@serg.mylan committed
2268 2269
    if (!mysql_num_rows(result))
      return;
2270 2271
    mysql_field_seek(result,0);
  }
2272
  separator.copy("+",1,charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2273 2274
  while ((field = mysql_fetch_field(result)))
  {
2275
    uint length= column_names ? field->name_length : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2276 2277 2278 2279 2280 2281 2282 2283 2284 2285
    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('+');
  }
monty@mysql.com's avatar
monty@mysql.com committed
2286 2287
  separator.append('\0');                       // End marker for \0
  tee_puts((char*) separator.ptr(), PAGER);
2288
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2289 2290
  {
    mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2291
    (void) tee_fputs("|", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2292 2293
    for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
    {
2294 2295
      tee_fprintf(PAGER, " %-*s|",(int) min(field->max_length,
                                            MAX_COLUMN_LENGTH),
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2296
		  field->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2297 2298
      num_flag[off]= IS_NUM(field->type);
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2299
    (void) tee_fputs("\n", PAGER);
monty@mysql.com's avatar
monty@mysql.com committed
2300
    tee_puts((char*) separator.ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2301 2302
  }

2303
  while ((cur= mysql_fetch_row(result)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2304
  {
bar@mysql.com's avatar
bar@mysql.com committed
2305
    ulong *lengths= mysql_fetch_lengths(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2306
    (void) tee_fputs("|", PAGER);
2307 2308
    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
2309
    {
2310 2311
      const char *str= cur[off] ? cur[off] : "NULL";
      field= mysql_fetch_field(result);
bar@mysql.com's avatar
bar@mysql.com committed
2312 2313
      uint maxlength= field->max_length;
      if (maxlength > MAX_COLUMN_LENGTH)
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2314
      {
2315 2316
	tee_fputs(str, PAGER);
	tee_fputs(" |", PAGER);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2317 2318
      }
      else
bar@mysql.com's avatar
bar@mysql.com committed
2319 2320 2321 2322 2323 2324 2325
      {
        uint currlength= (uint) lengths[off];
        uint numcells= charset_info->cset->numcells(charset_info, 
                                                    str, str + currlength);
        tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
                    maxlength + currlength - numcells, str);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2326
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2327
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2328
  }
monty@mysql.com's avatar
monty@mysql.com committed
2329
  tee_puts((char*) separator.ptr(), PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2330 2331 2332
  my_afree((gptr) num_flag);
}

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2334 2335 2336
static void
print_table_data_html(MYSQL_RES *result)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2337 2338
  MYSQL_ROW	cur;
  MYSQL_FIELD	*field;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2339 2340

  mysql_field_seek(result,0);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2341
  (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
2342
  if (column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2343 2344 2345
  {
    while((field = mysql_fetch_field(result)))
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2346 2347 2348
      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
2349
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2350
    (void) tee_fputs("</TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2351 2352 2353
  }
  while ((cur = mysql_fetch_row(result)))
  {
2354
    ulong *lengths=mysql_fetch_lengths(result);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2355
    (void) tee_fputs("<TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2356 2357
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2358
      (void) tee_fputs("<TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2359
      safe_put_field(cur[i],lengths[i]);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2360
      (void) tee_fputs("</TD>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2361
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2362
    (void) tee_fputs("</TR>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2363
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2364
  (void) tee_fputs("</TABLE>", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2365 2366 2367
}


2368 2369 2370 2371 2372 2373 2374 2375
static void
print_table_data_xml(MYSQL_RES *result)
{
  MYSQL_ROW   cur;
  MYSQL_FIELD *fields;

  mysql_field_seek(result,0);

2376
  tee_fputs("<?xml version=\"1.0\"?>\n\n<resultset statement=\"", PAGER);
2377
  xmlencode_print(glob_buffer.ptr(), (int)strlen(glob_buffer.ptr()));
2378
  tee_fputs("\">", PAGER);
2379 2380 2381 2382

  fields = mysql_fetch_fields(result);
  while ((cur = mysql_fetch_row(result)))
  {
2383
    ulong *lengths=mysql_fetch_lengths(result);
2384 2385 2386
    (void) tee_fputs("\n  <row>\n", PAGER);
    for (uint i=0; i < mysql_num_fields(result); i++)
    {
2387
      tee_fprintf(PAGER, "\t<field name=\"");
2388
      xmlencode_print(fields[i].name, (uint) strlen(fields[i].name));
2389
      tee_fprintf(PAGER, "\">");
2390
      xmlencode_print(cur[i], lengths[i]);
2391
      tee_fprintf(PAGER, "</field>\n");
2392 2393 2394 2395 2396 2397
    }
    (void) tee_fputs("  </row>\n", PAGER);
  }
  (void) tee_fputs("</resultset>\n", PAGER);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2398 2399 2400 2401 2402 2403 2404 2405 2406 2407

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)))
  {
2408
    uint length= field->name_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2409 2410 2411 2412 2413 2414 2415 2416 2417
    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
2418 2419
    tee_fprintf(PAGER, 
		"*************************** %d. row ***************************\n", row_count);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2420 2421 2422
    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
2423 2424
      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
2425 2426 2427 2428
    }
  }
}

2429

2430
/* print_warnings should be called right after executing a statement */
2431 2432

static void print_warnings()
2433
{
2434
  const char   *query;
2435 2436
  MYSQL_RES    *result;
  MYSQL_ROW    cur;
2437
  my_ulonglong num_rows;
2438 2439

  /* Get the warnings */
2440 2441
  query= "show warnings";
  mysql_real_query_for_lazy(query, strlen(query));
2442 2443 2444
  mysql_store_result_for_lazy(&result);

  /* Bail out when no warnings */
2445
  if (!(num_rows= mysql_num_rows(result)))
2446 2447 2448 2449 2450 2451 2452 2453
  {
    mysql_free_result(result);
    return;
  }

  /* Print the warnings */
  while ((cur= mysql_fetch_row(result)))
  {
2454
    tee_fprintf(PAGER, "%s (Code %s): %s\n", cur[0], cur[1], cur[2]);
2455 2456 2457
  }
  mysql_free_result(result);
}
2458

2459 2460

static const char *array_value(const char **array, char key)
2461
{
2462 2463 2464
  for (; *array; array+= 2)
    if (**array == key)
      return array[1];
2465 2466 2467
  return 0;
}

2468

2469
static void
2470
xmlencode_print(const char *src, uint length)
2471
{
2472 2473 2474
  if (!src)
    tee_fputs("NULL", PAGER);
  else
2475
  {
2476 2477 2478 2479 2480 2481 2482 2483 2484
    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);
    }
  }
2485
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2486

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2487 2488 2489 2490 2491

static void
safe_put_field(const char *pos,ulong length)
{
  if (!pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2492
    tee_fputs("NULL", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2493 2494 2495
  else
  {
    if (opt_raw_data)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2496
      tee_fputs(pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2497 2498 2499 2500
    else for (const char *end=pos+length ; pos != end ; pos++)
    {
#ifdef USE_MB
      int l;
2501 2502
      if (use_mb(charset_info) &&
          (l = my_ismbchar(charset_info, pos, end)))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2503
      {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2504
	  while (l--)
2505
	    tee_putc(*pos++, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2506 2507 2508 2509 2510
	  pos--;
	  continue;
      }
#endif
      if (!*pos)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2511
	tee_fputs("\\0", PAGER); // This makes everything hard
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2512
      else if (*pos == '\t')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2513
	tee_fputs("\\t", PAGER); // This would destroy tab format
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2514
      else if (*pos == '\n')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2515
	tee_fputs("\\n", PAGER); // This too
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2516
      else if (*pos == '\\')
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2517
	tee_fputs("\\\\", PAGER);
2518
	else
2519
	tee_putc(*pos, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531
    }
  }
}


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

2532
  if (opt_silent < 2 && column_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2533 2534 2535 2536 2537
  {
    int first=0;
    while ((field = mysql_fetch_field(result)))
    {
      if (first++)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2538 2539
	(void) tee_fputs("\t", PAGER);
      (void) tee_fputs(field->name, PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2540
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2541
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2542 2543 2544 2545 2546 2547 2548
  }
  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
2549
      (void) tee_fputs("\t", PAGER);
2550
      safe_put_field(cur[off], lengths[off]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2551
    }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2552
    (void) tee_fputs("\n", PAGER);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2553 2554 2555
  }
}

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2556 2557 2558 2559 2560 2561 2562
static int
com_tee(String *buffer, char *line __attribute__((unused)))
{
  char file_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
2563
  while (my_isspace(charset_info,*line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2564 2565 2566 2567 2568
    line++;
  if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
  {
    if (!strlen(outfile))
    {
2569
      printf("No previous outfile available, you must give a filename!\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2570 2571
      return 0;
    }
2572 2573 2574 2575 2576 2577 2578
    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
2579
  }
2580 2581

  /* eliminate the spaces before the parameters */
2582
  while (my_isspace(charset_info,*param))
2583 2584 2585
    param++;
  end= strmake(file_name, param, sizeof(file_name) - 1);
  /* remove end space from command line */
2586 2587
  while (end > file_name && (my_isspace(charset_info,end[-1]) || 
			     my_iscntrl(charset_info,end[-1])))
2588 2589
    end--;
  end[0]= 0;
2590
  if (end == file_name)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2591 2592 2593 2594
  {
    printf("No outfile specified!\n");
    return 0;
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2595
  init_tee(file_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2596 2597 2598
  return 0;
}

2599

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2600 2601 2602 2603 2604 2605 2606 2607 2608 2609
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;
}

2610
/*
2611
  Sorry, this command is not available in Windows.
2612 2613
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2614
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2615 2616 2617 2618 2619 2620 2621
static int
com_pager(String *buffer, char *line __attribute__((unused)))
{
  char pager_name[FN_REFLEN], *end, *param;

  if (status.batch)
    return 0;
2622 2623
  /* Skip spaces in front of the pager command */
  while (my_isspace(charset_info, *line))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2624
    line++;
2625 2626 2627 2628 2629 2630
  /* Skip the pager command */
  param= strchr(line, ' ');
  /* Skip the spaces between the command and the argument */
  while (param && my_isspace(charset_info, *param))
    param++;
  if (!param || !strlen(param)) // if pager was not given, use the default
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2631
  {
2632
    if (!default_pager_set)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2633
    {
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2634
      tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2635 2636 2637 2638 2639 2640 2641 2642 2643
      opt_nopager=1;
      strmov(pager, "stdout");
      PAGER= stdout;
      return 0;
    }
    strmov(pager, default_pager);
  }
  else
  {
2644
    end= strmake(pager_name, param, sizeof(pager_name)-1);
2645 2646
    while (end > pager_name && (my_isspace(charset_info,end[-1]) || 
                                my_iscntrl(charset_info,end[-1])))
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2647 2648 2649
      end--;
    end[0]=0;
    strmov(pager, pager_name);
2650
    strmov(default_pager, pager_name);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2651 2652
  }
  opt_nopager=0;
2653
  tee_fprintf(stdout, "PAGER set to '%s'\n", pager);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2654 2655 2656
  return 0;
}

2657

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2658 2659 2660 2661 2662 2663
static int
com_nopager(String *buffer __attribute__((unused)),
	    char *line __attribute__((unused)))
{
  strmov(pager, "stdout");
  opt_nopager=1;
2664
  PAGER= stdout;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2665 2666 2667
  tee_fprintf(stdout, "PAGER set to stdout\n");
  return 0;
}
2668
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2669

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

2671
/*
2672
  Sorry, you can't send the result to an editor in Win32
2673 2674
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2675
#ifdef USE_POPEN
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2676 2677 2678
static int
com_edit(String *buffer,char *line __attribute__((unused)))
{
2679
  char	filename[FN_REFLEN],buff[160];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2680 2681 2682
  int	fd,tmp;
  const char *editor;

2683 2684
  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
2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713
    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;
}
2714 2715
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2716 2717 2718 2719 2720 2721 2722

/* 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@mysql.com's avatar
monty@mysql.com committed
2723 2724
  /* let the screen auto close on a normal shutdown */
  NETWARE_SET_SCREEN_MODE(SCR_AUTOCLOSE_ON_EXIT);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2725 2726 2727 2728 2729 2730 2731 2732 2733
  status.exit_status=0;
  return 1;
}

static int
com_rehash(String *buffer __attribute__((unused)),
	 char *line __attribute__((unused)))
{
#ifdef HAVE_READLINE
2734
  build_completion_hash(1, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2735 2736 2737 2738
#endif
  return 0;
}

2739

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2740
#ifdef USE_POPEN
2741 2742 2743 2744
static int
com_shell(String *buffer, char *line __attribute__((unused)))
{
  char *shell_cmd;
2745 2746

  /* Skip space from line begin */
serg@serg.mylan's avatar
serg@serg.mylan committed
2747
  while (my_isspace(charset_info, *line))
2748
    line++;
2749 2750 2751 2752 2753
  if (!(shell_cmd = strchr(line, ' ')))
  {
    put_info("Usage: \\! shell-command", INFO_ERROR);
    return -1;
  }
2754 2755 2756 2757 2758
  /*
    The output of the shell command does not
    get directed to the pager or the outfile
  */
  if (system(shell_cmd) == -1)
2759 2760 2761 2762 2763 2764 2765 2766 2767
  {
    put_info(strerror(errno), INFO_ERROR, errno);
    return -1;
  }
  return 0;
}
#endif


bk@work.mysql.com's avatar
bk@work.mysql.com committed
2768 2769 2770
static int
com_print(String *buffer,char *line __attribute__((unused)))
{
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2771 2772
  tee_puts("--------------", stdout);
  (void) tee_fputs(buffer->c_ptr(), stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2773
  if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
2774
    tee_putc('\n', stdout);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2775
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2776 2777 2778 2779 2780 2781 2782
  return 0;					/* If empty buffer */
}

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

2787
  bzero(buff, sizeof(buff));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2788 2789
  if (buffer)
  {
2790 2791 2792
    strmov(buff, line);
    tmp= get_arg(buff, 0);
    if (tmp && *tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2793
    {
2794 2795 2796 2797
      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
2798 2799 2800 2801 2802 2803
      {
	my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
	current_host=my_strdup(tmp,MYF(MY_WME));
      }
    }
    else
2804
      rehash= 0;				// Quick re-connect
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2805 2806 2807
    buffer->length(0);				// command used
  }
  else
2808
    rehash= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2809
  error=sql_connect(current_host,current_db,current_user,opt_password,0);
2810
  rehash= save_rehash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2811 2812 2813

  if (connected)
  {
2814
    sprintf(buff,"Connection id:    %lu",mysql_thread_id(&mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2815
    put_info(buff,INFO_INFO);
2816
    sprintf(buff,"Current database: %.128s\n",
2817
	    current_db ? current_db : "*** NONE ***");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832
    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 */
2833
  while (my_isspace(charset_info,*line))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2834
    line++;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
2835 2836 2837
  if (!(param = strchr(line, ' ')))		// Skip command name
    return put_info("Usage: \\. <filename> | source <filename>", 
		    INFO_ERROR, 0);
2838
  while (my_isspace(charset_info,*param))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2839 2840
    param++;
  end=strmake(source_name,param,sizeof(source_name)-1);
2841 2842
  while (end > source_name && (my_isspace(charset_info,end[-1]) || 
                               my_iscntrl(charset_info,end[-1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2843 2844
    end--;
  end[0]=0;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2845
  unpack_filename(source_name,source_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2846
  /* open file name */
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2847
  if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2848 2849 2850 2851 2852 2853
  {
    char buff[FN_REFLEN+60];
    sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
    return put_info(buff, INFO_ERROR, 0);
  }

2854
  if (!(line_buff=batch_readline_init(opt_max_allowed_packet+512,sql_file)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867
  {
    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
2868
  error= read_and_execute(false);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2869 2870 2871 2872 2873 2874 2875
  status=old_status;				// Continue as before
  my_fclose(sql_file,MYF(0));
  batch_readline_end(line_buff);
  return error;
}


2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891
	/* ARGSUSED */
static int
com_delimiter(String *buffer __attribute__((unused)), char *line)
{
  char buff[256], *tmp;

  strmake(buff, line, sizeof(buff) - 1);
  tmp= get_arg(buff, 0);

  if (!tmp || !*tmp)
  {
    put_info("DELIMITER must be followed by a 'delimiter' character or string",
	     INFO_ERROR);
    return 0;
  }
  strmake(delimiter, tmp, sizeof(delimiter) - 1);
2892
  delimiter_length= (int)strlen(delimiter);
2893
  delimiter_str= delimiter;
2894 2895 2896
  return 0;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2897 2898 2899 2900
	/* ARGSUSED */
static int
com_use(String *buffer __attribute__((unused)), char *line)
{
2901
  char *tmp, buff[FN_REFLEN + 1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2902

2903
  bzero(buff, sizeof(buff));
2904
  strmov(buff, line);
2905
  tmp= get_arg(buff, 0);
2906
  if (!tmp || !*tmp)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2907
  {
2908
    put_info("USE must be followed by a database name", INFO_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2909 2910
    return 0;
  }
2911 2912 2913 2914
  /*
    We need to recheck the current database, because it may change
    under our feet, for example if DROP DATABASE or RENAME DATABASE
    (latter one not yet available by the time the comment was written)
2915
  */
2916
  get_current_db();
2917

serg@serg.mylan's avatar
serg@serg.mylan committed
2918
  if (!current_db || cmp_database(charset_info, current_db,tmp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2919 2920
  {
    if (one_database)
2921
      skip_updates= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2922 2923 2924 2925 2926 2927 2928
    else
    {
      /*
	reconnect once if connection is down or if connection was found to
	be down during query
      */
      if (!connected && reconnect())
2929
      return opt_reconnect ? -1 : 1;                        // Fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2930 2931 2932
      if (mysql_select_db(&mysql,tmp))
      {
	if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
2933
	  return put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2934 2935

	if (reconnect())
2936
        return opt_reconnect ? -1 : 1;                      // Fatal error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2937
	if (mysql_select_db(&mysql,tmp))
2938
	  return put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2939
      }
2940 2941
      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
2942
#ifdef HAVE_READLINE
2943
      build_completion_hash(rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2944 2945 2946 2947
#endif
    }
  }
  else
2948
    skip_updates= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2949 2950 2951 2952
  put_info("Database changed",INFO_INFO);
  return 0;
}

2953 2954 2955 2956 2957
static int
com_warnings(String *buffer __attribute__((unused)),
   char *line __attribute__((unused)))
{
  show_warnings = 1;
2958
  put_info("Show warnings enabled.",INFO_INFO);
2959 2960 2961 2962 2963 2964 2965 2966
  return 0;
}

static int
com_nowarnings(String *buffer __attribute__((unused)),
   char *line __attribute__((unused)))
{
  show_warnings = 0;
2967
  put_info("Show warnings disabled.",INFO_INFO);
2968 2969
  return 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2970

2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981
/*
  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)
2982
{
2983
  char *ptr, *start;
2984
  my_bool quoted= 0, valid_arg= 0;
2985
  char qtype= 0;
2986 2987

  ptr= line;
2988 2989
  if (get_next_arg)
  {
2990 2991
    for (; *ptr; ptr++) ;
    if (*(ptr + 1))
2992 2993 2994 2995 2996
      ptr++;
  }
  else
  {
    /* skip leading white spaces */
2997
    while (my_isspace(charset_info, *ptr))
2998 2999 3000
      ptr++;
    if (*ptr == '\\') // short command was used
      ptr+= 2;
3001 3002 3003
    else
      while (*ptr &&!my_isspace(charset_info, *ptr)) // skip command
        ptr++;
3004
  }
3005 3006
  if (!*ptr)
    return NullS;
3007
  while (my_isspace(charset_info, *ptr))
3008
    ptr++;
3009
  if (*ptr == '\'' || *ptr == '\"' || *ptr == '`')
3010
  {
3011
    qtype= *ptr;
3012 3013 3014
    quoted= 1;
    ptr++;
  }
3015
  for (start=ptr ; *ptr; ptr++)
3016
  {
3017
    if (*ptr == '\\' && ptr[1]) // escaped character
3018
    {
3019 3020
      // Remove the backslash
      strmov(ptr, ptr+1);
3021
    }
3022
    else if ((!quoted && *ptr == ' ') || (quoted && *ptr == qtype))
3023 3024 3025 3026 3027
    {
      *ptr= 0;
      break;
    }
  }
3028 3029
  valid_arg= ptr != start;
  return valid_arg ? start : NullS;
3030 3031 3032
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3033 3034 3035 3036
static int
sql_real_connect(char *host,char *database,char *user,char *password,
		 uint silent)
{
monty@mysql.com's avatar
monty@mysql.com committed
3037 3038 3039 3040 3041
  if (connected)
  {
    connected= 0;
    mysql_close(&mysql);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3042
  mysql_init(&mysql);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3043
  if (opt_connect_timeout)
3044 3045
  {
    uint timeout=opt_connect_timeout;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3046
    mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
3047 3048
		  (char*) &timeout);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3049 3050
  if (opt_compress)
    mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
3051 3052
  if (opt_secure_auth)
    mysql_options(&mysql, MYSQL_SECURE_AUTH, (char *) &opt_secure_auth);
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3053 3054
  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
3055 3056 3057
#ifdef HAVE_OPENSSL
  if (opt_use_ssl)
    mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
3058
		  opt_ssl_capath, opt_ssl_cipher);
3059 3060 3061 3062 3063 3064
#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
3065 3066 3067 3068 3069 3070 3071 3072 3073
#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);
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3074 3075
  if (default_charset_used)
    mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
3076 3077
  if (!mysql_real_connect(&mysql, host, user, password,
			  database, opt_mysql_port, opt_mysql_unix_port,
3078
			  connect_flag | CLIENT_MULTI_STATEMENTS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3079 3080 3081 3082 3083
  {
    if (!silent ||
	(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
	 mysql_errno(&mysql) != CR_CONNECTION_ERROR))
    {
3084
      (void) put_error(&mysql);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3085 3086 3087 3088 3089 3090
      (void) fflush(stdout);
      return ignore_errors ? -1 : 1;		// Abort
    }
    return -1;					// Retryable
  }
  connected=1;
3091
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3092
  mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens
3093 3094
#else
  mysql.reconnect= 1;
3095
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3096
#ifdef HAVE_READLINE
3097
  build_completion_hash(rehash, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114
#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
3115
	tee_fputs("\n", stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3116 3117 3118 3119 3120 3121 3122 3123 3124
	(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
3125
      tee_fputs("Waiting",stderr); (void) fflush(stderr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3126
    }
3127
    (void) sleep(wait_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141
    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
3142
  const char *status;
3143 3144
  char buff[22];
  ulonglong id;
bar@mysql.com's avatar
bar@mysql.com committed
3145 3146
  MYSQL_RES *result;
  LINT_INIT(result);
3147

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3148
  tee_puts("--------------", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3149 3150 3151
  usage(1);					/* Print version */
  if (connected)
  {
3152
    tee_fprintf(stdout, "\nConnection id:\t\t%lu\n",mysql_thread_id(&mysql));
bar@mysql.com's avatar
bar@mysql.com committed
3153 3154 3155 3156
    /* 
      Don't remove "limit 1", 
      it is protection againts SQL_SELECT_LIMIT=0
    */
3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167
    if (!mysql_query(&mysql,"select DATABASE(), USER() limit 1") &&
	(result=mysql_use_result(&mysql)))
    {
      MYSQL_ROW cur=mysql_fetch_row(result);
      if (cur)
      {
        tee_fprintf(stdout, "Current database:\t%s\n", cur[0] ? cur[0] : "");
        tee_fprintf(stdout, "Current user:\t\t%s\n", cur[1]);
      }
      mysql_free_result(result);
    } 
3168
#ifdef HAVE_OPENSSL
3169 3170
    if (mysql.net.vio && mysql.net.vio->ssl_arg &&
	SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg))
3171
      tee_fprintf(stdout, "SSL:\t\t\tCipher in use is %s\n",
3172
		  SSL_get_cipher((SSL*) mysql.net.vio->ssl_arg));
3173 3174
    else
#endif /* HAVE_OPENSSL */
3175
      tee_puts("SSL:\t\t\tNot in use", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3176 3177 3178 3179
  }
  else
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3180
    tee_fprintf(stdout, "\nNo connection\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3181 3182 3183 3184 3185 3186
    vidattr(A_NORMAL);
    return 0;
  }
  if (skip_updates)
  {
    vidattr(A_BOLD);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3187
    tee_fprintf(stdout, "\nAll updates ignored to this database\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3188 3189
    vidattr(A_NORMAL);
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3190
#ifdef USE_POPEN
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3191
  tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
3192
  tee_fprintf(stdout, "Using outfile:\t\t'%s'\n", opt_outfile ? outfile : "");
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3193
#endif
3194
  tee_fprintf(stdout, "Using delimiter:\t%s\n", delimiter);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3195 3196 3197
  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));
3198 3199 3200
  if ((id= mysql_insert_id(&mysql)))
    tee_fprintf(stdout, "Insert id:\t\t%s\n", llstr(id, buff));

bar@mysql.com's avatar
bar@mysql.com committed
3201 3202 3203 3204 3205
  /* 
    Don't remove "limit 1", 
    it is protection againts SQL_SELECT_LIMIT=0
  */
  if (!mysql_query(&mysql,"select @@character_set_client, @@character_set_connection, @@character_set_server, @@character_set_database limit 1") &&
bar@mysql.com's avatar
bar@mysql.com committed
3206 3207 3208 3209 3210
      (result=mysql_use_result(&mysql)))
  {
    MYSQL_ROW cur=mysql_fetch_row(result);
    if (cur)
    {
3211
      tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
bar@mysql.com's avatar
bar@mysql.com committed
3212
      tee_fprintf(stdout, "Db     characterset:\t%s\n", cur[3] ? cur[3] : "");
3213
      tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
bar@mysql.com's avatar
bar@mysql.com committed
3214 3215 3216 3217
      tee_fprintf(stdout, "Conn.  characterset:\t%s\n", cur[1] ? cur[1] : "");
    }
    mysql_free_result(result);
  }
3218 3219 3220 3221 3222 3223
  else
  {
    /* Probably pre-4.1 server */
    tee_fprintf(stdout, "Client characterset:\t%s\n", charset_info->csname);
    tee_fprintf(stdout, "Server characterset:\t%s\n", mysql.charset->csname);
  }
bar@mysql.com's avatar
bar@mysql.com committed
3224

3225
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3226
  if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3227
    tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3228
  else
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3229
    tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
3230 3231
  if (mysql.net.compress)
    tee_fprintf(stdout, "Protocol:\t\tCompressed\n");
3232
#endif
3233

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3234 3235 3236
  if ((status=mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
  {
    ulong sec;
3237 3238 3239 3240
    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
3241 3242 3243
    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
3244
      tee_puts(buff, stdout);			/* print nice time */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3245 3246 3247 3248
      while (*status == ' ') status++;		/* to next info */
    }
    if (status)
    {
3249
      tee_putc('\n', stdout);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3250
      tee_puts(status, stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3251 3252 3253 3254 3255
    }
  }
  if (safe_updates)
  {
    vidattr(A_BOLD);
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
3256
    tee_fprintf(stdout, "\nNote that you are running in safe_update_mode:\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3257
    vidattr(A_NORMAL);
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3258
    tee_fprintf(stdout, "\
jcole@tetra.spaceapes.com's avatar
jcole@tetra.spaceapes.com committed
3259 3260 3261
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
3262
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
3263
select_limit, max_join_size);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3264
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3265
  tee_puts("--------------\n", stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3266 3267 3268 3269 3270
  return 0;
}


static int
3271
put_info(const char *str,INFO_TYPE info_type, uint error, const char *sqlstate)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3272
{
3273
  FILE *file= (info_type == INFO_ERROR ? stderr : stdout);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3274
  static int inited=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3275

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3276 3277 3278 3279
  if (status.batch)
  {
    if (info_type == INFO_ERROR)
    {
3280 3281
      (void) fflush(file);
      fprintf(file,"ERROR");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3282
      if (error)
paul@kite-hub.kitebird.com's avatar
paul@kite-hub.kitebird.com committed
3283 3284 3285 3286 3287 3288
      {
	if (sqlstate)
	  (void) fprintf(file," %d (%s)",error, sqlstate);
        else
	  (void) fprintf(file," %d",error);
      }
3289
      if (status.query_start_line && line_numbers)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3290
      {
3291
	(void) fprintf(file," at line %lu",status.query_start_line);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3292
	if (status.file_name)
3293
	  (void) fprintf(file," in file: '%s'", status.file_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3294
      }
3295 3296
      (void) fprintf(file,": %s\n",str);
      (void) fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3297 3298 3299 3300
      if (!ignore_errors)
	return 1;
    }
    else if (info_type == INFO_RESULT && verbose > 1)
3301
      tee_puts(str, file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3302
    if (unbuffered)
3303
      fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316
    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)
    {
3317
      if (!opt_nobeep)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3318
	putchar('\007');		      	/* This should make a bell */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3319 3320
      vidattr(A_STANDOUT);
      if (error)
3321 3322 3323 3324 3325 3326
      {
	if (sqlstate)
          (void) tee_fprintf(file, "ERROR %d (%s): ", error, sqlstate);
        else
          (void) tee_fprintf(file, "ERROR %d: ", error);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3327
      else
3328
        tee_puts("ERROR: ", file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3329 3330 3331
    }
    else
      vidattr(A_BOLD);
3332
    (void) tee_puts(str, file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3333 3334 3335
    vidattr(A_NORMAL);
  }
  if (unbuffered)
3336
    fflush(file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3337 3338 3339
  return info_type == INFO_ERROR ? -1 : 0;
}

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

3341 3342 3343 3344 3345 3346 3347 3348
static int
put_error(MYSQL *mysql)
{
  return put_info(mysql_error(mysql), INFO_ERROR, mysql_errno(mysql),
		  mysql_sqlstate(mysql));
}  


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3349 3350 3351 3352
static void remove_cntrl(String &buffer)
{
  char *start,*end;
  end=(start=(char*) buffer.ptr())+buffer.length();
3353
  while (start < end && !my_isgraph(charset_info,end[-1]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3354 3355 3356 3357 3358
    end--;
  buffer.length((uint) (end-start));
}


jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3359 3360 3361 3362
void tee_fprintf(FILE *file, const char *fmt, ...)
{
  va_list args;

monty@mysql.com's avatar
monty@mysql.com committed
3363
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3364
  va_start(args, fmt);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3365
  (void) vfprintf(file, fmt, args);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3366 3367 3368
#ifdef OS2
  fflush( file);
#endif
3369 3370
  va_end(args);

jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3371
  if (opt_outfile)
3372 3373
  {
    va_start(args, fmt);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3374
    (void) vfprintf(OUTFILE, fmt, args);
3375 3376
    va_end(args);
  }
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3377 3378 3379 3380 3381
}


void tee_fputs(const char *s, FILE *file)
{
monty@mysql.com's avatar
monty@mysql.com committed
3382
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3383
  fputs(s, file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3384 3385 3386
#ifdef OS2
  fflush( file);
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3387 3388 3389 3390 3391 3392 3393
  if (opt_outfile)
    fputs(s, OUTFILE);
}


void tee_puts(const char *s, FILE *file)
{
monty@mysql.com's avatar
monty@mysql.com committed
3394
  NETWARE_YIELD;
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3395 3396
  fputs(s, file);
  fputs("\n", file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3397 3398 3399
#ifdef OS2
  fflush( file);
#endif
jani@prima.mysql.com's avatar
jani@prima.mysql.com committed
3400 3401 3402 3403 3404 3405 3406
  if (opt_outfile)
  {
    fputs(s, OUTFILE);
    fputs("\n", OUTFILE);
  }
}

3407 3408 3409
void tee_putc(int c, FILE *file)
{
  putc(c, file);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3410 3411 3412
#ifdef OS2
  fflush( file);
#endif
3413 3414 3415 3416
  if (opt_outfile)
    putc(c, OUTFILE);
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3417
#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3418 3419 3420
#include <time.h>
#else
#include <sys/times.h>
3421
#ifdef _SC_CLK_TCK				// For mit-pthreads
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3422 3423 3424
#undef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
#endif
3425
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3426 3427 3428

static ulong start_timer(void)
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3429
#if defined( __WIN__) || defined( OS2) || defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444
 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;
3445
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3446 3447 3448 3449 3450 3451
    buff=strmov(buff,tmp > 1 ? " days " : " day ");
  }
  if (sec >= 3600.0)
  {
    tmp=(ulong) floor(sec/3600.0);
    sec-=3600.0*tmp;
3452
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3453 3454 3455 3456 3457 3458
    buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
  }
  if (sec >= 60.0)
  {
    tmp=(ulong) floor(sec/60.0);
    sec-=60.0*tmp;
3459
    buff=int10_to_str((long) tmp, buff, 10);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483
    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),")");
}

3484 3485
static const char* construct_prompt()
{
3486 3487
  processed_prompt.free();			// Erase the old prompt
  time_t  lclock = time(NULL);			// Get the date struct
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3488
  struct tm *t = localtime(&lclock);
3489 3490

  /* parse thru the settings for the prompt */
3491 3492 3493
  for (char *c = current_prompt; *c ; *c++)
  {
    if (*c != PROMPT_CHAR)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3494
	processed_prompt.append(*c);
3495 3496
    else
    {
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3497 3498
      switch (*++c) {
      case '\0':
3499
	c--;			// stop it from going beyond if ends with %
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3500 3501 3502 3503 3504
	break;
      case 'c':
	add_int_to_prompt(++prompt_counter);
	break;
      case 'v':
3505 3506 3507 3508
	if (connected)
	  processed_prompt.append(mysql_get_server_info(&mysql));
	else
	  processed_prompt.append("not_connected");
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3509 3510 3511 3512 3513 3514
	break;
      case 'd':
	processed_prompt.append(current_db ? current_db : "(none)");
	break;
      case 'h':
      {
3515 3516
	const char *prompt;
	prompt= connected ? mysql_get_host_info(&mysql) : "not_connected";
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3517 3518 3519 3520 3521 3522 3523 3524 3525 3526
	if (strstr(prompt, "Localhost"))
	  processed_prompt.append("localhost");
	else
	{
	  const char *end=strcend(prompt,' ');
	  processed_prompt.append(prompt, (uint) (end-prompt));
	}
	break;
      }
      case 'p':
3527
      {
3528
#ifndef EMBEDDED_LIBRARY
3529 3530 3531 3532 3533
	if (!connected)
	{
	  processed_prompt.append("not_connected");
	  break;
	}
3534 3535 3536 3537 3538 3539 3540

	const char *host_info = mysql_get_host_info(&mysql);
	if (strstr(host_info, "memory")) 
	{
		processed_prompt.append( mysql.host );
	}
	else if (strstr(host_info,"TCP/IP") ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3541
	    !mysql.unix_socket)
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3542 3543
	  add_int_to_prompt(mysql.port);
	else
3544 3545
	{
	  char *pos=strrchr(mysql.unix_socket,'/');
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3546
 	  processed_prompt.append(pos ? pos+1 : mysql.unix_socket);
3547
	}
3548
#endif
3549
      }
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571
	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':
3572 3573
	if (t->tm_hour < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3574 3575 3576 3577 3578 3579 3580
	add_int_to_prompt(t->tm_hour);
	break;
      case 'r':
	int getHour;
	getHour = t->tm_hour % 12;
	if (getHour == 0)
	  getHour=12;
3581 3582
	if (getHour < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607
	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':
3608 3609
	if (t->tm_sec < 10)
	  processed_prompt.append('0');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644
	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();
}

3645 3646 3647

static void add_int_to_prompt(int toadd)
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3648 3649 3650 3651 3652
  char buffer[16];
  int10_to_str(toadd,buffer,10);
  processed_prompt.append(buffer);
}

3653 3654
static void init_username()
{
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3655 3656 3657 3658 3659 3660 3661
  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)))
3662 3663 3664 3665 3666 3667
  {
    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
3668 3669
}

3670 3671 3672
static int com_prompt(String *buffer, char *line)
{
  char *ptr=strchr(line, ' ');
jani@hynda.mysql.fi's avatar
merge  
jani@hynda.mysql.fi committed
3673 3674
  prompt_counter = 0;
  my_free(current_prompt,MYF(MY_ALLOW_ZERO_PTR));
3675 3676
  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
3677 3678 3679 3680 3681 3682
    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;
}

3683
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
/* 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));
}
3695
#endif /* EMBEDDED_LIBRARY */