/* Copyright (C) 2000 MySQL AB

   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.

   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.

   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 */

#include "client_priv.h"
#include <my_dir.h>

static my_bool opt_force= 0, opt_verbose= 0, tty_password= 0;
static char *user= (char*) "root", *basedir= 0, *datadir= 0, *opt_password= 0;
static my_bool upgrade_defaults_created= 0;
static my_string opt_mysql_port, opt_mysql_unix_port= 0;
static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace";
static my_bool info_flag= 0;

static struct my_option my_long_options[]=
{
  {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
   NO_ARG, 0, 0, 0, 0, 0, 0},
  {"basedir", 'b', "Specifies the directory where MySQL is installed",
   (gptr*) &basedir,
   (gptr*) &basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"datadir", 'd', "Specifies the data directory", (gptr*) &datadir,
   (gptr*) &datadir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef DBUG_OFF
  {"debug", '#', "This is a non-debug version. Catch this and exit",
   0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
#else
  {"debug", '#', "Output debug log", (gptr *) & default_dbug_option,
   (gptr *) & default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
#endif
  {"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},
  {"force", 'f', "Continue even if we get an sql-error.",
   (gptr*) &opt_force, (gptr*) &opt_force, 0, GET_BOOL, NO_ARG, 0, 0,
   0, 0, 0, 0},
  {"password", 'p',
   "Password to use when connecting to server. If password is not given it's solicited on the tty.",
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
  {"port", 'P', "Port number to use for connection.", (gptr*) &opt_mysql_port,
   (gptr*) &opt_mysql_port, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0,
   0},
  {"protocol", OPT_MYSQL_PROTOCOL,
   "The protocol of connection (tcp,socket,pipe,memory).",
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"socket", 'S', "Socket file to use for connection.",
   (gptr*) &opt_mysql_unix_port, (gptr*) &opt_mysql_unix_port, 0, GET_STR,
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"user", 'u', "User for login if not current user.", (gptr*) &user,
   (gptr*) &user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"verbose", 'v', "Display more output about the process", (gptr*) &opt_verbose,
    (gptr *) &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
static const char *load_default_groups[]=
{
  "mysql_upgrade", "client", 0
};

#include <help_end.h>

static my_bool
get_one_option(int optid, const struct my_option *opt __attribute__ ((unused)),
               char *argument)
{
  switch (optid) {
  case '?':
    puts
      ("MySQL utility script to upgrade database to the current server version");
    puts("");
    my_print_help(my_long_options);
    exit(0);
  case '#':
    DBUG_PUSH(argument ? argument : default_dbug_option);
    break;
  case 'f':
    opt_force= TRUE;
    break;
  case 'p':
    tty_password= 1;
    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;                            /* Cut length of argument */
      tty_password= 0;
    }
    break;
  default:;
  };
  return 0;
}


/* buffer should be not smaller than FN_REFLEN */
static my_bool test_file_exists_res(const char *dir, const char *fname,
                                    char *buffer, char **buf_end)
{
  MY_STAT stat_info;

  *buf_end= strxnmov(buffer, FN_REFLEN-1, dir, "/", fname, NullS);
  unpack_filename(buffer, buffer);
  return my_stat(buffer, &stat_info, MYF(0)) != 0;
}


static my_bool test_file_exists(const char *dir, const char *fname)
{
  char path[FN_REFLEN];
  char *path_end;
  return test_file_exists_res(dir, fname, path, &path_end);
}


static int create_check_file(const char *path)
{
  File check_file= my_open(path, O_CREAT | O_WRONLY, MYF(MY_FAE | MY_WME));
  int error;

  if (check_file < 0)
    return 1;

  error= my_write(check_file, VERSION, strlen(VERSION), MYF(MY_WME | MY_FNABP));
  error= my_close(check_file, MYF(MY_FAE | MY_WME)) || error;
  return error;
}


static int create_defaults_file(const char *path, const char *our_defaults_path)
{
  uint b_read;
  File our_defaults_file, defaults_file;
  char buffer[512];
  char *buffer_end;
  int error;

  /* check if the defaults file is needed at all */
  if (!opt_password)
    return 0;

  defaults_file= my_open(path, O_BINARY | O_CREAT | O_WRONLY,
                         MYF(MY_FAE | MY_WME));

  if (defaults_file < 0)
    return 1;
  upgrade_defaults_created= 1;
  if (our_defaults_path)
  {
    our_defaults_file= my_open(our_defaults_path, O_RDONLY,
                               MYF(MY_FAE | MY_WME));
    if (our_defaults_file < 0)
      return 1;
    do
    {
      if (((b_read= my_read(our_defaults_file, buffer,
                            sizeof(buffer), MYF(MY_WME))) == MY_FILE_ERROR) ||
          my_write(defaults_file, buffer, b_read, MYF(MY_FNABP | MY_WME)))
      {
        error= 1;
        goto close_return;
      }
    } while (b_read == sizeof(buffer));
  }
  buffer_end= strnmov(buffer, "\n[client]", sizeof(buffer));
  if (opt_password)
    buffer_end= strxnmov(buffer, sizeof(buffer),
                         "\npassword=", opt_password, NullS);
  error= my_write(defaults_file, buffer, (int) (buffer_end - buffer),
                  MYF(MY_WME | MY_FNABP));
close_return:
  return my_close(defaults_file, MYF(MY_WME)) || error;
}


int main(int argc, char **argv)
{
  char bindir[FN_REFLEN];
  char *bindir_end, *buf_end;
  char datadir_buf[FN_REFLEN];
  char mysqlcheck_line[FN_REFLEN], *mysqlcheck_end;
  char check_file_name[FN_REFLEN];
  int check_file;
  char fix_priv_tables_cmd[FN_REFLEN], *fix_cmd_end;
  char script_line[FN_REFLEN];
  int error;
  char *forced_defaults_file;
  char *forced_extra_defaults;
  char *defaults_group_suffix;
  char upgrade_defaults_path[FN_REFLEN], *defaults_to_use= 0;
  char port_socket[100], *port_socket_end;

  MY_INIT(argv[0]);
#ifdef __NETWARE__
  setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
#endif

  load_defaults("my", load_default_groups, &argc, &argv);

  if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
    exit(error);

  if (tty_password)
    opt_password= get_tty_password(NullS);

  /* Check if we want to force the use a specific default file */
  get_defaults_options(argc, argv,
                       &forced_defaults_file, &forced_extra_defaults,
                       &defaults_group_suffix);

  port_socket_end= port_socket;
  if (opt_mysql_port)
    port_socket_end= strxnmov(port_socket, sizeof(port_socket) - 1, " --port=",
                              opt_mysql_port, NullS);
  if (opt_mysql_unix_port)
    port_socket_end= strxnmov(port_socket_end,
                              sizeof(port_socket) -
                                (int)(port_socket_end - port_socket) - 1,
                              " --socket=", opt_mysql_unix_port, NullS);
  *port_socket_end= 0;

  if (basedir)
  {
    bindir_end= strmake(bindir, basedir, sizeof(bindir)-1);
  }
  else
  {
    if (test_file_exists("./share/mysql/english", "errmsg.sys")
        && (test_file_exists("./bin", "mysqld") ||
            test_file_exists("./libexec", "mysqld")))
    {
      getcwd(bindir, sizeof(bindir));
      bindir_end= bindir + strlen(bindir);
    }
    else
    {
      bindir_end= strmake(bindir, DEFAULT_MYSQL_HOME, sizeof(bindir)-1);
    }
  }

  if (!datadir)
  {
    datadir= datadir_buf;
    if (test_file_exists(bindir, "data/mysql"))
    {
      *strxnmov(datadir_buf, sizeof(datadir_buf)-1, bindir, "/data", NullS)= 0;
    }
    else if (test_file_exists(bindir, "var/mysql"))
    {
      *strxnmov(datadir_buf, sizeof(datadir_buf)-1, bindir, "/var", NullS)= 0;
    }
    else
      datadir= (char*) DATADIR;
  }

  strmake(bindir_end, "/bin", sizeof(bindir) - (int) (bindir_end - bindir)-1);

  if (!test_file_exists_res
      (bindir, "mysqlcheck", mysqlcheck_line, &mysqlcheck_end))
  {
    printf("Can't find program '%s'\n", mysqlcheck_line);
    puts("Please restart with --basedir=mysql-install-directory");
    exit(1);
  }

  if (!test_file_exists(datadir, "mysql/user.frm"))
  {
    puts
      ("Can't find data directory. Please restart with --datadir=path-to-data-dir");
    exit(1);
  }

  /* create the modified defaults file to be used by mysqlcheck */
  /* and mysql tools                                            */
  *strxnmov(upgrade_defaults_path, sizeof(upgrade_defaults_path)-1,
           datadir, "/upgrade_defaults", NullS)= 0;
  unpack_filename(upgrade_defaults_path, upgrade_defaults_path);
  if ((error=
       create_defaults_file(upgrade_defaults_path, forced_extra_defaults)))
    goto err_exit;

  defaults_to_use= upgrade_defaults_created ?
    upgrade_defaults_path : forced_extra_defaults;

  if (test_file_exists_res(datadir, "mysql_upgrade_info", check_file_name,
                           &buf_end) && !opt_force)
  {
    char chf_buffer[50];
    int b_read;
    check_file= my_open(check_file_name, O_RDONLY, MYF(0));
    b_read= my_read(check_file, chf_buffer, sizeof(chf_buffer)-1, MYF(0));
    chf_buffer[b_read]= 0;
    my_close(check_file, MYF(0));
    if (!strcmp(chf_buffer, VERSION))
    {
      if (opt_verbose)
        puts("mysql_upgrade already done for this version");
      goto fix_priv_tables;
    }
  }

  if (defaults_to_use)
  {
    mysqlcheck_end= strxnmov(mysqlcheck_end,
                             sizeof(mysqlcheck_line) - (int) (mysqlcheck_end -
                                                              mysqlcheck_line),
                             " --defaults-extra-file=", defaults_to_use,NullS);
  }

  mysqlcheck_end= strxnmov(mysqlcheck_end,
                           sizeof(mysqlcheck_line) -
                             (int) (mysqlcheck_end - mysqlcheck_line - 1),
                           " --check-upgrade --all-databases --auto-repair --user=",
                           user, port_socket, NullS);
  *mysqlcheck_end= 0;

  if (opt_verbose)
    printf("Running %s\n", mysqlcheck_line);
  if ((error= system(mysqlcheck_line)))
  {
    printf("Error executing '%s'\n", mysqlcheck_line);
    goto err_exit;
  }

  if ((error= create_check_file(check_file_name)))
    goto err_exit;

fix_priv_tables:
  if (!test_file_exists_res(bindir, "mysql", fix_priv_tables_cmd, &fix_cmd_end))
  {
    puts("Could not find MySQL command-line client (mysql).");
    puts
      ("Please use --basedir to specify the directory where MySQL is installed.");
    error= 1;
    goto err_exit;
  }

  if (!test_file_exists_res(basedir,
                            "support_files/mysql_fix_privilege_tables.sql",
                            script_line, &buf_end)
      && !test_file_exists_res(basedir, "share/mysql_fix_privilege_tables.sql",
                               script_line, &buf_end)
      && !test_file_exists_res(basedir,
                               "share/mysql/mysql_fix_privilege_tables.sql",
                               script_line, &buf_end)
      && !test_file_exists_res(basedir,
                               "scripts/mysql_fix_privilege_tables.sql",
                               script_line, &buf_end)
      && !test_file_exists_res("/usr/local/mysql/share/mysql",
                               "mysql_fix_privilege_tables.sql", script_line,
                               &buf_end))
  {
    puts("Could not find file mysql_fix_privilege_tables.sql");
    puts
      ("Please use --basedir to specify the directory where MySQL is installed");
    error= 1;
    goto err_exit;
  }

  if (defaults_to_use)
  {
    fix_cmd_end= strxnmov(fix_cmd_end,
                          sizeof(fix_priv_tables_cmd) -
                            (int) (fix_cmd_end - fix_priv_tables_cmd - 1),
                          " --defaults-extra-file=", defaults_to_use, NullS);
  }
  fix_cmd_end= strxnmov(fix_cmd_end,
           sizeof(fix_priv_tables_cmd) - (int) (fix_cmd_end -
                                                fix_priv_tables_cmd),
           " --user=", user, port_socket, " mysql < ", script_line, NullS);
  *fix_cmd_end= 0;

  if ((error= system(fix_priv_tables_cmd)))
  {
    /* Problem is that the 'Duplicate column' error           */
    /* which is not a bug for the script makes 'mysql' return */
    /* an error                                               */
    /* printf("Error executing '%s'\n", fix_priv_tables_cmd); */
  }

err_exit:
  if (upgrade_defaults_created)
    my_delete(upgrade_defaults_path, MYF(0));
  my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
  return error;
}                                               /* main */