home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / client / mysql.cpp < prev    next >
C/C++ Source or Header  |  2000-11-21  |  63KB  |  2,391 lines

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    
  3.    This program is free software; you can redistribute it and/or modify
  4.    it under the terms of the GNU General Public License as published by
  5.    the Free Software Foundation; either version 2 of the License, or
  6.    (at your option) any later version.
  7.    
  8.    This program is distributed in the hope that it will be useful,
  9.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.    GNU General Public License for more details.
  12.    
  13.    You should have received a copy of the GNU General Public License
  14.    along with this program; if not, write to the Free Software
  15.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  16.  
  17. /* mysql command tool
  18.  * Commands compatible with mSQL by David J. Hughes
  19.  *
  20.  * Written by:
  21.  *   Michael 'Monty' Widenius
  22.  *   Andi Gutmans  <andi@zend.com>
  23.  *   Zeev Suraski <zeev@zend.com>
  24.  *   Jani Tolonen <jani@mysql.com>
  25.  *
  26.  **/
  27.  
  28. #include <global.h>
  29. #include <my_sys.h> 
  30. #include <m_string.h>
  31. #include <m_ctype.h>
  32. #include "mysql.h"
  33. #include "errmsg.h"
  34. #include <my_dir.h>
  35. #ifndef __GNU_LIBRARY__
  36. #define __GNU_LIBRARY__              // Skip warnings in getopt.h
  37. #endif
  38. #include <getopt.h>
  39. #include "my_readline.h"
  40. #include <signal.h>
  41.  
  42. gptr sql_alloc(unsigned size);         // Don't use mysqld alloc for these
  43. void sql_element_free(void *ptr);
  44. #include "sql_string.h"
  45.  
  46. extern "C" {
  47. #if defined(HAVE_CURSES_H) && defined(HAVE_TERM_H)
  48. #include <curses.h>
  49. #include <term.h>
  50. #else
  51. #if defined(HAVE_TERMIOS_H)
  52. #include <termios.h>
  53. #include <unistd.h>
  54. #elif defined(HAVE_TERMBITS_H)
  55. #include <termbits.h>
  56. #elif defined(HAVE_ASM_TERMBITS_H) && (!defined __GLIBC__ || !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ > 0))
  57. #include <asm/termbits.h>        // Standard linux
  58. #endif
  59. #undef VOID
  60. #if defined(HAVE_TERMCAP_H)
  61. #include <termcap.h>
  62. #else
  63. #ifdef HAVE_CURSES_H
  64. #include <curses.h>
  65. #endif
  66. #undef SYSV                // hack to avoid syntax error
  67. #ifdef HAVE_TERM_H
  68. #include <term.h>
  69. #endif
  70. #endif
  71. #endif
  72.  
  73. #undef bcmp                // Fix problem with new readline
  74. #undef bzero
  75. #ifdef __WIN__
  76. #include <conio.h>
  77. #else
  78. #include <readline/readline.h>
  79. #define HAVE_READLINE
  80. #endif
  81.   //int vidattr(long unsigned int attrs);    // Was missing in sun curses
  82. }
  83.  
  84. #if !defined(HAVE_VIDATTR)
  85. #undef vidattr
  86. #define vidattr(A) {}            // Can't get this to work
  87. #endif
  88.  
  89. #ifdef __WIN__
  90. #define cmp_database(A,B) my_strcasecmp((A),(B))
  91. #else
  92. #define cmp_database(A,B) strcmp((A),(B))
  93. #endif
  94.  
  95. #include "completion_hash.h"
  96.  
  97. typedef struct st_status
  98. {
  99.   int exit_status;
  100.   ulong query_start_line;
  101.   char *file_name;
  102.   LINE_BUFFER *line_buff;
  103.   bool batch,add_to_history;
  104. } STATUS;
  105.  
  106.  
  107. static HashTable ht;
  108.  
  109. enum enum_info_type { INFO_INFO,INFO_ERROR,INFO_RESULT};
  110. typedef enum enum_info_type INFO_TYPE;
  111.  
  112. const char *VER="11.6";
  113.  
  114. static MYSQL mysql;            /* The connection */
  115. static bool info_flag=0,ignore_errors=0,wait_flag=0,quick=0,
  116.         connected=0,opt_raw_data=0,unbuffered=0,output_tables=0,
  117.         no_rehash=0,skip_updates=0,safe_updates=0,one_database=0,
  118.         opt_compress=0,
  119.         vertical=0,skip_line_numbers=0,skip_column_names=0,opt_html=0,
  120.         opt_nopager=1, opt_outfile=0, no_named_cmds=1;
  121. static uint verbose=0,opt_silent=0,opt_mysql_port=0,opt_connect_timeout=0;
  122. static my_string opt_mysql_unix_port=0;
  123. static int connect_flag=CLIENT_INTERACTIVE;
  124. static char *current_host,*current_db,*current_user=0,*opt_password=0,
  125.             *default_charset;
  126. static char *histfile;
  127. static String glob_buffer,old_buffer;
  128. static STATUS status;
  129. static ulong select_limit,max_join_size;
  130. static char default_pager[FN_REFLEN];
  131. char pager[FN_REFLEN], outfile[FN_REFLEN];
  132. FILE *PAGER, *OUTFILE;
  133.  
  134. #include "sslopt-vars.h"
  135.  
  136. #ifndef DBUG_OFF
  137. const char *default_dbug_option="d:t:o,/tmp/mysql.trace";
  138. #endif
  139.  
  140. void tee_fprintf(FILE *file, const char *fmt, ...);
  141. void tee_fputs(const char *s, FILE *file);
  142. void tee_puts(const char *s, FILE *file);
  143. void tee_putc(int c, FILE *file);
  144. /* The names of functions that actually do the manipulation. */
  145. static int get_options(int argc,char **argv);
  146. static int com_quit(String *str,char*),
  147.        com_go(String *str,char*), com_ego(String *str,char*),
  148.        com_print(String *str,char*),
  149.        com_help(String *str,char*), com_clear(String *str,char*),
  150.        com_connect(String *str,char*), com_status(String *str,char*),
  151.        com_use(String *str,char*), com_source(String *str, char*),
  152.        com_rehash(String *str, char*), com_tee(String *str, char*),
  153.            com_notee(String *str, char*);
  154.  
  155. #ifndef __WIN__
  156. static int com_nopager(String *str, char*), com_pager(String *str, char*),
  157.        com_edit(String *str,char*);
  158. #endif
  159.  
  160. static int read_lines(bool execute_commands);
  161. static int sql_connect(char *host,char *database,char *user,char *password,
  162.                uint silent);
  163. static int put_info(const char *str,INFO_TYPE info,uint error=0);
  164. static void safe_put_field(const char *pos,ulong length);
  165. static void init_pager();
  166. static void end_pager();
  167. static void init_tee();
  168. static void end_tee();
  169.  
  170. /* A structure which contains information on the commands this program
  171.    can understand. */
  172.  
  173. typedef struct {
  174.   const char *name;        /* User printable name of the function. */
  175.   char cmd_char;        /* msql command character */
  176.   int (*func)(String *str,char *); /* Function to call to do the job. */
  177.   bool takes_params;        /* Max parameters for command */
  178.   const char *doc;        /* Documentation for this function.  */
  179. } COMMANDS;
  180.  
  181. static COMMANDS commands[] = {
  182.   { "help",   'h', com_help,   0, "Display this help." },
  183.   { "?",      '?', com_help,   0, "Synonym for `help'." },
  184.   { "clear",  'c', com_clear,  0, "Clear command."},
  185.   { "connect",'r', com_connect,1,
  186.     "Reconnect to the server. Optional arguments are db and host." },
  187. #ifndef __WIN__
  188.   { "edit",   'e', com_edit,   0, "Edit command with $EDITOR."},
  189. #endif
  190.   { "ego",    'G', com_ego,    0,
  191.     "Send command to mysql server, display result vertically."},
  192.   { "exit",   'q', com_quit,   0, "Exit mysql. Same as quit."},
  193.   { "go",     'g', com_go,     0, "Send command to mysql server." },
  194. #ifndef __WIN__
  195.   { "nopager",'n', com_nopager,0, "Disable pager, print to stdout." },
  196. #endif
  197.   { "notee",  't', com_notee,  0, "Don't write into outfile." },
  198. #ifndef __WIN__
  199.   { "pager",  'P', com_pager,  1, 
  200.     "Set PAGER [to_pager]. Print the query results via PAGER." },
  201. #endif
  202.   { "print",  'p', com_print,  0, "Print current command." },
  203.   { "quit",   'q', com_quit,   0, "Quit mysql." },
  204.   { "rehash", '#', com_rehash, 0, "Rebuild completion hash." },
  205.   { "source", '.', com_source, 1,
  206.     "Execute a SQL script file. Takes a file name as an argument."},
  207.   { "status", 's', com_status, 0, "Get status information from the server."},
  208.   { "tee",    'T', com_tee,    1, 
  209.     "Set outfile [to_outfile]. Append everything into given outfile." },
  210.   { "use",    'u', com_use,    1,
  211.     "Use another database. Takes database name as argument." },
  212.  
  213.   /* Get bash-like expansion for some commands */
  214.   { "create table",     0, 0, 0, ""},
  215.   { "create database",  0, 0, 0, ""},
  216.   { "drop",             0, 0, 0, ""},
  217.   { "select",           0, 0, 0, ""},
  218.   { "insert",           0, 0, 0, ""},
  219.   { "replace",          0, 0, 0, ""},
  220.   { "update",           0, 0, 0, ""},
  221.   { "delete",           0, 0, 0, ""},
  222.   { "explain",          0, 0, 0, ""},
  223.   { "show databases",   0, 0, 0, ""},
  224.   { "show fields from", 0, 0, 0, ""},
  225.   { "show keys from",   0, 0, 0, ""},
  226.   { "show tables",      0, 0, 0, ""},
  227.   { "load data from",   0, 0, 0, ""},
  228.   { "alter table",      0, 0, 0, ""},
  229.   { "set option",       0, 0, 0, ""},
  230.   { "lock tables",      0, 0, 0, ""},
  231.   { "unlock tables",    0, 0, 0, ""},
  232.   { (char *)NULL,       0, 0, 0, ""}
  233. };
  234.  
  235. static const char *load_default_groups[]= { "mysql","client",0 };
  236.  
  237. #ifdef HAVE_READLINE
  238. extern "C" void add_history(char *command); /* From readline directory */
  239. extern "C" int read_history(char *command);
  240. extern "C" int write_history(char *command);
  241. static void initialize_readline (char *name);
  242. #endif
  243.  
  244. static COMMANDS *find_command (char *name,char cmd_name);
  245. static bool add_line(String &buffer,char *line,char *in_string);
  246. static void remove_cntrl(String &buffer);
  247. static void print_table_data(MYSQL_RES *result);
  248. static void print_table_data_html(MYSQL_RES *result);
  249. static void print_tab_data(MYSQL_RES *result);
  250. static void print_table_data_vertically(MYSQL_RES *result);
  251. static ulong start_timer(void);
  252. static void end_timer(ulong start_time,char *buff);
  253. static void mysql_end_timer(ulong start_time,char *buff);
  254. static void nice_time(double sec,char *buff,bool part_second);
  255. static sig_handler mysql_end(int sig);
  256.  
  257.  
  258. int main(int argc,char *argv[])
  259. {
  260.   char buff[80];
  261.  
  262.   MY_INIT(argv[0]);
  263.   DBUG_ENTER("main");
  264.   DBUG_PROCESS(argv[0]);
  265.  
  266.   strmov(outfile, "\0");   // no (default) outfile, unless given at least once
  267.   strmov(pager, "stdout"); // the default, if --pager wasn't given
  268.   if (!isatty(0) || !isatty(1))
  269.   {
  270.     status.batch=1; opt_silent=1;
  271.     ignore_errors=0;
  272.   }
  273.   else
  274.     status.add_to_history=1;
  275.   status.exit_status=1;
  276.   load_defaults("my",load_default_groups,&argc,&argv);
  277.   if (get_options(argc,(char **) argv))
  278.   {
  279.     my_end(0);
  280.     exit(1);
  281.   }
  282.   free_defaults(argv);
  283.   if (status.batch && !status.line_buff &&
  284.       !(status.line_buff=batch_readline_init(max_allowed_packet+512,stdin)))
  285.     exit(1);
  286.   glob_buffer.realloc(512);
  287.   completion_hash_init(&ht,50);
  288.   if (sql_connect(current_host,current_db,current_user,opt_password,
  289.           opt_silent))
  290.   {
  291.     quick=1;                    // Avoid history
  292.     status.exit_status=1;
  293.     mysql_end(-1);
  294.   }
  295.   if (!status.batch)
  296.     ignore_errors=1;                // Don't abort monitor
  297.   signal(SIGINT, mysql_end);            // Catch SIGINT to clean up
  298.  
  299.   /*
  300.   **  Run in interactive mode like the ingres/postgres monitor
  301.   */
  302.  
  303.   put_info("Welcome to the MySQL monitor.  Commands end with ; or \\g.",
  304.        INFO_INFO);
  305.   sprintf((char*) glob_buffer.ptr(),
  306.       "Your MySQL connection id is %ld to server version: %s\n",
  307.       mysql_thread_id(&mysql),mysql_get_server_info(&mysql));
  308.   put_info((char*) glob_buffer.ptr(),INFO_INFO);
  309.  
  310. #ifdef HAVE_READLINE
  311.   initialize_readline(my_progname);
  312.   if (!status.batch && !quick && !opt_html)
  313.   {
  314.     /*read-history from file, default ~/.mysql_history*/
  315.     if (getenv("MYSQL_HISTFILE"))
  316.       histfile=my_strdup(getenv("MYSQL_HISTFILE"),MYF(MY_WME));
  317.     else if (getenv("HOME"))
  318.     {
  319.       histfile=(char*) my_malloc((uint) strlen(getenv("HOME"))
  320.                  + (uint) strlen("/.mysql_history")+2,
  321.                  MYF(MY_WME));
  322.       if (histfile)
  323.     sprintf(histfile,"%s/.mysql_history",getenv("HOME"));
  324.     }
  325.     if (histfile)
  326.     {
  327.       if (verbose)
  328.     tee_fprintf(stdout, "Reading history-file %s\n",histfile);
  329.       read_history(histfile);
  330.     }
  331.   }
  332. #endif
  333.   sprintf(buff, 
  334.       "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer\n");
  335.   put_info(buff,INFO_INFO);
  336.   status.exit_status=read_lines(1);        // read lines and execute them
  337.   if (opt_outfile)
  338.     end_tee();
  339.   mysql_end(0);
  340. #ifndef _lint
  341.   DBUG_RETURN(0);                // Keep compiler happy
  342. #endif
  343. }
  344.  
  345. sig_handler mysql_end(int sig)
  346. {
  347.   if (connected)
  348.     mysql_close(&mysql);
  349. #ifdef HAVE_READLINE
  350.   if (!status.batch && !quick && ! opt_html)
  351.   {
  352.     /* write-history */
  353.     if (verbose)
  354.       tee_fprintf(stdout, "Writing history-file %s\n",histfile);
  355.     write_history(histfile);
  356.   }
  357.   batch_readline_end(status.line_buff);
  358.   completion_hash_free(&ht);
  359. #endif
  360.   if (sig >= 0)
  361.     put_info(sig ? "Aborted" : "Bye", INFO_RESULT);
  362.   glob_buffer.free();
  363.   old_buffer.free();
  364.   my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
  365.   my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR));
  366.   my_free(histfile,MYF(MY_ALLOW_ZERO_PTR));
  367.   my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
  368.   my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
  369.   my_free(current_user,MYF(MY_ALLOW_ZERO_PTR));
  370.   my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
  371.   exit(status.exit_status);
  372. }
  373.  
  374. enum options {OPT_CHARSETS_DIR=256, OPT_DEFAULT_CHARSET, OPT_TIMEOUT,
  375.           OPT_PAGER, OPT_NOPAGER, OPT_TEE, OPT_NOTEE} ;
  376.  
  377.  
  378. static struct option long_options[] =
  379. {
  380.   {"i-am-a-dummy",  no_argument,       0, 'U'},
  381.   {"batch",        no_argument,       0, 'B'},
  382.   {"character-sets-dir",required_argument, 0, OPT_CHARSETS_DIR},
  383.   {"compress",        no_argument,       0, 'C'},
  384. #ifndef DBUG_OFF
  385.   {"debug",        optional_argument,       0, '#'},
  386. #endif
  387.   {"database",        required_argument,     0, 'D'},
  388.   {"debug-info",    no_argument,       0, 'T'},
  389.   {"default-character-set", required_argument,0, OPT_DEFAULT_CHARSET},
  390.   {"enable-named-commands", no_argument,   0, 'G'},
  391.   {"execute",        required_argument,       0, 'e'},
  392.   {"force",        no_argument,       0, 'f'},
  393.   {"help",        no_argument,       0, '?'},
  394.   {"html",        no_argument,       0, 'H'},
  395.   {"host",        required_argument,       0, 'h'},
  396.   {"ignore-spaces", no_argument,       0, 'i'},
  397.   {"no-auto-rehash",no_argument,       0, 'A'},
  398.   {"no-named-commands", no_argument,       0, 'g'},
  399.   {"no-tee",        no_argument,           0, OPT_NOTEE},
  400. #ifndef __WIN__
  401.   {"no-pager",      no_argument,           0, OPT_NOPAGER},
  402.   {"nopager",       no_argument,           0, OPT_NOPAGER},  /* we are kind */
  403.   {"pager",         optional_argument,     0, OPT_PAGER},
  404. #endif
  405.   {"notee",         no_argument,           0, OPT_NOTEE},    /* we are kind */
  406.   {"tee",           required_argument,     0, OPT_TEE},
  407.   {"one-database",  no_argument,       0, 'o'},
  408.   {"password",        optional_argument,       0, 'p'},
  409. #ifdef __WIN__
  410.   {"pipe",        no_argument,       0, 'W'},
  411. #endif
  412.   {"port",        required_argument,       0, 'P'},
  413.   {"quick",        no_argument,       0, 'q'},
  414.   {"set-variable",  required_argument,       0, 'O'},
  415.   {"raw",        no_argument,       0, 'r'},
  416.   {"safe-updates",  no_argument,       0, 'U'},
  417.   {"silent",        no_argument,       0, 's'},
  418.   {"skip-column-names",no_argument,       0, 'N'},
  419.   {"skip-line-numbers",no_argument,       0, 'L'},
  420.   {"socket",        required_argument,       0, 'S'},
  421. #include "sslopt-longopts.h"
  422.   {"table",        no_argument,       0, 't'},
  423.   {"timeout",        required_argument,       0, OPT_TIMEOUT},
  424. #ifndef DONT_ALLOW_USER_CHANGE
  425.   {"user",        required_argument,       0, 'u'},
  426. #endif
  427.   {"unbuffered",    no_argument,       0, 'n'},
  428.   {"verbose",        no_argument,       0, 'v'},
  429.   {"version",        no_argument,       0, 'V'},
  430.   {"vertical",        no_argument,       0, 'E'},
  431.   {"wait",        no_argument,       0, 'w'},
  432.   {0, 0, 0, 0}
  433. };
  434.  
  435.  
  436. CHANGEABLE_VAR changeable_vars[] = {
  437.   { "max_allowed_packet", (long*) &max_allowed_packet,16*1024L*1024L,4096,
  438.     24*1024L*1024L, MALLOC_OVERHEAD,1024},
  439.   { "net_buffer_length",(long*) &net_buffer_length,16384,1024,24*1024*1024L,
  440.     MALLOC_OVERHEAD,1024},
  441.   { "select_limit", (long*) &select_limit, 1000L, 1, ~0L, 0, 1},
  442.   { "max_join_size", (long*) &max_join_size, 1000000L, 1, ~0L, 0, 1},
  443.   { 0, 0, 0, 0, 0, 0, 0}
  444. };
  445.  
  446.  
  447. static void usage(int version)
  448. {
  449.   printf("%s  Ver %s Distrib %s, for %s (%s)\n",
  450.      my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
  451.   if (version)
  452.     return;
  453.   puts("Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB");
  454.   puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\nand you are welcome to modify and redistribute it under the GPL license\n");
  455.   printf("Usage: %s [OPTIONS] [database]\n", my_progname);
  456.   printf("\n\
  457.   -?, --help        Display this help and exit.\n\
  458.   -A, --no-auto-rehash  No automatic rehashing. One has to use 'rehash' to\n\
  459.             get table and field completion. This gives a quicker\n\
  460.             start of mysql and disables rehashing on reconnect.\n\
  461.   -B, --batch        Print results with a tab as separator, each row on\n\
  462.             a new line. Doesn't use history file.\n\
  463.   --character-sets-dir=...\n\
  464.                         Directory where character sets are located.\n\
  465.   -C, --compress    Use compression in server/client protocol.\n");
  466. #ifndef DBUG_OFF
  467.   printf("\
  468.   -#, --debug[=...]     Debug log. Default is '%s'.\n",default_dbug_option);
  469. #endif
  470.   printf("\
  471.   -D, --database=..    Database to use.\n\
  472.   --default-character-set=...\n\
  473.                         Set the default character set.\n\
  474.   -e, --execute=...     Execute command and quit. (Output like with --batch)\n\
  475.   -E, --vertical        Print the output of a query (rows) vertically.\n\
  476.   -f, --force           Continue even if we get an sql error.\n\
  477.   -g, --no-named-commands\n\
  478.             Named commands are disabled. Use \\* form only, or\n\
  479.                         use named commands only in the beginning of a line\n\
  480.                         ending with a semicolon (;) Since version 10.9 the\n\
  481.                         client now starts with this option ENABLED by\n\
  482.                         default! Disable with '-G'. Long format commands\n\
  483.                         still work from the first line.\n\
  484.   -G, --enable-named-commands\n\
  485.                         Named commands are enabled. Opposite to -g.\n\
  486.   -i, --ignore-space    Ignore space after function names.\n\
  487.   -h, --host=...    Connect to host.\n\
  488.   -H, --html        Produce HTML output.\n\
  489.   -L, --skip-line-numbers\n\
  490.                         Don't write line number for errors.\n");
  491. #ifndef __WIN__
  492.   printf("\
  493.   --no-pager            Disable pager and print to stdout. See interactive\n\
  494.                         help (\\h) also.\n");
  495. #endif
  496.   printf("\
  497.   --no-tee              Disable outfile. See interactive help (\\h) also.\n\
  498.   -n, --unbuffered    Flush buffer after each query.\n\
  499.   -N, --skip-column-names\n\
  500.                         Don't write column names in results.\n\
  501.   -O, --set-variable var=option\n\
  502.             Give a variable an value. --help lists variables.\n\
  503.   -o, --one-database    Only update the default database. This is useful\n\
  504.             for skipping updates to other database in the update\n\
  505.             log.\n");
  506. #ifndef __WIN__
  507.   printf("\
  508.   --pager[=...]         Output type. Default is your ENV variable PAGER.\n\
  509.                         Valid pagers are less, more, cat [> filename], etc.\n\
  510.                         See interactive help (\\h) also. This option does\n\
  511.                         not work in batch mode.\n");
  512. #endif
  513.   printf("\
  514.   -p[password], --password[=...]\n\
  515.             Password to use when connecting to server\n\
  516.             If password is not given it's asked from the tty.\n");
  517. #ifdef __WIN__
  518.   puts("  -W, --pipe        Use named pipes to connect to server");
  519. #endif
  520.   printf("\n\
  521.   -P  --port=...    Port number to use for connection.\n\
  522.   -q, --quick        Don't cache result, print it row by row. This may\n\
  523.             slow down the server if the output is suspended.\n\
  524.             Doesn't use history file.\n\
  525.   -r, --raw        Write fields without conversion. Used with --batch\n\
  526.   -s, --silent        Be more silent.\n\
  527.   -S  --socket=...    Socket file to use for connection.\n");
  528. #include "sslopt-usage.h"
  529.   printf("\
  530.   -t  --table        Output in table format.\n\
  531.   -T, --debug-info    Print some debug info at exit.\n\
  532.   --tee=...             Append everything into outfile. See interactive help\n\
  533.                         (\\h) also. Does not work in batch mode.\n");
  534. #ifndef DONT_ALLOW_USER_CHANGE
  535.   printf("\
  536.   -u, --user=#        User for login if not current user.\n");
  537. #endif
  538.   printf("\
  539.   -U, --safe-updates[=#], --i-am-a-dummy[=#]\n\
  540.                 Only allow UPDATE and DELETE that uses keys.\n\
  541.   -v, --verbose        Write more. (-v -v -v gives the table output format)\n\
  542.   -V, --version        Output version information and exit.\n\
  543.   -w, --wait        Wait and retry if connection is down.\n");
  544.   print_defaults("my",load_default_groups);
  545.  
  546.   printf("\nPossible variables for option --set-variable (-O) are:\n");
  547.   for (uint i=0 ; changeable_vars[i].name ; i++)
  548.     printf("%-20s  current value: %lu\n",
  549.        changeable_vars[i].name,
  550.        (ulong) *changeable_vars[i].varptr);
  551. }
  552.  
  553.  
  554. static int get_options(int argc, char **argv)
  555. {
  556.   int c,option_index=0;
  557.   bool tty_password=0;
  558.  
  559.   set_all_changeable_vars(changeable_vars);
  560.   while ((c=getopt_long(argc,argv,
  561.             "?ABCD:LfgGHinNoqrstTUvVwWEe:h:O:P:S:u:#::p::",
  562.             long_options, &option_index)) != EOF)
  563.   {
  564.     switch(c) {
  565.     case OPT_DEFAULT_CHARSET:
  566.       default_charset= optarg;
  567.       break;
  568.     case OPT_CHARSETS_DIR:
  569.       charsets_dir= optarg;
  570.       break;
  571.     case OPT_TEE:
  572.       if (!opt_outfile && strlen(optarg))
  573.       {
  574.     strmov(outfile, optarg);
  575.     opt_outfile=1;
  576.     init_tee();
  577.       }
  578.       break;
  579.     case OPT_NOTEE:
  580.       if (opt_outfile)
  581.     end_tee();
  582.       opt_outfile=0;
  583.       break;
  584.     case OPT_PAGER:
  585.       opt_nopager=0;
  586.       if (optarg)
  587.     strmov(pager, optarg);
  588.       else
  589.     strmov(pager, (char*) getenv("PAGER"));
  590.       strmov(default_pager, pager);
  591.       break;
  592.     case OPT_NOPAGER:
  593.       opt_nopager=1;
  594.       break;
  595.     case 'D':
  596.       my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));      
  597.       current_db=my_strdup(optarg,MYF(MY_WME));
  598.       break;
  599.     case 'e':
  600.       status.batch=1;
  601.       status.add_to_history=0;
  602.       batch_readline_end(status.line_buff);    // If multiple -e
  603.       if (!(status.line_buff=batch_readline_command(optarg)))
  604.     return 1;
  605.       ignore_errors=0;
  606.       break;
  607.     case 'f':
  608.       ignore_errors=1;
  609.       break;
  610.     case 'h':
  611.       my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
  612.       current_host=my_strdup(optarg,MYF(MY_WME));
  613.       break;
  614. #ifndef DONT_ALLOW_USER_CHANGE
  615.     case 'u':
  616.       my_free(current_user,MYF(MY_ALLOW_ZERO_PTR));
  617.       current_user= my_strdup(optarg,MYF(MY_WME));
  618.       break;
  619. #endif
  620.     case 'U':
  621.       if (!optarg)
  622.     safe_updates=1;
  623.       else
  624.     safe_updates=atoi(optarg) != 0;
  625.       break;
  626.     case 'o':
  627.       one_database=skip_updates=1;
  628.       break;
  629.     case 'O':
  630.       if (set_changeable_var(optarg, changeable_vars))
  631.       {
  632.     usage(0);
  633.     return(1);
  634.       }
  635.       break;
  636.     case 'p':
  637.       if (optarg)
  638.       {
  639.     char *start=optarg;
  640.     my_free(opt_password,MYF(MY_ALLOW_ZERO_PTR));
  641.     opt_password=my_strdup(optarg,MYF(MY_FAE));
  642.     while (*optarg) *optarg++= 'x';        // Destroy argument
  643.     if (*start)
  644.       start[1]=0;
  645.       }
  646.       else
  647.     tty_password=1;
  648.       break;
  649.     case 't':
  650.       output_tables=1;
  651.       break;
  652.     case 'r':
  653.       opt_raw_data=1;
  654.       break;
  655.     case '#':
  656.       DBUG_PUSH(optarg ? optarg : default_dbug_option);
  657.       info_flag=1;
  658.       break;
  659.     case 'q': quick=1; break;
  660.     case 's': opt_silent++; break;
  661.     case 'T': info_flag=1; break;
  662.     case 'n': unbuffered=1; break;
  663.     case 'v': verbose++; break;
  664.     case 'E': vertical=1; break;
  665.     case 'w': wait_flag=1; break;
  666.     case 'A': no_rehash=1; break;
  667.     case 'G': no_named_cmds=0; break;
  668.     case 'g': no_named_cmds=1; break;
  669.     case 'H': opt_html=1; break;
  670.     case 'i': connect_flag|= CLIENT_IGNORE_SPACE; break;
  671.     case 'B':
  672.       if (!status.batch)
  673.       {
  674.     status.batch=1;
  675.     status.add_to_history=0;
  676.     opt_silent++;                // more silent
  677.       }
  678.       break;
  679.     case 'C':
  680.       opt_compress=1;
  681.       break;
  682.     case 'L':
  683.       skip_line_numbers=1;
  684.       break;
  685.     case 'N':
  686.       skip_column_names=1;
  687.       break;
  688.     case 'P':
  689.       opt_mysql_port= (unsigned int) atoi(optarg);
  690.       break;
  691.     case 'S':
  692.       my_free(opt_mysql_unix_port,MYF(MY_ALLOW_ZERO_PTR));
  693.       opt_mysql_unix_port= my_strdup(optarg,MYF(0));
  694.       break;
  695.     case 'W':
  696. #ifdef __WIN__
  697.       opt_mysql_unix_port=my_strdup(MYSQL_NAMEDPIPE,MYF(0));
  698. #endif
  699.       break;
  700.     case OPT_TIMEOUT:
  701.       opt_connect_timeout=atoi(optarg);
  702.       break;
  703.     case 'V': usage(1); exit(0);
  704.     case 'I':
  705.     case '?':
  706.       usage(0);
  707.       exit(0);
  708. #include "sslopt-case.h"
  709.     default:
  710.       tee_fprintf(stderr,"illegal option: -%c\n",opterr);
  711.       usage(0);
  712.       exit(1);
  713.     }
  714.   }
  715.   if (status.batch) /* disable pager and outfile in this case */
  716.   {
  717.     strmov(default_pager, "stdout");
  718.     strmov(pager, "stdout");
  719.     opt_nopager=1;
  720.     opt_outfile=0;
  721.   }
  722.   if (default_charset)
  723.   {
  724.     if (set_default_charset_by_name(default_charset, MYF(MY_WME)))
  725.       exit(1);
  726.   }
  727.   argc-=optind;
  728.   argv+=optind;
  729.   if (argc > 1)
  730.   {
  731.     usage(0);
  732.     exit(1);
  733.   }
  734.   if (argc == 1)
  735.   {
  736.     my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
  737.     current_db= my_strdup(*argv,MYF(MY_WME));
  738.   }
  739.   if (!current_host)
  740.   {    /* If we don't have a hostname have a look at MYSQL_HOST */
  741.     char *tmp=(char *) getenv("MYSQL_HOST");
  742.     if (tmp)
  743.       current_host = my_strdup(tmp,MYF(MY_WME));
  744.   }
  745.   if (tty_password)
  746.     opt_password=get_tty_password(NullS);
  747.   return(0);
  748. }
  749.  
  750.  
  751. static int read_lines(bool execute_commands)
  752. {
  753. #ifdef __WIN__
  754.   char linebuffer[254];
  755. #endif
  756.   char    *line;
  757.   char    in_string=0;
  758.   ulong line_number=0;
  759.   COMMANDS *com;
  760.   status.exit_status=1;
  761.  
  762.   for (;;)
  763.   {
  764.     if (status.batch || !execute_commands)
  765.     {
  766.       line=batch_readline(status.line_buff);
  767.       line_number++;
  768.       if (!glob_buffer.length())
  769.     status.query_start_line=line_number;
  770.     }
  771.     else
  772.     {
  773. #ifdef __WIN__
  774.       if (opt_outfile && glob_buffer.is_empty())
  775.     fflush(OUTFILE);
  776.       tee_fputs(glob_buffer.is_empty() ? "mysql> " :
  777.         !in_string ? "    -> " :
  778.         in_string == '\'' ?
  779.         "    '> " : "    \"> ",stdout);
  780.       linebuffer[0]=(char) sizeof(linebuffer);
  781.       line=_cgets(linebuffer);
  782. #else
  783.       if (opt_outfile)
  784.       {
  785.     if (glob_buffer.is_empty())
  786.       fflush(OUTFILE);
  787.     fputs(glob_buffer.is_empty() ? "mysql> " :
  788.           !in_string ? "    -> " :
  789.           in_string == '\'' ?
  790.           "    '> " : "    \"> ", OUTFILE);
  791.       }
  792.       line=readline((char*) (glob_buffer.is_empty() ? "mysql> " :
  793.                  !in_string ? "    -> " :
  794.                  in_string == '\'' ?
  795.                  "    '> " : "    \"> "));
  796. #endif
  797.       if (opt_outfile)
  798.     fprintf(OUTFILE, "%s\n", line);
  799.     }
  800.     if (!line)                    // End of file
  801.     {
  802.       status.exit_status=0;
  803.       break;
  804.     }
  805.     if (!in_string && (line[0] == '#' ||
  806.                (line[0] == '-' && line[1] == '-') ||
  807.                line[0] == 0))
  808.       continue;                    // Skip comment lines
  809.  
  810.     /* Check if line is a mysql command line */
  811.     /* (We want to allow help, print and clear anywhere at line start */
  812.     if (execute_commands && (!no_named_cmds || glob_buffer.is_empty()) 
  813.     && !in_string && (com=find_command(line,0)))
  814.     {
  815.       if ((*com->func)(&glob_buffer,line) > 0)
  816.     break;
  817.       if (glob_buffer.is_empty())        // If buffer was emptied
  818.     in_string=0;
  819. #ifdef HAVE_READLINE
  820.       if (status.add_to_history)
  821.     add_history(line);
  822. #endif
  823.       continue;
  824.     }
  825.     if (add_line(glob_buffer,line,&in_string))
  826.       break;
  827.   }
  828.   /* if in batch mode, send last query even if it doesn't end with \g or go */
  829.  
  830.   if ((status.batch || !execute_commands) && !status.exit_status)
  831.   {
  832.     remove_cntrl(glob_buffer);
  833.     if (!glob_buffer.is_empty())
  834.     {
  835.       status.exit_status=1;
  836.       if (com_go(&glob_buffer,line) <= 0)
  837.     status.exit_status=0;
  838.     }
  839.   }
  840.   return status.exit_status;
  841. }
  842.  
  843.  
  844. static COMMANDS *find_command (char *name,char cmd_char)
  845. {
  846.   uint len;
  847.   char *end;
  848.  
  849.   if (!name)
  850.   {
  851.     len=0;
  852.     end=0;
  853.   }
  854.   else
  855.   {
  856.     while (isspace(*name))
  857.       name++;
  858.     if (strchr(name,';') || strstr(name,"\\g"))
  859.       return ((COMMANDS *) 0);
  860.     if ((end=strcont(name," \t")))
  861.     {
  862.       len=(uint) (end - name);
  863.       while (isspace(*end))
  864.     end++;
  865.       if (!*end)
  866.     end=0;                    // no arguments to function
  867.     }
  868.     else
  869.       len=(uint) strlen(name);
  870.   }
  871.  
  872.   for (uint i= 0; commands[i].name; i++)
  873.   {
  874.     if (commands[i].func &&
  875.     ((name && !my_casecmp(name,commands[i].name,len) &&
  876.       !commands[i].name[len] &&
  877.       (!end || (end && commands[i].takes_params))) ||
  878.      !name && commands[i].cmd_char == cmd_char))
  879.       return (&commands[i]);
  880.   }
  881.   return ((COMMANDS *) 0);
  882. }
  883.  
  884.  
  885. static bool add_line(String &buffer,char *line,char *in_string)
  886. {
  887.   uchar inchar;
  888.   char buff[80],*pos,*out;
  889.   COMMANDS *com;
  890.  
  891.   if (!line[0] && buffer.is_empty())
  892.     return 0;
  893. #ifdef HAVE_READLINE
  894.   if (status.add_to_history && line[0])
  895.     add_history(line);
  896. #endif
  897. #ifdef USE_MB
  898.   char *strend=line+(uint) strlen(line);
  899. #endif
  900.  
  901.   for (pos=out=line ; (inchar= (uchar) *pos) ; pos++)
  902.   {
  903.     if (isspace(inchar) && out == line && buffer.is_empty())
  904.       continue;
  905. #ifdef USE_MB
  906.     int l;
  907. /*    if ((l = ismbchar(pos, pos+MBMAXLEN))) {  Wei He: I think it's wrong! */
  908.     if (use_mb(default_charset_info) &&
  909.         (l = my_ismbchar(default_charset_info, pos, strend))) {
  910.     while (l--)
  911.         *out++ = *pos++;
  912.     pos--;
  913.     continue;
  914.     }
  915. #endif
  916.     if (inchar == '\\')
  917.     {                    // mSQL or postgreSQL style command ?
  918.       if (!(inchar = (uchar) *++pos))
  919.     break;                // readline adds one '\'
  920.       if (*in_string || inchar == 'N')
  921.       {                    // Don't allow commands in string
  922.     *out++='\\';
  923.     *out++= (char) inchar;
  924.     continue;
  925.       }
  926.       if ((com=find_command(NullS,(char) inchar)))
  927.       {
  928.     const String tmp(line,(uint) (out-line));
  929.     buffer.append(tmp);
  930.     if ((*com->func)(&buffer,pos-1) > 0)
  931.       return 1;                // Quit
  932.     if (com->takes_params)
  933.     {
  934.       for (pos++ ; *pos && *pos != ';' ; pos++) ;    // Remove parameters
  935.       if (!*pos)
  936.         pos--;
  937.     }
  938.     out=line;
  939.       }
  940.       else
  941.       {
  942.     sprintf(buff,"Unknown command '\\%c'.",inchar);
  943.     if (put_info(buff,INFO_ERROR) > 0)
  944.       return 1;
  945.     *out++='\\';
  946.     *out++=(char) inchar;
  947.     continue;
  948.       }
  949.     }
  950.     else if (inchar == ';' && !*in_string)
  951.     {                        // ';' is end of command
  952.       if (out != line)
  953.     buffer.append(line,(uint) (out-line));    // Add this line
  954.       if ((com=find_command(buffer.c_ptr(),0)))
  955.       {
  956.     if ((*com->func)(&buffer,buffer.c_ptr()) > 0)
  957.       return 1;                // Quit
  958.       }
  959.       else
  960.       {
  961.     int error=com_go(&buffer,0);
  962.     if (error)
  963.     {
  964.       return error < 0 ? 0 : 1;        // < 0 is not fatal
  965.     }
  966.       }
  967.       buffer.length(0);
  968.       out=line;
  969.     }
  970.     else if (!*in_string && (inchar == '#' ||
  971.                  inchar == '-' && pos[1] == '-' &&
  972.                  isspace(pos[2])))
  973.       break;                    // comment to end of line
  974.     else
  975.     {                        // Add found char to buffer
  976.       if (inchar == *in_string)
  977.     *in_string=0;
  978.       else if (!*in_string && (inchar == '\'' || inchar == '"'))
  979.     *in_string=(char) inchar;
  980.       *out++ = (char) inchar;
  981.     }
  982.   }
  983.   if (out != line || !buffer.is_empty())
  984.   {
  985.     *out++='\n';
  986.     uint length=(uint) (out-line);
  987.     if (buffer.length() + length >= buffer.alloced_length())
  988.       buffer.realloc(buffer.length()+length+IO_SIZE);
  989.     if (buffer.append(line,length))
  990.       return 1;
  991.   }
  992.   return 0;
  993. }
  994.  
  995. /* **************************************************************** */
  996. /*                                    */
  997. /*            Interface to Readline Completion            */
  998. /*                                    */
  999. /* **************************************************************** */
  1000.  
  1001. #ifdef HAVE_READLINE
  1002.  
  1003. static char *new_command_generator(char *text, int);
  1004. static char **new_mysql_completion (char *text, int start, int end);
  1005.  
  1006. /* Tell the GNU Readline library how to complete.  We want to try to complete
  1007.    on command names if this is the first word in the line, or on filenames
  1008.    if not. */
  1009.  
  1010. char **no_completion (char *text __attribute__ ((unused)),
  1011.               char *word __attribute__ ((unused)))
  1012. {
  1013.   return 0;                    /* No filename completion */
  1014. }
  1015.  
  1016. static void initialize_readline (char *name)
  1017. {
  1018.   /* Allow conditional parsing of the ~/.inputrc file. */
  1019.   rl_readline_name = name;
  1020.  
  1021.   /* Tell the completer that we want a crack first. */
  1022.   /* rl_attempted_completion_function = (CPPFunction *)mysql_completion;*/
  1023.   rl_attempted_completion_function = (CPPFunction *) new_mysql_completion;
  1024.   rl_completion_entry_function=(Function *) no_completion;
  1025. }
  1026.  
  1027. /* Attempt to complete on the contents of TEXT.  START and END show the
  1028.    region of TEXT that contains the word to complete.  We can use the
  1029.    entire line in case we want to do some simple parsing.  Return the
  1030.    array of matches, or NULL if there aren't any. */
  1031.  
  1032.  
  1033. static char **new_mysql_completion (char *text,
  1034.                     int start __attribute__((unused)),
  1035.                     int end __attribute__((unused)))
  1036. {
  1037.   if (!status.batch && !quick)
  1038.     return completion_matches(text, (CPFunction*) new_command_generator);
  1039.   else
  1040.     return (char**) 0;
  1041. }
  1042.  
  1043. static char *new_command_generator(char *text,int state)
  1044. {
  1045.   static int textlen;
  1046.   char *ptr;
  1047.   static Bucket *b;
  1048.   static entry *e;
  1049.   static uint i;
  1050.  
  1051.   if (!state) {
  1052.     textlen=(uint) strlen(text);
  1053.   }
  1054.  
  1055.   if (textlen>0) { /* lookup in the hash */
  1056.     if (!state) {
  1057.       uint len;
  1058.  
  1059.       b = find_all_matches(&ht,text,(uint) strlen(text),&len);
  1060.       if (!b) {
  1061.     return NullS;
  1062.       }
  1063.       e = b->pData;
  1064.     }
  1065.  
  1066.     while (e) {
  1067.       ptr= strdup(e->str);
  1068.       e = e->pNext;
  1069.       return ptr;
  1070.     }
  1071.   } else { /* traverse the entire hash, ugly but works */
  1072.  
  1073.     if (!state) {
  1074.       i=0;
  1075.       /* find the first used bucket */
  1076.       while (i<ht.nTableSize) {
  1077.     if (ht.arBuckets[i]) {
  1078.       b = ht.arBuckets[i];
  1079.       e = b->pData;
  1080.       break;
  1081.     }
  1082.     i++;
  1083.       }
  1084.     }
  1085.     ptr= NullS;
  1086.     while (e && !ptr) { /* find valid entry in bucket */
  1087.       if ((uint) strlen(e->str)==b->nKeyLength) {
  1088.     ptr = strdup(e->str);
  1089.       }
  1090.       /* find the next used entry */
  1091.       e = e->pNext;
  1092.       if (!e) { /* find the next used bucket */
  1093.     b = b->pNext;
  1094.     if (!b) {
  1095.       i++;
  1096.       while (i<ht.nTableSize) {
  1097.         if (ht.arBuckets[i]) {
  1098.           b = ht.arBuckets[i];
  1099.           e = b->pData;
  1100.           break;
  1101.         }
  1102.         i++;
  1103.       }
  1104.     } else {
  1105.       e = b->pData;
  1106.     }
  1107.       }
  1108.     }
  1109.     if (ptr) {
  1110.       return ptr;
  1111.     }
  1112.   }
  1113.   return NullS;
  1114. }
  1115.  
  1116.  
  1117. /* Build up the completion hash */
  1118.  
  1119. static void build_completion_hash(bool skip_rehash,bool write_info)
  1120. {
  1121.   COMMANDS *cmd=commands;
  1122.   static MYSQL_RES *databases=0,*tables=0,*fields;
  1123.   static char ***field_names= 0;
  1124.   MYSQL_ROW database_row,table_row;
  1125.   MYSQL_FIELD *sql_field;
  1126.   char buf[NAME_LEN*2+2];         // table name plus field name plus 2
  1127.   int i,j,num_fields;
  1128.   DBUG_ENTER("build_completion_hash");
  1129.  
  1130.   if (status.batch || quick || !current_db)
  1131.     DBUG_VOID_RETURN;            // We don't need completion in batches
  1132.  
  1133.   completion_hash_clean(&ht);
  1134.   if (tables)
  1135.   {
  1136.     mysql_free_result(tables);
  1137.     tables=0;
  1138.   }
  1139.   if (databases) {
  1140.     mysql_free_result(databases);
  1141.     databases=0;
  1142.   }
  1143.  
  1144.   /* hash SQL commands */
  1145.   while (cmd->name) {
  1146.     add_word(&ht,(char*) cmd->name);
  1147.     cmd++;
  1148.   }
  1149.   if (skip_rehash)
  1150.     DBUG_VOID_RETURN;
  1151.  
  1152.   /* hash MySQL functions (to be implemented) */
  1153.  
  1154.   /* hash all database names */
  1155.   if (mysql_query(&mysql,"show databases")==0) {
  1156.     if (!(databases = mysql_store_result(&mysql)))
  1157.       put_info(mysql_error(&mysql),INFO_INFO);
  1158.     else
  1159.     {
  1160.       while ((database_row=mysql_fetch_row(databases)))
  1161.     add_word(&ht,(char*) database_row[0]);
  1162.     }
  1163.   }
  1164.   /* hash all table names */
  1165.   if (mysql_query(&mysql,"show tables")==0)
  1166.   {
  1167.     if (!(tables = mysql_store_result(&mysql)))
  1168.       put_info(mysql_error(&mysql),INFO_INFO);
  1169.     else
  1170.     {
  1171.       if (mysql_num_rows(tables) > 0 && !opt_silent && write_info)
  1172.       {
  1173.     tee_fprintf(stdout, "\
  1174. Reading table information for completion of table and column names\n\
  1175. You can turn off this feature to get a quicker startup with -A\n\n");
  1176.       }
  1177.       while ((table_row=mysql_fetch_row(tables)))
  1178.       {
  1179.     if (!completion_hash_exists(&ht,(char*) table_row[0],
  1180.                     (uint) strlen((const char*) table_row[0])))
  1181.       add_word(&ht,table_row[0]);
  1182.       }
  1183.     }
  1184.   }
  1185.   if (field_names) {
  1186.     for (i=0; field_names[i]; i++) {
  1187.       for (j=0; field_names[i][j]; j++) {
  1188.     my_free(field_names[i][j],MYF(0));
  1189.       }
  1190.       my_free((gptr) field_names[i],MYF(0));
  1191.     }
  1192.     my_free((gptr) field_names,MYF(0));
  1193.   }
  1194.   field_names=0;
  1195.  
  1196.   /* hash all field names, both with the table prefix and without it */
  1197.   if (!tables) { /* no tables */
  1198.     DBUG_VOID_RETURN;
  1199.   }
  1200.   mysql_data_seek(tables,0);
  1201.   field_names = (char ***) my_malloc(sizeof(char **) *
  1202.                      (uint) (mysql_num_rows(tables)+1),
  1203.                      MYF(MY_WME));
  1204.   if (!field_names)
  1205.     DBUG_VOID_RETURN;
  1206.   field_names[mysql_num_rows(tables)]='\0';
  1207.   i=0;
  1208.   while ((table_row=mysql_fetch_row(tables)))
  1209.   {
  1210.     if ((fields=mysql_list_fields(&mysql,(const char*) table_row[0],NullS)))
  1211.     {
  1212.       num_fields=mysql_num_fields(fields);
  1213.       field_names[i] = (char **) my_malloc(sizeof(char *)*(num_fields*2+1),
  1214.                        MYF(0));
  1215.       if (!field_names[i])
  1216.       {
  1217.     continue;
  1218.       }
  1219.       field_names[i][num_fields*2]='\0';
  1220.       j=0;
  1221.       while ((sql_field=mysql_fetch_field(fields)))
  1222.       {
  1223.     sprintf(buf,"%s.%s",table_row[0],sql_field->name);
  1224.     field_names[i][j] = my_strdup(buf,MYF(0));
  1225.     add_word(&ht,field_names[i][j]);
  1226.     field_names[i][num_fields+j] = my_strdup(sql_field->name,MYF(0));
  1227.     if (!completion_hash_exists(&ht,field_names[i][num_fields+j],
  1228.                     (uint) strlen(field_names[i][num_fields+j])))
  1229.       add_word(&ht,field_names[i][num_fields+j]);
  1230.     j++;
  1231.       }
  1232.     }
  1233.     else
  1234.       tee_fprintf(stdout,
  1235.           "Didn't find any fields in table '%s'\n",table_row[0]);
  1236.     i++;
  1237.   }
  1238.   DBUG_VOID_RETURN;
  1239. }
  1240.  
  1241.  
  1242.     /* for gnu readline */
  1243.  
  1244. #ifndef HAVE_INDEX
  1245. #ifdef    __cplusplus
  1246. extern "C" {
  1247. #endif
  1248. extern char *index(const char *,pchar c),*rindex(const char *,pchar);
  1249.  
  1250. char *index(const char *s,pchar c)
  1251. {
  1252.   for (;;)
  1253.   {
  1254.      if (*s == (char) c) return (char*) s;
  1255.      if (!*s++) return NullS;
  1256.   }
  1257. }
  1258.  
  1259. char *rindex(const char *s,pchar c)
  1260. {
  1261.   reg3 char *t;
  1262.  
  1263.   t = NullS;
  1264.   do if (*s == (char) c) t = (char*) s; while (*s++);
  1265.   return (char*) t;
  1266. }
  1267. #ifdef    __cplusplus
  1268. }
  1269. #endif
  1270. #endif
  1271. #endif /* HAVE_READLINE */
  1272.  
  1273. static int reconnect(void)
  1274. {
  1275.   if (!status.batch)
  1276.   {
  1277.     put_info("No connection. Trying to reconnect...",INFO_INFO);
  1278.     (void) com_connect((String *) 0, 0);
  1279.     if(!no_rehash) com_rehash(NULL, NULL);
  1280.   }
  1281.   if (!connected)
  1282.     return put_info("Can't connect to the server\n",INFO_ERROR);
  1283.   return 0;
  1284. }
  1285.  
  1286.  
  1287. /***************************************************************************
  1288.  The different commands
  1289. ***************************************************************************/
  1290.  
  1291. static int
  1292. com_help (String *buffer __attribute__((unused)),
  1293.       char *line __attribute__((unused)))
  1294. {
  1295.   reg1 int i;
  1296.  
  1297.   put_info("\nMySQL commands:",INFO_INFO);
  1298.   if (no_named_cmds)
  1299.     put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
  1300.   for (i = 0; commands[i].name; i++)
  1301.   {
  1302.     if (commands[i].func)
  1303.       tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
  1304.           commands[i].cmd_char, commands[i].doc);
  1305.   }
  1306.   if (connected)
  1307.     tee_fprintf(stdout,
  1308.         "\nConnection id: %ld  (Can be used with mysqladmin kill)\n\n",
  1309.         mysql_thread_id(&mysql));
  1310.   else
  1311.     tee_fprintf(stdout, "Not connected!  Reconnect with 'connect'!\n\n");
  1312.   return 0;
  1313. }
  1314.  
  1315.  
  1316.     /* ARGSUSED */
  1317. static int
  1318. com_clear(String *buffer,char *line __attribute__((unused)))
  1319. {
  1320.   buffer->length(0);
  1321.   return 0;
  1322. }
  1323.  
  1324.  
  1325. /*
  1326. ** Execute command
  1327. ** Returns: 0  if ok
  1328. **        -1 if not fatal error
  1329. **        1  if fatal error
  1330. */
  1331.  
  1332.  
  1333. static int
  1334. com_go(String *buffer,char *line __attribute__((unused)))
  1335. {
  1336.   char        buff[160],time_buff[32];
  1337.   MYSQL_RES    *result;
  1338.   ulong        timer;
  1339.   uint        error=0;
  1340.  
  1341.   if (!status.batch)
  1342.   {
  1343.     old_buffer= *buffer;            // Save for edit command
  1344.     old_buffer.copy();
  1345.   }
  1346.  
  1347.     /* Remove garbage for nicer messages */
  1348.   LINT_INIT(buff[0]);
  1349.   remove_cntrl(*buffer);
  1350.  
  1351.   if (buffer->is_empty())
  1352.   {
  1353.     if (status.batch)                // Ignore empty quries
  1354.       return 0;
  1355.     return put_info("No query specified\n",INFO_ERROR);
  1356.  
  1357.   }
  1358.   if (!connected && reconnect())
  1359.   {
  1360.     buffer->length(0);                // Remove query on error
  1361.     return status.batch ? 1 : -1;        // Fatal error
  1362.   }
  1363.   if (verbose)
  1364.     (void) com_print(buffer,0);
  1365.  
  1366.   if (skip_updates &&
  1367.       (buffer->length() < 4 || my_sortcmp(buffer->ptr(),"SET ",4)))
  1368.   {
  1369.     (void) put_info("Ignoring query to other database",INFO_INFO);
  1370.     return 0;
  1371.   }
  1372.  
  1373.   timer=start_timer();
  1374.   for (uint retry=0;; retry++)
  1375.   {
  1376.     if (!mysql_real_query(&mysql,buffer->ptr(),buffer->length()))
  1377.       break;
  1378.     error=put_info(mysql_error(&mysql),INFO_ERROR, mysql_errno(&mysql));
  1379.     if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 
  1380.     || status.batch)
  1381.     {
  1382.       buffer->length(0);            // Remove query on error
  1383.       return error;
  1384.     }
  1385.     if (reconnect())
  1386.     {
  1387.       buffer->length(0);            // Remove query on error
  1388.       return error;
  1389.     }
  1390.   }
  1391.   error=0;
  1392.   buffer->length(0);
  1393.  
  1394.   if (quick)
  1395.   {
  1396.     if (!(result=mysql_use_result(&mysql)) && mysql_field_count(&mysql))
  1397.     {
  1398.       return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  1399.     }
  1400.   }
  1401.   else
  1402.   {
  1403.     if (!(result=mysql_store_result(&mysql)))
  1404.     {
  1405.       if (mysql_error(&mysql)[0])
  1406.       {
  1407.     return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  1408.       }
  1409.     }
  1410.   }
  1411.  
  1412.   if (verbose >= 3 || !opt_silent)
  1413.     mysql_end_timer(timer,time_buff);
  1414.   else
  1415.     time_buff[0]=0;
  1416.   if (result)
  1417.   {
  1418.     if (!mysql_num_rows(result) && ! quick)
  1419.     {
  1420.       sprintf(buff,"Empty set%s",time_buff);
  1421.     }
  1422.     else
  1423.     {
  1424.       init_pager();
  1425.       if (opt_html)
  1426.     print_table_data_html(result);
  1427.       else if (vertical)
  1428.     print_table_data_vertically(result);
  1429.       else if (opt_silent && verbose <= 2 && !output_tables)
  1430.     print_tab_data(result);
  1431.       else
  1432.     print_table_data(result);
  1433.       sprintf(buff,"%ld %s in set%s",
  1434.           (long) mysql_num_rows(result),
  1435.           (long) mysql_num_rows(result) == 1 ? "row" : "rows",
  1436.           time_buff);
  1437.       end_pager();
  1438.     }
  1439.   }
  1440.   else if (mysql_affected_rows(&mysql) == ~(ulonglong) 0)
  1441.     sprintf(buff,"Query OK%s",time_buff);
  1442.   else
  1443.     sprintf(buff,"Query OK, %ld %s affected%s",
  1444.         (long) mysql_affected_rows(&mysql),
  1445.         (long) mysql_affected_rows(&mysql) == 1 ? "row" : "rows",
  1446.         time_buff);
  1447.   put_info(buff,INFO_RESULT);
  1448.   if (mysql_info(&mysql))
  1449.     put_info(mysql_info(&mysql),INFO_RESULT);
  1450.   put_info("",INFO_RESULT);            // Empty row
  1451.  
  1452.   if (result && !mysql_eof(result))    /* Something wrong when using quick */
  1453.     error=put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  1454.   else if (unbuffered)
  1455.     fflush(stdout);
  1456.   mysql_free_result(result);
  1457.   return error;                /* New command follows */
  1458. }
  1459.  
  1460.  
  1461. static void init_pager()
  1462. {
  1463. #ifndef __WIN__
  1464.   if (!opt_nopager)
  1465.   {
  1466.     if (!(PAGER= popen(pager, "w")))
  1467.     {
  1468.       tee_fprintf(stdout, "popen() failed! defaulting PAGER to stdout!\n");
  1469.       PAGER= stdout;
  1470.     }
  1471.   }
  1472.   else
  1473. #endif
  1474.     PAGER= stdout;
  1475. }
  1476.  
  1477. static void end_pager()
  1478. {
  1479. #ifndef __WIN__
  1480.   if (!opt_nopager)
  1481.     pclose(PAGER);
  1482. #endif
  1483. }
  1484.  
  1485. static void init_tee()
  1486. {
  1487.   if (!(OUTFILE= my_fopen(outfile, O_APPEND | O_WRONLY, MYF(MY_WME))))
  1488.   {
  1489.     opt_outfile=0;
  1490.     init_pager();
  1491.     return;
  1492.   }
  1493. }
  1494.  
  1495. static void end_tee()
  1496. {
  1497.   my_fclose(OUTFILE, MYF(0));
  1498.   return;
  1499. }
  1500.  
  1501. static int
  1502. com_ego(String *buffer,char *line)
  1503. {
  1504.   int result;
  1505.   bool oldvertical=vertical;
  1506.   vertical=1;
  1507.   result=com_go(buffer,line);
  1508.   vertical=oldvertical;
  1509.   return result;
  1510. }
  1511.  
  1512.  
  1513. static void
  1514. print_table_data(MYSQL_RES *result)
  1515. {
  1516.   String separator(256);
  1517.   MYSQL_ROW    cur;
  1518.   MYSQL_FIELD    *field;
  1519.   bool        *num_flag;
  1520.  
  1521.   num_flag=(bool*) my_alloca(sizeof(bool)*mysql_num_fields(result));
  1522.   separator.copy("+",1);
  1523.   while ((field = mysql_fetch_field(result)))
  1524.   {
  1525.     uint length=skip_column_names ? 0 : (uint) strlen(field->name);
  1526.     if (quick)
  1527.       length=max(length,field->length);
  1528.     else
  1529.       length=max(length,field->max_length);
  1530.     if (length < 4 && !IS_NOT_NULL(field->flags))
  1531.       length=4;                    // Room for "NULL"
  1532.     field->max_length=length+1;
  1533.     separator.fill(separator.length()+length+2,'-');
  1534.     separator.append('+');
  1535.   }
  1536.   tee_puts(separator.c_ptr(), PAGER);
  1537.   if (!skip_column_names)
  1538.   {
  1539.     mysql_field_seek(result,0);
  1540.     (void) tee_fputs("|", PAGER);
  1541.     for (uint off=0; (field = mysql_fetch_field(result)) ; off++)
  1542.     {
  1543.       tee_fprintf(PAGER, " %-*s|",field->max_length,field->name);
  1544.       num_flag[off]= IS_NUM(field->type);
  1545.     }
  1546.     (void) tee_fputs("\n", PAGER);
  1547.     tee_puts(separator.c_ptr(), PAGER);
  1548.   }
  1549.  
  1550.   while ((cur = mysql_fetch_row(result)))
  1551.   {
  1552.     (void) tee_fputs("|", PAGER);
  1553.     mysql_field_seek(result,0);
  1554.     for (uint off=0 ; off < mysql_num_fields(result); off++)
  1555.     {
  1556.       field = mysql_fetch_field(result);
  1557.       uint length=field->max_length;
  1558.       tee_fprintf(PAGER, num_flag[off] ? "%*s |" : " %-*s|",
  1559.           length,cur[off] ? (char*) cur[off] : "NULL");
  1560.     }
  1561.     (void) tee_fputs("\n", PAGER);
  1562.   }
  1563.   tee_puts(separator.c_ptr(), PAGER);
  1564.   my_afree((gptr) num_flag);
  1565. }
  1566.  
  1567. static void
  1568. print_table_data_html(MYSQL_RES *result)
  1569. {
  1570.   MYSQL_ROW   cur;
  1571.   MYSQL_FIELD *field;
  1572.  
  1573.   mysql_field_seek(result,0);
  1574.   (void) tee_fputs("<TABLE BORDER=1><TR>", PAGER);
  1575.   if (!skip_column_names)
  1576.   {
  1577.     while((field = mysql_fetch_field(result)))
  1578.     {
  1579.       tee_fprintf(PAGER, "<TH>%s</TH>", (field->name ? 
  1580.                      (field->name[0] ? field->name : 
  1581.                       "   ") : "NULL"));
  1582.     }
  1583.     (void) tee_fputs("</TR>", PAGER);
  1584.   }
  1585.   while ((cur = mysql_fetch_row(result)))
  1586.   {
  1587.     (void) tee_fputs("<TR>", PAGER);
  1588.     for (uint i=0; i < mysql_num_fields(result); i++)
  1589.     {
  1590.       ulong *lengths=mysql_fetch_lengths(result);
  1591.       (void) tee_fputs("<TD>", PAGER);
  1592.       safe_put_field(cur[i],lengths[i]);
  1593.       (void) tee_fputs("</TD>", PAGER);
  1594.     }
  1595.     (void) tee_fputs("</TR>", PAGER);
  1596.   }
  1597.   (void) tee_fputs("</TABLE>", PAGER);
  1598. }
  1599.  
  1600.  
  1601.  
  1602. static void
  1603. print_table_data_vertically(MYSQL_RES *result)
  1604. {
  1605.   MYSQL_ROW    cur;
  1606.   uint        max_length=0;
  1607.   MYSQL_FIELD    *field;
  1608.  
  1609.   while ((field = mysql_fetch_field(result)))
  1610.   {
  1611.     uint length=(uint) strlen(field->name);
  1612.     if (length > max_length)
  1613.       max_length= length;
  1614.     field->max_length=length;
  1615.   }
  1616.  
  1617.   mysql_field_seek(result,0);
  1618.   for (uint row_count=1; (cur= mysql_fetch_row(result)); row_count++)
  1619.   {
  1620.     mysql_field_seek(result,0);
  1621.     tee_fprintf(PAGER, 
  1622.         "*************************** %d. row ***************************\n", row_count);
  1623.     for (uint off=0; off < mysql_num_fields(result); off++)
  1624.     {
  1625.       field= mysql_fetch_field(result);
  1626.       tee_fprintf(PAGER, "%*s: ",(int) max_length,field->name);
  1627.       tee_fprintf(PAGER, "%s\n",cur[off] ? (char*) cur[off] : "NULL");
  1628.     }
  1629.   }
  1630. }
  1631.  
  1632.  
  1633. static void
  1634. safe_put_field(const char *pos,ulong length)
  1635. {
  1636.   if (!pos)
  1637.     tee_fputs("NULL", PAGER);
  1638.   else
  1639.   {
  1640.     if (opt_raw_data)
  1641.       tee_fputs(pos, PAGER);
  1642.     else for (const char *end=pos+length ; pos != end ; pos++)
  1643.     {
  1644. #ifdef USE_MB
  1645.       int l;
  1646.       if (use_mb(default_charset_info) &&
  1647.           (l = my_ismbchar(default_charset_info, pos, end))) {
  1648.       while (l--)
  1649.         tee_putc(*pos++, PAGER);
  1650.       pos--;
  1651.       continue;
  1652.       }
  1653. #endif
  1654.       if (!*pos)
  1655.     tee_fputs("\\0", PAGER); // This makes everything hard
  1656.       else if (*pos == '\t')
  1657.     tee_fputs("\\t", PAGER); // This would destroy tab format
  1658.       else if (*pos == '\n')
  1659.     tee_fputs("\\n", PAGER); // This too
  1660.       else if (*pos == '\\')
  1661.     tee_fputs("\\\\", PAGER);
  1662.       else
  1663.     tee_putc(*pos, PAGER);
  1664.     }
  1665.   }
  1666. }
  1667.  
  1668.  
  1669. static void
  1670. print_tab_data(MYSQL_RES *result)
  1671. {
  1672.   MYSQL_ROW    cur;
  1673.   MYSQL_FIELD    *field;
  1674.   ulong        *lengths;
  1675.  
  1676.   if (opt_silent < 2 && !skip_column_names)
  1677.   {
  1678.     int first=0;
  1679.     while ((field = mysql_fetch_field(result)))
  1680.     {
  1681.       if (first++)
  1682.     (void) tee_fputs("\t", PAGER);
  1683.       (void) tee_fputs(field->name, PAGER);
  1684.     }
  1685.     (void) tee_fputs("\n", PAGER);
  1686.   }
  1687.   while ((cur = mysql_fetch_row(result)))
  1688.   {
  1689.     lengths=mysql_fetch_lengths(result);
  1690.     safe_put_field(cur[0],lengths[0]);
  1691.     for (uint off=1 ; off < mysql_num_fields(result); off++)
  1692.     {
  1693.       (void) tee_fputs("\t", PAGER);
  1694.       safe_put_field(cur[off],lengths[off]);
  1695.     }
  1696.     (void) tee_fputs("\n", PAGER);
  1697.   }
  1698. }
  1699.  
  1700. static int
  1701. com_tee(String *buffer, char *line __attribute__((unused)))
  1702. {
  1703.   char file_name[FN_REFLEN], *end, *param;
  1704.  
  1705.   if (status.batch)
  1706.     return 0;
  1707.   while (isspace(*line))
  1708.     line++;
  1709.   if (!(param = strchr(line, ' '))) // if outfile wasn't given, use the default
  1710.   {
  1711.     if (!strlen(outfile))
  1712.     {
  1713.       printf("No previous outfile available, you must give the filename!\n");
  1714.       opt_outfile=0;
  1715.       return 0;
  1716.     }
  1717.   }
  1718.   else
  1719.   {
  1720.     while (isspace(*param))
  1721.       param++;
  1722.     end=strmake(file_name, param, sizeof(file_name)-1);
  1723.     while (end > file_name && (isspace(end[-1]) || iscntrl(end[-1])))
  1724.       end--;
  1725.     end[0]=0;
  1726.     strmov(outfile, file_name);
  1727.   }
  1728.   if (!strlen(outfile))
  1729.   {
  1730.     printf("No outfile specified!\n");
  1731.     return 0;
  1732.   }
  1733.   if (!opt_outfile)
  1734.   {
  1735.     init_tee();
  1736.     opt_outfile=1;
  1737.   }
  1738.   tee_fprintf(stdout, "Logging to file '%s'\n", outfile);
  1739.   return 0;
  1740. }
  1741.  
  1742. static int
  1743. com_notee(String *buffer __attribute__((unused)),
  1744.       char *line __attribute__((unused)))
  1745. {
  1746.   if (opt_outfile)
  1747.     end_tee();
  1748.   opt_outfile=0;
  1749.   tee_fprintf(stdout, "Outfile disabled.\n");
  1750.   return 0;
  1751. }
  1752.  
  1753. /*
  1754. ** Sorry, this command is not available in Windows.
  1755. */
  1756.  
  1757. #ifndef __WIN__
  1758. static int
  1759. com_pager(String *buffer, char *line __attribute__((unused)))
  1760. {
  1761.   char pager_name[FN_REFLEN], *end, *param;
  1762.  
  1763.   if (status.batch)
  1764.     return 0;
  1765.   /* Skip space from file name */
  1766.   while (isspace(*line))
  1767.     line++;
  1768.   if (!(param = strchr(line, ' '))) // if pager was not given, use the default
  1769.   {
  1770.     if (!strlen(default_pager))
  1771.     {
  1772.       tee_fprintf(stdout, "Default pager wasn't set, using stdout.\n");
  1773.       opt_nopager=1;
  1774.       strmov(pager, "stdout");
  1775.       PAGER= stdout;
  1776.       return 0;
  1777.     }
  1778.     strmov(pager, default_pager);
  1779.   }
  1780.   else
  1781.   {
  1782.     while (isspace(*param))
  1783.       param++;
  1784.     end=strmake(pager_name, param, sizeof(pager_name)-1);
  1785.     while (end > pager_name && (isspace(end[-1]) || iscntrl(end[-1])))
  1786.       end--;
  1787.     end[0]=0;
  1788.     strmov(pager, pager_name);
  1789.   }
  1790.   opt_nopager=0;
  1791.   tee_fprintf(stdout, "PAGER set to %s\n", pager);
  1792.   return 0;
  1793. }
  1794.  
  1795.  
  1796. static int
  1797. com_nopager(String *buffer __attribute__((unused)),
  1798.         char *line __attribute__((unused)))
  1799. {
  1800.   strmov(pager, "stdout");
  1801.   opt_nopager=1;
  1802.   tee_fprintf(stdout, "PAGER set to stdout\n");
  1803.   return 0;
  1804. }
  1805. #endif
  1806.  
  1807.  
  1808. /*
  1809. ** Sorry, you can't send the result to an editor in Win32
  1810. */
  1811.  
  1812. #ifndef __WIN__
  1813. static int
  1814. com_edit(String *buffer,char *line __attribute__((unused)))
  1815. {
  1816.   char    filename[FN_REFLEN],buff[160];
  1817.   int    fd,tmp;
  1818.   const char *editor;
  1819.  
  1820.   if ((fd=create_temp_file(filename,NullS,"sql", O_CREAT | O_WRONLY,
  1821.                MYF(MY_WME))) < 0)
  1822.     goto err;
  1823.   if (buffer->is_empty() && !old_buffer.is_empty())
  1824.     (void) my_write(fd,(byte*) old_buffer.ptr(),old_buffer.length(),
  1825.             MYF(MY_WME));
  1826.   else
  1827.     (void) my_write(fd,(byte*) buffer->ptr(),buffer->length(),MYF(MY_WME));
  1828.   (void) my_close(fd,MYF(0));
  1829.  
  1830.   if (!(editor = (char *)getenv("EDITOR")) &&
  1831.       !(editor = (char *)getenv("VISUAL")))
  1832.     editor = "vi";
  1833.   strxmov(buff,editor," ",filename,NullS);
  1834.   (void) system(buff);
  1835.  
  1836.   MY_STAT stat_arg;
  1837.   if (!my_stat(filename,&stat_arg,MYF(MY_WME)))
  1838.     goto err;
  1839.   if ((fd = my_open(filename,O_RDONLY, MYF(MY_WME))) < 0)
  1840.     goto err;
  1841.   (void) buffer->alloc((uint) stat_arg.st_size);
  1842.   if ((tmp=read(fd,(char*) buffer->ptr(),buffer->alloced_length())) >= 0L)
  1843.     buffer->length((uint) tmp);
  1844.   else
  1845.     buffer->length(0);
  1846.   (void) my_close(fd,MYF(0));
  1847.   (void) my_delete(filename,MYF(MY_WME));
  1848. err:
  1849.   return 0;
  1850. }
  1851. #endif
  1852.  
  1853.  
  1854. /* If arg is given, exit without errors. This happens on command 'quit' */
  1855.  
  1856. static int
  1857. com_quit(String *buffer __attribute__((unused)),
  1858.      char *line __attribute__((unused)))
  1859. {
  1860.   status.exit_status=0;
  1861.   return 1;
  1862. }
  1863.  
  1864. static int
  1865. com_rehash(String *buffer __attribute__((unused)),
  1866.      char *line __attribute__((unused)))
  1867. {
  1868. #ifdef HAVE_READLINE
  1869.   build_completion_hash(0,0);
  1870. #endif
  1871.   return 0;
  1872. }
  1873.  
  1874. static int
  1875. com_print(String *buffer,char *line __attribute__((unused)))
  1876. {
  1877.   tee_puts("--------------", stdout);
  1878.   (void) tee_fputs(buffer->c_ptr(), stdout);
  1879.   if (!buffer->length() || (*buffer)[buffer->length()-1] != '\n')
  1880.     tee_putc('\n', stdout);
  1881.   tee_puts("--------------\n", stdout);
  1882.   return 0;                    /* If empty buffer */
  1883. }
  1884.  
  1885.     /* ARGSUSED */
  1886. static int
  1887. com_connect(String *buffer, char *line)
  1888. {
  1889.   char *tmp,buff[256];
  1890.   bool save_rehash=no_rehash;
  1891.   int error;
  1892.  
  1893.   if (buffer)
  1894.   {
  1895.     while (isspace(*line))
  1896.       line++;
  1897.     strnmov(buff,line,sizeof(buff)-1);        // Don't destroy history
  1898.     if (buff[0] == '\\')            // Short command
  1899.       buff[1]=' ';
  1900.     tmp=(char *) strtok(buff," \t");        // Skip connect command
  1901.     if (tmp && (tmp=(char *) strtok(NullS," \t;")))
  1902.     {
  1903.       my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
  1904.       current_db=my_strdup(tmp,MYF(MY_WME));
  1905.       if ((tmp=(char *) strtok(NullS," \t;")))
  1906.       {
  1907.     my_free(current_host,MYF(MY_ALLOW_ZERO_PTR));
  1908.     current_host=my_strdup(tmp,MYF(MY_WME));
  1909.       }
  1910.     }
  1911.     else
  1912.       no_rehash=1;                // Quick re-connect
  1913.     buffer->length(0);                // command used
  1914.   }
  1915.   else
  1916.     no_rehash=1;
  1917.   error=sql_connect(current_host,current_db,current_user,opt_password,0);
  1918.   no_rehash=save_rehash;
  1919.  
  1920.   if (connected)
  1921.   {
  1922.     sprintf(buff,"Connection id:    %ld",mysql_thread_id(&mysql));
  1923.     put_info(buff,INFO_INFO);
  1924.     sprintf(buff,"Current database: %s\n",
  1925.         current_db ? current_db : "*** NONE ***");
  1926.     put_info(buff,INFO_INFO);
  1927.   }
  1928.   return error;
  1929. }
  1930.  
  1931.  
  1932. static int com_source(String *buffer, char *line)
  1933. {
  1934.   char source_name[FN_REFLEN], *end, *param;
  1935.   LINE_BUFFER *line_buff;
  1936.   int error;
  1937.   STATUS old_status;
  1938.   FILE *sql_file;
  1939.  
  1940.   /* Skip space from file name */
  1941.   while (isspace(*line))
  1942.     line++;
  1943.   if (!(param = strchr(line, ' ')))        // Skip command name
  1944.     return put_info("Usage: \\. <filename> | source <filename>", 
  1945.             INFO_ERROR, 0);
  1946.   while (isspace(*param))
  1947.     param++;
  1948.   end=strmake(source_name,param,sizeof(source_name)-1);
  1949.   while (end > source_name && (isspace(end[-1]) || iscntrl(end[-1])))
  1950.     end--;
  1951.   end[0]=0;
  1952.   unpack_filename(source_name,source_name);
  1953.   /* open file name */
  1954.   if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0))))
  1955.   {
  1956.     char buff[FN_REFLEN+60];
  1957.     sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno);
  1958.     return put_info(buff, INFO_ERROR, 0);
  1959.   }
  1960.  
  1961.   if (!(line_buff=batch_readline_init(max_allowed_packet+512,sql_file)))
  1962.   {
  1963.     my_fclose(sql_file,MYF(0));
  1964.     return put_info("Can't initialize batch_readline", INFO_ERROR, 0);
  1965.   }
  1966.  
  1967.   /* Save old status */
  1968.   old_status=status;
  1969.   bfill((char*) &status,sizeof(status),(char) 0);
  1970.  
  1971.   status.batch=old_status.batch;        // Run in batch mode
  1972.   status.line_buff=line_buff;
  1973.   status.file_name=source_name;
  1974.   glob_buffer.length(0);            // Empty command buffer
  1975.   error=read_lines(0);                // Read lines from file
  1976.   status=old_status;                // Continue as before
  1977.   my_fclose(sql_file,MYF(0));
  1978.   batch_readline_end(line_buff);
  1979.   return error;
  1980. }
  1981.  
  1982.  
  1983.     /* ARGSUSED */
  1984. static int
  1985. com_use(String *buffer __attribute__((unused)), char *line)
  1986. {
  1987.   char *tmp;
  1988.   char buff[256];
  1989.  
  1990.   while (isspace(*line))
  1991.     line++;
  1992.   strnmov(buff,line,sizeof(buff)-1);        // Don't destroy history
  1993.   if (buff[0] == '\\')                // Short command
  1994.     buff[1]=' ';
  1995.   tmp=(char *) strtok(buff," \t;");        // Skip connect command
  1996.   if (!tmp || !(tmp=(char *) strtok(NullS," \t;")))
  1997.   {
  1998.     put_info("USE must be followed by a database name",INFO_ERROR);
  1999.     return 0;
  2000.   }
  2001.   if (!current_db || cmp_database(current_db,tmp))
  2002.   {
  2003.     if (one_database)
  2004.       skip_updates=1;
  2005.     else
  2006.     {
  2007.       /*
  2008.     reconnect once if connection is down or if connection was found to
  2009.     be down during query
  2010.       */
  2011.       if (!connected && reconnect())
  2012.     return status.batch ? 1 : -1;            // Fatal error
  2013.       if (mysql_select_db(&mysql,tmp))
  2014.       {
  2015.     if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR)
  2016.       return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  2017.  
  2018.     if (reconnect())
  2019.       return status.batch ? 1 : -1;            // Fatal error
  2020.     if (mysql_select_db(&mysql,tmp))
  2021.       return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  2022.       }
  2023. #ifdef HAVE_READLINE
  2024.       build_completion_hash(no_rehash,1);
  2025. #endif
  2026.       my_free(current_db,MYF(MY_ALLOW_ZERO_PTR));
  2027.       current_db=my_strdup(tmp,MYF(MY_WME));
  2028.     }
  2029.   }
  2030.   else
  2031.     skip_updates=0;
  2032.   put_info("Database changed",INFO_INFO);
  2033.   return 0;
  2034. }
  2035.  
  2036.  
  2037. static int
  2038. sql_real_connect(char *host,char *database,char *user,char *password,
  2039.          uint silent)
  2040. {
  2041.   if (connected)
  2042.   {                    /* if old is open, close it first */
  2043.     mysql_close(&mysql);
  2044.     connected= 0;
  2045.   }
  2046.   mysql_init(&mysql);
  2047.   if (opt_connect_timeout)
  2048.     mysql_options(&mysql,MYSQL_OPT_CONNECT_TIMEOUT,
  2049.           (char*) &opt_connect_timeout);
  2050.   if (opt_compress)
  2051.     mysql_options(&mysql,MYSQL_OPT_COMPRESS,NullS);
  2052. #ifdef HAVE_OPENSSL
  2053.   if (opt_use_ssl)
  2054.     mysql_ssl_set(&mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
  2055.           opt_ssl_capath);
  2056. #endif
  2057.   if (safe_updates)
  2058.   {
  2059.     char init_command[100];
  2060.     sprintf(init_command,
  2061.         "SET SQL_SAFE_UPDATES=1,SQL_SELECT_LIMIT=%lu,SQL_MAX_JOIN_SIZE=%lu",
  2062.         select_limit,max_join_size);
  2063.     mysql_options(&mysql, MYSQL_INIT_COMMAND, init_command);
  2064.   }
  2065.   if (!mysql_real_connect(&mysql,host,user,password,
  2066.               database,opt_mysql_port,opt_mysql_unix_port,
  2067.               connect_flag))
  2068.   {
  2069.     if (!silent ||
  2070.     (mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
  2071.      mysql_errno(&mysql) != CR_CONNECTION_ERROR))
  2072.     {
  2073.       put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
  2074.       (void) fflush(stdout);
  2075.       return ignore_errors ? -1 : 1;        // Abort
  2076.     }
  2077.     return -1;                    // Retryable
  2078.   }
  2079.   connected=1;
  2080.   mysql.reconnect=info_flag ? 1 : 0; // We want to know if this happens
  2081. #ifdef HAVE_READLINE
  2082.   build_completion_hash(no_rehash,1);
  2083. #endif
  2084.   return 0;
  2085. }
  2086.  
  2087.  
  2088. static int
  2089. sql_connect(char *host,char *database,char *user,char *password,uint silent)
  2090. {
  2091.   bool message=0;
  2092.   uint count=0;
  2093.   int error;
  2094.   for (;;)
  2095.   {
  2096.     if ((error=sql_real_connect(host,database,user,password,wait_flag)) >= 0)
  2097.     {
  2098.       if (count)
  2099.       {
  2100.     tee_fputs("\n", stderr);
  2101.     (void) fflush(stderr);
  2102.       }
  2103.       return error;
  2104.     }
  2105.     if (!wait_flag)
  2106.       return ignore_errors ? -1 : 1;
  2107.     if (!message && !silent)
  2108.     {
  2109.       message=1;
  2110.       tee_fputs("Waiting",stderr); (void) fflush(stderr);
  2111.     }
  2112.     (void) sleep(5);
  2113.     if (!silent)
  2114.     {
  2115.       putc('.',stderr); (void) fflush(stderr);
  2116.       count++;
  2117.     }
  2118.   }
  2119. }
  2120.  
  2121.  
  2122.  
  2123. static int
  2124. com_status(String *buffer __attribute__((unused)),
  2125.        char *line __attribute__((unused)))
  2126. {
  2127.   char *status;
  2128.   tee_puts("--------------", stdout);
  2129.   usage(1);                    /* Print version */
  2130.   if (connected)
  2131.   {
  2132.     MYSQL_RES *result;
  2133.     LINT_INIT(result);
  2134.     tee_fprintf(stdout, "\nConnection id:\t\t%ld\n",mysql_thread_id(&mysql));
  2135.     if (!mysql_query(&mysql,"select DATABASE(),USER()") &&
  2136.     (result=mysql_use_result(&mysql)))
  2137.     {
  2138.       MYSQL_ROW cur=mysql_fetch_row(result);
  2139.       tee_fprintf(stdout, "Current database:\t%s\n",cur[0]);
  2140.       tee_fprintf(stdout, "Current user:\t\t%s\n",cur[1]);
  2141.       (void) mysql_fetch_row(result);        // Read eof
  2142.     }
  2143.   }
  2144.   else
  2145.   {
  2146.     vidattr(A_BOLD);
  2147.     tee_fprintf(stdout, "\nNo connection\n");
  2148.     vidattr(A_NORMAL);
  2149.     return 0;
  2150.   }
  2151.   if (skip_updates)
  2152.   {
  2153.     vidattr(A_BOLD);
  2154.     tee_fprintf(stdout, "\nAll updates ignored to this database\n");
  2155.     vidattr(A_NORMAL);
  2156.   }
  2157. #ifndef __WIN__
  2158.   tee_fprintf(stdout, "Current pager:\t\t%s\n", pager);
  2159.   if (opt_outfile)
  2160.     tee_fprintf(stdout, "Using outfile:\t\tYes: '%s'\n", outfile);
  2161.   else
  2162.     printf("Using outfile:\t\tNo\n");
  2163. #endif
  2164.   tee_fprintf(stdout, "Server version:\t\t%s\n", mysql_get_server_info(&mysql));
  2165.   tee_fprintf(stdout, "Protocol version:\t%d\n", mysql_get_proto_info(&mysql));
  2166.   tee_fprintf(stdout, "Connection:\t\t%s\n", mysql_get_host_info(&mysql));
  2167.   tee_fprintf(stdout, "Language:\t\t%s\n", mysql.charset->name);
  2168.   if (strstr(mysql_get_host_info(&mysql),"TCP/IP") || ! mysql.unix_socket)
  2169.     tee_fprintf(stdout, "TCP port:\t\t%d\n", mysql.port);
  2170.   else
  2171.     tee_fprintf(stdout, "UNIX socket:\t\t%s\n", mysql.unix_socket);
  2172.   if ((status=mysql_stat(&mysql)) && !mysql_error(&mysql)[0])
  2173.   {
  2174.     char *pos,buff[40];
  2175.     ulong sec;
  2176.     pos=strchr(status,' ');
  2177.     *pos++=0;
  2178.     tee_fprintf(stdout, "%s\t\t\t", status);    /* print label */
  2179.     if ((status=str2int(pos,10,0,LONG_MAX,(long*) &sec)))
  2180.     {
  2181.       nice_time((double) sec,buff,0);
  2182.       tee_puts(buff, stdout);            /* print nice time */
  2183.       while (*status == ' ') status++;        /* to next info */
  2184.     }
  2185.     if (status)
  2186.     {
  2187.       tee_putc('\n', stdout);
  2188.       tee_puts(status, stdout);
  2189.     }
  2190.   }
  2191.   if (safe_updates)
  2192.   {
  2193.     vidattr(A_BOLD);
  2194.     tee_fprintf(stdout, "\nNote that we are running in safe_update_mode:\n");
  2195.     vidattr(A_NORMAL);
  2196.     tee_fprintf(stdout, "\
  2197. UPDATE and DELETE that doesn't use a key in the WHERE clause are not allowed\n\
  2198. (One can force UPDATE/DELETE by adding LIMIT # at the end of the command)\n\
  2199. SELECT has an automatic 'LIMIT %lu' if LIMIT is not used\n\
  2200. Max number of examined row combination in a join is set to: %lu\n\n",
  2201. select_limit,max_join_size);
  2202.   }
  2203.   tee_puts("--------------\n", stdout);
  2204.   return 0;
  2205. }
  2206.  
  2207.  
  2208. static int
  2209. put_info(const char *str,INFO_TYPE info_type,uint error)
  2210. {
  2211.   static int inited=0;
  2212.   
  2213.   if (status.batch)
  2214.   {
  2215.     if (info_type == INFO_ERROR)
  2216.     {
  2217.       (void) fflush(stdout);
  2218.       fprintf(stderr,"ERROR");
  2219.       if (error)
  2220.     (void) fprintf(stderr," %d",error);
  2221.       if (status.query_start_line && ! skip_line_numbers)
  2222.       {
  2223.     (void) fprintf(stderr," at line %lu",status.query_start_line);
  2224.     if (status.file_name)
  2225.       (void) fprintf(stderr," in file: '%s'", status.file_name);
  2226.       }
  2227.       (void) fprintf(stderr,": %s\n",str);
  2228.       (void) fflush(stderr);
  2229.       if (!ignore_errors)
  2230.     return 1;
  2231.     }
  2232.     else if (info_type == INFO_RESULT && verbose > 1)
  2233.       tee_puts(str, stdout);
  2234.     if (unbuffered)
  2235.       fflush(stdout);
  2236.     return info_type == INFO_ERROR ? -1 : 0;
  2237.   }
  2238.   if (!opt_silent || info_type == INFO_ERROR)
  2239.   {
  2240.     if (!inited)
  2241.     {
  2242.       inited=1;
  2243. #ifdef HAVE_SETUPTERM
  2244.       (void) setupterm((char *)0, 1, (int *) 0);
  2245. #endif
  2246.     }
  2247.     if (info_type == INFO_ERROR)
  2248.     {
  2249.       putchar('\007');                /* This should make a bell */
  2250.       vidattr(A_STANDOUT);
  2251.       if (error)
  2252.         (void) tee_fprintf(stderr, "ERROR %d: ", error);
  2253.       else
  2254.         tee_puts("ERROR: ", stdout);
  2255.     }
  2256.     else
  2257.       vidattr(A_BOLD);
  2258.     (void) tee_puts(str, stdout);
  2259.     vidattr(A_NORMAL);
  2260.   }
  2261.   if (unbuffered)
  2262.     fflush(stdout);
  2263.   return info_type == INFO_ERROR ? -1 : 0;
  2264. }
  2265.  
  2266.  
  2267. static void remove_cntrl(String &buffer)
  2268. {
  2269.   char *start,*end;
  2270.   end=(start=(char*) buffer.ptr())+buffer.length();
  2271.   while (start < end && !isgraph(end[-1]))
  2272.     end--;
  2273.   buffer.length((uint) (end-start));
  2274. }
  2275.  
  2276.  
  2277. void tee_fprintf(FILE *file, const char *fmt, ...)
  2278. {
  2279.   va_list args;
  2280.  
  2281.   va_start(args, fmt);
  2282.   (void) vfprintf(file, fmt, args);
  2283.   if (opt_outfile)
  2284.     (void) vfprintf(OUTFILE, fmt, args);
  2285.   va_end(args);
  2286. }
  2287.  
  2288.  
  2289. void tee_fputs(const char *s, FILE *file)
  2290. {
  2291.   fputs(s, file);
  2292.   if (opt_outfile)
  2293.     fputs(s, OUTFILE);
  2294. }
  2295.  
  2296.  
  2297. void tee_puts(const char *s, FILE *file)
  2298. {
  2299.   fputs(s, file);
  2300.   fputs("\n", file);
  2301.   if (opt_outfile)
  2302.   {
  2303.     fputs(s, OUTFILE);
  2304.     fputs("\n", OUTFILE);
  2305.   }
  2306. }
  2307.  
  2308. void tee_putc(int c, FILE *file)
  2309. {
  2310.   putc(c, file);
  2311.   if (opt_outfile)
  2312.     putc(c, OUTFILE);
  2313. }
  2314.  
  2315. #ifdef __WIN__
  2316. #include <time.h>
  2317. #else
  2318. #include <sys/times.h>
  2319. #undef CLOCKS_PER_SEC
  2320. #define CLOCKS_PER_SEC (sysconf(_SC_CLK_TCK))
  2321. #endif
  2322.  
  2323. static ulong start_timer(void)
  2324. {
  2325. #ifdef __WIN__
  2326.  return clock();
  2327. #else
  2328.   struct tms tms_tmp;
  2329.   return times(&tms_tmp);
  2330. #endif
  2331. }
  2332.  
  2333.  
  2334. static void nice_time(double sec,char *buff,bool part_second)
  2335. {
  2336.   ulong tmp;
  2337.   if (sec >= 3600.0*24)
  2338.   {
  2339.     tmp=(ulong) floor(sec/(3600.0*24));
  2340.     sec-=3600.0*24*tmp;
  2341.     buff=int2str((long) tmp,buff,10);
  2342.     buff=strmov(buff,tmp > 1 ? " days " : " day ");
  2343.   }
  2344.   if (sec >= 3600.0)
  2345.   {
  2346.     tmp=(ulong) floor(sec/3600.0);
  2347.     sec-=3600.0*tmp;
  2348.     buff=int2str((long) tmp,buff,10);
  2349.     buff=strmov(buff,tmp > 1 ? " hours " : " hour ");
  2350.   }
  2351.   if (sec >= 60.0)
  2352.   {
  2353.     tmp=(ulong) floor(sec/60.0);
  2354.     sec-=60.0*tmp;
  2355.     buff=int2str((long) tmp,buff,10);
  2356.     buff=strmov(buff," min ");
  2357.   }
  2358.   if (part_second)
  2359.     sprintf(buff,"%.2f sec",sec);
  2360.   else
  2361.     sprintf(buff,"%d sec",(int) sec);
  2362. }
  2363.  
  2364.  
  2365. static void end_timer(ulong start_time,char *buff)
  2366. {
  2367.   nice_time((double) (start_timer() - start_time) /
  2368.         CLOCKS_PER_SEC,buff,1);
  2369. }
  2370.  
  2371.  
  2372. static void mysql_end_timer(ulong start_time,char *buff)
  2373. {
  2374.   buff[0]=' ';
  2375.   buff[1]='(';
  2376.   end_timer(start_time,buff+2);
  2377.   strmov(strend(buff),")");
  2378. }
  2379.  
  2380. /* Keep sql_string library happy */
  2381.  
  2382. gptr sql_alloc(unsigned int Size)
  2383. {
  2384.   return my_malloc(Size,MYF(MY_WME));
  2385. }
  2386.  
  2387. void sql_element_free(void *ptr)
  2388. {
  2389.   my_free((gptr) ptr,MYF(0));
  2390. }
  2391.