diff --git a/.bzrignore b/.bzrignore index 8ea3b361c87..01ef6f2ffef 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1289,6 +1289,7 @@ mysql-test/funcs_1.log mysql-test/funcs_1.tar mysql-test/gmon.out mysql-test/install_test_db +mysql-test/lib/My/SafeProcess/my_safe_process mysql-test/lib/init_db.sql mysql-test/linux_sys_vars.inc mysql-test/load_sysvars.inc @@ -2097,10 +2098,13 @@ sql/.libs/udf_example.lai sql/.libs/udf_example.so.0 sql/.libs/udf_example.so.0.0.0 sql/client.c +sql/Doxyfile sql/f.c sql/gen_lex_hash sql/gmon.out sql/handlerton.cc +sql/html +sql/latex sql/lex_hash.h sql/link_sources sql/max/* diff --git a/BUILD/compile-dist b/BUILD/compile-dist index caee172f196..cf6cefc0969 100755 --- a/BUILD/compile-dist +++ b/BUILD/compile-dist @@ -11,16 +11,33 @@ test -f Makefile && make maintainer-clean path=`dirname $0` . $path/autorun.sh +gmake= +for x in gmake gnumake make; do + if $x --version 2>/dev/null | grep GNU > /dev/null; then + gmake=$x + break; + fi +done + +if [ -z "$gmake" ]; then + # Our build may not depend on GNU make, but I wouldn't count on it + echo "Please install GNU make, and ensure it is in your path as gnumake, gmake, or make" >&2 + exit 2 +fi + # Default to gcc for CC and CXX if test -z "$CXX" ; then + export CXX CXX=gcc # Set some required compile options if test -z "$CXXFLAGS" ; then + export CXXFLAGS CXXFLAGS="-felide-constructors -fno-exceptions -fno-rtti" fi fi if test -z "$CC" ; then + export CC CC=gcc fi @@ -28,36 +45,22 @@ fi # Use ccache, if available if ccache -V > /dev/null 2>&1 then - if echo "$CC" | grep "ccache" > /dev/null + if echo "$CC" | grep -v ccache > /dev/null then - : - else + export CC CC="ccache $CC" fi - if echo "$CXX" | grep "ccache" > /dev/null + if echo "$CXX" | grep -v ccache > /dev/null then - : - else + export CXX CXX="ccache $CXX" fi fi -if test -z "$MAKE" -then - if gmake -v > /dev/null 2>&1 - then - MAKE="gmake" - else - MAKE="make" - fi -fi - -export CC CXX MAKE - # Make sure to enable all features that affect "make dist" # Remember that configure restricts the man pages to the configured features ! ./configure \ --with-embedded-server \ --with-ndbcluster -$MAKE +$gmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 957cfd4c102..1bb50cbade3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,10 @@ IF(CYBOZU) ADD_DEFINITIONS(-DCYBOZU) ENDIF(CYBOZU) +IF(EXTRA_DEBUG) + ADD_DEFINITIONS(-D EXTRA_DEBUG) +ENDIF(EXTRA_DEBUG) + # in some places we use DBUG_OFF SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DDBUG_OFF") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") @@ -254,3 +258,4 @@ IF(WITH_EMBEDDED_SERVER) ADD_SUBDIRECTORY(libmysqld) ADD_SUBDIRECTORY(libmysqld/examples) ENDIF(WITH_EMBEDDED_SERVER) +ADD_SUBDIRECTORY(mysql-test/lib/My/SafeProcess) diff --git a/Makefile.am b/Makefile.am index b1f15e76ab1..0435489456b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (C) 2000-2006 MySQL AB +# Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. # # 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 @@ -121,6 +121,10 @@ test-reprepare: test: test-unit test-ns test-pr +smoke: + cd mysql-test ; \ + @PERL@ ./mysql-test-run.pl --do-test=s + test-full: test test-nr test-ps test-force: @@ -159,6 +163,8 @@ test-bt: @PERL@ ./mysql-test-run.pl --force --comment=partitions --suite=parts -cd mysql-test ; MTR_BUILD_THREAD=auto \ @PERL@ ./mysql-test-run.pl --force --comment=stress --suite=stress + -cd mysql-test ; MTR_BUILD_THREAD=auto \ + @PERL@ ./mysql-test-run.pl --force --comment=jp --suite=jp -if [ -d mysql-test/suite/nist ] ; then \ cd mysql-test ; MTR_BUILD_THREAD=auto \ @PERL@ ./mysql-test-run.pl --comment=nist --force --suite=nist ; \ @@ -175,15 +181,28 @@ test-bt: echo "no program found for 'embedded' tests - skipped testing" ; \ fi -# Re-enable the "jp" suite when bug#28563 is fixed -# -cd mysql-test ; MTR_BUILD_THREAD=auto \ -# @PERL@ ./mysql-test-run.pl --force --comment=jp --suite=jp +test-bt-fast: + -cd mysql-test ; MTR_BUILD_THREAD=auto \ + @PERL@ ./mysql-test-run.pl --comment=ps --force --timer \ + --skip-ndbcluster --ps-protocol --report-features + -if [ -e bin/ndbd -o -e storage/ndb/src/kernel/ndbd ] ; then \ + cd mysql-test ; \ + MTR_BUILD_THREAD=auto \ + @PERL@ ./mysql-test-run.pl --comment=ndb --force --timer \ + --with-ndbcluster-only ; \ + else \ + echo "no program found for 'ndbcluster' tests - skipped testing" ; \ + fi + -cd mysql-test ; MTR_BUILD_THREAD=auto \ + @PERL@ ./mysql-test-run.pl --force --comment=stress --suite=stress test-bt-debug: -cd mysql-test ; MTR_BUILD_THREAD=auto \ @PERL@ ./mysql-test-run.pl --comment=debug --force --timer \ --skip-ndbcluster --skip-rpl --report-features +test-bt-debug-fast: + # Keep these for a while test-pl: test test-full-pl: test-full diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index cbd4a1f306b..e96437d40d0 100755 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -32,8 +32,8 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc sql_string.cc ../mysys/my_conio.c) TARGET_LINK_LIBRARIES(mysql mysqlclient_notls wsock32) -ADD_EXECUTABLE(mysqltest mysqltest.c) -SET_SOURCE_FILES_PROPERTIES(mysqltest.c PROPERTIES COMPILE_FLAGS "-DTHREADS") +ADD_EXECUTABLE(mysqltest mysqltest.cc) +SET_SOURCE_FILES_PROPERTIES(mysqltest.cc PROPERTIES COMPILE_FLAGS "-DTHREADS") TARGET_LINK_LIBRARIES(mysqltest mysqlclient mysys regex wsock32 dbug) ADD_EXECUTABLE(mysqlcheck mysqlcheck.c) diff --git a/client/Makefile.am b/client/Makefile.am index c5c82ec0a42..94db565ba37 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -86,9 +86,9 @@ mysqlslap_LDADD = $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS) \ $(LIBMYSQLCLIENT_LA) \ $(top_builddir)/mysys/libmysys.a -mysqltest_SOURCES= mysqltest.c -mysqltest_CFLAGS= -DTHREAD -UUNDEF_THREADS_HACK -mysqltest_LDADD = $(CXXLDFLAGS) \ +mysqltest_SOURCES= mysqltest.cc +mysqltest_CXXFLAGS= -DTHREAD -UUNDEF_THREADS_HACK +mysqltest_LDADD = $(CXXLDFLAGS) $(CLIENT_THREAD_LIBS) \ @CLIENT_EXTRA_LDFLAGS@ \ $(LIBMYSQLCLIENT_LA) \ $(top_builddir)/mysys/libmysys.a \ diff --git a/client/mysql.cc b/client/mysql.cc index ff53d623d07..65512e695b8 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1535,7 +1535,7 @@ static void usage(int version) if (version) return; printf("\ -Copyright (C) 2000-2008 MySQL AB\n\ +Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.\n\ This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n\ and you are welcome to modify and redistribute it under the GPL license\n"); printf("Usage: %s [OPTIONS] [database]\n", my_progname); @@ -2020,7 +2020,7 @@ static bool add_line(String &buffer,char *line,char *in_string, { if (!preserve_comments) { - // Skip spaces at the beggining of a statement + // Skip spaces at the beginning of a statement if (my_isspace(charset_info,inchar) && (out == line) && buffer.is_empty()) continue; @@ -2044,7 +2044,8 @@ static bool add_line(String &buffer,char *line,char *in_string, } #endif if (!*ml_comment && inchar == '\\' && - !(mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)) + !(*in_string && + (mysql.server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES))) { // Found possbile one character command like \c @@ -2103,37 +2104,6 @@ static bool add_line(String &buffer,char *line,char *in_string, continue; } } - else if (!*ml_comment && !*in_string && - (end_of_line - pos) >= 10 && - !my_strnncoll(charset_info, (uchar*) pos, 10, - (const uchar*) "delimiter ", 10)) - { - // Flush previously accepted characters - if (out != line) - { - buffer.append(line, (uint32) (out - line)); - out= line; - } - - // Flush possible comments in the buffer - if (!buffer.is_empty()) - { - if (com_go(&buffer, 0) > 0) // < 0 is not fatal - DBUG_RETURN(1); - buffer.length(0); - } - - /* - Delimiter wants the get rest of the given line as argument to - allow one to change ';' to ';;' and back - */ - buffer.append(pos); - if (com_delimiter(&buffer, pos) > 0) - DBUG_RETURN(1); - - buffer.length(0); - break; - } else if (!*ml_comment && !*in_string && is_prefix(pos, delimiter)) { // Found a statement. Continue parsing after the delimiter @@ -2196,8 +2166,24 @@ static bool add_line(String &buffer,char *line,char *in_string, // comment to end of line if (preserve_comments) + { + bool started_with_nothing= !buffer.length(); + buffer.append(pos); + /* + A single-line comment by itself gets sent immediately so that + client commands (delimiter, status, etc) will be interpreted on + the next line. + */ + if (started_with_nothing) + { + if (com_go(&buffer, 0) > 0) // < 0 is not fatal + DBUG_RETURN(1); + buffer.length(0); + } + } + break; } else if (!*in_string && inchar == '/' && *(pos+1) == '*' && @@ -2284,8 +2270,10 @@ extern "C" char **new_mysql_completion (const char *text, int start, int end); if not. */ -#if defined(USE_NEW_READLINE_INTERFACE) || defined(USE_LIBEDIT_INTERFACE) +#if defined(USE_NEW_READLINE_INTERFACE) extern "C" char *no_completion(const char*,int) +#elif defined(USE_LIBEDIT_INTERFACE) +extern "C" int no_completion(const char*,int) #else extern "C" char *no_completion() #endif diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 4d2460dc4e7..190bb2383e9 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -620,6 +620,20 @@ static int run_mysqlcheck_upgrade(void) } +static int run_mysqlcheck_fixnames(void) +{ + verbose("Running 'mysqlcheck'..."); + return run_tool(mysqlcheck_path, + NULL, /* Send output from mysqlcheck directly to screen */ + "--no-defaults", + ds_args.str, + "--all-databases", + "--fix-db-names", + "--fix-table-names", + NULL); +} + + static const char *expected_errors[]= { "ERROR 1060", /* Duplicate column name */ @@ -782,7 +796,8 @@ int main(int argc, char **argv) /* Run "mysqlcheck" and "mysql_fix_privilege_tables.sql" */ - if (run_mysqlcheck_upgrade() || + if (run_mysqlcheck_fixnames() || + run_mysqlcheck_upgrade() || run_sql_fix_privilege_tables()) { /* diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index b3b699f61fd..1e3249dd92a 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -23,10 +23,6 @@ #include #include -#ifdef LATER_HAVE_NDBCLUSTER_DB -#include "../ndb/src/mgmclient/ndb_mgmclient.h" -#endif - #define ADMIN_VERSION "8.42" #define MAX_MYSQL_VAR 256 #define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */ @@ -46,10 +42,6 @@ static uint tcp_port = 0, option_wait = 0, option_silent=0, nr_iterations; static uint opt_count_iterations= 0, my_end_arg; static ulong opt_connect_timeout, opt_shutdown_timeout; static char * unix_port=0; -#ifdef LATER_HAVE_NDBCLUSTER_DB -static my_bool opt_ndbcluster=0; -static char *opt_ndb_connectstring=0; -#endif #ifdef HAVE_SMEM static char *shared_memory_base_name=0; @@ -105,9 +97,6 @@ enum commands { ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD -#ifdef LATER_HAVE_NDBCLUSTER_DB - ,ADMIN_NDB_MGM -#endif }; static const char *command_names[]= { "create", "drop", "shutdown", @@ -118,9 +107,6 @@ static const char *command_names[]= { "ping", "extended-status", "flush-status", "flush-privileges", "start-slave", "stop-slave", "flush-threads","old-password", -#ifdef LATER_HAVE_NDBCLUSTER_DB - "ndb-mgm", -#endif NullS }; @@ -224,14 +210,6 @@ static struct my_option my_long_options[] = {"shutdown_timeout", OPT_SHUTDOWN_TIMEOUT, "", (uchar**) &opt_shutdown_timeout, (uchar**) &opt_shutdown_timeout, 0, GET_ULONG, REQUIRED_ARG, SHUTDOWN_DEF_TIMEOUT, 0, 3600*12, 0, 1, 0}, -#ifdef LATER_HAVE_NDBCLUSTER_DB - {"ndbcluster", OPT_NDBCLUSTER, "" - "", (uchar**) &opt_ndbcluster, - (uchar**) &opt_ndbcluster, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"ndb-connectstring", OPT_NDB_CONNECTSTRING, "" - "", (uchar**) &opt_ndb_connectstring, - (uchar**) &opt_ndb_connectstring, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#endif { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -634,7 +612,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) case ADMIN_VER: new_line=1; print_version(); - puts("Copyright (C) 2000-2006 MySQL AB"); + puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc."); 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"); printf("Server version\t\t%s\n", mysql_get_server_info(mysql)); printf("Protocol version\t%d\n", mysql_get_proto_info(mysql)); @@ -984,24 +962,6 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } mysql->reconnect=1; /* Automatic reconnect is default */ break; -#ifdef LATER_HAVE_NDBCLUSTER_DB - case ADMIN_NDB_MGM: - { - if (argc < 2) - { - my_printf_error(0, "Too few arguments to ndb-mgm", error_flags); - return 1; - } - { - Ndb_mgmclient_handle cmd= - ndb_mgmclient_handle_create(opt_ndb_connectstring); - ndb_mgmclient_execute(cmd, --argc, ++argv); - ndb_mgmclient_handle_destroy(cmd); - } - argc= 0; - } - break; -#endif default: my_printf_error(0, "Unknown command: '%-.60s'", error_flags, argv[0]); return 1; @@ -1023,7 +983,7 @@ static void print_version(void) static void usage(void) { print_version(); - puts("Copyright (C) 2000-2006 MySQL AB"); + puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc."); 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"); puts("Administration program for the mysqld daemon."); printf("Usage: %s [OPTIONS] command command....\n", my_progname); diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 17ebca497e4..86e1b3352b4 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -40,15 +40,13 @@ static uint verbose = 0, opt_mysql_port=0; static int my_end_arg; static char * opt_mysql_unix_port = 0; static char *opt_password = 0, *current_user = 0, - *default_charset = (char *)MYSQL_DEFAULT_CHARSET_NAME, - *current_host = 0; + *default_charset= 0, *current_host= 0; static int first_error = 0; DYNAMIC_ARRAY tables4repair; #ifdef HAVE_SMEM static char *shared_memory_base_name=0; #endif static uint opt_protocol=0; -static CHARSET_INFO *charset_info= &my_charset_latin1; enum operations { DO_CHECK, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE }; @@ -282,12 +280,10 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case OPT_FIX_DB_NAMES: what_to_do= DO_UPGRADE; - default_charset= (char*) "utf8"; opt_databases= 1; break; case OPT_FIX_TABLE_NAMES: what_to_do= DO_UPGRADE; - default_charset= (char*) "utf8"; break; case 'p': if (argument) @@ -367,11 +363,20 @@ static int get_options(int *argc, char ***argv) what_to_do = DO_CHECK; } - /* TODO: This variable is not yet used */ - if (strcmp(default_charset, charset_info->csname) && - !(charset_info= get_charset_by_csname(default_charset, - MY_CS_PRIMARY, MYF(MY_WME)))) - exit(1); + /* + If there's no --default-character-set option given with + --fix-table-name or --fix-db-name set the default character set to "utf8". + */ + if (!default_charset && (opt_fix_db_names || opt_fix_table_names)) + { + default_charset= (char*) "utf8"; + } + if (default_charset && !get_charset_by_csname(default_charset, MY_CS_PRIMARY, + MYF(MY_WME))) + { + printf("Unsupported character set: %s\n", default_charset); + return 1; + } if (*argc > 0 && opt_alldbs) { printf("You should give only options, no arguments at all, with option\n"); @@ -779,6 +784,8 @@ static int dbConnect(char *host, char *user, char *passwd) if (shared_memory_base_name) mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name); #endif + if (default_charset) + mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd, NULL, opt_mysql_port, opt_mysql_unix_port, 0))) { diff --git a/client/mysqldump.c b/client/mysqldump.c index c068b2ff16e..44c53a0e4c2 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. 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 diff --git a/client/mysqlimport.c b/client/mysqlimport.c index ac3b0e8efba..09ba27b287a 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -193,7 +193,7 @@ static void print_version(void) static void usage(void) { print_version(); - puts("Copyright (C) 2000-2006 MySQL AB"); + puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc."); 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"); printf("\ Loads tables from text files in various formats. The base name of the\n\ diff --git a/client/mysqlshow.c b/client/mysqlshow.c index 167ff5c6198..0e696aed211 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -249,7 +249,7 @@ static void print_version(void) static void usage(void) { print_version(); - puts("Copyright (C) 2000-2006 MySQL AB"); + puts("Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc."); 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"); puts("Shows the structure of a mysql database (databases,tables and columns)\n"); printf("Usage: %s [OPTIONS] [database [table [column]]]\n",my_progname); diff --git a/client/mysqlslap.c b/client/mysqlslap.c index 5ca0b1cd207..9ba912d646c 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -688,9 +688,7 @@ static void usage(void) { print_version(); puts("Copyright (C) 2005 MySQL AB"); - 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"); + 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"); puts("Run a query multiple times against the server\n"); printf("Usage: %s [OPTIONS]\n",my_progname); print_defaults("my",load_default_groups); diff --git a/client/mysqltest.c b/client/mysqltest.cc similarity index 92% rename from client/mysqltest.c rename to client/mysqltest.cc index 6babc42bcd0..89b9c78a049 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.cc @@ -169,7 +169,6 @@ static ulonglong timer_start; static void timer_output(void); static ulonglong timer_now(void); -static ulonglong progress_start= 0; static ulong connection_retry_sleep= 100000; /* Microseconds */ @@ -186,12 +185,12 @@ DYNAMIC_ARRAY q_lines; #include "sslopt-vars.h" -struct +struct Parser { int read_lines,current_line; } parser; -struct +struct MasterPos { char file[FN_REFLEN]; ulong pos; @@ -200,7 +199,7 @@ struct /* if set, all results are concated and compared against this file */ const char *result_file_name= 0; -typedef struct st_var +typedef struct { char *name; int name_len; @@ -278,8 +277,9 @@ enum enum_commands { Q_REPLACE_REGEX, Q_REMOVE_FILE, Q_FILE_EXIST, Q_WRITE_FILE, Q_COPY_FILE, Q_PERL, Q_DIE, Q_EXIT, Q_SKIP, Q_CHMOD_FILE, Q_APPEND_FILE, Q_CAT_FILE, Q_DIFF_FILES, - Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR, Q_LIST_FILES, - Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE, + Q_SEND_QUIT, Q_CHANGE_USER, Q_MKDIR, Q_RMDIR, + Q_LIST_FILES, Q_LIST_FILES_WRITE_FILE, Q_LIST_FILES_APPEND_FILE, + Q_SEND_SHUTDOWN, Q_SHUTDOWN_SERVER, Q_UNKNOWN, /* Unknown command. */ Q_COMMENT, /* Comments, ignored. */ @@ -374,6 +374,8 @@ const char *command_names[]= "list_files", "list_files_write_file", "list_files_append_file", + "send_shutdown", + "shutdown_server", 0 }; @@ -423,7 +425,7 @@ struct st_command TYPELIB command_typelib= {array_elements(command_names),"", command_names, 0}; -DYNAMIC_STRING ds_res, ds_progress, ds_warning_messages; +DYNAMIC_STRING ds_res; char builtin_echo[FN_REFLEN]; @@ -433,8 +435,6 @@ void abort_not_supported_test(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); void verbose_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); -void warning_msg(const char *fmt, ...) - ATTRIBUTE_FORMAT(printf, 1, 2); void log_msg(const char *fmt, ...) ATTRIBUTE_FORMAT(printf, 1, 2); @@ -447,15 +447,15 @@ VAR* var_get(const char *var_name, const char** var_name_end, void eval_expr(VAR* v, const char *p, const char** p_end); my_bool match_delimiter(int c, const char *delim, uint length); void dump_result_to_reject_file(char *buf, int size); -void dump_result_to_log_file(char *buf, int size); void dump_warning_messages(); -void dump_progress(); void do_eval(DYNAMIC_STRING *query_eval, const char *query, const char *query_end, my_bool pass_through_escape_chars); void str_to_file(const char *fname, char *str, int size); void str_to_file2(const char *fname, char *str, int size, my_bool append); +void fix_win_paths(const char *val, int len); + #ifdef __WIN__ void free_tmp_sh_file(); void free_win_path_patterns(); @@ -484,6 +484,177 @@ void free_all_replace(){ } +class LogFile { + FILE* m_file; + char m_file_name[FN_REFLEN]; + size_t m_bytes_written; +public: + LogFile() : m_file(NULL), m_bytes_written(0) { + bzero(m_file_name, sizeof(m_file_name)); + } + + ~LogFile() { + close(); + } + + const char* file_name() const { return m_file_name; } + size_t bytes_written() const { return m_bytes_written; } + + void open(const char* dir, const char* name, const char* ext) + { + DBUG_ENTER("LogFile::open"); + DBUG_PRINT("enter", ("dir: '%s', name: '%s'", + dir, name)); + if (!name) + { + m_file= stdout; + DBUG_VOID_RETURN; + } + + fn_format(m_file_name, name, dir, ext, + *dir ? MY_REPLACE_DIR | MY_REPLACE_EXT : + MY_REPLACE_EXT); + + DBUG_PRINT("info", ("file_name: %s", m_file_name)); + + if ((m_file= fopen(m_file_name, "wb+")) == NULL) + die("Failed to open log file %s, errno: %d", m_file_name, errno); + + DBUG_VOID_RETURN; + } + + void close() + { + if (m_file) { + if (m_file != stdout) + fclose(m_file); + else + fflush(m_file); + } + m_file= NULL; + } + + void flush() + { + if (m_file && m_file != stdout) + { + if (fflush(m_file)) + die("Failed to flush '%s', errno: %d", m_file_name, errno); + } + } + + void write(DYNAMIC_STRING* ds) + { + DBUG_ENTER("LogFile::write"); + DBUG_ASSERT(m_file); + + if (ds->length == 0) + DBUG_VOID_RETURN; + DBUG_ASSERT(ds->str); + + if (fwrite(ds->str, 1, ds->length, m_file) != ds->length) + die("Failed to write %lu bytes to '%s', errno: %d", + (unsigned long)ds->length, m_file_name, errno); + m_bytes_written+= ds->length; + DBUG_VOID_RETURN; + } + + void show_tail(uint lines) { + DBUG_ENTER("LogFile::show_tail"); + + if (!m_file || m_file == stdout) + DBUG_VOID_RETURN; + + if (lines == 0) + DBUG_VOID_RETURN; + lines++; + + int show_offset= 0; + char buf[256]; + size_t bytes; + bool found_bof= false; + + /* Search backward in file until "lines" newline has been found */ + while (lines && !found_bof) + { + show_offset-= sizeof(buf); + while(fseek(m_file, show_offset, SEEK_END) != 0 && show_offset < 0) + { + found_bof= true; + // Seeking before start of file + show_offset++; + } + + if ((bytes= fread(buf, 1, sizeof(buf), m_file)) <= 0) + { + fprintf(stderr, "Failed to read from '%s', errno: %d\n", + m_file_name, errno); + DBUG_VOID_RETURN; + } + + DBUG_PRINT("info", ("Read %lu bytes from file, buf: %s", + (unsigned long)bytes, buf)); + + char* show_from= buf + bytes; + while(show_from > buf && lines > 0 ) + { + show_from--; + if (*show_from == '\n') + lines--; + } + if (show_from != buf) + { + // The last new line was found in this buf, adjust offset + show_offset+= (show_from - buf) + 1; + DBUG_PRINT("info", ("adjusted offset to %d", show_offset)); + } + DBUG_PRINT("info", ("show_offset: %d", show_offset)); + } + + fprintf(stderr, "\nThe result from queries just before the failure was:\n"); + + DBUG_PRINT("info", ("show_offset: %d", show_offset)); + if (!lines) + { + fprintf(stderr, "< snip >\n"); + + if (fseek(m_file, show_offset, SEEK_END) != 0) + { + fprintf(stderr, "Failed to seek to position %d in '%s', errno: %d", + show_offset, m_file_name, errno); + DBUG_VOID_RETURN; + } + + } + else { + DBUG_PRINT("info", ("Showing the whole file")); + if (fseek(m_file, 0L, SEEK_SET) != 0) + { + fprintf(stderr, "Failed to seek to pos 0 in '%s', errno: %d", + m_file_name, errno); + DBUG_VOID_RETURN; + } + } + + while ((bytes= fread(buf, 1, sizeof(buf), m_file)) > 0) + fwrite(buf, 1, bytes, stderr); + + if (!lines) + { + fprintf(stderr, + "\nMore results from queries before failure can be found in %s\n", + m_file_name); + } + fflush(stderr); + + DBUG_VOID_RETURN; + } +}; + +LogFile log_file; +LogFile progress_file; + + /* Disable functions that only exist in MySQL 4.0 */ #if MYSQL_VERSION_ID < 40000 void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {} @@ -623,12 +794,15 @@ void do_eval(DYNAMIC_STRING *query_eval, const char *query, break; } } +#ifdef __WIN__ + fix_win_paths(query_eval->str, query_eval->length); +#endif DBUG_VOID_RETURN; } /* - Run query and dump the result to stdout in vertical format + Run query and dump the result to stderr in vertical format NOTE! This function should be safe to call when an error has occured and thus any further errors will be ignored(although logged) @@ -960,8 +1134,6 @@ void free_used_memory() my_free(embedded_server_args[--embedded_server_arg_count],MYF(0)); delete_dynamic(&q_lines); dynstr_free(&ds_res); - dynstr_free(&ds_progress); - dynstr_free(&ds_warning_messages); free_all_replace(); my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR)); free_defaults(default_argv); @@ -1039,31 +1211,7 @@ void die(const char *fmt, ...) fprintf(stderr, "\n"); fflush(stderr); - /* Show results from queries just before failure */ - if (ds_res.length && opt_tail_lines) - { - int tail_lines= opt_tail_lines; - char* show_from= ds_res.str + ds_res.length - 1; - while(show_from > ds_res.str && tail_lines > 0 ) - { - show_from--; - if (*show_from == '\n') - tail_lines--; - } - fprintf(stderr, "\nThe result from queries just before the failure was:\n"); - if (show_from > ds_res.str) - fprintf(stderr, "< snip >"); - fprintf(stderr, "%s", show_from); - fflush(stderr); - } - - /* Dump the result that has been accumulated so far to .log file */ - if (result_file_name && ds_res.length) - dump_result_to_log_file(ds_res.str, ds_res.length); - - /* Dump warning messages */ - if (result_file_name && ds_warning_messages.length) - dump_warning_messages(); + log_file.show_tail(opt_tail_lines); /* Help debugging by displaying any warnings that might have @@ -1137,41 +1285,6 @@ void verbose_msg(const char *fmt, ...) } -void warning_msg(const char *fmt, ...) -{ - va_list args; - char buff[512]; - size_t len; - DBUG_ENTER("warning_msg"); - - va_start(args, fmt); - dynstr_append(&ds_warning_messages, "mysqltest: "); - if (start_lineno != 0) - { - dynstr_append(&ds_warning_messages, "Warning detected "); - if (cur_file && cur_file != file_stack) - { - len= my_snprintf(buff, sizeof(buff), "in included file %s ", - cur_file->file_name); - dynstr_append_mem(&ds_warning_messages, - buff, len); - } - len= my_snprintf(buff, sizeof(buff), "at line %d: ", - start_lineno); - dynstr_append_mem(&ds_warning_messages, - buff, len); - } - - len= my_vsnprintf(buff, sizeof(buff), fmt, args); - dynstr_append_mem(&ds_warning_messages, buff, len); - - dynstr_append(&ds_warning_messages, "\n"); - va_end(args); - - DBUG_VOID_RETURN; -} - - void log_msg(const char *fmt, ...) { va_list args; @@ -1328,6 +1441,36 @@ static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) } +/* + Test if diff is present. This is needed on Windows systems + as the OS returns 1 whether diff is successful or if it is + not present. + + We run diff -v and look for output in stdout. + We don't redirect stderr to stdout to make for a simplified check + Windows will output '"diff"' is not recognized... to stderr if it is + not present. +*/ + +int diff_check() +{ + char buf[512]= {0}; + FILE *res_file; + const char *cmd = "diff -v"; + int have_diff = 0; + + if (!(res_file= popen(cmd, "r"))) + die("popen(\"%s\", \"r\") failed", cmd); + + /* if diff is not present, nothing will be in stdout to increment have_diff */ + if (fgets(buf, sizeof(buf), res_file)) + { + have_diff += 1; + } + pclose(res_file); + return have_diff; +} + /* Show the diff of two files using the systems builtin diff command. If no such diff command exist, just dump the content @@ -1344,43 +1487,75 @@ static int run_tool(const char *tool_path, DYNAMIC_STRING *ds_res, ...) void show_diff(DYNAMIC_STRING* ds, const char* filename1, const char* filename2) { - DYNAMIC_STRING ds_tmp; + int have_diff = 0; if (init_dynamic_string(&ds_tmp, "", 256, 256)) die("Out of memory"); - /* First try with unified diff */ - if (run_tool("diff", - &ds_tmp, /* Get output from diff in ds_tmp */ - "-u", - filename1, - filename2, - "2>&1", - NULL) > 1) /* Most "diff" tools return >1 if error */ - { - dynstr_set(&ds_tmp, ""); + /* determine if we have diff on Windows + needs special processing due to return values + on that OS + This test is only done on Windows since it's only needed there + in order to correctly detect non-availibility of 'diff', and + the way it's implemented does not work with default 'diff' on Solaris. + */ +#ifdef __WIN__ + have_diff = diff_check(); +#else + have_diff = 1; +#endif - /* Fallback to context diff with "diff -c" */ + if (have_diff) + { + /* First try with unified diff */ if (run_tool("diff", &ds_tmp, /* Get output from diff in ds_tmp */ - "-c", + "-u", filename1, filename2, "2>&1", NULL) > 1) /* Most "diff" tools return >1 if error */ { - /* - Fallback to dump both files to result file and inform - about installing "diff" - */ dynstr_set(&ds_tmp, ""); - dynstr_append(&ds_tmp, + /* Fallback to context diff with "diff -c" */ + if (run_tool("diff", + &ds_tmp, /* Get output from diff in ds_tmp */ + "-c", + filename1, + filename2, + "2>&1", + NULL) > 1) /* Most "diff" tools return >1 if error */ + { + dynstr_set(&ds_tmp, ""); + + /* Fallback to simple diff with "diff" */ + if (run_tool("diff", + &ds_tmp, /* Get output from diff in ds_tmp */ + filename1, + filename2, + "2>&1", + NULL) > 1) /* Most "diff" tools return >1 if error */ + { + have_diff= 0; + } + } + } + } + + if (! have_diff) + { + /* + Fallback to dump both files to result file and inform + about installing "diff" + */ + dynstr_append(&ds_tmp, "\n"); + dynstr_append(&ds_tmp, "\n" "The two files differ but it was not possible to execute 'diff' in\n" -"order to show only the difference, tried both 'diff -u' or 'diff -c'.\n" -"Instead the whole content of the two files was shown for you to diff manually. ;)\n\n" +"order to show only the difference. Instead the whole content of the\n" +"two files was shown for you to diff manually.\n\n" "To get a better report you should install 'diff' on your system, which you\n" "for example can get from http://www.gnu.org/software/diffutils/diffutils.html\n" #ifdef __WIN__ @@ -1388,16 +1563,15 @@ void show_diff(DYNAMIC_STRING* ds, #endif "\n"); - dynstr_append(&ds_tmp, " --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); - cat_file(&ds_tmp, filename1); - dynstr_append(&ds_tmp, "<<<\n --- "); - dynstr_append(&ds_tmp, filename1); - dynstr_append(&ds_tmp, " >>>\n"); - cat_file(&ds_tmp, filename2); - dynstr_append(&ds_tmp, "<<<<\n"); - } + dynstr_append(&ds_tmp, " --- "); + dynstr_append(&ds_tmp, filename1); + dynstr_append(&ds_tmp, " >>>\n"); + cat_file(&ds_tmp, filename1); + dynstr_append(&ds_tmp, "<<<\n --- "); + dynstr_append(&ds_tmp, filename1); + dynstr_append(&ds_tmp, " >>>\n"); + cat_file(&ds_tmp, filename2); + dynstr_append(&ds_tmp, "<<<<\n"); } if (ds) @@ -1561,18 +1735,17 @@ int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname) /* - Check the content of ds against result file + Check the content of log against result file SYNOPSIS check_result - ds - content to be checked RETURN VALUES error - the function will not return */ -void check_result(DYNAMIC_STRING* ds) +void check_result() { const char* mess= "Result content mismatch\n"; @@ -1580,10 +1753,7 @@ void check_result(DYNAMIC_STRING* ds) DBUG_ASSERT(result_file_name); DBUG_PRINT("enter", ("result_file_name: %s", result_file_name)); - if (access(result_file_name, F_OK) != 0) - die("The specified result file does not exist: '%s'", result_file_name); - - switch (dyn_string_cmp(ds, result_file_name)) { + switch (compare_files(log_file.file_name(), result_file_name)) { case RESULT_OK: break; /* ok */ case RESULT_LENGTH_MISMATCH: @@ -1611,9 +1781,10 @@ void check_result(DYNAMIC_STRING* ds) fn_format(reject_file, result_file_name, opt_logdir, ".reject", MY_REPLACE_DIR | MY_REPLACE_EXT); } - str_to_file(reject_file, ds->str, ds->length); - dynstr_set(ds, NULL); /* Don't create a .log file */ + if (my_copy(log_file.file_name(), reject_file, MYF(0)) != 0) + die("Failed to copy '%s' to '%s', errno: %d", + log_file.file_name(), reject_file, errno); show_diff(NULL, result_file_name, reject_file); die(mess); @@ -1727,7 +1898,7 @@ VAR *var_init(VAR *v, const char *name, int name_len, const char *val, tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0; tmp_var->alloced = (v == 0); - if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME)))) + if (!(tmp_var->str_val = (char*)my_malloc(val_alloc_len+1, MYF(MY_WME)))) die("Out of memory"); memcpy(tmp_var->name, name, name_len); @@ -1913,17 +2084,6 @@ void var_set_errno(int sql_errno) } -/* - Update $mysql_get_server_version variable with version - of the currently connected server -*/ - -void var_set_mysql_get_server_version(MYSQL* mysql) -{ - var_set_int("$mysql_get_server_version", mysql_get_server_version(mysql)); -} - - /* Set variable from the result of a query @@ -2041,9 +2201,9 @@ void var_set_query_get_value(struct st_command *command, VAR *var) static DYNAMIC_STRING ds_col; static DYNAMIC_STRING ds_row; const struct command_arg query_get_value_args[] = { - "query", ARG_STRING, TRUE, &ds_query, "Query to run", - "column name", ARG_STRING, TRUE, &ds_col, "Name of column", - "row number", ARG_STRING, TRUE, &ds_row, "Number for row" + {"query", ARG_STRING, TRUE, &ds_query, "Query to run"}, + {"column name", ARG_STRING, TRUE, &ds_col, "Name of column"}, + {"row number", ARG_STRING, TRUE, &ds_row, "Number for row"} }; DBUG_ENTER("var_set_query_get_value"); @@ -2140,8 +2300,8 @@ void var_copy(VAR *dest, VAR *src) /* Alloc/realloc data for str_val in dest */ if (dest->alloced_len < src->alloced_len && !(dest->str_val= dest->str_val - ? my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) - : my_malloc(src->alloced_len, MYF(MY_WME)))) + ? (char*)my_realloc(dest->str_val, src->alloced_len, MYF(MY_WME)) + : (char*)my_malloc(src->alloced_len, MYF(MY_WME)))) die("Out of memory"); else dest->alloced_len= src->alloced_len; @@ -2162,8 +2322,16 @@ void eval_expr(VAR *v, const char *p, const char **p_end) if (*p == '$') { VAR *vp; + const char* expected_end= *p_end; // Remember var end if ((vp= var_get(p, p_end, 0, 0))) var_copy(v, vp); + + /* Make sure there was just a $variable and nothing else */ + const char* end= *p_end + 1; + if (end < expected_end) + die("Found junk '%.*s' after $variable in expression", + (int)(expected_end - end - 1), end); + DBUG_VOID_RETURN; } @@ -2199,9 +2367,9 @@ void eval_expr(VAR *v, const char *p, const char **p_end) v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ? MIN_VAR_ALLOC : new_val_len + 1; if (!(v->str_val = - v->str_val ? my_realloc(v->str_val, v->alloced_len+1, - MYF(MY_WME)) : - my_malloc(v->alloced_len+1, MYF(MY_WME)))) + v->str_val ? + (char*)my_realloc(v->str_val, v->alloced_len+1, MYF(MY_WME)) : + (char*)my_malloc(v->alloced_len+1, MYF(MY_WME)))) die("Out of memory"); } v->str_val_len = new_val_len; @@ -2218,8 +2386,19 @@ void eval_expr(VAR *v, const char *p, const char **p_end) int open_file(const char *name) { char buff[FN_REFLEN]; + size_t length; DBUG_ENTER("open_file"); DBUG_PRINT("enter", ("name: %s", name)); + + /* Extract path from current file and try it as base first */ + if (dirname_part(buff, cur_file->file_name, &length)) + { + strxmov(buff, buff, name, NullS); + if (access(buff, F_OK) == 0){ + DBUG_PRINT("info", ("The file exists")); + name= buff; + } + } if (!test_if_hard_path(name)) { strxmov(buff, opt_basedir, name, NullS); @@ -2233,7 +2412,7 @@ int open_file(const char *name) if (!(cur_file->file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) { cur_file--; - die("Could not open '%s' for reading", buff); + die("Could not open '%s' for reading, errno: %d", buff, errno); } cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); cur_file->lineno=1; @@ -2520,7 +2699,7 @@ enum enum_operator SYNOPSIS do_modify_var() query called command - operator operation to perform on the var + op operation to perform on the var DESCRIPTION dec $var_name @@ -2529,7 +2708,7 @@ enum enum_operator */ int do_modify_var(struct st_command *command, - enum enum_operator operator) + enum enum_operator op) { const char *p= command->first_argument; VAR* v; @@ -2539,7 +2718,7 @@ int do_modify_var(struct st_command *command, die("The argument to %.*s must be a variable (start with $)", command->first_word_len, command->query); v= var_get(p, &p, 1, 0); - switch (operator) { + switch (op) { case DO_DEC: v->int_val--; break; @@ -2789,7 +2968,7 @@ void do_mkdir(struct st_command *command) int error; static DYNAMIC_STRING ds_dirname; const struct command_arg mkdir_args[] = { - "dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create" + {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to create"} }; DBUG_ENTER("do_mkdir"); @@ -2819,7 +2998,7 @@ void do_rmdir(struct st_command *command) int error; static DYNAMIC_STRING ds_dirname; const struct command_arg rmdir_args[] = { - "dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove" + {"dirname", ARG_STRING, TRUE, &ds_dirname, "Directory to remove"} }; DBUG_ENTER("do_rmdir"); @@ -3194,6 +3373,14 @@ void do_diff_files(struct st_command *command) sizeof(diff_file_args)/sizeof(struct command_arg), ' '); + if (access(ds_filename.str, F_OK) != 0) + die("command \"diff_files\" failed, file '%s' does not exist", + ds_filename.str); + + if (access(ds_filename2.str, F_OK) != 0) + die("command \"diff_files\" failed, file '%s' does not exist", + ds_filename2.str); + if ((error= compare_files(ds_filename.str, ds_filename2.str))) { /* Compare of the two files failed, append them to output @@ -3479,24 +3666,19 @@ void do_wait_for_slave_to_stop(struct st_command *c __attribute__((unused))) } -void do_sync_with_master2(long offset) +void do_sync_with_master2(struct st_command *command, long offset) { MYSQL_RES *res; MYSQL_ROW row; MYSQL *mysql= &cur_con->mysql; char query_buf[FN_REFLEN+128]; - int tries= 0; - int rpl_parse; + int timeout= 300; /* seconds */ if (!master_pos.file[0]) die("Calling 'sync_with_master' without calling 'save_master_pos'"); - rpl_parse= mysql_rpl_parse_enabled(mysql); - mysql_disable_rpl_parse(mysql); - sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file, - master_pos.pos + offset); - -wait_for_position: + sprintf(query_buf, "select master_pos_wait('%s', %ld, %d)", + master_pos.file, master_pos.pos + offset, timeout); if (mysql_query(mysql, query_buf)) die("failed in '%s': %d: %s", query_buf, mysql_errno(mysql), @@ -3509,30 +3691,48 @@ wait_for_position: mysql_free_result(res); die("empty result in %s", query_buf); } - if (!row[0]) - { - /* - It may be that the slave SQL thread has not started yet, though START - SLAVE has been issued ? - */ - mysql_free_result(res); - if (tries++ == 30) - { - show_query(mysql, "SHOW MASTER STATUS"); - show_query(mysql, "SHOW SLAVE STATUS"); - die("could not sync with master ('%s' returned NULL)", query_buf); - } - sleep(1); /* So at most we will wait 30 seconds and make 31 tries */ - goto wait_for_position; - } + + int result= -99; + const char* result_str= row[0]; + if (result_str) + result= atoi(result_str); + mysql_free_result(res); - if (rpl_parse) - mysql_enable_rpl_parse(mysql); + + if (!result_str || result < 0) + { + /* master_pos_wait returned NULL or < 0 */ + show_query(mysql, "SHOW MASTER STATUS"); + show_query(mysql, "SHOW SLAVE STATUS"); + show_query(mysql, "SHOW PROCESSLIST"); + fprintf(stderr, "analyze: sync_with_master\n"); + + if (!result_str) + { + /* + master_pos_wait returned NULL. This indicates that + slave SQL thread is not started, the slave's master + information is not initialized, the arguments are + incorrect, or an error has occured + */ + die("%.*s failed: '%s' returned NULL "\ + "indicating slave SQL thread failure", + command->first_word_len, command->query, query_buf); + + } + + if (result == -1) + die("%.*s failed: '%s' returned -1 "\ + "indicating timeout after %d seconds", + command->first_word_len, command->query, query_buf, timeout); + else + die("%.*s failed: '%s' returned unknown result :%d", + command->first_word_len, command->query, query_buf, result); + } return; } - void do_sync_with_master(struct st_command *command) { long offset= 0; @@ -3547,7 +3747,7 @@ void do_sync_with_master(struct st_command *command) die("Invalid integer argument \"%s\"", offset_start); command->last_argument= p; } - do_sync_with_master2(offset); + do_sync_with_master2(command, offset); return; } @@ -3897,6 +4097,145 @@ void do_set_charset(struct st_command *command) } +/* + Run query and return one field in the result set from the + first row and +*/ + +int query_get_string(MYSQL* mysql, const char* query, + int column, DYNAMIC_STRING* ds) +{ + MYSQL_RES *res= NULL; + MYSQL_ROW row; + + if (mysql_query(mysql, query)) + die("'%s' failed: %d %s", query, + mysql_errno(mysql), mysql_error(mysql)); + if ((res= mysql_store_result(mysql)) == NULL) + die("Failed to store result: %d %s", + mysql_errno(mysql), mysql_error(mysql)); + + if ((row= mysql_fetch_row(res)) == NULL) + { + mysql_free_result(res); + ds= 0; + return 1; + } + init_dynamic_string(ds, (row[column] ? row[column] : "NULL"), ~0, 32); + mysql_free_result(res); + return 0; +} + + +static int my_kill(int pid, int sig) +{ +#ifdef __WIN__ + HANDLE proc; + if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL) + return -1; + if (sig == 0) + { + CloseHandle(proc); + return 0; + } + (void)TerminateProcess(proc, 201); + CloseHandle(proc); + return 1; +#else + return kill(pid, sig); +#endif +} + + + +/* + Shutdown the server of current connection and + make sure it goes away within seconds + + NOTE! Currently only works with local server + + SYNOPSIS + do_shutdown_server() + command called command + + DESCRIPTION + shutdown [] + +*/ + +void do_shutdown_server(struct st_command *command) +{ + int timeout=60, pid; + DYNAMIC_STRING ds_pidfile_name; + MYSQL* mysql = &cur_con->mysql; + static DYNAMIC_STRING ds_timeout; + const struct command_arg shutdown_args[] = { + {"timeout", ARG_STRING, FALSE, &ds_timeout, "Timeout before killing server"} + }; + DBUG_ENTER("do_shutdown_server"); + + check_command_args(command, command->first_argument, shutdown_args, + sizeof(shutdown_args)/sizeof(struct command_arg), + ' '); + + if (ds_timeout.length) + { + timeout= atoi(ds_timeout.str); + if (timeout == 0) + die("Illegal argument for timeout: '%s'", ds_timeout.str); + } + dynstr_free(&ds_timeout); + + /* Get the servers pid_file name and use it to read pid */ + if (query_get_string(mysql, "SHOW VARIABLES LIKE 'pid_file'", 1, + &ds_pidfile_name)) + die("Failed to get pid_file from server"); + + /* Read the pid from the file */ + { + int fd; + char buff[32]; + + if ((fd= my_open(ds_pidfile_name.str, O_RDONLY, MYF(0))) < 0) + die("Failed to open file '%s'", ds_pidfile_name.str); + dynstr_free(&ds_pidfile_name); + + if (my_read(fd, (uchar*)&buff, + sizeof(buff), MYF(0)) <= 0){ + my_close(fd, MYF(0)); + die("pid file was empty"); + } + my_close(fd, MYF(0)); + + pid= atoi(buff); + if (pid == 0) + die("Pidfile didn't contain a valid number"); + } + DBUG_PRINT("info", ("Got pid %d", pid)); + + /* Tell server to shutdown if timeout > 0*/ + if (timeout && mysql_shutdown(mysql, SHUTDOWN_DEFAULT)) + die("mysql_shutdown failed"); + + /* Check that server dies */ + while(timeout--){ + if (my_kill(pid, 0) < 0){ + DBUG_PRINT("info", ("Process %d does not exist anymore", pid)); + DBUG_VOID_RETURN; + } + DBUG_PRINT("info", ("Sleeping, timeout: %d", timeout)); + my_sleep(1000000L); + } + + /* Kill the server */ + DBUG_PRINT("info", ("Killing server, pid: %d", pid)); + (void)my_kill(pid, 9); + + DBUG_VOID_RETURN; + +} + + #if MYSQL_VERSION_ID >= 50000 /* List of error names to error codes, available from 5.0 */ typedef struct @@ -4160,59 +4499,73 @@ void set_reconnect(MYSQL* mysql, int val) } -int select_connection_name(const char *name) +/** + Change the current connection to the given st_connection, and update + $mysql_get_server_version and $CURRENT_CONNECTION accordingly. +*/ +void set_current_connection(struct st_connection *con) { - DBUG_ENTER("select_connection_name"); - DBUG_PRINT("enter",("name: '%s'", name)); - - if (!(cur_con= find_connection_by_name(name))) - die("connection '%s' not found in connection pool", name); - + cur_con= con; /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); - - DBUG_RETURN(0); + var_set_int("$mysql_get_server_version", + mysql_get_server_version(&con->mysql)); + /* Update $CURRENT_CONNECTION to the name of the current connection */ + var_set_string("$CURRENT_CONNECTION", con->name); } -int select_connection(struct st_command *command) +void select_connection_name(const char *name) { - char *name; - char *p= command->first_argument; - DBUG_ENTER("select_connection"); + DBUG_ENTER("select_connection_name"); + DBUG_PRINT("enter",("name: '%s'", name)); + st_connection *con= find_connection_by_name(name); - if (!*p) - die("Missing connection name in connect"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; - if (*p) - *p++= 0; - command->last_argument= p; - DBUG_RETURN(select_connection_name(name)); + if (!con) + die("connection '%s' not found in connection pool", name); + + set_current_connection(con); + + DBUG_VOID_RETURN; +} + + +void select_connection(struct st_command *command) +{ + DBUG_ENTER("select_connection"); + static DYNAMIC_STRING ds_connection; + const struct command_arg connection_args[] = { + { "connection_name", ARG_STRING, TRUE, &ds_connection, "Name of the connection that we switch to." } + }; + check_command_args(command, command->first_argument, connection_args, + sizeof(connection_args)/sizeof(struct command_arg), + ','); + + DBUG_PRINT("info", ("changing connection: %s", ds_connection.str)); + select_connection_name(ds_connection.str); + dynstr_free(&ds_connection); + DBUG_VOID_RETURN; } void do_close_connection(struct st_command *command) { - char *p= command->first_argument, *name; - struct st_connection *con; - DBUG_ENTER("close_connection"); - DBUG_PRINT("enter",("name: '%s'",p)); - if (!*p) - die("Missing connection name in disconnect"); - name= p; - while (*p && !my_isspace(charset_info,*p)) - p++; + struct st_connection *con; + static DYNAMIC_STRING ds_connection; + const struct command_arg close_connection_args[] = { + { "connection_name", ARG_STRING, TRUE, &ds_connection, + "Name of the connection to close." } + }; + check_command_args(command, command->first_argument, + close_connection_args, + sizeof(close_connection_args)/sizeof(struct command_arg), + ' '); - if (*p) - *p++= 0; - command->last_argument= p; + DBUG_PRINT("enter",("connection name: '%s'", ds_connection.str)); - if (!(con= find_connection_by_name(name))) - die("connection '%s' not found in connection pool", name); + if (!(con= find_connection_by_name(ds_connection.str))) + die("connection '%s' not found in connection pool", ds_connection.str); DBUG_PRINT("info", ("Closing connection %s", con->name)); #ifndef EMBEDDED_LIBRARY @@ -4251,6 +4604,13 @@ void do_close_connection(struct st_command *command) if (!(con->name = my_strdup("-closed_connection-", MYF(MY_WME)))) die("Out of memory"); + if (con == cur_con) + { + /* Current connection was closed */ + var_set_int("$mysql_get_server_version", 0xFFFFFFFF); + var_set_string("$CURRENT_CONNECTION", con->name); + } + DBUG_VOID_RETURN; } @@ -4597,15 +4957,12 @@ void do_connect(struct st_command *command) if (!(con_slot->name= my_strdup(ds_connection_name.str, MYF(MY_WME)))) die("Out of memory"); con_slot->name_len= strlen(con_slot->name); - cur_con= con_slot; - + set_current_connection(con_slot); + if (con_slot == next_con) next_con++; /* if we used the next_con slot, advance the pointer */ } - /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); - dynstr_free(&ds_connection_name); dynstr_free(&ds_host); dynstr_free(&ds_user); @@ -5055,55 +5412,6 @@ void convert_to_format_v1(char* query) } -/* - Check a command that is about to be sent (or should have been - sent if parsing was enabled) to mysql server for - suspicious things and generate warnings. -*/ - -void scan_command_for_warnings(struct st_command *command) -{ - const char *ptr= command->query; - DBUG_ENTER("scan_command_for_warnings"); - DBUG_PRINT("enter", ("query: %s", command->query)); - - while(*ptr) - { - /* - Look for query's that lines that start with a -- comment - and has a mysqltest command - */ - if (ptr[0] == '\n' && - ptr[1] && ptr[1] == '-' && - ptr[2] && ptr[2] == '-' && - ptr[3]) - { - uint type; - char save; - char *end, *start= (char*)ptr+3; - /* Skip leading spaces */ - while (*start && my_isspace(charset_info, *start)) - start++; - end= start; - /* Find end of command(next space) */ - while (*end && !my_isspace(charset_info, *end)) - end++; - save= *end; - *end= 0; - DBUG_PRINT("info", ("Checking '%s'", start)); - type= find_type(start, &command_typelib, 1+2); - if (type) - warning_msg("Embedded mysqltest command '--%s' detected in " - "query '%s' was this intentional? ", - start, command->query); - *end= save; - } - - ptr++; - } - DBUG_VOID_RETURN; -} - /* Check for unexpected "junk" after the end of query This is normally caused by missing delimiters or when @@ -5161,6 +5469,19 @@ void check_eol_junk(const char *eol) } +bool is_delimiter(const char* p) +{ + uint match= 0; + char* delim= delimiter; + while (*p && *p == *delim++) + { + match++; + p++; + } + + return (match == delimiter_length); +} + /* Create a command from a set of lines @@ -5227,9 +5548,11 @@ int read_command(struct st_command** command_ptr) if (!(command->query_buf= command->query= my_strdup(p, MYF(MY_WME)))) die("Out of memory"); - /* Calculate first word length(the command), terminated by space or ( */ + /* + Calculate first word length(the command), terminated + by 'space' , '(' or 'delimiter' */ p= command->query; - while (*p && !my_isspace(charset_info, *p) && *p != '(') + while (*p && !my_isspace(charset_info, *p) && *p != '(' && !is_delimiter(p)) p++; command->first_word_len= (uint) (p - command->query); DBUG_PRINT("info", ("first_word: %.*s", @@ -5448,7 +5771,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0); if (!(cur_file->file= my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0)))) - die("Could not open '%s' for reading: errno = %d", buff, errno); + die("Could not open '%s' for reading, errno: %d", buff, errno); cur_file->file_name= my_strdup(buff, MYF(MY_FAE)); cur_file->lineno= 1; break; @@ -5494,6 +5817,11 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), die("Can't use server argument"); } break; + case OPT_LOG_DIR: + /* Check that the file exists */ + if (access(opt_logdir, F_OK) != 0) + die("The specified log directory does not exist: '%s'", opt_logdir); + break; case 'F': read_embedded_server_arguments(argument); break; @@ -5535,6 +5863,14 @@ int parse_args(int argc, char **argv) if (debug_check_flag) my_end_arg= MY_CHECK_ERROR; + + if (!record) + { + /* Check that the result file exists */ + if (result_file_name && access(result_file_name, F_OK) != 0) + die("The specified result file '%s' does not exist", result_file_name); + } + return 0; } @@ -5565,11 +5901,11 @@ void str_to_file2(const char *fname, char *str, int size, my_bool append) flags|= O_TRUNC; if ((fd= my_open(buff, flags, MYF(MY_WME | MY_FFNF))) < 0) - die("Could not open '%s' for writing: errno = %d", buff, errno); + die("Could not open '%s' for writing, errno: %d", buff, errno); if (append && my_seek(fd, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR) - die("Could not find end of file '%s': errno = %d", buff, errno); + die("Could not find end of file '%s', errno: %d", buff, errno); if (my_write(fd, (uchar*)str, size, MYF(MY_WME|MY_FNABP))) - die("write failed"); + die("write failed, errno: %d", errno); my_close(fd, MYF(0)); } @@ -5589,37 +5925,6 @@ void str_to_file(const char *fname, char *str, int size) } -void dump_result_to_log_file(char *buf, int size) -{ - char log_file[FN_REFLEN]; - str_to_file(fn_format(log_file, result_file_name, opt_logdir, ".log", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - buf, size); - fprintf(stderr, "\nMore results from queries before failure can be found in %s\n", - log_file); -} - -void dump_progress(void) -{ - char progress_file[FN_REFLEN]; - str_to_file(fn_format(progress_file, result_file_name, - opt_logdir, ".progress", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - ds_progress.str, ds_progress.length); -} - -void dump_warning_messages(void) -{ - char warn_file[FN_REFLEN]; - - str_to_file(fn_format(warn_file, result_file_name, opt_logdir, ".warnings", - *opt_logdir ? MY_REPLACE_DIR | MY_REPLACE_EXT : - MY_REPLACE_EXT), - ds_warning_messages.str, ds_warning_messages.length); -} - void check_regerr(my_regex_t* r, int err) { char err_buf[1024]; @@ -5731,7 +6036,7 @@ void fix_win_paths(const char *val, int len) DBUG_PRINT("info", ("pattern: %s", *pattern)); /* Search for the path in string */ - while ((p= strstr(val, *pattern))) + while ((p= strstr((char*)val, *pattern))) { DBUG_PRINT("info", ("Found %s in val p: %s", *pattern, p)); @@ -5864,7 +6169,7 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, { uint max_length= fields[i].max_length + 1; my_bind[i].buffer_type= MYSQL_TYPE_STRING; - my_bind[i].buffer= (char *)my_malloc(max_length, MYF(MY_WME | MY_FAE)); + my_bind[i].buffer= my_malloc(max_length, MYF(MY_WME | MY_FAE)); my_bind[i].buffer_length= max_length; my_bind[i].is_null= &is_null[i]; my_bind[i].length= &length[i]; @@ -5880,7 +6185,7 @@ void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, while (mysql_stmt_fetch(stmt) == 0) { for (i= 0; i < num_fields; i++) - append_field(ds, i, &fields[i], my_bind[i].buffer, + append_field(ds, i, &fields[i], (char*)my_bind[i].buffer, *my_bind[i].length, *my_bind[i].is_null); if (!display_result_vertically) dynstr_append_mem(ds, "\n", 1); @@ -6634,9 +6939,6 @@ void run_query(struct st_connection *cn, struct st_command *command, int flags) init_dynamic_string(&ds_warnings, NULL, 0, 256); - /* Scan for warning before sending to server */ - scan_command_for_warnings(command); - /* Evaluate query if this is an eval command */ @@ -6966,28 +7268,10 @@ void get_command_type(struct st_command* command) } else { - /* -- comment that didn't contain a mysqltest command */ - command->type= Q_COMMENT; - warning_msg("Suspicious command '--%s' detected, was this intentional? "\ - "Use # instead of -- to avoid this warning", - command->query); - - if (command->first_word_len && - strcmp(command->query + command->first_word_len - 1, delimiter) == 0) - { - /* - Detect comment with command using extra delimiter - Ex --disable_query_log; - ^ Extra delimiter causing the command - to be skipped - */ - save= command->query[command->first_word_len-1]; - command->query[command->first_word_len-1]= 0; - if (find_type(command->query, &command_typelib, 1+2) > 0) - die("Extra delimiter \";\" found"); - command->query[command->first_word_len-1]= save; - - } + /* -- "comment" that didn't contain a mysqltest command */ + die("Found line beginning with -- that didn't contain "\ + "a valid mysqltest command, check your syntax or "\ + "use # if you intended to write a comment"); } } @@ -7006,22 +7290,25 @@ void get_command_type(struct st_command* command) /* Record how many milliseconds it took to execute the test file - up until the current line and save it in the dynamic string ds_progress. - - The ds_progress will be dumped to .progress when - test run completes + up until the current line and write it to .progress file */ void mark_progress(struct st_command* command __attribute__((unused)), int line) { + static ulonglong progress_start= 0; // < Beware + DYNAMIC_STRING ds_progress; + char buf[32], *end; ulonglong timer= timer_now(); if (!progress_start) progress_start= timer; timer-= progress_start; + if (init_dynamic_string(&ds_progress, "", 256, 256)) + die("Out of memory"); + /* Milliseconds since start */ end= longlong2str(timer, buf, 10); dynstr_append_mem(&ds_progress, buf, (int)(end-buf)); @@ -7043,6 +7330,10 @@ void mark_progress(struct st_command* command __attribute__((unused)), dynstr_append_mem(&ds_progress, "\n", 1); + progress_file.write(&ds_progress); + + dynstr_free(&ds_progress); + } #ifdef HAVE_STACKTRACE @@ -7077,6 +7368,13 @@ static sig_handler signal_handler(int sig) { fprintf(stderr, "mysqltest got " SIGNAL_FMT "\n", sig); dump_backtrace(); + + fprintf(stderr, "Writing a core file...\n"); + fflush(stderr); + my_write_core(sig); +#ifndef __WIN__ + exit(1); // Shouldn't get here but just in case +#endif } #ifdef __WIN__ @@ -7150,7 +7448,6 @@ int main(int argc, char **argv) my_bool q_send_flag= 0, abort_flag= 0; uint command_executed= 0, last_command_executed= 0; char save_file[FN_REFLEN]; - MY_STAT res_info; MY_INIT(argv[0]); save_file[0]= 0; @@ -7209,11 +7506,14 @@ int main(int argc, char **argv) init_win_path_patterns(); #endif - init_dynamic_string(&ds_res, "", 65536, 65536); - init_dynamic_string(&ds_progress, "", 0, 2048); - init_dynamic_string(&ds_warning_messages, "", 0, 2048); + init_dynamic_string(&ds_res, "", 2048, 2048); + parse_args(argc, argv); + log_file.open(opt_logdir, result_file_name, ".log"); + if (opt_mark_progress) + progress_file.open(opt_logdir, result_file_name, ".progress"); + var_set_int("$PS_PROTOCOL", ps_protocol); var_set_int("$SP_PROTOCOL", sp_protocol); var_set_int("$VIEW_PROTOCOL", view_protocol); @@ -7241,37 +7541,37 @@ int main(int argc, char **argv) if (cursor_protocol_enabled) ps_protocol_enabled= 1; - cur_con= connections; - if (!( mysql_init(&cur_con->mysql))) + st_connection *con= connections; + if (!( mysql_init(&con->mysql))) die("Failed in mysql_init()"); if (opt_compress) - mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS); - mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); - mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, + mysql_options(&con->mysql,MYSQL_OPT_COMPRESS,NullS); + mysql_options(&con->mysql, MYSQL_OPT_LOCAL_INFILE, 0); + mysql_options(&con->mysql, MYSQL_SET_CHARSET_NAME, charset_info->csname); if (opt_charsets_dir) - mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_DIR, + mysql_options(&con->mysql, MYSQL_SET_CHARSET_DIR, opt_charsets_dir); #ifdef HAVE_OPENSSL if (opt_use_ssl) { - mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, + mysql_ssl_set(&con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); #if MYSQL_VERSION_ID >= 50000 /* Turn on ssl_verify_server_cert only if host is "localhost" */ opt_ssl_verify_server_cert= opt_host && !strcmp(opt_host, "localhost"); - mysql_options(&cur_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, + mysql_options(&con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &opt_ssl_verify_server_cert); #endif } #endif - if (!(cur_con->name = my_strdup("default", MYF(MY_WME)))) + if (!(con->name = my_strdup("default", MYF(MY_WME)))) die("Out of memory"); - safe_connect(&cur_con->mysql, cur_con->name, opt_host, opt_user, opt_pass, + safe_connect(&con->mysql, con->name, opt_host, opt_user, opt_pass, opt_db, opt_port, unix_sock); /* Use all time until exit if no explicit 'start_timer' */ @@ -7284,8 +7584,7 @@ int main(int argc, char **argv) */ var_set_errno(-1); - /* Update $mysql_get_server_version to that of current connection */ - var_set_mysql_get_server_version(&cur_con->mysql); + set_current_connection(con); if (opt_include) { @@ -7302,8 +7601,8 @@ int main(int argc, char **argv) command->type != Q_ENABLE_PARSING && command->type != Q_DISABLE_PARSING) { + /* Parsing is disabled, silently convert this line to a comment */ command->type= Q_COMMENT; - scan_command_for_warnings(command); } if (cur_block->ok) @@ -7474,15 +7773,23 @@ int main(int argc, char **argv) select_connection(command); else select_connection_name("slave"); - do_sync_with_master2(0); + do_sync_with_master2(command, 0); break; } case Q_COMMENT: /* Ignore row */ command->last_argument= command->end; break; case Q_PING: - (void) mysql_ping(&cur_con->mysql); - break; + handle_command_error(command, mysql_ping(&cur_con->mysql)); + break; + case Q_SEND_SHUTDOWN: + handle_command_error(command, + mysql_shutdown(&cur_con->mysql, + SHUTDOWN_DEFAULT)); + break; + case Q_SHUTDOWN_SERVER: + do_shutdown_server(command); + break; case Q_EXEC: do_exec(command); command_executed++; @@ -7591,8 +7898,15 @@ int main(int argc, char **argv) parser.current_line += current_line_inc; if ( opt_mark_progress ) mark_progress(command, parser.current_line); + + /* Write result from command to log file immediately */ + log_file.write(&ds_res); + log_file.flush(); + dynstr_set(&ds_res, 0); } + log_file.close(); + start_lineno= 0; if (parsing_disabled) @@ -7601,9 +7915,9 @@ int main(int argc, char **argv) /* The whole test has been executed _sucessfully_. Time to compare result or save it to record file. - The entire output from test is now kept in ds_res. + The entire output from test is in the log file */ - if (ds_res.length) + if (log_file.bytes_written()) { if (result_file_name) { @@ -7611,22 +7925,29 @@ int main(int argc, char **argv) if (record) { - /* Recording - dump the output from test to result file */ - str_to_file(result_file_name, ds_res.str, ds_res.length); + /* Recording */ + + /* save a copy of the log to result file */ + if (my_copy(log_file.file_name(), result_file_name, MYF(0)) != 0) + die("Failed to copy '%s' to '%s', errno: %d", + log_file.file_name(), result_file_name, errno); + } else { - /* Check that the output from test is equal to result file - - detect missing result file - - detect zero size result file - */ - check_result(&ds_res); + /* Check that the output from test is equal to result file */ + check_result(); } } else { - /* No result_file_name specified to compare with, print to stdout */ - printf("%s", ds_res.str); + /* + No result_file_name specified, the result + has been printed to stdout, exit with error + unless script has called "exit" to indicate success + */ + if (abort_flag == 0) + die("Exit with failure! Call 'exit' in script to return with sucess"); } } else @@ -7634,25 +7955,8 @@ int main(int argc, char **argv) die("The test didn't produce any output"); } - if (!command_executed && - result_file_name && my_stat(result_file_name, &res_info, 0)) - { - /* - my_stat() successful on result file. Check if we have not run a - single query, but we do have a result file that contains data. - Note that we don't care, if my_stat() fails. For example, for a - non-existing or non-readable file, we assume it's fine to have - no query output from the test file, e.g. regarded as no error. - */ + if (!command_executed && result_file_name) die("No queries executed but result file found!"); - } - - if ( opt_mark_progress && result_file_name ) - dump_progress(); - - /* Dump warning messages */ - if (result_file_name && ds_warning_messages.length) - dump_warning_messages(); timer_output(); /* Yes, if we got this far the test has suceeded! Sakila smiles */ @@ -7721,12 +8025,11 @@ void do_get_replace_column(struct st_command *command) die("Missing argument in %s", command->query); /* Allocate a buffer for results */ - start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); + start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); while (*from) { char *to; uint column_number; - to= get_string(&buff, &from, command); if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS) die("Wrong column number to replace_column in '%s'", command->query); @@ -7804,7 +8107,7 @@ void do_get_replace(struct st_command *command) bzero((char*) &from_array,sizeof(from_array)); if (!*from) die("Missing argument in %s", command->query); - start= buff= my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); + start= buff= (char*)my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE)); while (*from) { char *to= buff; @@ -7812,6 +8115,9 @@ void do_get_replace(struct st_command *command) if (!*from) die("Wrong number of arguments to replace_result in '%s'", command->query); +#ifdef __WIN__ + fix_win_paths(to, from - to); +#endif insert_pointer_name(&from_array,to); to= get_string(&buff, &from, command); insert_pointer_name(&to_array,to); diff --git a/cmd-line-utils/libedit/Makefile.am b/cmd-line-utils/libedit/Makefile.am index 23ee20894ec..ddafa4aab44 100644 --- a/cmd-line-utils/libedit/Makefile.am +++ b/cmd-line-utils/libedit/Makefile.am @@ -1,6 +1,4 @@ ## Process this file with automake to create Makefile.in -# Makefile for the GNU readline library. -# Copyright (C) 1994,1996,1997 Free Software Foundation, Inc. ASRC = $(srcdir)/vi.c $(srcdir)/emacs.c $(srcdir)/common.c AHDR = vi.h emacs.h common.h @@ -12,33 +10,23 @@ noinst_LIBRARIES = libedit.a libedit_a_SOURCES = chared.c el.c history.c map.c prompt.c readline.c \ search.c tokenizer.c vi.c common.c emacs.c \ hist.c key.c parse.c read.c refresh.c sig.c term.c \ - tty.c help.c fcns.c - -EXTRA_libedit_a_SOURCES = np/unvis.c np/strlcpy.c np/vis.c np/strlcat.c \ - np/fgetln.c + tty.c help.c fcns.c filecomplete.c \ + np/unvis.c np/strlcpy.c np/vis.c np/strlcat.c \ + np/fgetln.c libedit_a_LIBADD = @LIBEDIT_LOBJECTS@ libedit_a_DEPENDENCIES = @LIBEDIT_LOBJECTS@ -noinst_HEADERS = readline/readline.h \ -\ - chared.h el.h el_term.h histedit.h key.h parse.h refresh.h sig.h \ - sys.h tokenizer.h config.h hist.h map.h prompt.h read.h \ - search.h tty.h libedit_term.h vis.h +pkginclude_HEADERS = readline/readline.h -EXTRA_DIST = makelist.sh np/unvis.c np/strlcpy.c np/vis.c np/vis.h np/strlcat.c np/fgetln.c +noinst_HEADERS = chared.h el.h el_term.h histedit.h key.h parse.h refresh.h sig.h \ + sys.h config.h hist.h map.h prompt.h read.h \ + search.h tty.h filecomplete.h np/vis.h + +EXTRA_DIST = makelist.sh CLEANFILES = makelist common.h emacs.h vi.h fcns.h help.h fcns.c help.c -# Make sure to include stuff from this directory first, to get right "config.h" -# Automake puts into DEFAULT_INCLUDES this source and corresponding -# build directory together with ../../include to let all make files -# find the central "config.h". This variable is used before INCLUDES -# above. But in automake 1.10 the order of these are changed. Put the -# includes of this directory into DEFS to always be sure it is first -# before DEFAULT_INCLUDES on the compile line. -DEFS = -DUNDEF_THREADS_HACK -DHAVE_CONFIG_H -DNO_KILL_INTR -I. -I$(srcdir) - SUFFIXES = .sh .sh: @@ -101,6 +89,4 @@ term.o: vi.h emacs.h common.h help.h fcns.h tty.o: vi.h emacs.h common.h help.h fcns.h help.o: vi.h emacs.h common.h help.h fcns.h fcns.o: vi.h emacs.h common.h help.h fcns.h - -# Don't update the files from bitkeeper -%::SCCS/s.% +filecomplete.o: vi.h emacs.h common.h help.h fcns.h diff --git a/cmd-line-utils/libedit/README b/cmd-line-utils/libedit/README new file mode 100644 index 00000000000..0b698a6150d --- /dev/null +++ b/cmd-line-utils/libedit/README @@ -0,0 +1,50 @@ +An approximate method to merge from upstream is: + + # Fetch latest from upstream (we also include some compat stuff) + $ CVS_RSH=ssh; export CVS_RSH + $ CVSROOT="anoncvs@stripped:/cvsroot" + $ cvs co -d libedit -P src/lib/libedit + $ mkdir libedit/np + $ for f in src/common/lib/libc/string/strlcat.c \ + > src/common/lib/libc/string/strlcpy.c \ + > src/include/vis.h \ + > src/lib/libc/gen/unvis.c \ + > src/lib/libc/gen/vis.c \ + > src/tools/compat/fgetln.c + > do + > cvs co -P ${f} + > mv ${f} libedit/np + > done + $ rm -rf src + $ cd libedit + + # Remove files we don't need/use + $ rm -rf CVS TEST Makefile shlib_version *.[0-9] + $ (cd readline; rm -rf CVS Makefile) + + # Rename files to match our naming + $ mv makelist makelist.sh + $ mv term.h el_term.h + + # Remove NetBSD-specific bits + $ for file in $(find . -type f) + > do + > cp ${file} ${file}.orig + > sed -e 's/#include "term.h"/#include "el_term.h"/g' \ + > -e 's/sig_handler/el_sig_handler/g' \ + > -e 's/isprint/el_isprint/g' \ + > -e '/^__RCSID/d' \ + > ${file}.orig >${file} + > rm ${file}.orig + > done + +then merge remaining bits by hand. All MySQL-specific changes should be +marked with XXXMYSQL to make them easier to identify and merge. To generate +a 'clean' diff against upstream you can use the above commands but use + + cvs co -D "2009/02/06 20:09:00" [..] + +to fetch the baseline of most recent merge. + +Please feed any fixes to Jonathan Perkin who will endeavour +to merge them upstream and keep diffs minimal. diff --git a/cmd-line-utils/libedit/TEST/test.c b/cmd-line-utils/libedit/TEST/test.c deleted file mode 100644 index 605341eac62..00000000000 --- a/cmd-line-utils/libedit/TEST/test.c +++ /dev/null @@ -1,269 +0,0 @@ -/* $NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $ */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas of Cornell University. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include "compat.h" -#ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ - The Regents of the University of California. All rights reserved.\n"); -#endif /* not lint */ - -#if !defined(lint) && !defined(SCCSID) -#if 0 -static char sccsid[] = "@(#)test.c 8.1 (Berkeley) 6/4/93"; -#else -__RCSID("$NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $"); -#endif -#endif /* not lint && not SCCSID */ - -/* - * test.c: A little test program - */ -#include "sys.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "histedit.h" -#include "tokenizer.h" - -static int continuation = 0; -static EditLine *el = NULL; - -static u_char complete(EditLine *, int); - int main(int, char **); -static char *prompt(EditLine *); -static void sig(int); - -static char * -prompt(EditLine *el) -{ - static char a[] = "Edit$"; - static char b[] = "Edit>"; - - return (continuation ? b : a); -} - -static void -sig(int i) -{ - - (void) fprintf(stderr, "Got signal %d.\n", i); - el_reset(el); -} - -static unsigned char -complete(EditLine *el, int ch) -{ - DIR *dd = opendir("."); - struct dirent *dp; - const char* ptr; - const LineInfo *lf = el_line(el); - int len; - - /* - * Find the last word - */ - for (ptr = lf->cursor - 1; !isspace(*ptr) && ptr > lf->buffer; ptr--) - continue; - len = lf->cursor - ++ptr; - - for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { - if (len > strlen(dp->d_name)) - continue; - if (strncmp(dp->d_name, ptr, len) == 0) { - closedir(dd); - if (el_insertstr(el, &dp->d_name[len]) == -1) - return (CC_ERROR); - else - return (CC_REFRESH); - } - } - - closedir(dd); - return (CC_ERROR); -} - -int -main(int argc, char *argv[]) -{ - int num; - const char *buf; - Tokenizer *tok; - int lastevent = 0, ncontinuation; - History *hist; - HistEvent ev; - - (void) signal(SIGINT, sig); - (void) signal(SIGQUIT, sig); - (void) signal(SIGHUP, sig); - (void) signal(SIGTERM, sig); - - hist = history_init(); /* Init the builtin history */ - /* Remember 100 events */ - history(hist, &ev, H_SETSIZE, 100); - - tok = tok_init(NULL); /* Initialize the tokenizer */ - - /* Initialize editline */ - el = el_init(*argv, stdin, stdout, stderr); - - el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */ - el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */ - el_set(el, EL_PROMPT, prompt); /* Set the prompt function */ - - /* Tell editline to use this history interface */ - el_set(el, EL_HIST, history, hist); - - /* Add a user-defined function */ - el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete); - - /* Bind tab to it */ - el_set(el, EL_BIND, "^I", "ed-complete", NULL); - - /* - * Bind j, k in vi command mode to previous and next line, instead - * of previous and next history. - */ - el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL); - el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL); - - /* - * Source the user's defaults file. - */ - el_source(el, NULL); - - while ((buf = el_gets(el, &num)) != NULL && num != 0) { - int ac; - char **av; -#ifdef DEBUG - (void) fprintf(stderr, "got %d %s", num, buf); -#endif - if (!continuation && num == 1) - continue; - - if (tok_line(tok, buf, &ac, &av) > 0) - ncontinuation = 1; - -#if 0 - if (continuation) { - /* - * Append to the right event in case the user - * moved around in history. - */ - if (history(hist, &ev, H_SET, lastevent) == -1) - err(1, "%d: %s\n", lastevent, ev.str); - history(hist, &ev, H_ADD , buf); - } else { - history(hist, &ev, H_ENTER, buf); - lastevent = ev.num; - } -#else - /* Simpler */ - history(hist, &ev, continuation ? H_APPEND : H_ENTER, buf); -#endif - - continuation = ncontinuation; - ncontinuation = 0; - - if (strcmp(av[0], "history") == 0) { - int rv; - - switch (ac) { - case 1: - for (rv = history(hist, &ev, H_LAST); rv != -1; - rv = history(hist, &ev, H_PREV)) - (void) fprintf(stdout, "%4d %s", - ev.num, ev.str); - break; - - case 2: - if (strcmp(av[1], "clear") == 0) - history(hist, &ev, H_CLEAR); - else - goto badhist; - break; - - case 3: - if (strcmp(av[1], "load") == 0) - history(hist, &ev, H_LOAD, av[2]); - else if (strcmp(av[1], "save") == 0) - history(hist, &ev, H_SAVE, av[2]); - break; - - badhist: - default: - (void) fprintf(stderr, - "Bad history arguments\n"); - break; - } - } else if (el_parse(el, ac, av) == -1) { - switch (fork()) { - case 0: - execvp(av[0], av); - perror(av[0]); - _exit(1); - /*NOTREACHED*/ - break; - - case -1: - perror("fork"); - break; - - default: - if (wait(&num) == -1) - perror("wait"); - (void) fprintf(stderr, "Exit %x\n", num); - break; - } - } - - tok_reset(tok); - } - - el_end(el); - tok_end(tok); - history_end(hist); - - return (0); -} diff --git a/cmd-line-utils/libedit/chared.c b/cmd-line-utils/libedit/chared.c index 4cb6e00d26e..e4823db7147 100644 --- a/cmd-line-utils/libedit/chared.c +++ b/cmd-line-utils/libedit/chared.c @@ -1,4 +1,4 @@ -/* $NetBSD: chared.c,v 1.22 2004/08/13 12:10:38 mycroft Exp $ */ +/* $NetBSD: chared.c,v 1.26 2009/02/06 12:45:25 sketch Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * chared.c: Character editor utilities @@ -40,6 +46,8 @@ #include #include "el.h" +private void ch__clearmacro (EditLine *); + /* value to leave unused in line buffer */ #define EL_LEAVE 2 @@ -51,13 +59,13 @@ cv_undo(EditLine *el) { c_undo_t *vu = &el->el_chared.c_undo; c_redo_t *r = &el->el_chared.c_redo; - int size; + unsigned int size; /* Save entire line for undo */ size = el->el_line.lastchar - el->el_line.buffer; vu->len = size; vu->cursor = el->el_line.cursor - el->el_line.buffer; - memcpy(vu->buf, el->el_line.buffer, (size_t)size); + memcpy(vu->buf, el->el_line.buffer, size); /* save command info for redo */ r->count = el->el_state.doingarg ? el->el_state.argument : 0; @@ -439,6 +447,8 @@ cv__endword(char *p, char *high, int n, int (*wtest)(int)) protected int ch_init(EditLine *el) { + c_macro_t *ma = &el->el_chared.c_macro; + el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ); if (el->el_line.buffer == NULL) return (-1); @@ -479,11 +489,10 @@ ch_init(EditLine *el) el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; - el->el_chared.c_macro.level = -1; - el->el_chared.c_macro.offset = 0; - el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO * - sizeof(char *)); - if (el->el_chared.c_macro.macro == NULL) + ma->level = -1; + ma->offset = 0; + ma->macro = (char **) el_malloc(EL_MAXMACRO * sizeof(char *)); + if (ma->macro == NULL) return (-1); return (0); } @@ -492,7 +501,7 @@ ch_init(EditLine *el) * Reset the character editor */ protected void -ch_reset(EditLine *el) +ch_reset(EditLine *el, int mclear) { el->el_line.cursor = el->el_line.buffer; el->el_line.lastchar = el->el_line.buffer; @@ -513,9 +522,19 @@ ch_reset(EditLine *el) el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; - el->el_chared.c_macro.level = -1; - el->el_history.eventno = 0; + + if (mclear) + ch__clearmacro(el); +} + +private void +ch__clearmacro(el) + EditLine *el; +{ + c_macro_t *ma = &el->el_chared.c_macro; + while (ma->level >= 0) + el_free((ptr_t)ma->macro[ma->level--]); } /* ch_enlargebufs(): @@ -623,9 +642,9 @@ ch_end(EditLine *el) el->el_chared.c_redo.cmd = ED_UNASSIGNED; el_free((ptr_t) el->el_chared.c_kill.buf); el->el_chared.c_kill.buf = NULL; + ch_reset(el, 1); el_free((ptr_t) el->el_chared.c_macro.macro); el->el_chared.c_macro.macro = NULL; - ch_reset(el); } diff --git a/cmd-line-utils/libedit/chared.h b/cmd-line-utils/libedit/chared.h index 2dd0a5795c7..fa8f5a58d83 100644 --- a/cmd-line-utils/libedit/chared.h +++ b/cmd-line-utils/libedit/chared.h @@ -1,4 +1,4 @@ -/* $NetBSD: chared.h,v 1.14 2004/08/13 12:10:39 mycroft Exp $ */ +/* $NetBSD: chared.h,v 1.17 2006/03/06 21:11:56 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -48,7 +48,7 @@ #define EL_MAXMACRO 10 /* - * This is a issue of basic "vi" look-and-feel. Defining VI_MOVE works + * This is an issue of basic "vi" look-and-feel. Defining VI_MOVE works * like real vi: i.e. the transition from command<->insert modes moves * the cursor. * @@ -116,11 +116,10 @@ typedef struct el_chared_t { } el_chared_t; -#define STReof "^D\b\b" #define STRQQ "\"\"" #define isglob(a) (strchr("*[]?", (a)) != NULL) -#define isword(a) (isprint(a)) +#define isword(a) (el_isprint(a)) #define NOP 0x00 #define DELETE 0x01 @@ -161,7 +160,7 @@ protected int c_gets(EditLine *, char *, const char *); protected int c_hpos(EditLine *); protected int ch_init(EditLine *); -protected void ch_reset(EditLine *); +protected void ch_reset(EditLine *, int); protected int ch_enlargebufs(EditLine *, size_t); protected void ch_end(EditLine *); diff --git a/cmd-line-utils/libedit/common.c b/cmd-line-utils/libedit/common.c index 81bf9bf29ff..d4d024eae10 100644 --- a/cmd-line-utils/libedit/common.c +++ b/cmd-line-utils/libedit/common.c @@ -1,4 +1,4 @@ -/* $NetBSD: common.c,v 1.16 2003/08/07 16:44:30 agc Exp $ */ +/* $NetBSD: common.c,v 1.21 2008/09/30 08:37:42 aymeric Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * common.c: Common Editor functions @@ -130,7 +136,7 @@ ed_delete_prev_word(EditLine *el, int c __attribute__((__unused__))) */ protected el_action_t /*ARGSUSED*/ -ed_delete_next_char(EditLine *el, int c __attribute__((__unused__))) +ed_delete_next_char(EditLine *el, int c) { #ifdef notdef /* XXX */ #define EL el->el_line @@ -147,9 +153,8 @@ ed_delete_next_char(EditLine *el, int c __attribute__((__unused__))) #ifdef KSHVI return (CC_ERROR); #else - term_overwrite(el, STReof, 4); - /* then do a EOF */ - term__flush(); + /* then do an EOF */ + term_writechar(el, c); return (CC_EOF); #endif } else { @@ -207,13 +212,13 @@ ed_move_to_end(EditLine *el, int c __attribute__((__unused__))) el->el_line.cursor = el->el_line.lastchar; if (el->el_map.type == MAP_VI) { -#ifdef VI_MOVE - el->el_line.cursor--; -#endif if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } +#ifdef VI_MOVE + el->el_line.cursor--; +#endif } return (CC_CURSOR); } @@ -609,7 +614,7 @@ protected el_action_t ed_start_over(EditLine *el, int c __attribute__((__unused__))) { - ch_reset(el); + ch_reset(el, 0); return (CC_REFRESH); } @@ -904,7 +909,7 @@ ed_command(EditLine *el, int c __attribute__((__unused__))) int tmplen; tmplen = c_gets(el, tmpbuf, "\n: "); - term__putc('\n'); + term__putc(el, '\n'); if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1) term_beep(el); diff --git a/cmd-line-utils/libedit/compat.h b/cmd-line-utils/libedit/compat.h deleted file mode 100644 index 3693a2db809..00000000000 --- a/cmd-line-utils/libedit/compat.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef __LIBEDIT_COMPATH_H -#define __LIBEDIT_COMPATH_H - -#define __RCSID(x) -#define __COPYRIGHT(x) - -#include "compat_conf.h" - -#ifndef HAVE_VIS_H -/* string visual representation - may want to reimplement */ -#define strvis(d,s,m) strcpy(d,s) -#define strunvis(d,s) strcpy(d,s) -#endif - -#ifndef HAVE_FGETLN -#include "fgetln.h" -#endif - -#ifndef HAVE_ISSETUGID -#define issetugid() (getuid()!=geteuid() || getegid()!=getgid()) -#endif - -#ifndef HAVE_STRLCPY -#include "strlcpy.h" -#endif - -#if HAVE_SYS_CDEFS_H -#include -#endif - -#ifndef __P -#ifdef __STDC__ -#define __P(x) x -#else -#define __P(x) () -#endif -#endif - -#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) -#define __attribute__(A) -#endif - -#endif diff --git a/cmd-line-utils/libedit/compat_conf.h b/cmd-line-utils/libedit/compat_conf.h deleted file mode 100644 index e2b9557f5b1..00000000000 --- a/cmd-line-utils/libedit/compat_conf.h +++ /dev/null @@ -1,2 +0,0 @@ - -#include "my_config.h" diff --git a/cmd-line-utils/libedit/config.h b/cmd-line-utils/libedit/config.h index 642123d1ddc..2c3989ee316 100644 --- a/cmd-line-utils/libedit/config.h +++ b/cmd-line-utils/libedit/config.h @@ -1,16 +1,2 @@ - #include "my_config.h" #include "sys.h" - -#if defined(LIBC_SCCS) && !defined(lint) -#define __RCSID(x) -#define __COPYRIGHT(x) -#endif -#define __RENAME(x) -#define _DIAGASSERT(x) - -#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) -#define __attribute__(A) -#endif - - diff --git a/cmd-line-utils/libedit/editline.3 b/cmd-line-utils/libedit/editline.3 deleted file mode 100644 index 1b812ebcc79..00000000000 --- a/cmd-line-utils/libedit/editline.3 +++ /dev/null @@ -1,619 +0,0 @@ -.\" $NetBSD: editline.3,v 1.21 2001/04/02 18:29:49 wiz Exp $ -.\" -.\" Copyright (c) 1997-1999 The NetBSD Foundation, Inc. -.\" All rights reserved. -.\" -.\" This file was contributed to The NetBSD Foundation by Luke Mewburn. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. Neither the name of The NetBSD Foundation nor the names of its -.\" contributors may be used to endorse or promote products derived -.\" from this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd November 12, 1999 -.Os -.Dt EDITLINE 3 -.Sh NAME -.Nm editline , -.Nm el_init , -.Nm el_end , -.Nm el_reset , -.Nm el_gets , -.Nm el_getc , -.Nm el_push , -.Nm el_parse , -.Nm el_set , -.Nm el_source , -.Nm el_resize , -.Nm el_line , -.Nm el_insertstr , -.Nm el_deletestr , -.Nm history_init , -.Nm history_end , -.Nm history -.Nd line editor and history functions -.Sh LIBRARY -.Lb libedit -.Sh SYNOPSIS -.Fd #include -.Ft EditLine * -.Fn el_init "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr" -.Ft void -.Fn el_end "EditLine *e" -.Ft void -.Fn el_reset "EditLine *e" -.Ft const char * -.Fn el_gets "EditLine *e" "int *count" -.Ft int -.Fn el_getc "EditLine *e" "char *ch" -.Ft void -.Fn el_push "EditLine *e" "const char *str" -.Ft int -.Fn el_parse "EditLine *e" "int argc" "char *argv[]" -.Ft int -.Fn el_set "EditLine *e" "int op" "..." -.Ft int -.Fn el_get "EditLine *e" "int op" "void *result" -.Ft int -.Fn el_source "EditLine *e" "const char *file" -.Ft void -.Fn el_resize "EditLine *e" -.Ft const LineInfo * -.Fn el_line "EditLine *e" -.Ft int -.Fn el_insertstr "EditLine *e" "const char *str" -.Ft void -.Fn el_deletestr "EditLine *e" "int count" -.Ft History * -.Fn history_init -.Ft void -.Fn history_end "History *h" -.Ft int -.Fn history "History *h" "HistEvent *ev" "int op" "..." -.Sh DESCRIPTION -The -.Nm -library provides generic line editing and history functions, -similar to those found in -.Xr sh 1 . -.Pp -These functions are available in the -.Nm libedit -library (which needs the -.Nm libtermcap -library). -Programs should be linked with -.Fl ledit ltermcap . -.Sh LINE EDITING FUNCTIONS -The line editing functions use a common data structure, -.Fa EditLine , -which is created by -.Fn el_init -and freed by -.Fn el_end . -.Pp -The following functions are available: -.Bl -tag -width 4n -.It Fn el_init -Initialise the line editor, and return a data structure -to be used by all other line editing functions. -.Fa prog -is the name of the invoking program, used when reading the -.Xr editrc 5 -file to determine which settings to use. -.Fa fin , -.Fa fout -and -.Fa ferr -are the input, output, and error streams (respectively) to use. -In this documentation, references to -.Dq the tty -are actually to this input/output stream combination. -.It Fn el_end -Clean up and finish with -.Fa e , -assumed to have been created with -.Fn el_init . -.It Fn el_reset -Reset the tty and the parser. -This should be called after an error which may have upset the tty's -state. -.It Fn el_gets -Read a line from the tty. -.Fa count -is modified to contain the number of characters read. -Returns the line read if successful, or -.Dv NULL -if no characters were read or if an error occurred. -.It Fn el_getc -Read a character from the tty. -.Fa ch -is modified to contain the character read. -Returns the number of characters read if successful, -1 otherwise. -.It Fn el_push -Pushes -.Fa str -back onto the input stream. -This is used by the macro expansion mechanism. -Refer to the description of -.Ic bind -.Fl s -in -.Xr editrc 5 -for more information. -.It Fn el_parse -Parses the -.Fa argv -array (which is -.Fa argc -elements in size) -to execute builtin -.Nm -commands. -If the command is prefixed with -.Dq prog: -then -.Fn el_parse -will only execute the command if -.Dq prog -matches the -.Fa prog -argument supplied to -.Fn el_init . -The return value is --1 if the command is unknown, -0 if there was no error or -.Dq prog -didn't match, or -1 if the command returned an error. -Refer to -.Xr editrc 5 -for more information. -.It Fn el_set -Set -.Nm -parameters. -.Fa op -determines which parameter to set, and each operation has its -own parameter list. -.Pp -The following values for -.Fa op -are supported, along with the required argument list: -.Bl -tag -width 4n -.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" -Define prompt printing function as -.Fa f , -which is to return a string that contains the prompt. -.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" -Define right side prompt printing function as -.Fa f , -which is to return a string that contains the prompt. -.It Dv EL_TERMINAL , Fa "const char *type" -Define terminal type of the tty to be -.Fa type , -or to -.Ev TERM -if -.Fa type -is -.Dv NULL . -.It Dv EL_EDITOR , Fa "const char *mode" -Set editing mode to -.Fa mode , -which must be one of -.Dq emacs -or -.Dq vi . -.It Dv EL_SIGNAL , Fa "int flag" -If -.Fa flag -is non-zero, -.Nm -will install its own signal handler for the following signals when -reading command input: -.Dv SIGCONT , -.Dv SIGHUP , -.Dv SIGINT , -.Dv SIGQUIT , -.Dv SIGSTOP , -.Dv SIGTERM , -.Dv SIGTSTP , -and -.Dv SIGWINCH . -Otherwise, the current signal handlers will be used. -.It Dv EL_BIND , Xo -.Fa "const char *" , -.Fa "..." , -.Dv NULL -.Xc -Perform the -.Ic bind -builtin command. -Refer to -.Xr editrc 5 -for more information. -.It Dv EL_ECHOTC , Xo -.Fa "const char *" , -.Fa "..." , -.Dv NULL -.Xc -Perform the -.Ic echotc -builtin command. -Refer to -.Xr editrc 5 -for more information. -.It Dv EL_SETTC , Xo -.Fa "const char *" , -.Fa "..." , -.Dv NULL -.Xc -Perform the -.Ic settc -builtin command. -Refer to -.Xr editrc 5 -for more information. -.It Dv EL_SETTY , Xo -.Fa "const char *" , -.Fa "..." , -.Dv NULL -.Xc -Perform the -.Ic setty -builtin command. -Refer to -.Xr editrc 5 -for more information. -.It Dv EL_TELLTC , Xo -.Fa "const char *" , -.Fa "..." , -.Dv NULL -.Xc -Perform the -.Ic telltc -builtin command. -Refer to -.Xr editrc 5 -for more information. -.It Dv EL_ADDFN , Xo -.Fa "const char *name" , -.Fa "const char *help" , -.Fa "unsigned char (*func)(EditLine *e, int ch) -.Xc -Add a user defined function, -.Fn func , -referred to as -.Fa name -which is invoked when a key which is bound to -.Fa name -is entered. -.Fa help -is a description of -.Fa name . -At invocation time, -.Fa ch -is the key which caused the invocation. -The return value of -.Fn func -should be one of: -.Bl -tag -width "CC_REDISPLAY" -.It Dv CC_NORM -Add a normal character. -.It Dv CC_NEWLINE -End of line was entered. -.It Dv CC_EOF -EOF was entered. -.It Dv CC_ARGHACK -Expecting further command input as arguments, do nothing visually. -.It Dv CC_REFRESH -Refresh display. -.It Dv CC_REFRESH_BEEP -Refresh display, and beep. -.It Dv CC_CURSOR -Cursor moved, so update and perform -.Dv CC_REFRESH. -.It Dv CC_REDISPLAY -Redisplay entire input line. -This is useful if a key binding outputs extra information. -.It Dv CC_ERROR -An error occurred. -Beep, and flush tty. -.It Dv CC_FATAL -Fatal error, reset tty to known state. -.El -.It Dv EL_HIST , Xo -.Fa "History *(*func)(History *, int op, ...)" , -.Fa "const char *ptr" -.Xc -Defines which history function to use, which is usually -.Fn history . -.Fa ptr -should be the value returned by -.Fn history_init . -.It Dv EL_EDITMODE , Fa "int flag" -If -.Fa flag -is non-zero, -editing is enabled (the default). -Note that this is only an indication, and does not -affect the operation of -.Nm "" . -At this time, it is the caller's responsibility to -check this -(using -.Fn el_get ) -to determine if editing should be enabled or not. -.El -.It Fn el_get -Get -.Nm -parameters. -.Fa op -determines which parameter to retrieve into -.Fa result . -.Pp -The following values for -.Fa op -are supported, along with actual type of -.Fa result : -.Bl -tag -width 4n -.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)" -Return a pointer to the function that displays the prompt. -.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)" -Return a pointer to the function that displays the rightside prompt. -.It Dv EL_EDITOR , Fa "const char *" -Return the name of the editor, which will be one of -.Dq emacs -or -.Dq vi . -.It Dv EL_SIGNAL , Fa "int *" -Return non-zero if -.Nm -has installed private signal handlers (see -.Fn el_get -above). -.It Dv EL_EDITMODE, Fa "int *" -Return non-zero if editing is enabled. -.El -.It Fn el_source -Initialise -.Nm -by reading the contents of -.Fa file . -.Fn el_parse -is called for each line in -.Fa file . -If -.Fa file -is -.Dv NULL , -try -.Pa $PWD/.editrc -then -.Pa $HOME/.editrc . -Refer to -.Xr editrc 5 -for details on the format of -.Fa file . -.It Fn el_resize -Must be called if the terminal size changes. -If -.Dv EL_SIGNAL -has been set with -.Fn el_set , -then this is done automatically. -Otherwise, it's the responsibility of the application to call -.Fn el_resize -on the appropriate occasions. -.It Fn el_line -Return the editing information for the current line in a -.Fa LineInfo -structure, which is defined as follows: -.Bd -literal -typedef struct lineinfo { - const char *buffer; /* address of buffer */ - const char *cursor; /* address of cursor */ - const char *lastchar; /* address of last character */ -} LineInfo; -.Ed -.It Fn el_insertstr -Insert -.Fa str -into the line at the cursor. -Returns -1 if -.Fa str -is empty or won't fit, and 0 otherwise. -.It Fn el_deletestr -Delete -.Fa num -characters before the cursor. -.El -.Sh HISTORY LIST FUNCTIONS -The history functions use a common data structure, -.Fa History , -which is created by -.Fn history_init -and freed by -.Fn history_end . -.Pp -The following functions are available: -.Bl -tag -width 4n -.It Fn history_init -Initialise the history list, and return a data structure -to be used by all other history list functions. -.It Fn history_end -Clean up and finish with -.Fa h , -assumed to have been created with -.Fn history_init . -.It Fn history -Perform operation -.Fa op -on the history list, with optional arguments as needed by the -operation. -.Fa ev -is changed accordingly to operation. -The following values for -.Fa op -are supported, along with the required argument list: -.Bl -tag -width 4n -.It Dv H_SETSIZE , Fa "int size" -Set size of history to -.Fa size -elements. -.It Dv H_GETSIZE -Get number of events currently in history. -.It Dv H_END -Cleans up and finishes with -.Fa h , -assumed to be created with -.Fn history_init . -.It Dv H_CLEAR -Clear the history. -.It Dv H_FUNC , Xo -.Fa "void *ptr" , -.Fa "history_gfun_t first" , -.Fa "history_gfun_t next" , -.Fa "history_gfun_t last" , -.Fa "history_gfun_t prev" , -.Fa "history_gfun_t curr" , -.Fa "history_sfun_t set" , -.Fa "history_vfun_t clear" , -.Fa "history_efun_t enter" , -.Fa "history_efun_t add" -.Xc -Define functions to perform various history operations. -.Fa ptr -is the argument given to a function when it's invoked. -.It Dv H_FIRST -Return the first element in the history. -.It Dv H_LAST -Return the last element in the history. -.It Dv H_PREV -Return the previous element in the history. -.It Dv H_NEXT -Return the next element in the history. -.It Dv H_CURR -Return the current element in the history. -.It Dv H_SET -Set the cursor to point to the requested element. -.It Dv H_ADD , Fa "const char *str" -Append -.Fa str -to the current element of the history, or create an element with -.It Dv H_APPEND , Fa "const char *str" -Append -.Fa str -to the last new element of the history. -.It Dv H_ENTER , Fa "const char *str" -Add -.Fa str -as a new element to the history, and, if necessary, -removing the oldest entry to keep the list to the created size. -.It Dv H_PREV_STR , Fa "const char *str" -Return the closest previous event that starts with -.Fa str . -.It Dv H_NEXT_STR , Fa "const char *str" -Return the closest next event that starts with -.Fa str . -.It Dv H_PREV_EVENT , Fa "int e" -Return the previous event numbered -.Fa e . -.It Dv H_NEXT_EVENT , Fa "int e" -Return the next event numbered -.Fa e . -.It Dv H_LOAD , Fa "const char *file" -Load the history list stored in -.Fa file . -.It Dv H_SAVE , Fa "const char *file" -Save the history list to -.Fa file . -.El -.Pp -.Fn history -returns 0 if the operation -.Fa op -succeeds. Otherwise, -1 is returned and -.Fa ev -is updated to contain more details about the error. -.El -.\"XXX.Sh EXAMPLES -.\"XXX: provide some examples -.Sh SEE ALSO -.Xr editrc 5 , -.Xr sh 1 , -.Xr signal 3 , -.Xr termcap 3 -.Sh HISTORY -The -.Nm -library first appeared in -.Bx 4.4 . -.Dv CC_REDISPLAY -appeared in -.Nx 1.3 . -.Dv CC_REFRESH_BEEP , -.Dv EL_EDITMODE -and the readline emulation appeared in -.Nx 1.4 . -.Dv EL_RPROMPT -appeared in -.Nx 1.5 . -.Sh AUTHORS -The -.Nm -library was written by Christos Zoulas. -Luke Mewburn wrote this manual and implemented -.Dv CC_REDISPLAY , -.Dv CC_REFRESH_BEEP , -.Dv EL_EDITMODE , -and -.Dv EL_RPROMPT . -Jaromir Dolecek implemented the readline emulation. -.Sh BUGS -The tokenization functions are not publically defined in -.Fd . -.Pp -At this time, it is the responsibility of the caller to -check the result of the -.Dv EL_EDITMODE -operation of -.Fn el_get -(after an -.Fn el_source -or -.Fn el_parse ) -to determine if -.Nm -should be used for further input. -I.e., -.Dv EL_EDITMODE -is purely an indication of the result of the most recent -.Xr editrc 5 -.Ic edit -command. diff --git a/cmd-line-utils/libedit/editrc.5 b/cmd-line-utils/libedit/editrc.5 deleted file mode 100644 index b1122618939..00000000000 --- a/cmd-line-utils/libedit/editrc.5 +++ /dev/null @@ -1,491 +0,0 @@ -.\" $NetBSD: editrc.5,v 1.11 2001/06/19 13:42:09 wiz Exp $ -.\" -.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc. -.\" All rights reserved. -.\" -.\" This file was contributed to The NetBSD Foundation by Luke Mewburn. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. Neither the name of The NetBSD Foundation nor the names of its -.\" contributors may be used to endorse or promote products derived -.\" from this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd November 8, 2000 -.Os -.Dt EDITRC 5 -.Sh NAME -.Nm editrc -.Nd configuration file for editline library -.Sh SYNOPSIS -.Nm -.Sh DESCRIPTION -The -.Nm -file defines various settings to be used by the -.Xr editline 3 -library. -.Pp -The format of each line is: -.Dl [prog:]command [arg [...]] -.Pp -.Ar command -is one of the -.Xr editline 3 -builtin commands. -Refer to -.Sx BUILTIN COMMANDS -for more information. -.Pp -.Ar prog -is the program name string that a program defines when it calls -.Xr el_init 3 -to setup -.Xr editline 3 , -which is usually -.Va argv[0] . -.Ar command -will be executed for any program which matches -.Ar prog . -.Pp -.Ar prog -may also be a -.Xr regex 3 -style -regular expression, in which case -.Ar command -will be executed for any program that matches the regular expression. -.Pp -If -.Ar prog -is absent, -.Ar command -is executed for all programs. -.Sh BUILTIN COMMANDS -The -.Nm editline -library has some builtin commands, which affect the way -that the line editing and history functions operate. -These are based on similar named builtins present in the -.Xr tcsh 1 -shell. -.Pp -The following builtin commands are available: -.Bl -tag -width 4n -.It Ic bind Xo -.Op Fl a -.Op Fl e -.Op Fl k -.Op Fl l -.Op Fl r -.Op Fl s -.Op Fl v -.Op Ar key Op Ar command -.Xc -Without options, list all bound keys, and the editor command to which -each is bound. -If -.Ar key -is supplied, show the bindings for -.Ar key . -If -.Ar key command -is supplied, bind -.Ar command -to -.Ar key . -Options include: -.Bl -tag -width 4n -.It Fl e -Bind all keys to the standard GNU Emacs-like bindings. -.It Fl v -Bind all keys to the standard -.Xr vi 1 -like -bindings. -.It Fl a -List or change key bindings in the -.Xr vi 1 -mode alternate (command mode) key map. -.It Fl k -.Ar key -is interpreted as a symbolic arrow key name, which may be one of -.Sq up , -.Sq down , -.Sq left -or -.Sq right . -.It Fl l -List all editor commands and a short description of each. -.It Fl r -Remove a key's binding. -.It Fl s -.Ar command -is taken as a literal string and treated as terminal input when -.Ar key -is typed. -Bound keys in -.Ar command -are themselves reinterpreted, and this continues for ten levels of -interpretation. -.El -.Pp -.Ar command -may be one of the commands documented in -.Sx "EDITOR COMMANDS" -below, or another key. -.Pp -.Ar key -and -.Ar command -can contain control characters of the form -.Sm off -.Sq No ^ Ar character -.Sm on -.Po -e.g. -.Sq ^A -.Pc , -and the following backslashed escape sequences: -.Pp -.Bl -tag -compact -offset indent -width 4n -.It Ic \ea -Bell -.It Ic \eb -Backspace -.It Ic \ee -Escape -.It Ic \ef -Formfeed -.It Ic \en -Newline -.It Ic \er -Carriage return -.It Ic \et -Horizontal tab -.It Ic \ev -Vertical tab -.Sm off -.It Sy \e Ar nnn -.Sm on -The ASCII character corresponding to the octal number -.Ar nnn . -.El -.Pp -.Sq \e -nullifies the special meaning of the following character, -if it has any, notably -.Sq \e -and -.Sq ^ . -.It Ic echotc Xo -.Op Fl sv -.Ar arg -.Ar ... -.Xc -Exercise terminal capabilities given in -.Ar arg Ar ... . -If -.Ar arg -is -.Sq baud , -.Sq cols , -.Sq lines , -.Sq rows , -.Sq meta or -.Sq tabs , -the value of that capability is printed, with -.Dq yes -or -.Dq no -indicating that the terminal does or does not have that capability. -.Pp -.Fl s -returns an emptry string for non-existent capabilities, rather than -causing an error. -.Fl v -causes messages to be verbose. -.It Ic edit Op Li on | Li off -Enable or disable the -.Nm editline -functionality in a program. -.It Ic history -List the history. -.It Ic telltc -List the values of all the terminal capabilities (see -.Xr termcap 5 ). -.It Ic settc Ar cap Ar val -Set the terminal capability -.Ar cap -to -.Ar val , -as defined in -.Xr termcap 5 . -No sanity checking is done. -.It Ic setty Xo -.Op Fl a -.Op Fl d -.Op Fl q -.Op Fl x -.Op Ar +mode -.Op Ar -mode -.Op Ar mode -.Xc -Control which tty modes that -.Nm -won't allow the user to change. -.Fl d , -.Fl q -or -.Fl x -tells -.Ic setty -to act on the -.Sq edit , -.Sq quote -or -.Sq execute -set of tty modes respectively; defaulting to -.Fl x . -.Pp -Without other arguments, -.Ic setty -lists the modes in the chosen set which are fixed on -.Po -.Sq +mode -.Pc -or off -.Po -.Sq -mode -.Pc . -.Fl a -lists all tty modes in the chosen set regardless of the setting. -With -.Ar +mode , -.Ar -mode -or -.Ar mode , -fixes -.Ar mode -on or off or removes control of -.Ar mode -in the chosen set. -.El -.Sh EDITOR COMMANDS -The following editor commands are available for use in key bindings: -.\" Section automatically generated with makelist -.Bl -tag -width 4n -.It Ic vi-paste-next -Vi paste previous deletion to the right of the cursor. -.It Ic vi-paste-prev -Vi paste previous deletion to the left of the cursor. -.It Ic vi-prev-space-word -Vi move to the previous space delimited word. -.It Ic vi-prev-word -Vi move to the previous word. -.It Ic vi-next-space-word -Vi move to the next space delimited word. -.It Ic vi-next-word -Vi move to the next word. -.It Ic vi-change-case -Vi change case of character under the cursor and advance one character. -.It Ic vi-change-meta -Vi change prefix command. -.It Ic vi-insert-at-bol -Vi enter insert mode at the beginning of line. -.It Ic vi-replace-char -Vi replace character under the cursor with the next character typed. -.It Ic vi-replace-mode -Vi enter replace mode. -.It Ic vi-substitute-char -Vi replace character under the cursor and enter insert mode. -.It Ic vi-substitute-line -Vi substitute entire line. -.It Ic vi-change-to-eol -Vi change to end of line. -.It Ic vi-insert -Vi enter insert mode. -.It Ic vi-add -Vi enter insert mode after the cursor. -.It Ic vi-add-at-eol -Vi enter insert mode at end of line. -.It Ic vi-delete-meta -Vi delete prefix command. -.It Ic vi-end-word -Vi move to the end of the current space delimited word. -.It Ic vi-to-end-word -Vi move to the end of the current word. -.It Ic vi-undo -Vi undo last change. -.It Ic vi-command-mode -Vi enter command mode (use alternative key bindings). -.It Ic vi-zero -Vi move to the beginning of line. -.It Ic vi-delete-prev-char -Vi move to previous character (backspace). -.It Ic vi-list-or-eof -Vi list choices for completion or indicate end of file if empty line. -.It Ic vi-kill-line-prev -Vi cut from beginning of line to cursor. -.It Ic vi-search-prev -Vi search history previous. -.It Ic vi-search-next -Vi search history next. -.It Ic vi-repeat-search-next -Vi repeat current search in the same search direction. -.It Ic vi-repeat-search-prev -Vi repeat current search in the opposite search direction. -.It Ic vi-next-char -Vi move to the character specified next. -.It Ic vi-prev-char -Vi move to the character specified previous. -.It Ic vi-to-next-char -Vi move up to the character specified next. -.It Ic vi-to-prev-char -Vi move up to the character specified previous. -.It Ic vi-repeat-next-char -Vi repeat current character search in the same search direction. -.It Ic vi-repeat-prev-char -Vi repeat current character search in the opposite search direction. -.It Ic em-delete-or-list -Delete character under cursor or list completions if at end of line. -.It Ic em-delete-next-word -Cut from cursor to end of current word. -.It Ic em-yank -Paste cut buffer at cursor position. -.It Ic em-kill-line -Cut the entire line and save in cut buffer. -.It Ic em-kill-region -Cut area between mark and cursor and save in cut buffer. -.It Ic em-copy-region -Copy area between mark and cursor to cut buffer. -.It Ic em-gosmacs-traspose -Exchange the two characters before the cursor. -.It Ic em-next-word -Move next to end of current word. -.It Ic em-upper-case -Uppercase the characters from cursor to end of current word. -.It Ic em-capitol-case -Capitalize the characters from cursor to end of current word. -.It Ic em-lower-case -Lowercase the characters from cursor to end of current word. -.It Ic em-set-mark -Set the mark at cursor. -.It Ic em-exchange-mark -Exchange the cursor and mark. -.It Ic em-universal-argument -Universal argument (argument times 4). -.It Ic em-meta-next -Add 8th bit to next character typed. -.It Ic em-toggle-overwrite -Switch from insert to overwrite mode or vice versa. -.It Ic em-copy-prev-word -Copy current word to cursor. -.It Ic em-inc-search-next -Emacs incremental next search. -.It Ic em-inc-search-prev -Emacs incremental reverse search. -.It Ic ed-end-of-file -Indicate end of file. -.It Ic ed-insert -Add character to the line. -.It Ic ed-delete-prev-word -Delete from beginning of current word to cursor. -.It Ic ed-delete-next-char -Delete character under cursor. -.It Ic ed-kill-line -Cut to the end of line. -.It Ic ed-move-to-end -Move cursor to the end of line. -.It Ic ed-move-to-beg -Move cursor to the beginning of line. -.It Ic ed-transpose-chars -Exchange the character to the left of the cursor with the one under it. -.It Ic ed-next-char -Move to the right one character. -.It Ic ed-prev-word -Move to the beginning of the current word. -.It Ic ed-prev-char -Move to the left one character. -.It Ic ed-quoted-insert -Add the next character typed verbatim. -.It Ic ed-digit -Adds to argument or enters a digit. -.It Ic ed-argument-digit -Digit that starts argument. -.It Ic ed-unassigned -Indicates unbound character. -.It Ic ed-tty-sigint -Tty interrupt character. -.It Ic ed-tty-dsusp -Tty delayed suspend character. -.It Ic ed-tty-flush-output -Tty flush output characters. -.It Ic ed-tty-sigquit -Tty quit character. -.It Ic ed-tty-sigtstp -Tty suspend character. -.It Ic ed-tty-stop-output -Tty disallow output characters. -.It Ic ed-tty-start-output -Tty allow output characters. -.It Ic ed-newline -Execute command. -.It Ic ed-delete-prev-char -Delete the character to the left of the cursor. -.It Ic ed-clear-screen -Clear screen leaving current line at the top. -.It Ic ed-redisplay -Redisplay everything. -.It Ic ed-start-over -Erase current line and start from scratch. -.It Ic ed-sequence-lead-in -First character in a bound sequence. -.It Ic ed-prev-history -Move to the previous history line. -.It Ic ed-next-history -Move to the next history line. -.It Ic ed-search-prev-history -Search previous in history for a line matching the current. -.It Ic ed-search-next-history -Search next in history for a line matching the current. -.It Ic ed-prev-line -Move up one line. -.It Ic ed-next-line -Move down one line. -.It Ic ed-command -Editline extended command. -.El -.\" End of section automatically generated with makelist -.Sh SEE ALSO -.Xr editline 3 , -.Xr regex 3 , -.Xr termcap 5 -.Sh AUTHORS -The -.Nm editline -library was written by Christos Zoulas, -and this manual was written by Luke Mewburn, -with some sections inspired by -.Xr tcsh 1 . diff --git a/cmd-line-utils/libedit/el.c b/cmd-line-utils/libedit/el.c index c32a01b2151..d99946eb68f 100644 --- a/cmd-line-utils/libedit/el.c +++ b/cmd-line-utils/libedit/el.c @@ -1,4 +1,4 @@ -/* $NetBSD: el.c,v 1.39 2004/07/08 00:51:36 christos Exp $ */ +/* $NetBSD: el.c,v 1.47 2009/01/18 12:17:24 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * el.c: EditLine interface functions @@ -58,9 +64,12 @@ el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) memset(el, 0, sizeof(EditLine)); - el->el_infd = fileno(fin); + el->el_infile = fin; el->el_outfile = fout; el->el_errfile = ferr; + + el->el_infd = fileno(fin); + if ((el->el_prog = el_strdup(prog)) == NULL) { el_free(el); return NULL; @@ -126,7 +135,7 @@ el_reset(EditLine *el) { tty_cookedmode(el); - ch_reset(el); /* XXX: Do we want that? */ + ch_reset(el, 0); /* XXX: Do we want that? */ } @@ -136,29 +145,29 @@ el_reset(EditLine *el) public int el_set(EditLine *el, int op, ...) { - va_list va; + va_list ap; int rv = 0; if (el == NULL) return (-1); - va_start(va, op); + va_start(ap, op); switch (op) { case EL_PROMPT: case EL_RPROMPT: - rv = prompt_set(el, va_arg(va, el_pfunc_t), op); + rv = prompt_set(el, va_arg(ap, el_pfunc_t), op); break; case EL_TERMINAL: - rv = term_set(el, va_arg(va, char *)); + rv = term_set(el, va_arg(ap, char *)); break; case EL_EDITOR: - rv = map_set_editor(el, va_arg(va, char *)); + rv = map_set_editor(el, va_arg(ap, char *)); break; case EL_SIGNAL: - if (va_arg(va, int)) + if (va_arg(ap, int)) el->el_flags |= HANDLE_SIGNALS; else el->el_flags &= ~HANDLE_SIGNALS; @@ -167,6 +176,7 @@ el_set(EditLine *el, int op, ...) case EL_BIND: case EL_TELLTC: case EL_SETTC: + case EL_GETTC: case EL_ECHOTC: case EL_SETTY: { @@ -174,7 +184,7 @@ el_set(EditLine *el, int op, ...) int i; for (i = 1; i < 20; i++) - if ((argv[i] = va_arg(va, char *)) == NULL) + if ((argv[i] = va_arg(ap, char *)) == NULL) break; switch (op) { @@ -213,9 +223,9 @@ el_set(EditLine *el, int op, ...) case EL_ADDFN: { - char *name = va_arg(va, char *); - char *help = va_arg(va, char *); - el_func_t func = va_arg(va, el_func_t); + char *name = va_arg(ap, char *); + char *help = va_arg(ap, char *); + el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; @@ -223,15 +233,15 @@ el_set(EditLine *el, int op, ...) case EL_HIST: { - hist_fun_t func = va_arg(va, hist_fun_t); - ptr_t ptr = va_arg(va, char *); + hist_fun_t func = va_arg(ap, hist_fun_t); + ptr_t ptr = va_arg(ap, char *); rv = hist_set(el, func, ptr); break; } case EL_EDITMODE: - if (va_arg(va, int)) + if (va_arg(ap, int)) el->el_flags &= ~EDIT_DISABLED; else el->el_flags |= EDIT_DISABLED; @@ -240,17 +250,17 @@ el_set(EditLine *el, int op, ...) case EL_GETCFN: { - el_rfunc_t rc = va_arg(va, el_rfunc_t); + el_rfunc_t rc = va_arg(ap, el_rfunc_t); rv = el_read_setfn(el, rc); break; } case EL_CLIENTDATA: - el->el_data = va_arg(va, void *); + el->el_data = va_arg(ap, void *); break; case EL_UNBUFFERED: - rv = va_arg(va, int); + rv = va_arg(ap, int); if (rv && !(el->el_flags & UNBUFFERED)) { el->el_flags |= UNBUFFERED; read_prepare(el); @@ -262,7 +272,7 @@ el_set(EditLine *el, int op, ...) break; case EL_PREP_TERM: - rv = va_arg(va, int); + rv = va_arg(ap, int); if (rv) (void) tty_rawmode(el); else @@ -270,12 +280,45 @@ el_set(EditLine *el, int op, ...) rv = 0; break; + case EL_SETFP: + { + FILE *fp; + int what; + + what = va_arg(ap, int); + fp = va_arg(ap, FILE *); + + rv = 0; + switch (what) { + case 0: + el->el_infile = fp; + el->el_infd = fileno(fp); + break; + case 1: + el->el_outfile = fp; + break; + case 2: + el->el_errfile = fp; + break; + default: + rv = -1; + break; + } + break; + } + + case EL_REFRESH: + re_clear_display(el); + re_refresh(el); + term__flush(el); + break; + default: rv = -1; break; } - va_end(va); + va_end(ap); return (rv); } @@ -284,90 +327,71 @@ el_set(EditLine *el, int op, ...) * retrieve the editline parameters */ public int -el_get(EditLine *el, int op, void *ret) +el_get(EditLine *el, int op, ...) { + va_list ap; int rv; - if (el == NULL || ret == NULL) - return (-1); + if (el == NULL) + return -1; + + va_start(ap, op); + switch (op) { case EL_PROMPT: case EL_RPROMPT: - rv = prompt_get(el, (void *) &ret, op); + rv = prompt_get(el, va_arg(ap, el_pfunc_t *), op); break; case EL_EDITOR: - rv = map_get_editor(el, (void *) &ret); + rv = map_get_editor(el, va_arg(ap, const char **)); break; case EL_SIGNAL: - *((int *) ret) = (el->el_flags & HANDLE_SIGNALS); + *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); rv = 0; break; case EL_EDITMODE: - *((int *) ret) = (!(el->el_flags & EDIT_DISABLED)); + *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); rv = 0; break; case EL_TERMINAL: - term_get(el, (const char **)ret); + term_get(el, va_arg(ap, const char **)); rv = 0; break; -#if 0 /* XXX */ - case EL_BIND: - case EL_TELLTC: - case EL_SETTC: - case EL_ECHOTC: - case EL_SETTY: + case EL_GETTC: { - const char *argv[20]; + static char name[] = "gettc"; + char *argv[20]; int i; - for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++) - if ((argv[i] = va_arg(va, char *)) == NULL) + for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++) + if ((argv[i] = va_arg(ap, char *)) == NULL) break; switch (op) { - case EL_BIND: - argv[0] = "bind"; - rv = map_bind(el, i, argv); - break; - - case EL_TELLTC: - argv[0] = "telltc"; - rv = term_telltc(el, i, argv); - break; - - case EL_SETTC: - argv[0] = "settc"; - rv = term_settc(el, i, argv); - break; - - case EL_ECHOTC: - argv[0] = "echotc"; - rv = term_echotc(el, i, argv); - break; - - case EL_SETTY: - argv[0] = "setty"; - rv = tty_stty(el, i, argv); + case EL_GETTC: + argv[0] = name; + rv = term_gettc(el, i, argv); break; default: rv = -1; - EL_ABORT((el->errfile, "Bad op %d\n", op)); + EL_ABORT((el->el_errfile, "Bad op %d\n", op)); break; } break; } +#if 0 /* XXX */ case EL_ADDFN: { - char *name = va_arg(va, char *); - char *help = va_arg(va, char *); - el_func_t func = va_arg(va, el_func_t); + char *name = va_arg(ap, char *); + char *help = va_arg(ap, char *); + el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; @@ -375,31 +399,57 @@ el_get(EditLine *el, int op, void *ret) case EL_HIST: { - hist_fun_t func = va_arg(va, hist_fun_t); - ptr_t ptr = va_arg(va, char *); + hist_fun_t func = va_arg(ap, hist_fun_t); + ptr_t ptr = va_arg(ap, char *); rv = hist_set(el, func, ptr); } break; #endif /* XXX */ case EL_GETCFN: - *((el_rfunc_t *)ret) = el_read_getfn(el); + *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); rv = 0; break; case EL_CLIENTDATA: - *((void **)ret) = el->el_data; + *va_arg(ap, void **) = el->el_data; rv = 0; break; case EL_UNBUFFERED: - *((int *) ret) = (!(el->el_flags & UNBUFFERED)); + *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED)); rv = 0; break; + case EL_GETFP: + { + int what; + FILE **fpp; + + what = va_arg(ap, int); + fpp = va_arg(ap, FILE **); + rv = 0; + switch (what) { + case 0: + *fpp = el->el_infile; + break; + case 1: + *fpp = el->el_outfile; + break; + case 2: + *fpp = el->el_errfile; + break; + default: + rv = -1; + break; + } + break; + } default: rv = -1; + break; } + va_end(ap); return (rv); } @@ -428,17 +478,17 @@ el_source(EditLine *el, const char *fname) fp = NULL; if (fname == NULL) { +#ifdef HAVE_ISSETUGID static const char elpath[] = "/.editrc"; +/* XXXMYSQL: Portability fix (for which platforms?) */ #ifdef MAXPATHLEN char path[MAXPATHLEN]; #else char path[4096]; #endif -#ifdef HAVE_ISSETUGID if (issetugid()) return (-1); -#endif if ((ptr = getenv("HOME")) == NULL) return (-1); if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) @@ -446,6 +496,14 @@ el_source(EditLine *el, const char *fname) if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) return (-1); fname = path; +#else + /* + * If issetugid() is missing, always return an error, in order + * to keep from inadvertently opening up the user to a security + * hole. + */ + return (-1); +#endif } if (fp == NULL) fp = fopen(fname, "r"); diff --git a/cmd-line-utils/libedit/el.h b/cmd-line-utils/libedit/el.h index d9379d7c8aa..05d88ad88ba 100644 --- a/cmd-line-utils/libedit/el.h +++ b/cmd-line-utils/libedit/el.h @@ -1,4 +1,4 @@ -/* $NetBSD: el.h,v 1.16 2003/10/18 23:48:42 christos Exp $ */ +/* $NetBSD: el.h,v 1.17 2006/12/15 22:13:33 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -110,6 +110,7 @@ typedef struct el_state_t { struct editline { char *el_prog; /* the program name */ + FILE *el_infile; /* Stdio stuff */ FILE *el_outfile; /* Stdio stuff */ FILE *el_errfile; /* Stdio stuff */ int el_infd; /* Input file descriptor */ @@ -136,7 +137,8 @@ struct editline { protected int el_editmode(EditLine *, int, const char **); -#define el_isprint(x) ((unsigned char) (x) < 0x80 ? isprint(x) : 1) +/* XXXMYSQL: Bug#23097 mysql can't insert korean on mysql prompt. */ +#define el_isprint(x) ((unsigned char) (x) < 0x80 ? isprint(x) : 1) #ifdef DEBUG #define EL_ABORT(a) do { \ diff --git a/cmd-line-utils/libedit/el_term.h b/cmd-line-utils/libedit/el_term.h index 00ca48e38e2..0e7ddd555f4 100644 --- a/cmd-line-utils/libedit/el_term.h +++ b/cmd-line-utils/libedit/el_term.h @@ -1,4 +1,4 @@ -/* $NetBSD: term.h,v 1.15 2003/09/14 21:48:55 christos Exp $ */ +/* $NetBSD: term.h,v 1.19 2008/09/10 15:45:37 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -81,25 +81,6 @@ typedef struct { #define A_K_EN 5 #define A_K_NKEYS 6 -#ifdef _SUNOS -extern int tgetent(char *, const char *); -extern int tgetflag(char *); -extern int tgetnum(char *); -extern int tputs(const char *, int, int (*)(int)); -extern char* tgoto(const char*, int, int); -extern char* tgetstr(char*, char**); -#endif - - -#if !HAVE_DECL_TGOTO -/* - 'tgoto' is not declared in the system header files, this causes - problems on 64-bit systems. The function returns a 64 bit pointer - but caller see it as "int" and it's thus truncated to 32-bit -*/ -extern char* tgoto(const char*, int, int); -#endif - protected void term_move_to_line(EditLine *, int); protected void term_move_to_char(EditLine *, int); protected void term_clear_EOL(EditLine *, int); @@ -119,10 +100,12 @@ protected void term_end(EditLine *); protected void term_get(EditLine *, const char **); protected int term_set(EditLine *, const char *); protected int term_settc(EditLine *, int, const char **); +protected int term_gettc(EditLine *, int, char **); protected int term_telltc(EditLine *, int, const char **); protected int term_echotc(EditLine *, int, const char **); -protected int term__putc(int); -protected void term__flush(void); +protected void term_writec(EditLine *, int); +protected int term__putc(EditLine *, int); +protected void term__flush(EditLine *); /* * Easy access macros @@ -134,6 +117,7 @@ protected void term__flush(void); #define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL) #define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB) #define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME) +#define EL_CAN_UP (EL_FLAGS & TERM_CAN_UP) #define EL_HAS_META (EL_FLAGS & TERM_HAS_META) #define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS) #define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS) diff --git a/cmd-line-utils/libedit/emacs.c b/cmd-line-utils/libedit/emacs.c index 79f2bf0c818..135bd75f566 100644 --- a/cmd-line-utils/libedit/emacs.c +++ b/cmd-line-utils/libedit/emacs.c @@ -1,4 +1,4 @@ -/* $NetBSD: emacs.c,v 1.19 2004/10/28 21:14:52 dsl Exp $ */ +/* $NetBSD: emacs.c,v 1.21 2006/03/06 21:11:56 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * emacs.c: Emacs functions @@ -45,15 +51,14 @@ */ protected el_action_t /*ARGSUSED*/ -em_delete_or_list(EditLine *el, int c __attribute__((__unused__))) +em_delete_or_list(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.lastchar) { /* if I'm at the end */ if (el->el_line.cursor == el->el_line.buffer) { /* and the beginning */ - term_overwrite(el, STReof, 4); /* then do a EOF */ - term__flush(); + term_writec(el, c); /* then do an EOF */ return (CC_EOF); } else { /* diff --git a/cmd-line-utils/libedit/fgetln.h b/cmd-line-utils/libedit/fgetln.h deleted file mode 100644 index b2ddce01da9..00000000000 --- a/cmd-line-utils/libedit/fgetln.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -char *fgetln(FILE *stream, size_t *len); diff --git a/cmd-line-utils/libedit/filecomplete.c b/cmd-line-utils/libedit/filecomplete.c new file mode 100644 index 00000000000..4c63f57bc45 --- /dev/null +++ b/cmd-line-utils/libedit/filecomplete.c @@ -0,0 +1,558 @@ +/* $NetBSD: filecomplete.c,v 1.13 2009/01/26 17:32:41 apb Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jaromir Dolecek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* AIX requires this to be the first thing in the file. */ +#if defined (_AIX) && !defined (__GNUC__) + #pragma alloca +#endif + +#include "config.h" + +/* XXXMYSQL */ +#ifdef __GNUC__ +# undef alloca +# define alloca(n) __builtin_alloca (n) +#else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifndef _AIX +extern char *alloca (); +# endif +# endif +#endif + +#if !defined(lint) && !defined(SCCSID) +#endif /* not lint && not SCCSID */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_VIS_H +#include +#else +#include "np/vis.h" +#endif +#ifdef HAVE_ALLOCA_H +#include +#endif +#include "el.h" +#include "fcns.h" /* for EL_NUM_FCNS */ +#include "histedit.h" +#include "filecomplete.h" + +static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', + '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; + + +/********************************/ +/* completion functions */ + +/* + * does tilde expansion of strings of type ``~user/foo'' + * if ``user'' isn't valid user name or ``txt'' doesn't start + * w/ '~', returns pointer to strdup()ed copy of ``txt'' + * + * it's callers's responsibility to free() returned string + */ +char * +fn_tilde_expand(const char *txt) +{ + struct passwd pwres, *pass; + char *temp; + size_t len = 0; + char pwbuf[1024]; + + if (txt[0] != '~') + return (strdup(txt)); + + temp = strchr(txt + 1, '/'); + if (temp == NULL) { + temp = strdup(txt + 1); + if (temp == NULL) + return NULL; + } else { + len = temp - txt + 1; /* text until string after slash */ + temp = malloc(len); + if (temp == NULL) + return NULL; + (void)strncpy(temp, txt + 1, len - 2); + temp[len - 2] = '\0'; + } + /* XXXMYSQL: use non-_r functions for now */ + if (temp[0] == 0) { + pass = getpwuid(getuid()); + } else { + pass = getpwnam(temp); + } + free(temp); /* value no more needed */ + if (pass == NULL) + return (strdup(txt)); + + /* update pointer txt to point at string immedially following */ + /* first slash */ + txt += len; + + temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); + if (temp == NULL) + return NULL; + (void)sprintf(temp, "%s/%s", pass->pw_dir, txt); + + return (temp); +} + + +/* + * return first found file name starting by the ``text'' or NULL if no + * such file can be found + * value of ``state'' is ignored + * + * it's caller's responsibility to free returned string + */ +char * +fn_filename_completion_function(const char *text, int state) +{ + static DIR *dir = NULL; + static char *filename = NULL, *dirname = NULL, *dirpath = NULL; + static size_t filename_len = 0; + struct dirent *entry; + char *temp; + size_t len; + + if (state == 0 || dir == NULL) { + temp = strrchr(text, '/'); + if (temp) { + char *nptr; + temp++; + nptr = realloc(filename, strlen(temp) + 1); + if (nptr == NULL) { + free(filename); + return NULL; + } + filename = nptr; + (void)strcpy(filename, temp); + len = temp - text; /* including last slash */ + nptr = realloc(dirname, len + 1); + if (nptr == NULL) { + free(filename); + return NULL; + } + dirname = nptr; + (void)strncpy(dirname, text, len); + dirname[len] = '\0'; + } else { + if (*text == 0) + filename = NULL; + else { + filename = strdup(text); + if (filename == NULL) + return NULL; + } + dirname = NULL; + } + + if (dir != NULL) { + (void)closedir(dir); + dir = NULL; + } + + /* support for ``~user'' syntax */ + free(dirpath); + + if (dirname == NULL && (dirname = strdup("./")) == NULL) + return NULL; + + if (*dirname == '~') + dirpath = fn_tilde_expand(dirname); + else + dirpath = strdup(dirname); + + if (dirpath == NULL) + return NULL; + + dir = opendir(dirpath); + if (!dir) + return (NULL); /* cannot open the directory */ + + /* will be used in cycle */ + filename_len = filename ? strlen(filename) : 0; + } + + /* find the match */ + while ((entry = readdir(dir)) != NULL) { + /* skip . and .. */ + if (entry->d_name[0] == '.' && (!entry->d_name[1] + || (entry->d_name[1] == '.' && !entry->d_name[2]))) + continue; + if (filename_len == 0) + break; + /* otherwise, get first entry where first */ + /* filename_len characters are equal */ + if (entry->d_name[0] == filename[0] +#if HAVE_STRUCT_DIRENT_D_NAMLEN + && entry->d_namlen >= filename_len +#else + && strlen(entry->d_name) >= filename_len +#endif + && strncmp(entry->d_name, filename, + filename_len) == 0) + break; + } + + if (entry) { /* match found */ + +#if HAVE_STRUCT_DIRENT_D_NAMLEN + len = entry->d_namlen; +#else + len = strlen(entry->d_name); +#endif + + temp = malloc(strlen(dirname) + len + 1); + if (temp == NULL) + return NULL; + (void)sprintf(temp, "%s%s", dirname, entry->d_name); + } else { + (void)closedir(dir); + dir = NULL; + temp = NULL; + } + + return (temp); +} + + +static const char * +append_char_function(const char *name) +{ + struct stat stbuf; + char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; + const char *rs = " "; + + if (stat(expname ? expname : name, &stbuf) == -1) + goto out; + if (S_ISDIR(stbuf.st_mode)) + rs = "/"; +out: + if (expname) + free(expname); + return rs; +} +/* + * returns list of completions for text given + * non-static for readline. + */ +char ** completion_matches(const char *, char *(*)(const char *, int)); +char ** +completion_matches(const char *text, char *(*genfunc)(const char *, int)) +{ + char **match_list = NULL, *retstr, *prevstr; + size_t match_list_len, max_equal, which, i; + size_t matches; + + matches = 0; + match_list_len = 1; + while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { + /* allow for list terminator here */ + if (matches + 3 >= match_list_len) { + char **nmatch_list; + while (matches + 3 >= match_list_len) + match_list_len <<= 1; + nmatch_list = realloc(match_list, + match_list_len * sizeof(char *)); + if (nmatch_list == NULL) { + free(match_list); + return NULL; + } + match_list = nmatch_list; + + } + match_list[++matches] = retstr; + } + + if (!match_list) + return NULL; /* nothing found */ + + /* find least denominator and insert it to match_list[0] */ + which = 2; + prevstr = match_list[1]; + max_equal = strlen(prevstr); + for (; which <= matches; which++) { + for (i = 0; i < max_equal && + prevstr[i] == match_list[which][i]; i++) + continue; + max_equal = i; + } + + retstr = malloc(max_equal + 1); + if (retstr == NULL) { + free(match_list); + return NULL; + } + (void)strncpy(retstr, match_list[1], max_equal); + retstr[max_equal] = '\0'; + match_list[0] = retstr; + + /* add NULL as last pointer to the array */ + match_list[matches + 1] = (char *) NULL; + + return (match_list); +} + +/* + * Sort function for qsort(). Just wrapper around strcasecmp(). + */ +static int +_fn_qsort_string_compare(const void *i1, const void *i2) +{ + const char *s1 = ((const char * const *)i1)[0]; + const char *s2 = ((const char * const *)i2)[0]; + + return strcasecmp(s1, s2); +} + +/* + * Display list of strings in columnar format on readline's output stream. + * 'matches' is list of strings, 'len' is number of strings in 'matches', + * 'max' is maximum length of string in 'matches'. + */ +void +fn_display_match_list (EditLine *el, char **matches, int len, int max) +{ + int i, idx, limit, count; + int screenwidth = el->el_term.t_size.h; + + /* + * Find out how many entries can be put on one line, count + * with two spaces between strings. + */ + limit = screenwidth / (max + 2); + if (limit == 0) + limit = 1; + + /* how many lines of output */ + count = len / limit; + if (count * limit < len) + count++; + + /* Sort the items if they are not already sorted. */ + qsort(&matches[1], (size_t)(len - 1), sizeof(char *), + _fn_qsort_string_compare); + + idx = 1; + for(; count > 0; count--) { + for(i = 0; i < limit && matches[idx]; i++, idx++) + (void)fprintf(el->el_outfile, "%-*s ", max, + matches[idx]); + (void)fprintf(el->el_outfile, "\n"); + } +} + +/* + * Complete the word at or before point, + * 'what_to_do' says what to do with the completion. + * \t means do standard completion. + * `?' means list the possible completions. + * `*' means insert all of the possible completions. + * `!' means to do standard completion, and list all possible completions if + * there is more than one. + * + * Note: '*' support is not implemented + * '!' could never be invoked + */ +int +fn_complete(EditLine *el, + char *(*complet_func)(const char *, int), + char **(*attempted_completion_function)(const char *, int, int), + const char *word_break, const char *special_prefixes, + const char *(*app_func)(const char *), int query_items, + int *completion_type, int *over, int *point, int *end) +{ + const LineInfo *li; + char *temp, **matches; + const char *ctemp; + size_t len; + int what_to_do = '\t'; + int retval = CC_NORM; + + if (el->el_state.lastcmd == el->el_state.thiscmd) + what_to_do = '?'; + + /* readline's rl_complete() has to be told what we did... */ + if (completion_type != NULL) + *completion_type = what_to_do; + + if (!complet_func) + complet_func = fn_filename_completion_function; + if (!app_func) + app_func = append_char_function; + + /* We now look backwards for the start of a filename/variable word */ + li = el_line(el); + ctemp = (const char *) li->cursor; + while (ctemp > li->buffer + && !strchr(word_break, ctemp[-1]) + && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) ) + ctemp--; + + len = li->cursor - ctemp; +#if defined(__SSP__) || defined(__SSP_ALL__) + temp = malloc(len + 1); +#else + temp = alloca(len + 1); +#endif + (void)strncpy(temp, ctemp, len); + temp[len] = '\0'; + + /* these can be used by function called in completion_matches() */ + /* or (*attempted_completion_function)() */ + if (point != 0) + *point = li->cursor - li->buffer; + if (end != NULL) + *end = li->lastchar - li->buffer; + + if (attempted_completion_function) { + int cur_off = li->cursor - li->buffer; + matches = (*attempted_completion_function) (temp, + (int)(cur_off - len), cur_off); + } else + matches = 0; + if (!attempted_completion_function || + (over != NULL && !*over && !matches)) + matches = completion_matches(temp, complet_func); + + if (over != NULL) + *over = 0; + + if (matches) { + int i; + int matches_num, maxlen, match_len, match_display=1; + + retval = CC_REFRESH; + /* + * Only replace the completed string with common part of + * possible matches if there is possible completion. + */ + if (matches[0][0] != '\0') { + el_deletestr(el, (int) len); + el_insertstr(el, matches[0]); + } + + if (what_to_do == '?') + goto display_matches; + + if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { + /* + * We found exact match. Add a space after + * it, unless we do filename completion and the + * object is a directory. + */ + el_insertstr(el, (*app_func)(matches[0])); + } else if (what_to_do == '!') { + display_matches: + /* + * More than one match and requested to list possible + * matches. + */ + + for(i=1, maxlen=0; matches[i]; i++) { + match_len = strlen(matches[i]); + if (match_len > maxlen) + maxlen = match_len; + } + matches_num = i - 1; + + /* newline to get on next line from command line */ + (void)fprintf(el->el_outfile, "\n"); + + /* + * If there are too many items, ask user for display + * confirmation. + */ + if (matches_num > query_items) { + (void)fprintf(el->el_outfile, + "Display all %d possibilities? (y or n) ", + matches_num); + (void)fflush(el->el_outfile); + if (getc(stdin) != 'y') + match_display = 0; + (void)fprintf(el->el_outfile, "\n"); + } + + if (match_display) + fn_display_match_list(el, matches, matches_num, + maxlen); + retval = CC_REDISPLAY; + } else if (matches[0][0]) { + /* + * There was some common match, but the name was + * not complete enough. Next tab will print possible + * completions. + */ + el_beep(el); + } else { + /* lcd is not a valid object - further specification */ + /* is needed */ + el_beep(el); + retval = CC_NORM; + } + + /* free elements of array and the array itself */ + for (i = 0; matches[i]; i++) + free(matches[i]); + free(matches); + matches = NULL; + } +#if defined(__SSP__) || defined(__SSP_ALL__) + free(temp); +#endif + return retval; +} + +/* + * el-compatible wrapper around rl_complete; needed for key binding + */ +/* ARGSUSED */ +unsigned char +_el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) +{ + return (unsigned char)fn_complete(el, NULL, NULL, + break_chars, NULL, NULL, 100, + NULL, NULL, NULL, NULL); +} diff --git a/cmd-line-utils/libedit/fgetln.c b/cmd-line-utils/libedit/filecomplete.h similarity index 50% rename from cmd-line-utils/libedit/fgetln.c rename to cmd-line-utils/libedit/filecomplete.h index 5b95b2f6584..12e0c6f14b0 100644 --- a/cmd-line-utils/libedit/fgetln.c +++ b/cmd-line-utils/libedit/filecomplete.h @@ -1,11 +1,11 @@ -/* $NetBSD: fgetln.c,v 1.2 2003/12/10 01:30:27 lukem Exp $ */ +/* $NetBSD: filecomplete.h,v 1.6 2008/04/29 06:53:01 martin Exp $ */ /*- - * Copyright (c) 1998 The NetBSD Foundation, Inc. + * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. + * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -35,54 +28,17 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _FILECOMPLETE_H_ +#define _FILECOMPLETE_H_ -#include -#include -#include -#include -#include -#include +int fn_complete(EditLine *, + char *(*)(const char *, int), + char **(*)(const char *, int, int), + const char *, const char *, const char *(*)(const char *), int, + int *, int *, int *, int *); +void fn_display_match_list(EditLine *, char **, int, int); +char *fn_tilde_expand(const char *); +char *fn_filename_completion_function(const char *, int); -char * -fgetln(FILE *fp, size_t *len) -{ - static char *buf = NULL; - static size_t bufsiz = 0; - char *ptr; - - - if (buf == NULL) { - bufsiz = BUFSIZ; - if ((buf = malloc(bufsiz)) == NULL) - return NULL; - } - - if (fgets(buf, bufsiz, fp) == NULL) - return NULL; - *len = 0; - - while ((ptr = strchr(&buf[*len], '\n')) == NULL) { - size_t nbufsiz = bufsiz + BUFSIZ; - char *nbuf = realloc(buf, nbufsiz); - - if (nbuf == NULL) { - int oerrno = errno; - free(buf); - errno = oerrno; - buf = NULL; - return NULL; - } else - buf = nbuf; - - *len = bufsiz; - if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) - return buf; - - bufsiz = nbufsiz; - } - - *len = (ptr - buf) + 1; - return buf; -} - +#endif diff --git a/cmd-line-utils/libedit/hist.c b/cmd-line-utils/libedit/hist.c index e8f5c0f39ba..c0b23ee6641 100644 --- a/cmd-line-utils/libedit/hist.c +++ b/cmd-line-utils/libedit/hist.c @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * hist.c: History access functions diff --git a/cmd-line-utils/libedit/histedit.h b/cmd-line-utils/libedit/histedit.h index c58eb62dcfa..37823141c06 100644 --- a/cmd-line-utils/libedit/histedit.h +++ b/cmd-line-utils/libedit/histedit.h @@ -1,4 +1,4 @@ -/* $NetBSD: histedit.h,v 1.25 2003/12/05 13:37:48 lukem Exp $ */ +/* $NetBSD: histedit.h,v 1.35 2009/02/05 19:15:44 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -41,11 +41,15 @@ #define _HISTEDIT_H_ #define LIBEDIT_MAJOR 2 -#define LIBEDIT_MINOR 9 +#define LIBEDIT_MINOR 11 #include #include +#ifdef __cplusplus +extern "C" { +#endif + /* * ==== Editing ==== */ @@ -88,7 +92,7 @@ void el_reset(EditLine *); */ const char *el_gets(EditLine *, int *); int el_getc(EditLine *, char *); -void el_push(EditLine *, char *); +void el_push(EditLine *, const char *); /* * Beep! @@ -105,7 +109,8 @@ int el_parse(EditLine *, int, const char **); * Low level editline access functions */ int el_set(EditLine *, int, ...); -int el_get(EditLine *, int, void *); +int el_get(EditLine *, int, ...); +unsigned char _el_fn_complete(EditLine *, int); /* * el_set/el_get parameters @@ -128,8 +133,12 @@ int el_get(EditLine *, int, void *); #define EL_CLIENTDATA 14 /* , void *); */ #define EL_UNBUFFERED 15 /* , int); */ #define EL_PREP_TERM 16 /* , int); */ +#define EL_GETTC 17 /* , const char *, ..., NULL); */ +#define EL_GETFP 18 /* , int, FILE **); */ +#define EL_SETFP 19 /* , int, FILE *); */ +#define EL_REFRESH 20 /* , void); */ -#define EL_BUILTIN_GETCFN (NULL) +#define EL_BUILTIN_GETCFN (NULL) /* * Source named file or $PWD/.editrc or $HOME/.editrc @@ -192,6 +201,7 @@ int history(History *, HistEvent *, int, ...); #define H_CLEAR 19 /* , void); */ #define H_SETUNIQUE 20 /* , int); */ #define H_GETUNIQUE 21 /* , void); */ +#define H_DEL 22 /* , int); */ /* @@ -211,4 +221,8 @@ int tok_line(Tokenizer *, const LineInfo *, int tok_str(Tokenizer *, const char *, int *, const char ***); +#ifdef __cplusplus +} +#endif + #endif /* _HISTEDIT_H_ */ diff --git a/cmd-line-utils/libedit/history.c b/cmd-line-utils/libedit/history.c index c0fa7cc717d..3080dd231f6 100644 --- a/cmd-line-utils/libedit/history.c +++ b/cmd-line-utils/libedit/history.c @@ -1,4 +1,4 @@ -/* $NetBSD: history.c,v 1.28 2004/11/27 18:31:45 christos Exp $ */ +/* $NetBSD: history.c,v 1.33 2009/02/06 14:40:32 sketch Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * hist.c: History access functions @@ -40,7 +46,11 @@ #include #include #include +#ifdef HAVE_VIS_H #include +#else +#include "np/vis.h" +#endif #include static const char hist_cookie[] = "_HiStOrY_V2_\n"; @@ -61,6 +71,7 @@ struct history { history_gfun_t h_prev; /* Get the previous element */ history_gfun_t h_curr; /* Get the current element */ history_sfun_t h_set; /* Set the current element */ + history_sfun_t h_del; /* Set the given element */ history_vfun_t h_clear; /* Clear the history list */ history_efun_t h_enter; /* Add an element */ history_efun_t h_add; /* Append to an element */ @@ -75,6 +86,7 @@ struct history { #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev) #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str) #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str) +#define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n) #define h_strdup(a) strdup(a) #define h_malloc(a) malloc(a) @@ -122,16 +134,18 @@ typedef struct history_t { #define H_UNIQUE 1 /* Store only unique elements */ } history_t; -private int history_def_first(ptr_t, HistEvent *); -private int history_def_last(ptr_t, HistEvent *); private int history_def_next(ptr_t, HistEvent *); +private int history_def_first(ptr_t, HistEvent *); private int history_def_prev(ptr_t, HistEvent *); +private int history_def_last(ptr_t, HistEvent *); private int history_def_curr(ptr_t, HistEvent *); -private int history_def_set(ptr_t, HistEvent *, const int n); +private int history_def_set(ptr_t, HistEvent *, const int); +private void history_def_clear(ptr_t, HistEvent *); private int history_def_enter(ptr_t, HistEvent *, const char *); private int history_def_add(ptr_t, HistEvent *, const char *); +private int history_def_del(ptr_t, HistEvent *, const int); + private int history_def_init(ptr_t *, HistEvent *, int); -private void history_def_clear(ptr_t, HistEvent *); private int history_def_insert(history_t *, HistEvent *, const char *); private void history_def_delete(history_t *, HistEvent *, hentry_t *); @@ -353,6 +367,24 @@ history_def_add(ptr_t p, HistEvent *ev, const char *str) } +/* history_def_del(): + * Delete element hp of the h list + */ +/* ARGSUSED */ +private int +history_def_del(ptr_t p, HistEvent *ev __attribute__((__unused__)), + const int num) +{ + history_t *h = (history_t *) p; + if (history_def_set(h, ev, num) != 0) + return (-1); + ev->str = strdup(h->cursor->ev.str); + ev->num = h->cursor->ev.num; + history_def_delete(h, ev, h->cursor); + return (0); +} + + /* history_def_delete(): * Delete element hp of the h list */ @@ -364,6 +396,8 @@ history_def_delete(history_t *h, HistEventPrivate *evp = (void *)&hp->ev; if (hp == &h->list) abort(); + if (h->cursor == hp) + h->cursor = hp->prev; hp->prev->next = hp->next; hp->next->prev = hp->prev; h_free((ptr_t) evp->str); @@ -497,6 +531,7 @@ history_init(void) h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; + h->h_del = history_def_del; return (h); } @@ -512,6 +547,8 @@ history_end(History *h) if (h->h_next == history_def_next) history_def_clear(h->h_ref, &ev); + h_free(h->h_ref); + h_free(h); } @@ -597,7 +634,7 @@ history_set_fun(History *h, History *nh) if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || - nh->h_ref == NULL) { + nh->h_del == NULL || nh->h_ref == NULL) { if (h->h_next != history_def_next) { history_def_init(&h->h_ref, &ev, 0); h->h_first = history_def_first; @@ -609,6 +646,7 @@ history_set_fun(History *h, History *nh) h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; + h->h_del = history_def_del; } return (-1); } @@ -625,6 +663,7 @@ history_set_fun(History *h, History *nh) h->h_clear = nh->h_clear; h->h_enter = nh->h_enter; h->h_add = nh->h_add; + h->h_del = nh->h_del; return (0); } @@ -676,8 +715,8 @@ history_load(History *h, const char *fname) (void) strunvis(ptr, line); line[sz] = c; if (HENTER(h, &ev, ptr) == -1) { - i = -1; - goto oomem; + i = -1; + goto oomem; } } oomem: @@ -841,6 +880,10 @@ history(History *h, HistEvent *ev, int fun, ...) retval = HADD(h, ev, str); break; + case H_DEL: + retval = HDEL(h, ev, va_arg(va, const int)); + break; + case H_ENTER: str = va_arg(va, const char *); if ((retval = HENTER(h, ev, str)) != -1) @@ -925,6 +968,7 @@ history(History *h, HistEvent *ev, int fun, ...) hf.h_clear = va_arg(va, history_vfun_t); hf.h_enter = va_arg(va, history_efun_t); hf.h_add = va_arg(va, history_efun_t); + hf.h_del = va_arg(va, history_sfun_t); if ((retval = history_set_fun(h, &hf)) == -1) he_seterrev(ev, _HE_PARAM_MISSING); diff --git a/cmd-line-utils/libedit/key.c b/cmd-line-utils/libedit/key.c index 35fcf0651b2..cda02816861 100644 --- a/cmd-line-utils/libedit/key.c +++ b/cmd-line-utils/libedit/key.c @@ -1,4 +1,4 @@ -/* $NetBSD: key.c,v 1.15 2003/10/18 23:48:42 christos Exp $ */ +/* $NetBSD: key.c,v 1.19 2006/03/23 20:22:51 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,14 +32,20 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * key.c: This module contains the procedures for maintaining * the extended-key map. * * An extended-key (key) is a sequence of keystrokes introduced - * with an sequence introducer and consisting of an arbitrary + * with a sequence introducer and consisting of an arbitrary * number of characters. This module maintains a map (the el->el_key.map) * to convert these extended-key sequences into input strs * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE). @@ -78,12 +84,12 @@ private int node_trav(EditLine *, key_node_t *, char *, private int node__try(EditLine *, key_node_t *, const char *, key_value_t *, int); private key_node_t *node__get(int); +private void node__free(key_node_t *); private void node__put(EditLine *, key_node_t *); private int node__delete(EditLine *, key_node_t **, const char *); private int node_lookup(EditLine *, const char *, key_node_t *, int); private int node_enum(EditLine *, key_node_t *, int); -private int key__decode_char(char *, int, int); #define KEY_BUFSIZ EL_BUFSIZ @@ -103,7 +109,6 @@ key_init(EditLine *el) return (0); } - /* key_end(): * Free the key maps */ @@ -113,8 +118,7 @@ key_end(EditLine *el) el_free((ptr_t) el->el_key.buf); el->el_key.buf = NULL; - /* XXX: provide a function to clear the keys */ - el->el_key.map = NULL; + node__free(el->el_key.map); } @@ -443,7 +447,7 @@ node__put(EditLine *el, key_node_t *ptr) /* node__get(): - * Returns pointer to an key_node_t for ch. + * Returns pointer to a key_node_t for ch. */ private key_node_t * node__get(int ch) @@ -461,7 +465,15 @@ node__get(int ch) return (ptr); } - +private void +node__free(key_node_t *k) +{ + if (k == NULL) + return; + node__free(k->sibling); + node__free(k->next); + el_free((ptr_t) k); +} /* node_lookup(): * look for the str starting at node ptr. @@ -483,7 +495,7 @@ node_lookup(EditLine *el, const char *str, key_node_t *ptr, int cnt) /* If match put this char into el->el_key.buf. Recurse */ if (ptr->ch == *str) { /* match found */ - ncnt = key__decode_char(el->el_key.buf, cnt, + ncnt = key__decode_char(el->el_key.buf, KEY_BUFSIZ, cnt, (unsigned char) ptr->ch); if (ptr->next != NULL) /* not yet at leaf */ @@ -537,7 +549,8 @@ node_enum(EditLine *el, key_node_t *ptr, int cnt) return (-1); } /* put this char at end of str */ - ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch); + ncnt = key__decode_char(el->el_key.buf, KEY_BUFSIZ, cnt, + (unsigned char)ptr->ch); if (ptr->next == NULL) { /* print this key and function */ el->el_key.buf[ncnt + 1] = '"'; @@ -568,9 +581,10 @@ key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype) switch (ntype) { case XK_STR: case XK_EXE: - (void) fprintf(el->el_outfile, fmt, key, - key__decode_str(val->str, unparsbuf, - ntype == XK_STR ? "\"\"" : "[]")); + (void) key__decode_str(val->str, unparsbuf, + sizeof(unparsbuf), + ntype == XK_STR ? "\"\"" : "[]"); + (void) fprintf(el->el_outfile, fmt, key, unparsbuf); break; case XK_CMD: for (fp = el->el_map.help; fp->name; fp++) @@ -595,83 +609,97 @@ key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype) } +#define ADDC(c) \ + if (b < eb) \ + *b++ = c; \ + else \ + b++ /* key__decode_char(): * Put a printable form of char in buf. */ -private int -key__decode_char(char *buf, int cnt, int ch) +protected int +key__decode_char(char *buf, int cnt, int off, int ch) { + char *sb = buf + off; + char *eb = buf + cnt; + char *b = sb; if (ch == 0) { - buf[cnt++] = '^'; - buf[cnt] = '@'; - return (cnt); + ADDC('^'); + ADDC('@'); + return b - sb; } if (iscntrl(ch)) { - buf[cnt++] = '^'; + ADDC('^'); if (ch == '\177') - buf[cnt] = '?'; + ADDC('?'); else - buf[cnt] = ch | 0100; + ADDC(ch | 0100); } else if (ch == '^') { - buf[cnt++] = '\\'; - buf[cnt] = '^'; + ADDC('\\'); + ADDC('^'); } else if (ch == '\\') { - buf[cnt++] = '\\'; - buf[cnt] = '\\'; + ADDC('\\'); + ADDC('\\'); } else if (ch == ' ' || (el_isprint(ch) && !isspace(ch))) { - buf[cnt] = ch; + ADDC(ch); } else { - buf[cnt++] = '\\'; - buf[cnt++] = (((unsigned int) ch >> 6) & 7) + '0'; - buf[cnt++] = (((unsigned int) ch >> 3) & 7) + '0'; - buf[cnt] = (ch & 7) + '0'; + ADDC('\\'); + ADDC((((unsigned int) ch >> 6) & 7) + '0'); + ADDC((((unsigned int) ch >> 3) & 7) + '0'); + ADDC((ch & 7) + '0'); } - return (cnt); + return b - sb; } /* key__decode_str(): * Make a printable version of the ey */ -protected char * -key__decode_str(const char *str, char *buf, const char *sep) +protected int +key__decode_str(const char *str, char *buf, int len, const char *sep) { - char *b; + char *b = buf, *eb = b + len; const char *p; b = buf; - if (sep[0] != '\0') - *b++ = sep[0]; - if (*str == 0) { - *b++ = '^'; - *b++ = '@'; - if (sep[0] != '\0' && sep[1] != '\0') - *b++ = sep[1]; - *b++ = 0; - return (buf); + if (sep[0] != '\0') { + ADDC(sep[0]); + } + if (*str == '\0') { + ADDC('^'); + ADDC('@'); + if (sep[0] != '\0' && sep[1] != '\0') { + ADDC(sep[1]); + } + goto done; } for (p = str; *p != 0; p++) { if (iscntrl((unsigned char) *p)) { - *b++ = '^'; - if (*p == '\177') - *b++ = '?'; - else - *b++ = *p | 0100; + ADDC('^'); + if (*p == '\177') { + ADDC('?'); + } else { + ADDC(*p | 0100); + } } else if (*p == '^' || *p == '\\') { - *b++ = '\\'; - *b++ = *p; + ADDC('\\'); + ADDC(*p); } else if (*p == ' ' || (el_isprint((unsigned char) *p) && !isspace((unsigned char) *p))) { - *b++ = *p; + ADDC(*p); } else { - *b++ = '\\'; - *b++ = (((unsigned int) *p >> 6) & 7) + '0'; - *b++ = (((unsigned int) *p >> 3) & 7) + '0'; - *b++ = (*p & 7) + '0'; + ADDC('\\'); + ADDC((((unsigned int) *p >> 6) & 7) + '0'); + ADDC((((unsigned int) *p >> 3) & 7) + '0'); + ADDC((*p & 7) + '0'); } } - if (sep[0] != '\0' && sep[1] != '\0') - *b++ = sep[1]; - *b++ = 0; - return (buf); /* should check for overflow */ + if (sep[0] != '\0' && sep[1] != '\0') { + ADDC(sep[1]); + } +done: + ADDC('\0'); + if (b - buf >= len) + buf[len - 1] = '\0'; + return b - buf; } diff --git a/cmd-line-utils/libedit/key.h b/cmd-line-utils/libedit/key.h index 39a075c504e..9c6844e6d99 100644 --- a/cmd-line-utils/libedit/key.h +++ b/cmd-line-utils/libedit/key.h @@ -1,4 +1,4 @@ -/* $NetBSD: key.h,v 1.8 2003/08/07 16:44:32 agc Exp $ */ +/* $NetBSD: key.h,v 1.10 2006/03/23 20:22:51 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -74,6 +74,8 @@ protected int key_delete(EditLine *, const char *); protected void key_print(EditLine *, const char *); protected void key_kprint(EditLine *, const char *, key_value_t *, int); -protected char *key__decode_str(const char *, char *, const char *); +protected int key__decode_str(const char *, char *, int, + const char *); +protected int key__decode_char(char *, int, int, int); #endif /* _h_el_key */ diff --git a/cmd-line-utils/libedit/libedit_term.h b/cmd-line-utils/libedit/libedit_term.h deleted file mode 100644 index 9f03c549515..00000000000 --- a/cmd-line-utils/libedit/libedit_term.h +++ /dev/null @@ -1,124 +0,0 @@ -/* $NetBSD: term.h,v 1.12 2001/01/04 15:56:32 christos Exp $ */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas of Cornell University. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)term.h 8.1 (Berkeley) 6/4/93 - */ - -/* - * el.term.h: Termcap header - */ -#ifndef _h_el_term -#define _h_el_term - -#include "histedit.h" - -typedef struct { /* Symbolic function key bindings */ - const char *name; /* name of the key */ - int key; /* Index in termcap table */ - key_value_t fun; /* Function bound to it */ - int type; /* Type of function */ -} fkey_t; - -typedef struct { - coord_t t_size; /* # lines and cols */ - int t_flags; -#define TERM_CAN_INSERT 0x001 /* Has insert cap */ -#define TERM_CAN_DELETE 0x002 /* Has delete cap */ -#define TERM_CAN_CEOL 0x004 /* Has CEOL cap */ -#define TERM_CAN_TAB 0x008 /* Can use tabs */ -#define TERM_CAN_ME 0x010 /* Can turn all attrs. */ -#define TERM_CAN_UP 0x020 /* Can move up */ -#define TERM_HAS_META 0x040 /* Has a meta key */ -#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */ -#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */ - char *t_buf; /* Termcap buffer */ - int t_loc; /* location used */ - char **t_str; /* termcap strings */ - int *t_val; /* termcap values */ - char *t_cap; /* Termcap buffer */ - fkey_t *t_fkey; /* Array of keys */ -} el_term_t; - -/* - * fKey indexes - */ -#define A_K_DN 0 -#define A_K_UP 1 -#define A_K_LT 2 -#define A_K_RT 3 -#define A_K_HO 4 -#define A_K_EN 5 -#define A_K_NKEYS 6 - -protected void term_move_to_line(EditLine *, int); -protected void term_move_to_char(EditLine *, int); -protected void term_clear_EOL(EditLine *, int); -protected void term_overwrite(EditLine *, const char *, int); -protected void term_insertwrite(EditLine *, char *, int); -protected void term_deletechars(EditLine *, int); -protected void term_clear_screen(EditLine *); -protected void term_beep(EditLine *); -protected int term_change_size(EditLine *, int, int); -protected int term_get_size(EditLine *, int *, int *); -protected int term_init(EditLine *); -protected void term_bind_arrow(EditLine *); -protected void term_print_arrow(EditLine *, const char *); -protected int term_clear_arrow(EditLine *, const char *); -protected int term_set_arrow(EditLine *, const char *, key_value_t *, int); -protected void term_end(EditLine *); -protected int term_set(EditLine *, const char *); -protected int term_settc(EditLine *, int, const char **); -protected int term_telltc(EditLine *, int, const char **); -protected int term_echotc(EditLine *, int, const char **); -protected int term__putc(int); -protected void term__flush(void); - -/* - * Easy access macros - */ -#define EL_FLAGS (el)->el_term.t_flags - -#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT) -#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE) -#define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL) -#define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB) -#define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME) -#define EL_HAS_META (EL_FLAGS & TERM_HAS_META) -#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS) -#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS) - -#endif /* _h_el_term */ diff --git a/cmd-line-utils/libedit/makelist.sh b/cmd-line-utils/libedit/makelist.sh index f15b3d1eb9f..fdd3f934e15 100644 --- a/cmd-line-utils/libedit/makelist.sh +++ b/cmd-line-utils/libedit/makelist.sh @@ -1,5 +1,5 @@ #!/bin/sh - -# $NetBSD: makelist,v 1.8 2003/03/10 21:21:10 christos Exp $ +# $NetBSD: makelist,v 1.11 2005/10/22 16:45:03 christos Exp $ # # Copyright (c) 1992, 1993 # The Regents of the University of California. All rights reserved. @@ -15,11 +15,7 @@ # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors +# 3. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # @@ -68,6 +64,7 @@ case $FLAG in /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { + # XXXMYSQL: support CRLF name = substr($2, 1, index($2,"(") - 1); # # XXX: need a space between name and prototype so that -fc and -fh @@ -87,7 +84,7 @@ case $FLAG in cat $FILES | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); - printf("#include \"config.h\"\n#include \"el.h\"\n"); + printf("#include \"sys.h\"\n#include \"el.h\"\n"); printf("private const struct el_bindings_t el_func_help[] = {\n"); low = "abcdefghijklmnopqrstuvwxyz_"; high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; @@ -97,6 +94,7 @@ case $FLAG in /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { + # XXXMYSQL: support CRLF name = substr($2, 1, index($2,"(") - 1); uname = ""; fname = ""; @@ -117,13 +115,13 @@ case $FLAG in printf(" \""); for (i = 2; i < NF; i++) printf("%s ", $i); - sub("\r", "", $i); + # XXXMYSQL: support CRLF + sub("\r", "", $i); printf("%s\" },\n", $i); ok = 0; } } END { - printf(" { NULL, 0, NULL }\n"); printf("};\n"); printf("\nprotected const el_bindings_t* help__get()"); printf("{ return el_func_help; }\n"); @@ -144,6 +142,7 @@ case $FLAG in # generate fcns.h from various .h files # +# XXXMYSQL: use portable tr syntax -fh) cat $FILES | $AWK '/el_action_t/ { print $3 }' | \ sort | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | $AWK ' @@ -170,7 +169,7 @@ case $FLAG in cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); - printf("#include \"config.h\"\n#include \"el.h\"\n"); + printf("#include \"sys.h\"\n#include \"el.h\"\n"); printf("private const el_func_t el_func[] = {"); maxlen = 80; needn = 1; @@ -220,6 +219,7 @@ case $FLAG in /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { + # XXXMYSQL: support CRLF name = substr($2, 1, index($2, "(") - 1); fname = ""; for (i = 1; i <= length(name); i++) { diff --git a/cmd-line-utils/libedit/map.c b/cmd-line-utils/libedit/map.c index 6be9279b5e5..693b56c82ba 100644 --- a/cmd-line-utils/libedit/map.c +++ b/cmd-line-utils/libedit/map.c @@ -1,4 +1,4 @@ -/* $NetBSD: map.c,v 1.20 2004/08/13 12:10:39 mycroft Exp $ */ +/* $NetBSD: map.c,v 1.24 2006/04/09 01:36:51 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * map.c: Editor function definitions @@ -1118,11 +1124,12 @@ private void map_print_key(EditLine *el, el_action_t *map, const char *in) { char outbuf[EL_BUFSIZ]; - el_bindings_t *bp; + el_bindings_t *bp, *ep; if (in[0] == '\0' || in[1] == '\0') { - (void) key__decode_str(in, outbuf, ""); - for (bp = el->el_map.help; bp->name != NULL; bp++) + (void) key__decode_str(in, outbuf, sizeof(outbuf), ""); + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) if (bp->func == map[(unsigned char) *in]) { (void) fprintf(el->el_outfile, "%s\t->\t%s\n", outbuf, bp->name); @@ -1139,7 +1146,7 @@ map_print_key(EditLine *el, el_action_t *map, const char *in) private void map_print_some_keys(EditLine *el, el_action_t *map, int first, int last) { - el_bindings_t *bp; + el_bindings_t *bp, *ep; char firstbuf[2], lastbuf[2]; char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ]; @@ -1148,39 +1155,47 @@ map_print_some_keys(EditLine *el, el_action_t *map, int first, int last) lastbuf[0] = last; lastbuf[1] = 0; if (map[first] == ED_UNASSIGNED) { - if (first == last) + if (first == last) { + (void) key__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, - "%-15s-> is undefined\n", - key__decode_str(firstbuf, unparsbuf, STRQQ)); + "%-15s-> is undefined\n", unparsbuf); + } return; } - for (bp = el->el_map.help; bp->name != NULL; bp++) { + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) { if (bp->func == map[first]) { if (first == last) { + (void) key__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "%-15s-> %s\n", - key__decode_str(firstbuf, unparsbuf, STRQQ), - bp->name); + unparsbuf, bp->name); } else { + (void) key__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); + (void) key__decode_str(lastbuf, extrabuf, + sizeof(extrabuf), STRQQ); (void) fprintf(el->el_outfile, "%-4s to %-7s-> %s\n", - key__decode_str(firstbuf, unparsbuf, STRQQ), - key__decode_str(lastbuf, extrabuf, STRQQ), - bp->name); + unparsbuf, extrabuf, bp->name); } return; } } #ifdef MAP_DEBUG if (map == el->el_map.key) { + (void) key__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, - "BUG!!! %s isn't bound to anything.\n", - key__decode_str(firstbuf, unparsbuf, STRQQ)); + "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n", first, el->el_map.key[first]); } else { + (void) key__decode_str(firstbuf, unparsbuf, + sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, - "BUG!!! %s isn't bound to anything.\n", - key__decode_str(firstbuf, unparsbuf, STRQQ)); + "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n", first, el->el_map.alt[first]); } @@ -1237,7 +1252,7 @@ map_bind(EditLine *el, int argc, const char **argv) char outbuf[EL_BUFSIZ]; const char *in = NULL; char *out = NULL; - el_bindings_t *bp; + el_bindings_t *bp, *ep; int cmd; int key; @@ -1279,8 +1294,8 @@ map_bind(EditLine *el, int argc, const char **argv) return (0); case 'l': - for (bp = el->el_map.help; bp->name != NULL; - bp++) + ep = &el->el_map.help[el->el_map.nfunc]; + for (bp = el->el_map.help; bp < ep; bp++) (void) fprintf(el->el_outfile, "%s\n\t%s\n", bp->name, bp->description); @@ -1367,7 +1382,7 @@ map_bind(EditLine *el, int argc, const char **argv) break; default: - EL_ABORT((el->el_errfile, "Bad XK_ type\n", ntype)); + EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); break; } return (0); @@ -1381,7 +1396,7 @@ protected int map_addfunc(EditLine *el, const char *name, const char *help, el_func_t func) { void *p; - int nf = el->el_map.nfunc + 2; + int nf = el->el_map.nfunc + 1; if (name == NULL || help == NULL || func == NULL) return (-1); @@ -1400,7 +1415,6 @@ map_addfunc(EditLine *el, const char *name, const char *help, el_func_t func) el->el_map.help[nf].name = name; el->el_map.help[nf].func = nf; el->el_map.help[nf].description = help; - el->el_map.help[++nf].name = NULL; el->el_map.nfunc++; return (0); diff --git a/cmd-line-utils/libedit/np/fgetln.c b/cmd-line-utils/libedit/np/fgetln.c index 93da9914dc8..898abc758dc 100644 --- a/cmd-line-utils/libedit/np/fgetln.c +++ b/cmd-line-utils/libedit/np/fgetln.c @@ -1,4 +1,4 @@ -/* $NetBSD: fgetln.c,v 1.1.1.1 1999/04/12 07:43:21 crooksa Exp $ */ +/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -36,17 +29,24 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else #include "config.h" -#include +#endif + +#if !HAVE_FGETLN #include +#ifndef HAVE_NBTOOL_CONFIG_H +/* These headers are required, but included from nbtool_config.h */ +#include #include #include #include +#endif char * -fgetln(fp, len) - FILE *fp; - size_t *len; +fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsiz = 0; @@ -61,8 +61,8 @@ fgetln(fp, len) if (fgets(buf, bufsiz, fp) == NULL) return NULL; - *len = 0; + *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); @@ -76,13 +76,33 @@ fgetln(fp, len) } else buf = nbuf; - *len = bufsiz; - if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) + if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) { + buf[bufsiz] = '\0'; + *len = strlen(buf); return buf; + } + *len = bufsiz; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } + +#endif + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + char *p; + size_t len; + + while ((p = fgetln(stdin, &len)) != NULL) { + (void)printf("%zu %s", len, p); + free(p); + } + return 0; +} +#endif diff --git a/cmd-line-utils/libedit/np/strlcat.c b/cmd-line-utils/libedit/np/strlcat.c index 6c9f1e92d79..4e2897d8f35 100644 --- a/cmd-line-utils/libedit/np/strlcat.c +++ b/cmd-line-utils/libedit/np/strlcat.c @@ -1,59 +1,68 @@ +/* $NetBSD: strlcat.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ +/* $OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $ */ + /* * Copyright (c) 1998 Todd C. Miller - * All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#if !defined(_KERNEL) && !defined(_STANDALONE) +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else #include "config.h" -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $"; -#endif /* LIBC_SCCS and not lint */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD: src/lib/libc/string/strlcat.c,v 1.2.4.2 2001/07/09 23:30:06 obrien Exp $"; #endif +#if defined(LIBC_SCCS) && !defined(lint) +#endif /* LIBC_SCCS and not lint */ + +#ifdef _LIBC +#include "namespace.h" +#endif #include +#include #include +#ifdef _LIBC +# ifdef __weak_alias +__weak_alias(strlcat, _strlcat) +# endif +#endif + +#else +#include +#endif /* !_KERNEL && !_STANDALONE */ + +#if !HAVE_STRLCAT /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(initial dst) + strlen(src); if retval >= siz, - * truncation occurred. + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. */ -size_t strlcat(dst, src, siz) - char *dst; - const char *src; - size_t siz; +size_t +strlcat(char *dst, const char *src, size_t siz) { - register char *d = dst; - register const char *s = src; - register size_t n = siz; + char *d = dst; + const char *s = src; + size_t n = siz; size_t dlen; + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); + /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; @@ -73,3 +82,4 @@ size_t strlcat(dst, src, siz) return(dlen + (s - src)); /* count does not include NUL */ } +#endif diff --git a/cmd-line-utils/libedit/np/strlcpy.c b/cmd-line-utils/libedit/np/strlcpy.c index 1f154bcf2ea..092a9757c0f 100644 --- a/cmd-line-utils/libedit/np/strlcpy.c +++ b/cmd-line-utils/libedit/np/strlcpy.c @@ -1,59 +1,63 @@ -/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ +/* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ +/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller - * All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#if !defined(_KERNEL) && !defined(_STANDALONE) +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else #include "config.h" +#endif #if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; -#endif #endif /* LIBC_SCCS and not lint */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD: src/lib/libc/string/strlcpy.c,v 1.2.4.1 2001/07/09 23:30:06 obrien Exp $"; -#endif +#ifdef _LIBC +#include "namespace.h" +#endif #include +#include #include +#ifdef _LIBC +# ifdef __weak_alias +__weak_alias(strlcpy, _strlcpy) +# endif +#endif +#else +#include +#endif /* !_KERNEL && !_STANDALONE */ + + +#if !HAVE_STRLCPY /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ -size_t strlcpy(dst, src, siz) - char *dst; - const char *src; - size_t siz; +size_t +strlcpy(char *dst, const char *src, size_t siz) { - register char *d = dst; - register const char *s = src; - register size_t n = siz; + char *d = dst; + const char *s = src; + size_t n = siz; + + _DIAGASSERT(dst != NULL); + _DIAGASSERT(src != NULL); /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { @@ -73,3 +77,4 @@ size_t strlcpy(dst, src, siz) return(s - src - 1); /* count does not include NUL */ } +#endif diff --git a/cmd-line-utils/libedit/np/unvis.c b/cmd-line-utils/libedit/np/unvis.c index 895ff2059ac..3c37c231ceb 100644 --- a/cmd-line-utils/libedit/np/unvis.c +++ b/cmd-line-utils/libedit/np/unvis.c @@ -1,4 +1,4 @@ -/* $NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */ +/* $NetBSD: unvis.c,v 1.28 2005/09/13 01:44:09 christos Exp $ */ /*- * Copyright (c) 1989, 1993 @@ -12,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -34,34 +30,30 @@ */ #include "config.h" + #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93"; #else -__RCSID("$NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ -#define __LIBC12_SOURCE__ - #include #include #include #include +#ifdef HAVE_VIS_H +#include +#else #include "np/vis.h" +#endif #ifdef __weak_alias __weak_alias(strunvis,_strunvis) -__weak_alias(unvis,_unvis) #endif -#ifdef __warn_references -__warn_references(unvis, - "warning: reference to compatibility unvis(); include for correct reference") -#endif - -#if !HAVE_VIS_H +#if !HAVE_VIS /* * decode driven by state machine */ @@ -72,30 +64,22 @@ __warn_references(unvis, #define S_CTRL 4 /* control char started (^) */ #define S_OCTAL2 5 /* octal digit 2 */ #define S_OCTAL3 6 /* octal digit 3 */ -#define S_HEX1 7 /* hex digit */ -#define S_HEX2 8 /* hex digit 2 */ +#define S_HEX1 7 /* hex digit */ +#define S_HEX2 8 /* hex digit 2 */ #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10)) +/* + * unvis - decode characters previously encoded by vis + */ int unvis(cp, c, astate, flag) char *cp; int c; int *astate, flag; { - return __unvis13(cp, (int)c, astate, flag); -} - -/* - * unvis - decode characters previously encoded by vis - */ -int -__unvis13(cp, c, astate, flag) - char *cp; - int c; - int *astate, flag; -{ + unsigned char uc = (unsigned char)c; _DIAGASSERT(cp != NULL); _DIAGASSERT(astate != NULL); @@ -105,7 +89,7 @@ __unvis13(cp, c, astate, flag) || *astate == S_HEX2) { *astate = S_GROUND; return (UNVIS_VALID); - } + } return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); } @@ -116,7 +100,7 @@ __unvis13(cp, c, astate, flag) if (c == '\\') { *astate = S_START; return (0); - } + } if ((flag & VIS_HTTPSTYLE) && c == '%') { *astate = S_HEX1; return (0); @@ -193,7 +177,7 @@ __unvis13(cp, c, astate, flag) } *astate = S_GROUND; return (UNVIS_SYNBAD); - + case S_META: if (c == '-') *astate = S_META1; @@ -204,12 +188,12 @@ __unvis13(cp, c, astate, flag) return (UNVIS_SYNBAD); } return (0); - + case S_META1: *astate = S_GROUND; *cp |= c; return (UNVIS_VALID); - + case S_CTRL: if (c == '?') *cp |= 0177; @@ -219,23 +203,23 @@ __unvis13(cp, c, astate, flag) return (UNVIS_VALID); case S_OCTAL2: /* second possible octal digit */ - if (isoctal(c)) { - /* - * yes - and maybe a third + if (isoctal(uc)) { + /* + * yes - and maybe a third */ *cp = (*cp << 3) + (c - '0'); - *astate = S_OCTAL3; + *astate = S_OCTAL3; return (0); - } - /* - * no - done with current sequence, push back passed char + } + /* + * no - done with current sequence, push back passed char */ *astate = S_GROUND; return (UNVIS_VALIDPUSH); case S_OCTAL3: /* third possible octal digit */ *astate = S_GROUND; - if (isoctal(c)) { + if (isoctal(uc)) { *cp = (*cp << 3) + (c - '0'); return (UNVIS_VALID); } @@ -243,27 +227,30 @@ __unvis13(cp, c, astate, flag) * we were done, push back passed char */ return (UNVIS_VALIDPUSH); + case S_HEX1: - if (isxdigit(c)) { - *cp = xtod(c); + if (isxdigit(uc)) { + *cp = xtod(uc); *astate = S_HEX2; return (0); } - /* - * no - done with current sequence, push back passed char + /* + * no - done with current sequence, push back passed char */ *astate = S_GROUND; return (UNVIS_VALIDPUSH); + case S_HEX2: - *astate = S_GROUND; - if (isxdigit(c)) { - *cp = xtod(c) | (*cp << 4); + *astate = S_GROUND; + if (isxdigit(uc)) { + *cp = xtod(uc) | (*cp << 4); return (UNVIS_VALID); } - return (UNVIS_VALIDPUSH); - default: - /* - * decoder in unknown state - (probably uninitialized) + return (UNVIS_VALIDPUSH); + + default: + /* + * decoder in unknown state - (probably uninitialized) */ *astate = S_GROUND; return (UNVIS_SYNBAD); @@ -271,7 +258,7 @@ __unvis13(cp, c, astate, flag) } /* - * strunvis - decode src into dst + * strunvis - decode src into dst * * Number of chars decoded into dst is returned, -1 on error. * Dst is null terminated. @@ -291,8 +278,8 @@ strunvisx(dst, src, flag) _DIAGASSERT(dst != NULL); while ((c = *src++) != '\0') { - again: - switch (__unvis13(dst, c, &state, flag)) { + again: + switch (unvis(dst, c, &state, flag)) { case UNVIS_VALID: dst++; break; @@ -306,7 +293,7 @@ strunvisx(dst, src, flag) return (-1); } } - if (__unvis13(dst, c, &state, UNVIS_END) == UNVIS_VALID) + if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) dst++; *dst = '\0'; return (dst - start); diff --git a/cmd-line-utils/libedit/np/vis.c b/cmd-line-utils/libedit/np/vis.c index e8f5c195f10..2a746274681 100644 --- a/cmd-line-utils/libedit/np/vis.c +++ b/cmd-line-utils/libedit/np/vis.c @@ -1,7 +1,6 @@ -/* $NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */ +/* $NetBSD: vis.c,v 1.38 2008/09/04 09:41:44 lukem Exp $ */ /*- - * Copyright (c) 1999 The NetBSD Foundation, Inc. * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * @@ -13,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -34,21 +29,47 @@ * SUCH DAMAGE. */ +/*- + * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + #include "config.h" #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #include + #include -#ifdef HAVE_ALLOCA_H -#include +#ifdef HAVE_VIS_H +#include +#else +#include "np/vis.h" #endif #include -#include "np/vis.h" - #ifdef __weak_alias __weak_alias(strsvis,_strsvis) __weak_alias(strsvisx,_strsvisx) @@ -58,63 +79,61 @@ __weak_alias(svis,_svis) __weak_alias(vis,_vis) #endif -#if !HAVE_VIS_H +#if !HAVE_VIS || !HAVE_SVIS #include #include #include #include -#include -#undef BELL -#if defined(__STDC__) -#define BELL '\a' -#else -#define BELL '\007' -#endif -#define isoctal(c) (((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7') +static char *do_svis(char *, int, int, int, const char *); + +#undef BELL +#define BELL '\a' + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define iswhite(c) (c == ' ' || c == '\t' || c == '\n') #define issafe(c) (c == '\b' || c == BELL || c == '\r') #define xtoa(c) "0123456789abcdef"[c] -#define MAXEXTRAS 5 +#define MAXEXTRAS 5 +#define MAKEEXTRALIST(flag, extra, orig_str) \ +do { \ + const char *orig = orig_str; \ + const char *o = orig; \ + char *e; \ + while (*o++) \ + continue; \ + extra = malloc((size_t)((o - orig) + MAXEXTRAS)); \ + if (!extra) break; \ + for (o = orig, e = extra; (*e++ = *o++) != '\0';) \ + continue; \ + e--; \ + if (flag & VIS_SP) *e++ = ' '; \ + if (flag & VIS_TAB) *e++ = '\t'; \ + if (flag & VIS_NL) *e++ = '\n'; \ + if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \ + *e = '\0'; \ +} while (/*CONSTCOND*/0) -char *MAKEEXTRALIST(unsigned int flag, const char *orig) -{ - const char *o = orig; - char *e, *extra; - while (*o++) - continue; - extra = (char*) malloc((size_t)((o - orig) + MAXEXTRAS)); - assert(extra); - for (o = orig, e = extra; (*e++ = *o++) != '\0';) - continue; - e--; - if (flag & VIS_SP) *e++ = ' '; - if (flag & VIS_TAB) *e++ = '\t'; - if (flag & VIS_NL) *e++ = '\n'; - if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; - *e = '\0'; - return extra; +/* + * This is do_hvis, for HTTP style (RFC 1808) + */ +static char * +do_hvis(char *dst, int c, int flag, int nextc, const char *extra) +{ + if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { + *dst++ = '%'; + *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); + *dst++ = xtoa((unsigned int)c & 0xf); + } else { + dst = do_svis(dst, c, flag, nextc, extra); + } + return dst; } - /* - * This is HVIS, the macro of vis used to HTTP style (RFC 1808) - */ -#define HVIS(dst, c, flag, nextc, extra) \ -do \ - if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \ - *dst++ = '%'; \ - *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \ - *dst++ = xtoa((unsigned int)c & 0xf); \ - } else { \ - SVIS(dst, c, flag, nextc, extra); \ - } \ -while (/*CONSTCOND*/0) - -/* - * This is SVIS, the central macro of vis. + * This is do_vis, the central code of vis. * dst: Pointer to the destination buffer * c: Character to encode * flag: Flag word @@ -122,95 +141,103 @@ while (/*CONSTCOND*/0) * extra: Pointer to the list of extra characters to be * backslash-protected. */ -#define SVIS(dst, c, flag, nextc, extra) \ -do { \ - int isextra, isc; \ - isextra = strchr(extra, c) != NULL; \ - if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \ - ((flag & VIS_SAFE) && issafe(c)))) { \ - *dst++ = c; \ - break; \ - } \ - isc = 0; \ - if (flag & VIS_CSTYLE) { \ - switch (c) { \ - case '\n': \ - isc = 1; *dst++ = '\\'; *dst++ = 'n'; \ - break; \ - case '\r': \ - isc = 1; *dst++ = '\\'; *dst++ = 'r'; \ - break; \ - case '\b': \ - isc = 1; *dst++ = '\\'; *dst++ = 'b'; \ - break; \ - case BELL: \ - isc = 1; *dst++ = '\\'; *dst++ = 'a'; \ - break; \ - case '\v': \ - isc = 1; *dst++ = '\\'; *dst++ = 'v'; \ - break; \ - case '\t': \ - isc = 1; *dst++ = '\\'; *dst++ = 't'; \ - break; \ - case '\f': \ - isc = 1; *dst++ = '\\'; *dst++ = 'f'; \ - break; \ - case ' ': \ - isc = 1; *dst++ = '\\'; *dst++ = 's'; \ - break; \ - case '\0': \ - isc = 1; *dst++ = '\\'; *dst++ = '0'; \ - if (isoctal(nextc)) { \ - *dst++ = '0'; \ - *dst++ = '0'; \ - } \ - } \ - } \ - if (isc) break; \ - if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \ - *dst++ = '\\'; \ - *dst++ = (unsigned char)(((unsigned int)(unsigned char)c >> 6) & 03) + '0'; \ - *dst++ = (unsigned char)(((unsigned int)(unsigned char)c >> 3) & 07) + '0'; \ - *dst++ = (c & 07) + '0'; \ - } else { \ - if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \ - if (c & 0200) { \ - c &= 0177; *dst++ = 'M'; \ - } \ - if (iscntrl(c)) { \ - *dst++ = '^'; \ - if (c == 0177) \ - *dst++ = '?'; \ - else \ - *dst++ = c + '@'; \ - } else { \ - *dst++ = '-'; *dst++ = c; \ - } \ - } \ -} while (/*CONSTCOND*/0) +static char * +do_svis(char *dst, int c, int flag, int nextc, const char *extra) +{ + int isextra; + isextra = strchr(extra, c) != NULL; + if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || + ((flag & VIS_SAFE) && issafe(c)))) { + *dst++ = c; + return dst; + } + if (flag & VIS_CSTYLE) { + switch (c) { + case '\n': + *dst++ = '\\'; *dst++ = 'n'; + return dst; + case '\r': + *dst++ = '\\'; *dst++ = 'r'; + return dst; + case '\b': + *dst++ = '\\'; *dst++ = 'b'; + return dst; + case BELL: + *dst++ = '\\'; *dst++ = 'a'; + return dst; + case '\v': + *dst++ = '\\'; *dst++ = 'v'; + return dst; + case '\t': + *dst++ = '\\'; *dst++ = 't'; + return dst; + case '\f': + *dst++ = '\\'; *dst++ = 'f'; + return dst; + case ' ': + *dst++ = '\\'; *dst++ = 's'; + return dst; + case '\0': + *dst++ = '\\'; *dst++ = '0'; + if (isoctal(nextc)) { + *dst++ = '0'; + *dst++ = '0'; + } + return dst; + default: + if (isgraph(c)) { + *dst++ = '\\'; *dst++ = c; + return dst; + } + } + } + if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { + *dst++ = '\\'; + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; + *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; + *dst++ = (c & 07) + '0'; + } else { + if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; + if (c & 0200) { + c &= 0177; *dst++ = 'M'; + } + if (iscntrl(c)) { + *dst++ = '^'; + if (c == 0177) + *dst++ = '?'; + else + *dst++ = c + '@'; + } else { + *dst++ = '-'; *dst++ = c; + } + } + return dst; +} /* * svis - visually encode characters, also encoding the characters - * pointed to by `extra' + * pointed to by `extra' */ char * -svis(dst, c, flag, nextc, extra) - char *dst; - int c, flag, nextc; - const char *extra; +svis(char *dst, int c, int flag, int nextc, const char *extra) { - char *nextra, *to_be_freed; + char *nextra = NULL; + _DIAGASSERT(dst != NULL); _DIAGASSERT(extra != NULL); - nextra= to_be_freed= MAKEEXTRALIST(flag, extra); + MAKEEXTRALIST(flag, nextra, extra); + if (!nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return dst; + } if (flag & VIS_HTTPSTYLE) - HVIS(dst, c, flag, nextc, nextra); + dst = do_hvis(dst, c, flag, nextc, nextra); else - SVIS(dst, c, flag, nextc, nextra); + dst = do_svis(dst, c, flag, nextc, nextra); + free(nextra); *dst = '\0'; - free(to_be_freed); - return(dst); + return dst; } @@ -221,140 +248,146 @@ svis(dst, c, flag, nextc, extra) * be encoded, too. These functions are useful e. g. to * encode strings in such a way so that they are not interpreted * by a shell. - * + * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, - * is returned. + * is returned. * * Strsvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int -strsvis(dst, src, flag, extra) - char *dst; - const char *src; - int flag; - const char *extra; +strsvis(char *dst, const char *csrc, int flag, const char *extra) { - char c; + int c; char *start; - char *nextra, *to_be_freed; + char *nextra = NULL; + const unsigned char *src = (const unsigned char *)csrc; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); _DIAGASSERT(extra != NULL); - nextra= to_be_freed= MAKEEXTRALIST(flag, extra); + MAKEEXTRALIST(flag, nextra, extra); + if (!nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return 0; + } if (flag & VIS_HTTPSTYLE) { for (start = dst; (c = *src++) != '\0'; /* empty */) - HVIS(dst, c, flag, *src, nextra); + dst = do_hvis(dst, c, flag, *src, nextra); } else { for (start = dst; (c = *src++) != '\0'; /* empty */) - SVIS(dst, c, flag, *src, nextra); + dst = do_svis(dst, c, flag, *src, nextra); } + free(nextra); *dst = '\0'; - free(to_be_freed); return (dst - start); } int -strsvisx(dst, src, len, flag, extra) - char *dst; - const char *src; - size_t len; - int flag; - const char *extra; +strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra) { - char c; + unsigned char c; char *start; - char *nextra, *to_be_freed; + char *nextra = NULL; + const unsigned char *src = (const unsigned char *)csrc; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); _DIAGASSERT(extra != NULL); - nextra= to_be_freed= MAKEEXTRALIST(flag, extra); + MAKEEXTRALIST(flag, nextra, extra); + if (! nextra) { + *dst = '\0'; /* can't create nextra, return "" */ + return 0; + } if (flag & VIS_HTTPSTYLE) { for (start = dst; len > 0; len--) { c = *src++; - HVIS(dst, c, flag, len ? *src : '\0', nextra); + dst = do_hvis(dst, c, flag, + len > 1 ? *src : '\0', nextra); } } else { for (start = dst; len > 0; len--) { c = *src++; - SVIS(dst, c, flag, len ? *src : '\0', nextra); + dst = do_svis(dst, c, flag, + len > 1 ? *src : '\0', nextra); } } + free(nextra); *dst = '\0'; - free(to_be_freed); return (dst - start); } +#endif - +#if !HAVE_VIS /* * vis - visually encode characters */ char * -vis(dst, c, flag, nextc) - char *dst; - int c, flag, nextc; - +vis(char *dst, int c, int flag, int nextc) { - char *extra, *to_be_freed; + char *extra = NULL; + unsigned char uc = (unsigned char)c; _DIAGASSERT(dst != NULL); - extra= to_be_freed= MAKEEXTRALIST(flag, ""); - + MAKEEXTRALIST(flag, extra, ""); + if (! extra) { + *dst = '\0'; /* can't create extra, return "" */ + return dst; + } if (flag & VIS_HTTPSTYLE) - HVIS(dst, c, flag, nextc, extra); + dst = do_hvis(dst, uc, flag, nextc, extra); else - SVIS(dst, c, flag, nextc, extra); + dst = do_svis(dst, uc, flag, nextc, extra); + free(extra); *dst = '\0'; - free(to_be_freed); - return (dst); + return dst; } /* * strvis, strvisx - visually encode characters from src into dst - * + * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, - * is returned. + * is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int -strvis(dst, src, flag) - char *dst; - const char *src; - int flag; +strvis(char *dst, const char *src, int flag) { - char *extra; - int tmp; + char *extra = NULL; + int rv; - extra= MAKEEXTRALIST(flag, ""); - tmp= strsvis(dst, src, flag, extra); + MAKEEXTRALIST(flag, extra, ""); + if (!extra) { + *dst = '\0'; /* can't create extra, return "" */ + return 0; + } + rv = strsvis(dst, src, flag, extra); free(extra); - return tmp; + return rv; } int -strvisx(dst, src, len, flag) - char *dst; - const char *src; - size_t len; - int flag; +strvisx(char *dst, const char *src, size_t len, int flag) { - char *extra; - int tmp; + char *extra = NULL; + int rv; - extra= MAKEEXTRALIST(flag, ""); - tmp= strsvisx(dst, src, len, flag, extra); + MAKEEXTRALIST(flag, extra, ""); + if (!extra) { + *dst = '\0'; /* can't create extra, return "" */ + return 0; + } + rv = strsvisx(dst, src, len, flag, extra); free(extra); - return tmp; + return rv; } #endif diff --git a/cmd-line-utils/libedit/np/vis.h b/cmd-line-utils/libedit/np/vis.h index 1a49c9e3ed2..11f5b740e2d 100644 --- a/cmd-line-utils/libedit/np/vis.h +++ b/cmd-line-utils/libedit/np/vis.h @@ -1,4 +1,4 @@ -/* $NetBSD: vis.h,v 1.12 2002/03/23 17:39:05 christos Exp $ */ +/* $NetBSD: vis.h,v 1.16 2005/09/13 01:44:32 christos Exp $ */ /*- * Copyright (c) 1990, 1993 @@ -12,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -38,9 +34,7 @@ #ifndef _VIS_H_ #define _VIS_H_ -#ifdef HAVE_SYS_CDEFS_H -#include -#endif +#include /* * to select alternate encoding format @@ -78,6 +72,7 @@ */ #define UNVIS_END 1 /* no more characters */ +__BEGIN_DECLS char *vis(char *, int, int, int); char *svis(char *, int, int, int, const char *); int strvis(char *, const char *, int); @@ -86,11 +81,7 @@ int strvisx(char *, const char *, size_t, int); int strsvisx(char *, const char *, size_t, int, const char *); int strunvis(char *, const char *); int strunvisx(char *, const char *, int); -#ifdef __LIBC12_SOURCE__ int unvis(char *, int, int *, int); -int __unvis13(char *, int, int *, int); -#else -int unvis(char *, int, int *, int) __RENAME(__unvis13); -#endif +__END_DECLS #endif /* !_VIS_H_ */ diff --git a/cmd-line-utils/libedit/parse.c b/cmd-line-utils/libedit/parse.c index 993cf5b752d..5bdefb5a0e4 100644 --- a/cmd-line-utils/libedit/parse.c +++ b/cmd-line-utils/libedit/parse.c @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.20 2003/12/05 13:37:48 lukem Exp $ */ +/* $NetBSD: parse.c,v 1.22 2005/05/29 04:58:15 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * parse.c: parse an editline extended command @@ -129,7 +135,7 @@ el_parse(EditLine *el, int argc, const char *argv[]) * the appropriate character or -1 if the escape is not valid */ protected int -parse__escape(const char **const ptr) +parse__escape(const char **ptr) { const char *p; int c; diff --git a/cmd-line-utils/libedit/parse.h b/cmd-line-utils/libedit/parse.h index 4b796666b8e..58dced1aeaa 100644 --- a/cmd-line-utils/libedit/parse.h +++ b/cmd-line-utils/libedit/parse.h @@ -1,4 +1,4 @@ -/* $NetBSD: parse.h,v 1.5 2003/08/07 16:44:32 agc Exp $ */ +/* $NetBSD: parse.h,v 1.6 2005/05/29 04:58:15 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -41,7 +41,7 @@ #define _h_el_parse protected int parse_line(EditLine *, const char *); -protected int parse__escape(const char ** const); +protected int parse__escape(const char **); protected char *parse__string(char *, const char *); protected int parse_cmd(EditLine *, const char *); diff --git a/cmd-line-utils/libedit/prompt.c b/cmd-line-utils/libedit/prompt.c index 455dd60331b..982943afd30 100644 --- a/cmd-line-utils/libedit/prompt.c +++ b/cmd-line-utils/libedit/prompt.c @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * prompt.c: Prompt printing functions diff --git a/cmd-line-utils/libedit/read.c b/cmd-line-utils/libedit/read.c index 51848c2038e..ac768142e79 100644 --- a/cmd-line-utils/libedit/read.c +++ b/cmd-line-utils/libedit/read.c @@ -1,4 +1,4 @@ -/* $NetBSD: read.c,v 1.35 2005/03/09 23:55:02 christos Exp $ */ +/* $NetBSD: read.c,v 1.43 2009/02/05 19:15:44 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * read.c: Clean this junk up! This is horrible code. @@ -50,6 +56,7 @@ private int read__fixio(int, int); private int read_preread(EditLine *); private int read_char(EditLine *, char *); private int read_getcmd(EditLine *, el_action_t *, char *); +private void read_pop(c_macro_t *); /* read_init(): * Initialize the read stuff @@ -205,7 +212,7 @@ read_preread(EditLine *el) * Push a macro */ public void -el_push(EditLine *el, char *str) +el_push(EditLine *el, const char *str) { c_macro_t *ma = &el->el_chared.c_macro; @@ -216,7 +223,7 @@ el_push(EditLine *el, char *str) ma->level--; } term_beep(el); - term__flush(); + term__flush(el); } @@ -294,6 +301,19 @@ read_char(EditLine *el, char *cp) return (num_read); } +/* read_pop(): + * Pop a macro from the stack + */ +private void +read_pop(c_macro_t *ma) +{ + int i; + + el_free(ma->macro[0]); + for (i = ma->level--; i > 0; i--) + ma->macro[i - 1] = ma->macro[i]; + ma->offset = 0; +} /* el_getc(): * Read a character @@ -304,26 +324,28 @@ el_getc(EditLine *el, char *cp) int num_read; c_macro_t *ma = &el->el_chared.c_macro; - term__flush(); + term__flush(el); for (;;) { if (ma->level < 0) { if (!read_preread(el)) break; } + if (ma->level < 0) break; - if (ma->macro[ma->level][ma->offset] == '\0') { - el_free(ma->macro[ma->level--]); - ma->offset = 0; + if (ma->macro[0][ma->offset] == '\0') { + read_pop(ma); continue; } - *cp = ma->macro[ma->level][ma->offset++] & 0377; - if (ma->macro[ma->level][ma->offset] == '\0') { + + *cp = ma->macro[0][ma->offset++] & 0377; + + if (ma->macro[0][ma->offset] == '\0') { /* Needed for QuoteMode On */ - el_free(ma->macro[ma->level--]); - ma->offset = 0; + read_pop(ma); } + return (1); } @@ -357,11 +379,11 @@ read_prepare(EditLine *el) we have the wrong size. */ el_resize(el); re_clear_display(el); /* reset the display stuff */ - ch_reset(el); + ch_reset(el, 0); re_refresh(el); /* print the prompt */ if (el->el_flags & UNBUFFERED) - term__flush(); + term__flush(el); } protected void @@ -438,7 +460,7 @@ el_gets(EditLine *el, int *nread) else cp = el->el_line.lastchar; - term__flush(); + term__flush(el); while ((*el->el_read.read_char)(el, cp) == 1) { /* make sure there is space next character */ @@ -478,7 +500,7 @@ el_gets(EditLine *el, int *nread) #endif /* DEBUG_READ */ break; } - if ((unsigned int)cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */ + if ((unsigned int)cmdnum >= (unsigned int)el->el_map.nfunc) { /* BUG CHECK command */ #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "ERROR: illegal command from key 0%o\r\n", ch); @@ -570,7 +592,7 @@ el_gets(EditLine *el, int *nread) #endif /* DEBUG_READ */ /* put (real) cursor in a known place */ re_clear_display(el); /* reset the display stuff */ - ch_reset(el); /* reset the input pointers */ + ch_reset(el, 1); /* reset the input pointers */ re_refresh(el); /* print the prompt again */ break; @@ -581,7 +603,7 @@ el_gets(EditLine *el, int *nread) "*** editor ERROR ***\r\n\n"); #endif /* DEBUG_READ */ term_beep(el); - term__flush(); + term__flush(el); break; } el->el_state.argument = 1; @@ -591,7 +613,7 @@ el_gets(EditLine *el, int *nread) break; } - term__flush(); /* flush any buffered output */ + term__flush(el); /* flush any buffered output */ /* make sure the tty is set up correctly */ if ((el->el_flags & UNBUFFERED) == 0) { read_finish(el); diff --git a/cmd-line-utils/libedit/read.h b/cmd-line-utils/libedit/read.h index 1982f47253b..bd8d4c1f5bb 100644 --- a/cmd-line-utils/libedit/read.h +++ b/cmd-line-utils/libedit/read.h @@ -1,4 +1,4 @@ -/* $NetBSD: read.h,v 1.4 2004/02/27 14:52:18 christos Exp $ */ +/* $NetBSD: read.h,v 1.6 2008/04/29 06:53:01 martin Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED diff --git a/cmd-line-utils/libedit/readline.c b/cmd-line-utils/libedit/readline.c index 004fcf7d183..ca8796fbd37 100644 --- a/cmd-line-utils/libedit/readline.c +++ b/cmd-line-utils/libedit/readline.c @@ -1,4 +1,4 @@ -/* $NetBSD: readline.c,v 1.49 2005/03/10 19:34:46 christos Exp $ */ +/* $NetBSD: readline.c,v 1.78 2009/02/05 19:15:26 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -36,25 +29,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* AIX requires this to be the first thing in the file. */ -#if defined (_AIX) && !defined (__GNUC__) - #pragma alloca -#endif - -#include - -#ifdef __GNUC__ -# undef alloca -# define alloca(n) __builtin_alloca (n) -#else -# ifdef HAVE_ALLOCA_H -# include -# else -# ifndef _AIX -extern char *alloca (); -# endif -# endif -#endif +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#endif /* not lint && not SCCSID */ #include #include @@ -68,12 +45,23 @@ extern char *alloca (); #include #include #include +#include +#ifdef HAVE_VIS_H #include - -#include "readline/readline.h" +#else +#include "np/vis.h" +#endif +#ifdef HAVE_ALLOCA_H +#include +#endif #include "el.h" #include "fcns.h" /* for EL_NUM_FCNS */ #include "histedit.h" +#include "readline/readline.h" +#include "filecomplete.h" + +void rl_prep_terminal(int); +void rl_deprep_terminal(void); /* for rl_complete() */ #define TAB '\r' @@ -94,9 +82,12 @@ FILE *rl_outstream = NULL; int rl_point = 0; int rl_end = 0; char *rl_line_buffer = NULL; -VFunction *rl_linefunc = NULL; +VCPFunction *rl_linefunc = NULL; int rl_done = 0; VFunction *rl_event_hook = NULL; +KEYMAP_ENTRY_ARRAY emacs_standard_keymap, + emacs_meta_keymap, + emacs_ctlx_keymap; int history_base = 1; /* probably never subject to change */ int history_length = 0; @@ -112,21 +103,23 @@ int rl_attempted_completion_over = 0; char *rl_basic_word_break_characters = break_chars; char *rl_completer_word_break_characters = NULL; char *rl_completer_quote_characters = NULL; -CPFunction *rl_completion_entry_function = NULL; +Function *rl_completion_entry_function = NULL; CPPFunction *rl_attempted_completion_function = NULL; Function *rl_pre_input_hook = NULL; Function *rl_startup1_hook = NULL; -Function *rl_getc_function = NULL; +int (*rl_getc_function)(FILE *) = NULL; char *rl_terminal_name = NULL; int rl_already_prompted = 0; int rl_filename_completion_desired = 0; int rl_ignore_completion_duplicates = 0; int rl_catch_signals = 1; +int readline_echoing_p = 1; +int _rl_print_completions_horizontally = 0; VFunction *rl_redisplay_function = NULL; Function *rl_startup_hook = NULL; VFunction *rl_completion_display_matches_hook = NULL; -VFunction *rl_prep_term_function = NULL; -VFunction *rl_deprep_term_function = NULL; +VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal; +VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal; /* * The current prompt string. @@ -150,7 +143,7 @@ int rl_completion_query_items = 100; * in the parsed text when it is passed to the completion function. * Shell uses this to help determine what kind of completing to do. */ -char *rl_special_prefixes = (char *)NULL; +char *rl_special_prefixes = NULL; /* * This is the character appended to the completed words if at the end of @@ -160,25 +153,21 @@ int rl_completion_append_character = ' '; /* stuff below is used internally by libedit for readline emulation */ -/* if not zero, non-unique completions always show list of possible matches */ -static int _rl_complete_show_all = 0; - static History *h = NULL; static EditLine *e = NULL; static Function *map[256]; -static int el_rl_complete_cmdnum = 0; +static jmp_buf topbuf; /* internal functions */ static unsigned char _el_rl_complete(EditLine *, int); static unsigned char _el_rl_tstp(EditLine *, int); static char *_get_prompt(EditLine *); +static int _getc_function(EditLine *, char *); static HIST_ENTRY *_move_history(int); static int _history_expand_command(const char *, size_t, size_t, char **); static char *_rl_compat_sub(const char *, const char *, const char *, int); -static int _rl_complete_internal(int); -static int _rl_qsort_string_compare(const void *, const void *); static int _rl_event_read_char(EditLine *, char *); static void _rl_update_pos(void); @@ -205,16 +194,49 @@ _move_history(int op) return (HIST_ENTRY *) NULL; rl_he.line = ev.str; - rl_he.data = (histdata_t) &(ev.num); + rl_he.data = NULL; return (&rl_he); } +/* + * read one key from user defined input function + */ +static int +/*ARGSUSED*/ +_getc_function(EditLine *el, char *c) +{ + int i; + + i = (*rl_getc_function)(NULL); + if (i == -1) + return 0; + *c = i; + return 1; +} + + /* * READLINE compatibility stuff */ +/* + * Set the prompt + */ +int +rl_set_prompt(const char *prompt) +{ + if (!prompt) + prompt = ""; + if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) + return 0; + if (rl_prompt) + free(rl_prompt); + rl_prompt = strdup(prompt); + return rl_prompt == NULL ? -1 : 0; +} + /* * initialize rl compat stuff */ @@ -223,7 +245,6 @@ rl_initialize(void) { HistEvent ev; const LineInfo *li; - int i; int editmode = 1; struct termios t; @@ -257,9 +278,12 @@ rl_initialize(void) max_input_history = INT_MAX; el_set(e, EL_HIST, history, h); + /* setup getc function if valid */ + if (rl_getc_function) + el_set(e, EL_GETCFN, _getc_function); + /* for proper prompt printing in readline() */ - rl_prompt = strdup(""); - if (rl_prompt == NULL) { + if (rl_set_prompt("") == -1) { history_end(h); el_end(e); return -1; @@ -291,17 +315,6 @@ rl_initialize(void) "ReadLine compatible suspend function", _el_rl_tstp); el_set(e, EL_BIND, "^Z", "rl_tstp", NULL); - - /* - * Find out where the rl_complete function was added; this is - * used later to detect that lastcmd was also rl_complete. - */ - for(i=EL_NUM_FCNS; i < e->el_map.nfunc; i++) { - if (e->el_map.func[i] == _el_rl_complete) { - el_rl_complete_cmdnum = i; - break; - } - } /* read settings from configuration file */ el_source(e, NULL); @@ -327,9 +340,10 @@ rl_initialize(void) * trailing newline (if there is any) */ char * -readline(const char *prompt) +readline(const char *p) { HistEvent ev; + const char * volatile prompt = p; int count; const char *ret; char *buf; @@ -340,15 +354,11 @@ readline(const char *prompt) rl_done = 0; + (void)setjmp(topbuf); + /* update prompt accordingly to what has been passed */ - if (!prompt) - prompt = ""; - if (strcmp(rl_prompt, prompt) != 0) { - free(rl_prompt); - rl_prompt = strdup(prompt); - if (rl_prompt == NULL) - return NULL; - } + if (rl_set_prompt(prompt) == -1) + return NULL; if (rl_pre_input_hook) (*rl_pre_input_hook)(NULL, 0); @@ -446,7 +456,7 @@ _rl_compat_sub(const char *str, const char *what, const char *with, } else *r++ = *s++; } - *r = 0; + *r = '\0'; return(result); } @@ -467,7 +477,7 @@ get_history_event(const char *cmd, int *cindex, int qchar) return(NULL); /* find out which event to take */ - if (cmd[idx] == history_expansion_char || cmd[idx] == 0) { + if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') { if (history(h, &ev, H_FIRST) != 0) return(NULL); *cindex = cmd[idx]? (idx + 1):idx; @@ -689,7 +699,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, if (aptr) free(aptr); - if (*cmd == 0 || (cmd - (command + offs) >= cmdlen)) { + if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) { *result = tmp; return(1); } @@ -699,7 +709,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, continue; else if (*cmd == 'h') { /* remove trailing path */ if ((aptr = strrchr(tmp, '/')) != NULL) - *aptr = 0; + *aptr = '\0'; } else if (*cmd == 't') { /* remove leading path */ if ((aptr = strrchr(tmp, '/')) != NULL) { aptr = strdup(aptr + 1); @@ -708,7 +718,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, } } else if (*cmd == 'r') { /* remove trailing suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) - *aptr = 0; + *aptr = '\0'; } else if (*cmd == 'e') { /* remove all but suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) { aptr = strdup(aptr); @@ -732,6 +742,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, what = realloc(from, size); if (what == NULL) { free(from); + free(tmp); return 0; } len = 0; @@ -744,6 +755,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, (size <<= 1)); if (nwhat == NULL) { free(what); + free(tmp); return 0; } what = nwhat; @@ -756,10 +768,13 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, free(what); if (search) { from = strdup(search); - if (from == NULL) + if (from == NULL) { + free(tmp); return 0; + } } else { from = NULL; + free(tmp); return (-1); } } @@ -771,6 +786,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, with = realloc(to, size); if (with == NULL) { free(to); + free(tmp); return -1; } len = 0; @@ -782,6 +798,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen, nwith = realloc(with, size); if (nwith == NULL) { free(with); + free(tmp); return -1; } with = nwith; @@ -850,12 +867,14 @@ history_expand(char *str, char **output) return 0; } -#define ADD_STRING(what, len) \ +#define ADD_STRING(what, len, fr) \ { \ if (idx + len + 1 > size) { \ char *nresult = realloc(result, (size += len + 1));\ if (nresult == NULL) { \ free(*output); \ + if (/*CONSTCOND*/fr) \ + free(tmp); \ return 0; \ } \ result = nresult; \ @@ -867,6 +886,7 @@ history_expand(char *str, char **output) result = NULL; size = idx = 0; + tmp = NULL; for (i = 0; str[i];) { int qchar, loop_again; size_t len, start, j; @@ -904,13 +924,11 @@ loop: goto loop; } len = i - start; - tmp = &str[start]; - ADD_STRING(tmp, len); + ADD_STRING(&str[start], len, 0); if (str[i] == '\0' || str[i] != history_expansion_char) { len = j - i; - tmp = &str[i]; - ADD_STRING(tmp, len); + ADD_STRING(&str[i], len, 0); if (start == 0) ret = 0; else @@ -920,8 +938,11 @@ loop: ret = _history_expand_command (str, i, (j - i), &tmp); if (ret > 0 && tmp) { len = strlen(tmp); - ADD_STRING(tmp, len); + ADD_STRING(tmp, len, 1); + } + if (tmp) { free(tmp); + tmp = NULL; } i = j; } @@ -973,23 +994,23 @@ history_arg_extract(int start, int end, const char *str) if (start < 0) start = end; - if (start < 0 || end < 0 || start > max || end > max || start > end) + if (start < 0 || end < 0 || (size_t)start > max || (size_t)end > max || start > end) return(NULL); - for (i = start, len = 0; i <= end; i++) + for (i = start, len = 0; i <= (size_t)end; i++) len += strlen(arr[i]) + 1; len++; result = malloc(len); if (result == NULL) return NULL; - for (i = start, len = 0; i <= end; i++) { + for (i = start, len = 0; i <= (size_t)end; i++) { (void)strcpy(result + len, arr[i]); len += strlen(arr[i]); - if (i < end) + if (i < (size_t)end) result[len++] = ' '; } - result[len] = 0; + result[len] = '\0'; for (i = 0; arr[i]; i++) free(arr[i]); @@ -1152,7 +1173,7 @@ history_get(int num) return (NULL); /* error */ /* look backwards for event matching specified offset */ - if (history(h, &ev, H_NEXT_EVENT, num)) + if (history(h, &ev, H_NEXT_EVENT, num + 1)) return (NULL); she.line = ev.str; @@ -1184,6 +1205,31 @@ add_history(const char *line) } +/* + * remove the specified entry from the history list and return it. + */ +HIST_ENTRY * +remove_history(int num) +{ + HIST_ENTRY *she; + HistEvent ev; + + if (h == NULL || e == NULL) + rl_initialize(); + + if (history(h, &ev, H_DEL, num) != 0) + return NULL; + + if ((she = malloc(sizeof(*she))) == NULL) + return NULL; + + she->line = ev.str; + she->data = NULL; + + return she; +} + + /* * clear the history list - delete all entries */ @@ -1377,172 +1423,18 @@ history_search_pos(const char *str, /********************************/ /* completion functions */ -/* - * does tilde expansion of strings of type ``~user/foo'' - * if ``user'' isn't valid user name or ``txt'' doesn't start - * w/ '~', returns pointer to strdup()ed copy of ``txt'' - * - * it's callers's responsibility to free() returned string - */ char * -tilde_expand(char *txt) +tilde_expand(char *name) { - struct passwd *pass; - char *temp; - size_t len = 0; - - if (txt[0] != '~') - return (strdup(txt)); - - temp = strchr(txt + 1, '/'); - if (temp == NULL) { - temp = strdup(txt + 1); - if (temp == NULL) - return NULL; - } else { - len = temp - txt + 1; /* text until string after slash */ - temp = malloc(len); - if (temp == NULL) - return NULL; - (void)strncpy(temp, txt + 1, len - 2); - temp[len - 2] = '\0'; - } - pass = getpwnam(temp); - free(temp); /* value no more needed */ - if (pass == NULL) - return (strdup(txt)); - - /* update pointer txt to point at string immedially following */ - /* first slash */ - txt += len; - - temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); - if (temp == NULL) - return NULL; - (void)sprintf(temp, "%s/%s", pass->pw_dir, txt); - - return (temp); + return fn_tilde_expand(name); } - -/* - * return first found file name starting by the ``text'' or NULL if no - * such file can be found - * value of ``state'' is ignored - * - * it's caller's responsibility to free returned string - */ char * -filename_completion_function(const char *text, int state) +filename_completion_function(const char *name, int state) { - static DIR *dir = NULL; - static char *filename = NULL, *dirname = NULL; - static size_t filename_len = 0; - struct dirent *entry; - char *temp; - size_t len; - - if (state == 0 || dir == NULL) { - temp = strrchr(text, '/'); - if (temp) { - char *nptr; - temp++; - nptr = realloc(filename, strlen(temp) + 1); - if (nptr == NULL) { - free(filename); - return NULL; - } - filename = nptr; - (void)strcpy(filename, temp); - len = temp - text; /* including last slash */ - nptr = realloc(dirname, len + 1); - if (nptr == NULL) { - free(filename); - return NULL; - } - dirname = nptr; - (void)strncpy(dirname, text, len); - dirname[len] = '\0'; - } else { - if (*text == 0) - filename = NULL; - else { - filename = strdup(text); - if (filename == NULL) - return NULL; - } - dirname = NULL; - } - - /* support for ``~user'' syntax */ - if (dirname && *dirname == '~') { - char *nptr; - temp = tilde_expand(dirname); - if (temp == NULL) - return NULL; - nptr = realloc(dirname, strlen(temp) + 1); - if (nptr == NULL) { - free(dirname); - return NULL; - } - dirname = nptr; - (void)strcpy(dirname, temp); /* safe */ - free(temp); /* no longer needed */ - } - /* will be used in cycle */ - filename_len = filename ? strlen(filename) : 0; - - if (dir != NULL) { - (void)closedir(dir); - dir = NULL; - } - dir = opendir(dirname ? dirname : "."); - if (!dir) - return (NULL); /* cannot open the directory */ - } - /* find the match */ - while ((entry = readdir(dir)) != NULL) { - /* skip . and .. */ - if (entry->d_name[0] == '.' && (!entry->d_name[1] - || (entry->d_name[1] == '.' && !entry->d_name[2]))) - continue; - if (filename_len == 0) - break; - /* otherwise, get first entry where first */ - /* filename_len characters are equal */ - if (entry->d_name[0] == filename[0] - /* Some dirents have d_namlen, but it is not portable. */ - && strlen(entry->d_name) >= filename_len - && strncmp(entry->d_name, filename, - filename_len) == 0) - break; - } - - if (entry) { /* match found */ - - struct stat stbuf; - /* Some dirents have d_namlen, but it is not portable. */ - len = strlen(entry->d_name) + - ((dirname) ? strlen(dirname) : 0) + 1 + 1; - temp = malloc(len); - if (temp == NULL) - return NULL; - (void)sprintf(temp, "%s%s", - dirname ? dirname : "", entry->d_name); /* safe */ - - /* test, if it's directory */ - if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) - strcat(temp, "/"); /* safe */ - } else { - (void)closedir(dir); - dir = NULL; - temp = NULL; - } - - return (temp); + return fn_filename_completion_function(name, state); } - /* * a completion generator for usernames; returns _first_ username * which starts with supplied text @@ -1564,6 +1456,7 @@ username_completion_function(const char *text, int state) if (state == 0) setpwent(); + /* XXXMYSQL: just use non-_r functions for now */ while ((pwd = getpwent()) && text[0] == pwd->pw_name[0] && strcmp(text, pwd->pw_name) == 0); @@ -1575,16 +1468,6 @@ username_completion_function(const char *text, int state) } -/* - * el-compatible wrapper around rl_complete; needed for key binding - */ -/* ARGSUSED */ -static unsigned char -_el_rl_complete(EditLine *el __attribute__((__unused__)), int ch) -{ - return (unsigned char) rl_complete(0, ch); -} - /* * el-compatible wrapper to send TSTP on ^Z */ @@ -1596,273 +1479,36 @@ _el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__un return CC_NORM; } -/* - * returns list of completions for text given - */ -char ** -completion_matches(const char *text, CPFunction *genfunc) -{ - char **match_list = NULL, *retstr, *prevstr; - size_t match_list_len, max_equal, which, i; - size_t matches; - - if (h == NULL || e == NULL) - rl_initialize(); - - matches = 0; - match_list_len = 1; - while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { - /* allow for list terminator here */ - if (matches + 3 >= match_list_len) { - char **nmatch_list; - while (matches + 3 >= match_list_len) - match_list_len <<= 1; - nmatch_list = realloc(match_list, - match_list_len * sizeof(char *)); - if (nmatch_list == NULL) { - free(match_list); - return NULL; - } - match_list = nmatch_list; - - } - match_list[++matches] = retstr; - } - - if (!match_list) - return NULL; /* nothing found */ - - /* find least denominator and insert it to match_list[0] */ - which = 2; - prevstr = match_list[1]; - max_equal = strlen(prevstr); - for (; which <= matches; which++) { - for (i = 0; i < max_equal && - prevstr[i] == match_list[which][i]; i++) - continue; - max_equal = i; - } - - retstr = malloc(max_equal + 1); - if (retstr == NULL) { - free(match_list); - return NULL; - } - (void)strncpy(retstr, match_list[1], max_equal); - retstr[max_equal] = '\0'; - match_list[0] = retstr; - - /* add NULL as last pointer to the array */ - match_list[matches + 1] = (char *) NULL; - - return (match_list); -} - -/* - * Sort function for qsort(). Just wrapper around strcasecmp(). - */ -static int -_rl_qsort_string_compare(i1, i2) - const void *i1, *i2; -{ - const char *s1 = ((const char * const *)i1)[0]; - const char *s2 = ((const char * const *)i2)[0]; - - return strcasecmp(s1, s2); -} - /* * Display list of strings in columnar format on readline's output stream. * 'matches' is list of strings, 'len' is number of strings in 'matches', * 'max' is maximum length of string in 'matches'. */ void -rl_display_match_list (matches, len, max) - char **matches; - int len, max; +rl_display_match_list(char **matches, int len, int max) { - int i, idx, limit, count; - int screenwidth = e->el_term.t_size.h; - /* - * Find out how many entries can be put on one line, count - * with two spaces between strings. - */ - limit = screenwidth / (max + 2); - if (limit == 0) - limit = 1; - - /* how many lines of output */ - count = len / limit; - if (count * limit < len) - count++; - - /* Sort the items if they are not already sorted. */ - qsort(&matches[1], (size_t)(len - 1), sizeof(char *), - _rl_qsort_string_compare); - - idx = 1; - for(; count > 0; count--) { - for(i = 0; i < limit && matches[idx]; i++, idx++) - (void)fprintf(e->el_outfile, "%-*s ", max, - matches[idx]); - (void)fprintf(e->el_outfile, "\n"); - } + fn_display_match_list(e, matches, len, max); } -/* - * Complete the word at or before point, called by rl_complete() - * 'what_to_do' says what to do with the completion. - * `?' means list the possible completions. - * TAB means do standard completion. - * `*' means insert all of the possible completions. - * `!' means to do standard completion, and list all possible completions if - * there is more than one. - * - * Note: '*' support is not implemented - */ -static int -_rl_complete_internal(int what_to_do) +static const char * +/*ARGSUSED*/ +_rl_completion_append_character_function(const char *dummy + __attribute__((__unused__))) { - CPFunction *complet_func; - const LineInfo *li; - char *temp, **matches; - const char *ctemp; - size_t len; - - rl_completion_type = what_to_do; - - if (h == NULL || e == NULL) - rl_initialize(); - - complet_func = rl_completion_entry_function; - if (!complet_func) - complet_func = filename_completion_function; - - /* We now look backwards for the start of a filename/variable word */ - li = el_line(e); - ctemp = (const char *) li->cursor; - while (ctemp > li->buffer - && !strchr(rl_basic_word_break_characters, ctemp[-1]) - && (!rl_special_prefixes - || !strchr(rl_special_prefixes, ctemp[-1]) ) ) - ctemp--; - - len = li->cursor - ctemp; - temp = alloca(len + 1); - (void)strncpy(temp, ctemp, len); - temp[len] = '\0'; - - /* these can be used by function called in completion_matches() */ - /* or (*rl_attempted_completion_function)() */ - _rl_update_pos(); - - if (rl_attempted_completion_function) { - int end = li->cursor - li->buffer; - matches = (*rl_attempted_completion_function) (temp, (int) - (end - len), end); - } else - matches = 0; - if (!rl_attempted_completion_function || !matches) - matches = completion_matches(temp, complet_func); - - if (matches) { - int i, retval = CC_REFRESH; - int matches_num, maxlen, match_len, match_display=1; - - /* - * Only replace the completed string with common part of - * possible matches if there is possible completion. - */ - if (matches[0][0] != '\0') { - el_deletestr(e, (int) len); - el_insertstr(e, matches[0]); - } - - if (what_to_do == '?') - goto display_matches; - - if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { - /* - * We found exact match. Add a space after - * it, unless we do filename completion and the - * object is a directory. - */ - size_t alen = strlen(matches[0]); - if ((complet_func != filename_completion_function - || (alen > 0 && (matches[0])[alen - 1] != '/')) - && rl_completion_append_character) { - char buf[2]; - buf[0] = rl_completion_append_character; - buf[1] = '\0'; - el_insertstr(e, buf); - } - } else if (what_to_do == '!') { - display_matches: - /* - * More than one match and requested to list possible - * matches. - */ - - for(i=1, maxlen=0; matches[i]; i++) { - match_len = strlen(matches[i]); - if (match_len > maxlen) - maxlen = match_len; - } - matches_num = i - 1; - - /* newline to get on next line from command line */ - (void)fprintf(e->el_outfile, "\n"); - - /* - * If there are too many items, ask user for display - * confirmation. - */ - if (matches_num > rl_completion_query_items) { - (void)fprintf(e->el_outfile, - "Display all %d possibilities? (y or n) ", - matches_num); - (void)fflush(e->el_outfile); - if (getc(stdin) != 'y') - match_display = 0; - (void)fprintf(e->el_outfile, "\n"); - } - - if (match_display) - rl_display_match_list(matches, matches_num, - maxlen); - retval = CC_REDISPLAY; - } else if (matches[0][0]) { - /* - * There was some common match, but the name was - * not complete enough. Next tab will print possible - * completions. - */ - el_beep(e); - } else { - /* lcd is not a valid object - further specification */ - /* is needed */ - el_beep(e); - retval = CC_NORM; - } - - /* free elements of array and the array itself */ - for (i = 0; matches[i]; i++) - free(matches[i]); - free(matches), matches = NULL; - - return (retval); - } - return (CC_NORM); + static char buf[2]; + buf[0] = rl_completion_append_character; + buf[1] = '\0'; + return buf; } /* * complete word at current point */ +/* ARGSUSED */ int -/*ARGSUSED*/ -rl_complete(int ignore, int invoking_key) +rl_complete(int ignore __attribute__((__unused__)), int invoking_key) { if (h == NULL || e == NULL) rl_initialize(); @@ -1873,15 +1519,26 @@ rl_complete(int ignore, int invoking_key) arr[1] = '\0'; el_insertstr(e, arr); return (CC_REFRESH); - } else if (e->el_state.lastcmd == el_rl_complete_cmdnum) - return _rl_complete_internal('?'); - else if (_rl_complete_show_all) - return _rl_complete_internal('!'); - else - return _rl_complete_internal(TAB); + } + + /* Just look at how many global variables modify this operation! */ + return fn_complete(e, + (CPFunction *)rl_completion_entry_function, + rl_attempted_completion_function, + rl_basic_word_break_characters, rl_special_prefixes, + _rl_completion_append_character_function, rl_completion_query_items, + &rl_completion_type, &rl_attempted_completion_over, + &rl_point, &rl_end); } +/* ARGSUSED */ +static unsigned char +_el_rl_complete(EditLine *el __attribute__((__unused__)), int ch) +{ + return (unsigned char)rl_complete(0, ch); +} + /* * misc other functions */ @@ -1989,7 +1646,7 @@ int rl_add_defun(const char *name, Function *fun, int c) { char dest[8]; - if (c >= sizeof(map) / sizeof(map[0]) || c < 0) + if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) return -1; map[(unsigned char)c] = fun; el_set(e, EL_ADDFN, name, name, rl_bind_wrapper); @@ -2007,11 +1664,7 @@ rl_callback_read_char() if (buf == NULL || count-- <= 0) return; -#ifdef CTRL2 /* _AIX */ - if (count == 0 && buf[0] == CTRL2('d')) -#else - if (count == 0 && buf[0] == CTRL('d')) -#endif + if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF]) done = 1; if (buf[count] == '\n' || buf[count] == '\r') done = 2; @@ -2029,14 +1682,12 @@ rl_callback_read_char() } void -rl_callback_handler_install (const char *prompt, VFunction *linefunc) +rl_callback_handler_install(const char *prompt, VCPFunction *linefunc) { if (e == NULL) { rl_initialize(); } - if (rl_prompt) - free(rl_prompt); - rl_prompt = prompt ? strdup(strchr(prompt, *prompt)) : NULL; + (void)rl_set_prompt(prompt); rl_linefunc = linefunc; el_set(e, EL_UNBUFFERED, 1); } @@ -2045,17 +1696,14 @@ void rl_callback_handler_remove(void) { el_set(e, EL_UNBUFFERED, 0); + rl_linefunc = NULL; } void rl_redisplay(void) { char a[2]; -#ifdef CTRL2 /* _AIX */ - a[0] = CTRL2('r'); -#else - a[0] = CTRL('r'); -#endif + a[0] = e->el_tty.t_c[TS_IO][C_REPRINT]; a[1] = '\0'; el_push(e, a); } @@ -2079,7 +1727,7 @@ rl_prep_terminal(int meta_flag) } void -rl_deprep_terminal() +rl_deprep_terminal(void) { el_set(e, EL_PREP_TERM, 0); } @@ -2104,6 +1752,16 @@ rl_parse_and_bind(const char *line) return (argc ? 1 : 0); } +int +rl_variable_bind(const char *var, const char *value) +{ + /* + * The proper return value is undocument, but this is what the + * readline source seems to do. + */ + return ((el_set(e, EL_BIND, "", var, value) == -1) ? 1 : 0); +} + void rl_stuff_char(int c) { @@ -2119,7 +1777,7 @@ _rl_event_read_char(EditLine *el, char *cp) { int n, num_read = 0; - *cp = 0; + *cp = '\0'; while (rl_event_hook) { (*rl_event_hook)(); @@ -2164,3 +1822,142 @@ _rl_update_pos(void) rl_point = li->cursor - li->buffer; rl_end = li->lastchar - li->buffer; } + +void +rl_get_screen_size(int *rows, int *cols) +{ + if (rows) + el_get(e, EL_GETTC, "li", rows); + if (cols) + el_get(e, EL_GETTC, "co", cols); +} + +void +rl_set_screen_size(int rows, int cols) +{ + char buf[64]; + (void)snprintf(buf, sizeof(buf), "%d", rows); + el_set(e, EL_SETTC, "li", buf); + (void)snprintf(buf, sizeof(buf), "%d", cols); + el_set(e, EL_SETTC, "co", buf); +} + +char ** +rl_completion_matches(const char *str, rl_compentry_func_t *fun) +{ + size_t len, max, i, j, min; + char **list, *match, *a, *b; + + len = 1; + max = 10; + if ((list = malloc(max * sizeof(*list))) == NULL) + return NULL; + + while ((match = (*fun)(str, (int)(len - 1))) != NULL) { + if (len == max) { + char **nl; + max += 10; + if ((nl = realloc(list, max * sizeof(*nl))) == NULL) + goto out; + list = nl; + } + list[len++] = match; + } + if (len == 1) + goto out; + list[len] = NULL; + if (len == 2) { + if ((list[0] = strdup(list[1])) == NULL) + goto out; + return list; + } + qsort(&list[1], len - 1, sizeof(*list), + (int (*)(const void *, const void *)) strcmp); + min = SIZE_T_MAX; + for (i = 1, a = list[i]; i < len - 1; i++, a = b) { + b = list[i + 1]; + for (j = 0; a[j] && a[j] == b[j]; j++) + continue; + if (min > j) + min = j; + } + if (min == 0 && *str) { + if ((list[0] = strdup(str)) == NULL) + goto out; + } else { + if ((list[0] = malloc(min + 1)) == NULL) + goto out; + (void)memcpy(list[0], list[1], min); + list[0][min] = '\0'; + } + return list; + +out: + free(list); + return NULL; +} + +char * +rl_filename_completion_function (const char *text, int state) +{ + return fn_filename_completion_function(text, state); +} + +void +rl_forced_update_display(void) +{ + el_set(e, EL_REFRESH); +} + +int +_rl_abort_internal(void) +{ + el_beep(e); + longjmp(topbuf, 1); + /*NOTREACHED*/ +} + +int +_rl_qsort_string_compare(char **s1, char **s2) +{ + return strcoll(*s1, *s2); +} + +int +/*ARGSUSED*/ +rl_kill_text(int from, int to) +{ + return 0; +} + +Keymap +rl_make_bare_keymap(void) +{ + return NULL; +} + +Keymap +rl_get_keymap(void) +{ + return NULL; +} + +void +/*ARGSUSED*/ +rl_set_keymap(Keymap k) +{ +} + +int +/*ARGSUSED*/ +rl_generic_bind(int type, const char * keyseq, const char * data, Keymap k) +{ + return 0; +} + +int +/*ARGSUSED*/ +rl_bind_key_in_map(int key, Function *fun, Keymap k) +{ + return 0; +} diff --git a/cmd-line-utils/libedit/readline/readline.h b/cmd-line-utils/libedit/readline/readline.h index 6b1fa186512..c4806734bc5 100644 --- a/cmd-line-utils/libedit/readline/readline.h +++ b/cmd-line-utils/libedit/readline/readline.h @@ -1,4 +1,4 @@ -/* $NetBSD: readline.h,v 1.12 2004/09/08 18:15:37 christos Exp $ */ +/* $NetBSD: readline.h,v 1.24 2009/02/05 19:15:26 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -45,14 +38,14 @@ /* typedefs */ typedef int Function(const char *, int); typedef void VFunction(void); +typedef void VCPFunction(char *); typedef char *CPFunction(const char *, int); typedef char **CPPFunction(const char *, int, int); - -typedef void *histdata_t; +typedef char *rl_compentry_func_t(const char *, int); typedef struct _hist_entry { const char *line; - histdata_t *data; + const char *data; } HIST_ENTRY; typedef struct _keymap_entry { @@ -73,7 +66,7 @@ typedef KEYMAP_ENTRY *Keymap; #ifndef CTRL #include -#if defined(__GLIBC__) || defined(__MWERKS__) +#if !defined(__sun__) && !defined(__hpux__) #include #endif #ifndef CTRL @@ -102,8 +95,9 @@ extern int max_input_history; extern char *rl_basic_word_break_characters; extern char *rl_completer_word_break_characters; extern char *rl_completer_quote_characters; -extern CPFunction *rl_completion_entry_function; +extern Function *rl_completion_entry_function; extern CPPFunction *rl_attempted_completion_function; +extern int rl_attempted_completion_over; extern int rl_completion_type; extern int rl_completion_query_items; extern char *rl_special_prefixes; @@ -122,11 +116,13 @@ extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_ctlx_keymap; extern int rl_filename_completion_desired; extern int rl_ignore_completion_duplicates; -extern Function *rl_getc_function; +extern int (*rl_getc_function)(FILE *); extern VFunction *rl_redisplay_function; extern VFunction *rl_completion_display_matches_hook; extern VFunction *rl_prep_term_function; extern VFunction *rl_deprep_term_function; +extern int readline_echoing_p; +extern int _rl_print_completions_horizontally; /* supported functions */ char *readline(const char *); @@ -141,6 +137,7 @@ int history_is_stifled(void); int where_history(void); HIST_ENTRY *current_history(void); HIST_ENTRY *history_get(int); +HIST_ENTRY *remove_history(int); int history_total_bytes(void); int history_set_pos(int); HIST_ENTRY *previous_history(void); @@ -168,7 +165,7 @@ void rl_reset_terminal(const char *); int rl_bind_key(int, int (*)(int, int)); int rl_newline(int, int); void rl_callback_read_char(void); -void rl_callback_handler_install(const char *, VFunction *); +void rl_callback_handler_install(const char *, VCPFunction *); void rl_callback_handler_remove(void); void rl_redisplay(void); int rl_get_previous_history(int, int); @@ -176,13 +173,24 @@ void rl_prep_terminal(int); void rl_deprep_terminal(void); int rl_read_init_file(const char *); int rl_parse_and_bind(const char *); +int rl_variable_bind(const char *, const char *); void rl_stuff_char(int); int rl_add_defun(const char *, Function *, int); +void rl_get_screen_size(int *, int *); +void rl_set_screen_size(int, int); +char *rl_filename_completion_function (const char *, int); +int _rl_abort_internal(void); +int _rl_qsort_string_compare(char **, char **); +char **rl_completion_matches(const char *, rl_compentry_func_t *); +void rl_forced_update_display(void); +int rl_set_prompt(const char *); /* * The following are not implemented */ +int rl_kill_text(int, int); Keymap rl_get_keymap(void); +void rl_set_keymap(Keymap); Keymap rl_make_bare_keymap(void); int rl_generic_bind(int, const char *, const char *, Keymap); int rl_bind_key_in_map(int, Function *, Keymap); diff --git a/cmd-line-utils/libedit/refresh.c b/cmd-line-utils/libedit/refresh.c index 46aca15ef08..5edd1fe78fc 100644 --- a/cmd-line-utils/libedit/refresh.c +++ b/cmd-line-utils/libedit/refresh.c @@ -1,4 +1,4 @@ -/* $NetBSD: refresh.c,v 1.26 2003/08/07 16:44:33 agc Exp $ */ +/* $NetBSD: refresh.c,v 1.28 2008/09/10 15:45:37 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * refresh.c: Lower level screen refreshing functions @@ -49,6 +55,7 @@ private void re_update_line(EditLine *, char *, char *, int); private void re_insert (EditLine *, char *, int, int, char *, int); private void re_delete(EditLine *, char *, int, int, int); private void re_fastputc(EditLine *, int); +private void re_clear_eol(EditLine *, int, int, int); private void re__strncopy(char *, char *, size_t); private void re__copy_and_pad(char *, const char *, size_t); @@ -315,9 +322,9 @@ re_goto_bottom(EditLine *el) { term_move_to_line(el, el->el_refresh.r_oldcv); - term__putc('\n'); + term__putc(el, '\n'); re_clear_display(el); - term__flush(); + term__flush(el); } @@ -340,7 +347,7 @@ re_insert(EditLine *el __attribute__((__unused__)), ELRE_DEBUG(1, (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); - ELRE_DEBUG(1, (__F, "s == \"%s\"n", s)); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); /* open up the space for num chars */ if (num > 0) { @@ -353,7 +360,7 @@ re_insert(EditLine *el __attribute__((__unused__)), ELRE_DEBUG(1, (__F, "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); - ELRE_DEBUG(1, (__F, "s == \"%s\"n", s)); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); /* copy the characters */ for (a = d + dat; (a < d + dlen) && (num > 0); num--) @@ -362,7 +369,7 @@ re_insert(EditLine *el __attribute__((__unused__)), ELRE_DEBUG(1, (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", num, dat, dlen, d, s)); - ELRE_DEBUG(1, (__F, "s == \"%s\"n", s)); + ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); } @@ -411,6 +418,32 @@ re__strncopy(char *a, char *b, size_t n) *a++ = *b++; } +/* re_clear_eol(): + * Find the number of characters we need to clear till the end of line + * in order to make sure that we have cleared the previous contents of + * the line. fx and sx is the number of characters inserted or deleted + * int the first or second diff, diff is the difference between the + * number of characters between the new and old line. + */ +private void +re_clear_eol(EditLine *el, int fx, int sx, int diff) +{ + + ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", + sx, fx, diff)); + + if (fx < 0) + fx = -fx; + if (sx < 0) + sx = -sx; + if (fx > diff) + diff = fx; + if (sx > diff) + diff = sx; + + ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); + term_clear_EOL(el, diff); +} /***************************************************************** re_update_line() is based on finding the middle difference of each line @@ -626,7 +659,7 @@ re_update_line(EditLine *el, char *old, char *new, int i) fx = (nsb - nfd) - (osb - ofd); sx = (nls - nse) - (ols - ose); - ELRE_DEBUG(1, (__F, "\n")); + ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n", ofd - old, osb - old, ose - old, ols - old, oe - old)); ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n", @@ -775,9 +808,7 @@ re_update_line(EditLine *el, char *old, char *new, int i) * write (nsb-nfd) chars of new starting at nfd */ term_overwrite(el, nfd, (nsb - nfd)); - ELRE_DEBUG(1, (__F, - "cleareol %d\n", (oe - old) - (ne - new))); - term_clear_EOL(el, (oe - old) - (ne - new)); + re_clear_eol(el, fx, sx, (oe - old) - (ne - new)); /* * Done */ @@ -818,10 +849,7 @@ re_update_line(EditLine *el, char *old, char *new, int i) ELRE_DEBUG(1, (__F, "but with nothing left to save\r\n")); term_overwrite(el, nse, (nls - nse)); - ELRE_DEBUG(1, (__F, - "cleareol %d\n", (oe - old) - (ne - new))); - if ((oe - old) - (ne - new) != 0) - term_clear_EOL(el, (oe - old) - (ne - new)); + re_clear_eol(el, fx, sx, (oe - old) - (ne - new)); } } /* @@ -982,7 +1010,7 @@ re_refresh_cursor(EditLine *el) /* now go there */ term_move_to_line(el, v); term_move_to_char(el, h); - term__flush(); + term__flush(el); } @@ -993,7 +1021,7 @@ private void re_fastputc(EditLine *el, int c) { - term__putc(c); + term__putc(el, c); el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; if (el->el_cursor.h >= el->el_term.t_size.h) { /* if we must overflow */ @@ -1020,12 +1048,12 @@ re_fastputc(EditLine *el, int c) } if (EL_HAS_AUTO_MARGINS) { if (EL_HAS_MAGIC_MARGINS) { - term__putc(' '); - term__putc('\b'); + term__putc(el, ' '); + term__putc(el, '\b'); } } else { - term__putc('\r'); - term__putc('\n'); + term__putc(el, '\r'); + term__putc(el, '\n'); } } } @@ -1065,7 +1093,7 @@ re_fastaddc(EditLine *el) re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0')); re_fastputc(el, (c & 7) + '0'); } - term__flush(); + term__flush(el); } @@ -1104,7 +1132,7 @@ re_clear_lines(EditLine *el) } else { term_move_to_line(el, el->el_refresh.r_oldcv); /* go to last line */ - term__putc('\r'); /* go to BOL */ - term__putc('\n'); /* go to new line */ + term__putc(el, '\r'); /* go to BOL */ + term__putc(el, '\n'); /* go to new line */ } } diff --git a/cmd-line-utils/libedit/search.c b/cmd-line-utils/libedit/search.c index 850c5f27140..df50c7e7370 100644 --- a/cmd-line-utils/libedit/search.c +++ b/cmd-line-utils/libedit/search.c @@ -32,12 +32,17 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * search.c: History and character search functions */ -#include #include #if defined(REGEX) #include diff --git a/cmd-line-utils/libedit/sig.c b/cmd-line-utils/libedit/sig.c index 8e70933d606..5307ee6ec60 100644 --- a/cmd-line-utils/libedit/sig.c +++ b/cmd-line-utils/libedit/sig.c @@ -1,4 +1,4 @@ -/* $NetBSD: sig.c,v 1.11 2003/08/07 16:44:33 agc Exp $ */ +/* $NetBSD: sig.c,v 1.12 2008/09/10 15:45:37 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * sig.c: Signal handling stuff. @@ -51,15 +57,15 @@ private const int sighdl[] = { - 1 }; -private void sig_handler(int); +private void el_sig_handler(int); -/* sig_handler(): +/* el_sig_handler(): * This is the handler called for all signals * XXX: we cannot pass any data so we just store the old editline * state in a private variable */ private void -sig_handler(int signo) +el_sig_handler(int signo) { int i; sigset_t nset, oset; @@ -73,7 +79,7 @@ sig_handler(int signo) tty_rawmode(sel); if (ed_redisplay(sel, 0) == CC_REFRESH) re_refresh(sel); - term__flush(); + term__flush(sel); break; case SIGWINCH: @@ -154,7 +160,7 @@ sig_set(EditLine *el) for (i = 0; sighdl[i] != -1; i++) { el_signalhandler_t s; /* This could happen if we get interrupted */ - if ((s = signal(sighdl[i], sig_handler)) != sig_handler) + if ((s = signal(sighdl[i], el_sig_handler)) != el_sig_handler) el->el_signal[i] = s; } sel = el; diff --git a/cmd-line-utils/libedit/sig.h b/cmd-line-utils/libedit/sig.h index 0bf1fc37e39..2bd3c516d46 100644 --- a/cmd-line-utils/libedit/sig.h +++ b/cmd-line-utils/libedit/sig.h @@ -1,4 +1,4 @@ -/* $NetBSD: sig.h,v 1.5 2003/08/07 16:44:33 agc Exp $ */ +/* $NetBSD: sig.h,v 1.6 2008/07/12 15:27:14 christos Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -51,7 +51,6 @@ #define ALLSIGS \ _DO(SIGINT) \ _DO(SIGTSTP) \ - _DO(SIGSTOP) \ _DO(SIGQUIT) \ _DO(SIGHUP) \ _DO(SIGTERM) \ diff --git a/cmd-line-utils/libedit/strlcpy.c b/cmd-line-utils/libedit/strlcpy.c deleted file mode 100644 index e38d6cf1c4b..00000000000 --- a/cmd-line-utils/libedit/strlcpy.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $NetBSD: strlcpy.c,v 1.14 2003/10/27 00:12:42 lukem Exp $ */ -/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE - * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include -#include -#include - -#ifdef _LIBC -# ifdef __weak_alias -__weak_alias(strlcpy, _strlcpy) -# endif -#endif - -#if !HAVE_STRLCPY -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -#ifdef _LIBC -_strlcpy(dst, src, siz) -#else -strlcpy(dst, src, siz) -#endif - char *dst; - const char *src; - size_t siz; -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - _DIAGASSERT(dst != NULL); - _DIAGASSERT(src != NULL); - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} -#endif diff --git a/cmd-line-utils/libedit/strlcpy.h b/cmd-line-utils/libedit/strlcpy.h deleted file mode 100644 index e4d3a7ffa3f..00000000000 --- a/cmd-line-utils/libedit/strlcpy.h +++ /dev/null @@ -1,2 +0,0 @@ -size_t strlcpy(char *dst, const char *src, size_t size); -size_t strlcat(char *dst, const char *src, size_t size); diff --git a/cmd-line-utils/libedit/sys.h b/cmd-line-utils/libedit/sys.h index c8a29dbfb05..a0369affbb0 100644 --- a/cmd-line-utils/libedit/sys.h +++ b/cmd-line-utils/libedit/sys.h @@ -48,14 +48,14 @@ # define __attribute__(A) #endif -#ifndef __P -# define __P(x) x -#endif - #ifndef _DIAGASSERT # define _DIAGASSERT(x) #endif +#ifndef SIZE_T_MAX +# define SIZE_T_MAX UINT_MAX +#endif + #ifndef __BEGIN_DECLS # ifdef __cplusplus # define __BEGIN_DECLS extern "C" { @@ -113,6 +113,25 @@ char *fgetln(FILE *fp, size_t *len); #define REGEX /* Use POSIX.2 regular expression functions */ #undef REGEXP /* Use UNIX V8 regular expression functions */ +#ifdef __SunOS +extern int tgetent(char *, const char *); +extern int tgetflag(char *); +extern int tgetnum(char *); +extern int tputs(const char *, int, int (*)(int)); +extern char* tgoto(const char*, int, int); +extern char* tgetstr(char*, char**); +#endif + +/* XXXMYSQL: Bug#10218 Command line recall rolls into segfault */ +#if !HAVE_DECL_TGOTO +/* + 'tgoto' is not declared in the system header files, this causes + problems on 64-bit systems. The function returns a 64 bit pointer + but caller see it as "int" and it's thus truncated to 32-bit +*/ +extern char* tgoto(const char*, int, int); +#endif + #ifdef notdef # undef REGEX # undef REGEXP diff --git a/cmd-line-utils/libedit/term.c b/cmd-line-utils/libedit/term.c index b516d6753c3..488c760da14 100644 --- a/cmd-line-utils/libedit/term.c +++ b/cmd-line-utils/libedit/term.c @@ -1,4 +1,4 @@ -/* $NetBSD: term.c,v 1.40 2004/05/22 23:21:28 christos Exp $ */ +/* $NetBSD: term.c,v 1.48 2009/02/06 20:08:13 sketch Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * term.c: Editor/termcap-curses interface @@ -44,21 +50,28 @@ #include #include #include - +#if 0 /* TODO: do we need this */ +#ifdef HAVE_TERMCAP_H +#include +#endif +#endif #ifdef HAVE_CURSES_H -# include -#elif HAVE_NCURSES_H -# include +#include +#endif +#ifdef HAVE_NCURSES_H +#include #endif - /* Solaris's term.h does horrid things. */ -#if (defined(HAVE_TERM_H) && !defined(_SUNOS)) -# include +#if (defined(HAVE_TERM_H) && !defined(__SunOS)) +#include #endif - #include #include +#ifdef _REENTRANT +#include +#endif + #include "el.h" /* @@ -263,9 +276,13 @@ private int term_alloc_display(EditLine *); private void term_alloc(EditLine *, const struct termcapstr *, const char *); private void term_init_arrow(EditLine *); private void term_reset_arrow(EditLine *); +private int term_putc(int); +private void term_tputs(EditLine *, const char *, int); - -private FILE *term_outfile = NULL; /* XXX: How do we fix that? */ +#ifdef _REENTRANT +private pthread_mutex_t term_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +private FILE *term_outfile = NULL; /* term_setflags(): @@ -313,7 +330,6 @@ term_setflags(EditLine *el) #endif /* DEBUG_SCREEN */ } - /* term_init(): * Initialize the terminal stuff */ @@ -339,7 +355,6 @@ term_init(EditLine *el) if (el->el_term.t_val == NULL) return (-1); (void) memset(el->el_term.t_val, 0, T_val * sizeof(int)); - term_outfile = el->el_outfile; (void) term_set(el, NULL); term_init_arrow(el); return (0); @@ -390,7 +405,8 @@ term_alloc(EditLine *el, const struct termcapstr *t, const char *cap) * New string is shorter; no need to allocate space */ if (clen <= tlen) { - (void) strcpy(*str, cap); /* XXX strcpy is safe */ + if (*str) + (void) strcpy(*str, cap); /* XXX strcpy is safe */ return; } /* @@ -464,8 +480,12 @@ term_alloc_display(EditLine *el) return (-1); for (i = 0; i < c->v; i++) { b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); - if (b[i] == NULL) + if (b[i] == NULL) { + while (--i >= 0) + el_free((ptr_t) b[i]); + el_free((ptr_t) b); return (-1); + } } b[c->v] = NULL; el->el_display = b; @@ -475,8 +495,12 @@ term_alloc_display(EditLine *el) return (-1); for (i = 0; i < c->v; i++) { b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); - if (b[i] == NULL) + if (b[i] == NULL) { + while (--i >= 0) + el_free((ptr_t) b[i]); + el_free((ptr_t) b); return (-1); + } } b[c->v] = NULL; el->el_vdisplay = b; @@ -542,12 +566,12 @@ term_move_to_line(EditLine *el, int where) del--; } else { if ((del > 1) && GoodStr(T_DO)) { - (void) tputs(tgoto(Str(T_DO), del, del), - del, term__putc); + term_tputs(el, tgoto(Str(T_DO), del, + del), del); del = 0; } else { for (; del > 0; del--) - term__putc('\n'); + term__putc(el, '\n'); /* because the \n will become \r\n */ el->el_cursor.h = 0; } @@ -555,12 +579,11 @@ term_move_to_line(EditLine *el, int where) } } else { /* del < 0 */ if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) - (void) tputs(tgoto(Str(T_UP), -del, -del), -del, - term__putc); + term_tputs(el, tgoto(Str(T_UP), -del, -del), -del); else { if (GoodStr(T_up)) for (; del < 0; del++) - (void) tputs(Str(T_up), 1, term__putc); + term_tputs(el, Str(T_up), 1); } } el->el_cursor.v = where;/* now where is here */ @@ -587,7 +610,7 @@ mc_again: return; } if (!where) { /* if where is first column */ - term__putc('\r'); /* do a CR */ + term__putc(el, '\r'); /* do a CR */ el->el_cursor.h = 0; return; } @@ -595,12 +618,11 @@ mc_again: if ((del < -4 || del > 4) && GoodStr(T_ch)) /* go there directly */ - (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc); + term_tputs(el, tgoto(Str(T_ch), where, where), where); else { if (del > 0) { /* moving forward */ if ((del > 4) && GoodStr(T_RI)) - (void) tputs(tgoto(Str(T_RI), del, del), - del, term__putc); + term_tputs(el, tgoto(Str(T_RI), del, del), del); else { /* if I can do tabs, use them */ if (EL_CAN_TAB) { @@ -611,7 +633,7 @@ mc_again: (el->el_cursor.h & 0370); i < (where & 0370); i += 8) - term__putc('\t'); + term__putc(el, '\t'); /* then tab over */ el->el_cursor.h = where & 0370; } @@ -631,8 +653,8 @@ mc_again: } } else { /* del < 0 := moving backward */ if ((-del > 4) && GoodStr(T_LE)) - (void) tputs(tgoto(Str(T_LE), -del, -del), - -del, term__putc); + term_tputs(el, tgoto(Str(T_LE), -del, -del), + -del); else { /* can't go directly there */ /* * if the "cost" is greater than the "cost" @@ -643,12 +665,12 @@ mc_again: (((unsigned int) where >> 3) + (where & 07))) : (-del > where)) { - term__putc('\r'); /* do a CR */ + term__putc(el, '\r'); /* do a CR */ el->el_cursor.h = 0; goto mc_again; /* and try again */ } for (i = 0; i < -del; i++) - term__putc('\b'); + term__putc(el, '\b'); } } } @@ -673,7 +695,7 @@ term_overwrite(EditLine *el, const char *cp, int n) return; } do { - term__putc(*cp++); + term__putc(el, *cp++); el->el_cursor.h++; } while (--n); @@ -689,7 +711,7 @@ term_overwrite(EditLine *el, const char *cp, int n) != '\0') term_overwrite(el, &c, 1); else - term__putc(' '); + term__putc(el, ' '); el->el_cursor.h = 1; } } else /* no wrap, but cursor stays on screen */ @@ -723,19 +745,18 @@ term_deletechars(EditLine *el, int num) if (GoodStr(T_DC)) /* if I have multiple delete */ if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more * expen. */ - (void) tputs(tgoto(Str(T_DC), num, num), - num, term__putc); + term_tputs(el, tgoto(Str(T_DC), num, num), num); return; } if (GoodStr(T_dm)) /* if I have delete mode */ - (void) tputs(Str(T_dm), 1, term__putc); + term_tputs(el, Str(T_dm), 1); if (GoodStr(T_dc)) /* else do one at a time */ while (num--) - (void) tputs(Str(T_dc), 1, term__putc); + term_tputs(el, Str(T_dc), 1); if (GoodStr(T_ed)) /* if I have delete mode */ - (void) tputs(Str(T_ed), 1, term__putc); + term_tputs(el, Str(T_ed), 1); } @@ -764,37 +785,35 @@ term_insertwrite(EditLine *el, char *cp, int num) if (GoodStr(T_IC)) /* if I have multiple insert */ if ((num > 1) || !GoodStr(T_ic)) { /* if ic would be more expensive */ - (void) tputs(tgoto(Str(T_IC), num, num), - num, term__putc); + term_tputs(el, tgoto(Str(T_IC), num, num), num); term_overwrite(el, cp, num); /* this updates el_cursor.h */ return; } if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ - (void) tputs(Str(T_im), 1, term__putc); + term_tputs(el, Str(T_im), 1); el->el_cursor.h += num; do - term__putc(*cp++); + term__putc(el, *cp++); while (--num); if (GoodStr(T_ip)) /* have to make num chars insert */ - (void) tputs(Str(T_ip), 1, term__putc); + term_tputs(el, Str(T_ip), 1); - (void) tputs(Str(T_ei), 1, term__putc); + term_tputs(el, Str(T_ei), 1); return; } do { if (GoodStr(T_ic)) /* have to make num chars insert */ - (void) tputs(Str(T_ic), 1, term__putc); - /* insert a char */ + term_tputs(el, Str(T_ic), 1); - term__putc(*cp++); + term__putc(el, *cp++); el->el_cursor.h++; if (GoodStr(T_ip)) /* have to make num chars insert */ - (void) tputs(Str(T_ip), 1, term__putc); + term_tputs(el, Str(T_ip), 1); /* pad the inserted char */ } while (--num); @@ -810,10 +829,10 @@ term_clear_EOL(EditLine *el, int num) int i; if (EL_CAN_CEOL && GoodStr(T_ce)) - (void) tputs(Str(T_ce), 1, term__putc); + term_tputs(el, Str(T_ce), 1); else { for (i = 0; i < num; i++) - term__putc(' '); + term__putc(el, ' '); el->el_cursor.h += num; /* have written num spaces */ } } @@ -828,14 +847,14 @@ term_clear_screen(EditLine *el) if (GoodStr(T_cl)) /* send the clear screen code */ - (void) tputs(Str(T_cl), Val(T_li), term__putc); + term_tputs(el, Str(T_cl), Val(T_li)); else if (GoodStr(T_ho) && GoodStr(T_cd)) { - (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */ + term_tputs(el, Str(T_ho), Val(T_li)); /* home */ /* clear to bottom of screen */ - (void) tputs(Str(T_cd), Val(T_li), term__putc); + term_tputs(el, Str(T_cd), Val(T_li)); } else { - term__putc('\r'); - term__putc('\n'); + term__putc(el, '\r'); + term__putc(el, '\n'); } } @@ -848,9 +867,9 @@ term_beep(EditLine *el) { if (GoodStr(T_bl)) /* what termcap says we should use */ - (void) tputs(Str(T_bl), 1, term__putc); + term_tputs(el, Str(T_bl), 1); else - term__putc('\007'); /* an ASCII bell; ^G */ + term__putc(el, '\007'); /* an ASCII bell; ^G */ } @@ -862,9 +881,9 @@ protected void term_clear_to_bottom(EditLine *el) { if (GoodStr(T_cd)) - (void) tputs(Str(T_cd), Val(T_li), term__putc); + term_tputs(el, Str(T_cd), Val(T_li)); else if (GoodStr(T_ce)) - (void) tputs(Str(T_ce), Val(T_li), term__putc); + term_tputs(el, Str(T_ce), Val(T_li)); } #endif @@ -936,7 +955,7 @@ term_set(EditLine *el, const char *term) Val(T_co) = tgetnum("co"); Val(T_li) = tgetnum("li"); for (t = tstr; t->name != NULL; t++) { - /* XXX: some systems tgetstr needs non const */ + /* XXX: some systems' tgetstr needs non const */ term_alloc(el, t, tgetstr(strchr(t->name, *t->name), &area)); } @@ -1220,26 +1239,62 @@ term_bind_arrow(EditLine *el) } } +/* term_putc(): + * Add a character + */ +private int +term_putc(int c) +{ + + if (term_outfile == NULL) + return -1; + return fputc(c, term_outfile); +} + +private void +term_tputs(EditLine *el, const char *cap, int affcnt) +{ +#ifdef _REENTRANT + pthread_mutex_lock(&term_mutex); +#endif + term_outfile = el->el_outfile; + (void)tputs(cap, affcnt, term_putc); +#ifdef _REENTRANT + pthread_mutex_unlock(&term_mutex); +#endif +} /* term__putc(): * Add a character */ protected int -term__putc(int c) +term__putc(EditLine *el, int c) { - return (fputc(c, term_outfile)); + return fputc(c, el->el_outfile); } - /* term__flush(): * Flush output */ protected void -term__flush(void) +term__flush(EditLine *el) { - (void) fflush(term_outfile); + (void) fflush(el->el_outfile); +} + +/* term_writec(): + * Write the given character out, in a human readable form + */ +protected void +term_writec(EditLine *el, int c) +{ + char buf[8]; + int cnt = key__decode_char(buf, sizeof(buf), 0, c); + buf[cnt] = '\0'; + term_overwrite(el, buf, cnt); + term__flush(el); } @@ -1269,11 +1324,17 @@ term_telltc(EditLine *el, int argc __attribute__((__unused__)), (void) fprintf(el->el_outfile, "\tIt %s magic margins\n", EL_HAS_MAGIC_MARGINS ? "has" : "does not have"); - for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) + for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) { + const char *ub; + if (*ts && **ts) { + (void) key__decode_str(*ts, upbuf, sizeof(upbuf), ""); + ub = upbuf; + } else { + ub = "(empty)"; + } (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", - t->long_name, - t->name, *ts && **ts ? - key__decode_str(*ts, upbuf, "") : "(empty)"); + t->long_name, t->name, ub); + } (void) fputc('\n', el->el_outfile); return (0); } @@ -1292,7 +1353,7 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)), const char *what, *how; if (argv == NULL || argv[1] == NULL || argv[2] == NULL) - return (-1); + return -1; what = argv[1]; how = argv[2]; @@ -1307,7 +1368,7 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)), if (ts->name != NULL) { term_alloc(el, ts, how); term_setflags(el); - return (0); + return 0; } /* * Do the numeric ones second @@ -1316,46 +1377,100 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)), if (strcmp(tv->name, what) == 0) break; - if (tv->name != NULL) { - if (tv == &tval[T_pt] || tv == &tval[T_km] || - tv == &tval[T_am] || tv == &tval[T_xn]) { - if (strcmp(how, "yes") == 0) - el->el_term.t_val[tv - tval] = 1; - else if (strcmp(how, "no") == 0) - el->el_term.t_val[tv - tval] = 0; - else { - (void) fprintf(el->el_errfile, - "settc: Bad value `%s'.\n", how); - return (-1); - } - term_setflags(el); - if (term_change_size(el, Val(T_li), Val(T_co)) == -1) - return (-1); - return (0); - } else { - long i; - char *ep; + if (tv->name != NULL) + return -1; - i = strtol(how, &ep, 10); - if (*ep != '\0') { - (void) fprintf(el->el_errfile, - "settc: Bad value `%s'.\n", how); - return (-1); - } - el->el_term.t_val[tv - tval] = (int) i; - el->el_term.t_size.v = Val(T_co); - el->el_term.t_size.h = Val(T_li); - if (tv == &tval[T_co] || tv == &tval[T_li]) - if (term_change_size(el, Val(T_li), Val(T_co)) - == -1) - return (-1); - return (0); + if (tv == &tval[T_pt] || tv == &tval[T_km] || + tv == &tval[T_am] || tv == &tval[T_xn]) { + if (strcmp(how, "yes") == 0) + el->el_term.t_val[tv - tval] = 1; + else if (strcmp(how, "no") == 0) + el->el_term.t_val[tv - tval] = 0; + else { + (void) fprintf(el->el_errfile, + "%s: Bad value `%s'.\n", argv[0], how); + return -1; } + term_setflags(el); + if (term_change_size(el, Val(T_li), Val(T_co)) == -1) + return -1; + return 0; + } else { + long i; + char *ep; + + i = strtol(how, &ep, 10); + if (*ep != '\0') { + (void) fprintf(el->el_errfile, + "%s: Bad value `%s'.\n", argv[0], how); + return -1; + } + el->el_term.t_val[tv - tval] = (int) i; + el->el_term.t_size.v = Val(T_co); + el->el_term.t_size.h = Val(T_li); + if (tv == &tval[T_co] || tv == &tval[T_li]) + if (term_change_size(el, Val(T_li), Val(T_co)) + == -1) + return -1; + return 0; } - return (-1); } +/* term_gettc(): + * Get the current terminal characteristics + */ +protected int +/*ARGSUSED*/ +term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv) +{ + const struct termcapstr *ts; + const struct termcapval *tv; + char *what; + void *how; + + if (argv == NULL || argv[1] == NULL || argv[2] == NULL) + return (-1); + + what = argv[1]; + how = argv[2]; + + /* + * Do the strings first + */ + for (ts = tstr; ts->name != NULL; ts++) + if (strcmp(ts->name, what) == 0) + break; + + if (ts->name != NULL) { + *(char **)how = el->el_term.t_str[ts - tstr]; + return 0; + } + /* + * Do the numeric ones second + */ + for (tv = tval; tv->name != NULL; tv++) + if (strcmp(tv->name, what) == 0) + break; + + if (tv->name == NULL) + return -1; + + if (tv == &tval[T_pt] || tv == &tval[T_km] || + tv == &tval[T_am] || tv == &tval[T_xn]) { + static char yes[] = "yes"; + static char no[] = "no"; + if (el->el_term.t_val[tv - tval]) + *(char **)how = yes; + else + *(char **)how = no; + return 0; + } else { + *(int *)how = el->el_term.t_val[tv - tval]; + return 0; + } +} + /* term_echotc(): * Print the termcap string out with variable substitution */ @@ -1441,7 +1556,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)), break; } if (t->name == NULL) { - /* XXX: some systems tgetstr needs non const */ + /* XXX: some systems' tgetstr needs non const */ scap = tgetstr(strchr(*argv, **argv), &area); } if (!scap || scap[0] == '\0') { @@ -1494,7 +1609,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)), *argv); return (-1); } - (void) tputs(scap, 1, term__putc); + term_tputs(el, scap, 1); break; case 1: argv++; @@ -1522,7 +1637,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)), *argv); return (-1); } - (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc); + term_tputs(el, tgoto(scap, arg_cols, arg_rows), 1); break; default: /* This is wrong, but I will ignore it... */ @@ -1578,8 +1693,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)), *argv); return (-1); } - (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, - term__putc); + term_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows); break; } return (0); diff --git a/cmd-line-utils/libedit/tokenizer.c b/cmd-line-utils/libedit/tokenizer.c index 561b41740f8..5161cdd0a22 100644 --- a/cmd-line-utils/libedit/tokenizer.c +++ b/cmd-line-utils/libedit/tokenizer.c @@ -32,7 +32,13 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * tokenize.c: Bourne shell like tokenizer diff --git a/cmd-line-utils/libedit/tokenizer.h b/cmd-line-utils/libedit/tokenizer.h deleted file mode 100644 index 7cc7a3346e4..00000000000 --- a/cmd-line-utils/libedit/tokenizer.h +++ /dev/null @@ -1,54 +0,0 @@ -/* $NetBSD: tokenizer.h,v 1.5 2002/03/18 16:01:00 christos Exp $ */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Christos Zoulas of Cornell University. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)tokenizer.h 8.1 (Berkeley) 6/4/93 - */ - -/* - * tokenizer.h: Header file for tokenizer routines - */ -#ifndef _h_tokenizer -#define _h_tokenizer - -typedef struct tokenizer Tokenizer; - -Tokenizer *tok_init(const char *); -void tok_reset(Tokenizer *); -void tok_end(Tokenizer *); -int tok_line(Tokenizer *, const char *, int *, const char ***); - -#endif /* _h_tokenizer */ diff --git a/cmd-line-utils/libedit/tty.c b/cmd-line-utils/libedit/tty.c index 6f73fb4f9e7..3706905fc79 100644 --- a/cmd-line-utils/libedit/tty.c +++ b/cmd-line-utils/libedit/tty.c @@ -1,4 +1,4 @@ -/* $NetBSD: tty.c,v 1.21 2004/08/13 12:10:39 mycroft Exp $ */ +/* $NetBSD: tty.c,v 1.28 2009/02/06 19:53:23 sketch Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,18 +32,25 @@ * SUCH DAMAGE. */ -#include +#include "config.h" +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * tty.c: tty interface stuff */ #include +#include #include "tty.h" #include "el.h" typedef struct ttymodes_t { const char *m_name; - u_int m_value; + unsigned int m_value; int m_type; } ttymodes_t; @@ -438,13 +445,12 @@ private const ttymodes_t ttymodes[] = { -#define tty_getty(el, td) tcgetattr((el)->el_infd, (td)) -#define tty_setty(el, td) tcsetattr((el)->el_infd, TCSADRAIN, (td)) - #define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1) #define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8) #define tty__cooked_mode(td) ((td)->c_lflag & ICANON) +private int tty_getty(EditLine *, struct termios *); +private int tty_setty(EditLine *, int, const struct termios *); private int tty__getcharindex(int); private void tty__getchar(struct termios *, unsigned char *); private void tty__setchar(struct termios *, unsigned char *); @@ -453,6 +459,29 @@ private int tty_setup(EditLine *); #define t_qu t_ts +/* tty_getty(): + * Wrapper for tcgetattr to handle EINTR + */ +private int +tty_getty(EditLine *el, struct termios *t) +{ + int rv; + while ((rv = tcgetattr(el->el_infd, t)) == -1 && errno == EINTR) + continue; + return rv; +} + +/* tty_setty(): + * Wrapper for tcsetattr to handle EINTR + */ +private int +tty_setty(EditLine *el, int action, const struct termios *t) +{ + int rv; + while ((rv = tcsetattr(el->el_infd, action, t)) == -1 && errno == EINTR) + continue; + return rv; +} /* tty_setup(): * Get the tty parameters and initialize the editing state @@ -514,7 +543,7 @@ tty_setup(EditLine *el) el->el_tty.t_c[TS_IO][rst]; } tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); - if (tty_setty(el, &el->el_tty.t_ex) == -1) { + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_setup: tty_setty: %s\n", @@ -522,8 +551,11 @@ tty_setup(EditLine *el) #endif /* DEBUG_TTY */ return (-1); } - } else + } +#ifdef notdef + else tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); +#endif el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask; el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask; @@ -1040,7 +1072,7 @@ tty_rawmode(EditLine *el) } } } - if (tty_setty(el, &el->el_tty.t_ed) == -1) { + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n", strerror(errno)); @@ -1065,7 +1097,7 @@ tty_cookedmode(EditLine *el) if (el->el_flags & EDIT_DISABLED) return (0); - if (tty_setty(el, &el->el_tty.t_ex) == -1) { + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_cookedmode: tty_setty: %s\n", @@ -1101,7 +1133,7 @@ tty_quotemode(EditLine *el) el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask; el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask; - if (tty_setty(el, &el->el_tty.t_qu) == -1) { + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_qu) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n", strerror(errno)); @@ -1122,7 +1154,7 @@ tty_noquotemode(EditLine *el) if (el->el_tty.t_mode != QU_IO) return (0); - if (tty_setty(el, &el->el_tty.t_ed) == -1) { + if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n", strerror(errno)); @@ -1193,10 +1225,14 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) st = len = strlen(el->el_tty.t_t[z][m->m_type].t_name); } - x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) - ? '+' : '\0'; - x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value) - ? '-' : x; + if (i != -1) { + x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) + ? '+' : '\0'; + x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value) + ? '-' : x; + } else { + x = '\0'; + } if (x != '\0' || aflag) { @@ -1221,7 +1257,7 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) return (0); } while (argv && (s = *argv++)) { - char *p; + const char *p; switch (*s) { case '+': case '-': @@ -1232,10 +1268,10 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) break; } d = s; - if ((p = strchr(s, '=')) != NULL) - *p++ = '\0'; + p = strchr(s, '='); for (m = ttymodes; m->m_name; m++) - if (strcmp(m->m_name, d) == 0 && + if ((p ? strncmp(m->m_name, d, (size_t)(p - d)) : + strcmp(m->m_name, d)) == 0 && (p == NULL || m->m_type == MD_CHAR)) break; @@ -1246,7 +1282,7 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) } if (p) { int c = ffs((int)m->m_value); - int v = *p ? parse__escape((const char **const) &p) : + int v = *++p ? parse__escape((const char **) &p) : el->el_tty.t_vdisable; assert(c-- != 0); c = tty__getcharindex(c); @@ -1269,6 +1305,17 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) break; } } + + if (el->el_tty.t_mode == z) { + if (tty_setty(el, TCSADRAIN, tios) == -1) { +#ifdef DEBUG_TTY + (void) fprintf(el->el_errfile, + "tty_stty: tty_setty: %s\n", strerror(errno)); +#endif /* DEBUG_TTY */ + return (-1); + } + } + return (0); } diff --git a/cmd-line-utils/libedit/tty.h b/cmd-line-utils/libedit/tty.h index cc7c4ad8c66..10e9b98c953 100644 --- a/cmd-line-utils/libedit/tty.h +++ b/cmd-line-utils/libedit/tty.h @@ -1,4 +1,4 @@ -/* $NetBSD: tty.h,v 1.10 2003/08/07 16:44:34 agc Exp $ */ +/* $NetBSD: tty.h,v 1.11 2005/06/01 11:37:52 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -450,8 +450,8 @@ typedef struct { const char *t_name; - u_int t_setmask; - u_int t_clrmask; + unsigned int t_setmask; + unsigned int t_clrmask; } ttyperm_t[NN_IO][MD_NN]; typedef unsigned char ttychar_t[NN_IO][C_NCC]; diff --git a/cmd-line-utils/libedit/unvis.c b/cmd-line-utils/libedit/unvis.c deleted file mode 100644 index ffa8ac4251c..00000000000 --- a/cmd-line-utils/libedit/unvis.c +++ /dev/null @@ -1,311 +0,0 @@ -/* $NetBSD: unvis.c,v 1.24 2003/08/07 16:42:59 agc Exp $ */ - -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -#define __LIBC12_SOURCE__ - -#include - -#include -#include -#include -#include - -#ifdef __weak_alias -__weak_alias(strunvis,_strunvis) -__weak_alias(unvis,_unvis) -#endif - -#ifdef __warn_references -__warn_references(unvis, - "warning: reference to compatibility unvis(); include for correct reference") -#endif - -#if !HAVE_VIS -/* - * decode driven by state machine - */ -#define S_GROUND 0 /* haven't seen escape char */ -#define S_START 1 /* start decoding special sequence */ -#define S_META 2 /* metachar started (M) */ -#define S_META1 3 /* metachar more, regular char (-) */ -#define S_CTRL 4 /* control char started (^) */ -#define S_OCTAL2 5 /* octal digit 2 */ -#define S_OCTAL3 6 /* octal digit 3 */ -#define S_HEX1 7 /* hex digit */ -#define S_HEX2 8 /* hex digit 2 */ - -#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10)) - -int -unvis(cp, c, astate, flag) - char *cp; - int c; - int *astate, flag; -{ - return __unvis13(cp, (int)c, astate, flag); -} - -/* - * unvis - decode characters previously encoded by vis - */ -int -__unvis13(cp, c, astate, flag) - char *cp; - int c; - int *astate, flag; -{ - - _DIAGASSERT(cp != NULL); - _DIAGASSERT(astate != NULL); - - if (flag & UNVIS_END) { - if (*astate == S_OCTAL2 || *astate == S_OCTAL3 - || *astate == S_HEX2) { - *astate = S_GROUND; - return (UNVIS_VALID); - } - return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); - } - - switch (*astate) { - - case S_GROUND: - *cp = 0; - if (c == '\\') { - *astate = S_START; - return (0); - } - if ((flag & VIS_HTTPSTYLE) && c == '%') { - *astate = S_HEX1; - return (0); - } - *cp = c; - return (UNVIS_VALID); - - case S_START: - switch(c) { - case '\\': - *cp = c; - *astate = S_GROUND; - return (UNVIS_VALID); - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - *cp = (c - '0'); - *astate = S_OCTAL2; - return (0); - case 'M': - *cp = (char)0200; - *astate = S_META; - return (0); - case '^': - *astate = S_CTRL; - return (0); - case 'n': - *cp = '\n'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'r': - *cp = '\r'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'b': - *cp = '\b'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'a': - *cp = '\007'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'v': - *cp = '\v'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 't': - *cp = '\t'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'f': - *cp = '\f'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 's': - *cp = ' '; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'E': - *cp = '\033'; - *astate = S_GROUND; - return (UNVIS_VALID); - case '\n': - /* - * hidden newline - */ - *astate = S_GROUND; - return (UNVIS_NOCHAR); - case '$': - /* - * hidden marker - */ - *astate = S_GROUND; - return (UNVIS_NOCHAR); - } - *astate = S_GROUND; - return (UNVIS_SYNBAD); - - case S_META: - if (c == '-') - *astate = S_META1; - else if (c == '^') - *astate = S_CTRL; - else { - *astate = S_GROUND; - return (UNVIS_SYNBAD); - } - return (0); - - case S_META1: - *astate = S_GROUND; - *cp |= c; - return (UNVIS_VALID); - - case S_CTRL: - if (c == '?') - *cp |= 0177; - else - *cp |= c & 037; - *astate = S_GROUND; - return (UNVIS_VALID); - - case S_OCTAL2: /* second possible octal digit */ - if (isoctal(c)) { - /* - * yes - and maybe a third - */ - *cp = (*cp << 3) + (c - '0'); - *astate = S_OCTAL3; - return (0); - } - /* - * no - done with current sequence, push back passed char - */ - *astate = S_GROUND; - return (UNVIS_VALIDPUSH); - - case S_OCTAL3: /* third possible octal digit */ - *astate = S_GROUND; - if (isoctal(c)) { - *cp = (*cp << 3) + (c - '0'); - return (UNVIS_VALID); - } - /* - * we were done, push back passed char - */ - return (UNVIS_VALIDPUSH); - case S_HEX1: - if (isxdigit(c)) { - *cp = xtod(c); - *astate = S_HEX2; - return (0); - } - /* - * no - done with current sequence, push back passed char - */ - *astate = S_GROUND; - return (UNVIS_VALIDPUSH); - case S_HEX2: - *astate = S_GROUND; - if (isxdigit(c)) { - *cp = xtod(c) | (*cp << 4); - return (UNVIS_VALID); - } - return (UNVIS_VALIDPUSH); - default: - /* - * decoder in unknown state - (probably uninitialized) - */ - *astate = S_GROUND; - return (UNVIS_SYNBAD); - } -} - -/* - * strunvis - decode src into dst - * - * Number of chars decoded into dst is returned, -1 on error. - * Dst is null terminated. - */ - -int -strunvisx(dst, src, flag) - char *dst; - const char *src; - int flag; -{ - char c; - char *start = dst; - int state = 0; - - _DIAGASSERT(src != NULL); - _DIAGASSERT(dst != NULL); - - while ((c = *src++) != '\0') { - again: - switch (__unvis13(dst, c, &state, flag)) { - case UNVIS_VALID: - dst++; - break; - case UNVIS_VALIDPUSH: - dst++; - goto again; - case 0: - case UNVIS_NOCHAR: - break; - default: - return (-1); - } - } - if (__unvis13(dst, c, &state, UNVIS_END) == UNVIS_VALID) - dst++; - *dst = '\0'; - return (dst - start); -} - -int -strunvis(dst, src) - char *dst; - const char *src; -{ - return strunvisx(dst, src, 0); -} -#endif diff --git a/cmd-line-utils/libedit/vi.c b/cmd-line-utils/libedit/vi.c index b977ce716c6..602383f3231 100644 --- a/cmd-line-utils/libedit/vi.c +++ b/cmd-line-utils/libedit/vi.c @@ -1,4 +1,4 @@ -/* $NetBSD: vi.c,v 1.20 2004/08/13 12:10:39 mycroft Exp $ */ +/* $NetBSD: vi.c,v 1.28 2009/02/06 13:14:37 sketch Exp $ */ /*- * Copyright (c) 1992, 1993 @@ -32,11 +32,17 @@ * SUCH DAMAGE. */ -#include +#include "config.h" #include #include #include +#if !defined(lint) && !defined(SCCSID) +#if 0 +static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93"; +#else +#endif +#endif /* not lint && not SCCSID */ /* * vi.c: Vi mode commands. @@ -64,8 +70,10 @@ cv_action(EditLine *el, int c) el->el_line.lastchar - el->el_line.buffer); el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = 0; - el->el_line.lastchar = el->el_line.buffer; - el->el_line.cursor = el->el_line.buffer; + if (!(c & YANK)) { + el->el_line.lastchar = el->el_line.buffer; + el->el_line.cursor = el->el_line.buffer; + } if (c & INSERT) el->el_map.current = el->el_map.key; @@ -82,7 +90,6 @@ cv_action(EditLine *el, int c) private el_action_t cv_paste(EditLine *el, int c) { - char *ptr; c_kill_t *k = &el->el_chared.c_kill; int len = k->last - k->buf; @@ -96,12 +103,12 @@ cv_paste(EditLine *el, int c) if (!c && el->el_line.cursor < el->el_line.lastchar) el->el_line.cursor++; - ptr = el->el_line.cursor; c_insert(el, len); if (el->el_line.cursor + len > el->el_line.lastchar) return (CC_ERROR); - (void) memcpy(ptr, k->buf, len +0u); + (void) memcpy(el->el_line.cursor, k->buf, len +0u); + return (CC_REFRESH); } @@ -592,13 +599,12 @@ vi_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) */ protected el_action_t /*ARGSUSED*/ -vi_list_or_eof(EditLine *el, int c __attribute__((__unused__))) +vi_list_or_eof(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.lastchar) { if (el->el_line.cursor == el->el_line.buffer) { - term_overwrite(el, STReof, 4); /* then do a EOF */ - term__flush(); + term_writec(el, c); /* then do a EOF */ return (CC_EOF); } else { /* @@ -888,7 +894,7 @@ vi_yank(EditLine *el, int c) /* vi_comment_out(): * Vi comment out current command - * [c] + * [#] */ protected el_action_t /*ARGSUSED*/ @@ -905,18 +911,19 @@ vi_comment_out(EditLine *el, int c) /* vi_alias(): * Vi include shell alias * [@] - * NB: posix impiles that we should enter insert mode, however + * NB: posix implies that we should enter insert mode, however * this is against historical precedent... */ +#ifdef __weak_reference +extern char *get_alias_text(const char *) __weak_reference(get_alias_text); +#endif protected el_action_t /*ARGSUSED*/ vi_alias(EditLine *el, int c) { -#ifdef __weak_extern +#ifdef __weak_reference char alias_name[3]; char *alias_text; - extern char *get_alias_text(const char *); - __weak_extern(get_alias_text); if (get_alias_text == 0) { return CC_ERROR; @@ -1014,7 +1021,7 @@ vi_histedit(EditLine *el, int c) return CC_ERROR; case 0: close(fd); - execlp("vi", "vi", tempfile, (char *) NULL); + execlp("vi", "vi", tempfile, (char *)NULL); exit(0); /*NOTREACHED*/ default: diff --git a/cmd-line-utils/libedit/vis.c b/cmd-line-utils/libedit/vis.c deleted file mode 100644 index 127d28733a8..00000000000 --- a/cmd-line-utils/libedit/vis.c +++ /dev/null @@ -1,392 +0,0 @@ -/* $NetBSD: vis.c,v 1.27 2004/02/26 23:01:15 enami Exp $ */ - -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/*- - * Copyright (c) 1999 The NetBSD Foundation, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* AIX requires this to be the first thing in the file. */ -#if defined (_AIX) && !defined (__GNUC__) - #pragma alloca -#endif - -#include - -#ifdef __GNUC__ -# undef alloca -# define alloca(n) __builtin_alloca (n) -#else -# ifdef HAVE_ALLOCA_H -# include -# else -# ifndef _AIX -extern char *alloca (); -# endif -# endif -#endif - -#include - -#include -#include -#include - -#ifdef __weak_alias -__weak_alias(strsvis,_strsvis) -__weak_alias(strsvisx,_strsvisx) -__weak_alias(strvis,_strvis) -__weak_alias(strvisx,_strvisx) -__weak_alias(svis,_svis) -__weak_alias(vis,_vis) -#endif - -#if !HAVE_VIS || !HAVE_SVIS -#include -#include -#include -#include - -#undef BELL -#define BELL '\a' - -#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define iswhite(c) (c == ' ' || c == '\t' || c == '\n') -#define issafe(c) (c == '\b' || c == BELL || c == '\r') -#define xtoa(c) "0123456789abcdef"[c] - -#define MAXEXTRAS 5 - - -#define MAKEEXTRALIST(flag, extra, orig) \ -do { \ - const char *o = orig; \ - char *e; \ - while (*o++) \ - continue; \ - extra = alloca((size_t)((o - orig) + MAXEXTRAS)); \ - for (o = orig, e = extra; (*e++ = *o++) != '\0';) \ - continue; \ - e--; \ - if (flag & VIS_SP) *e++ = ' '; \ - if (flag & VIS_TAB) *e++ = '\t'; \ - if (flag & VIS_NL) *e++ = '\n'; \ - if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \ - *e = '\0'; \ -} while (/*CONSTCOND*/0) - - -/* - * This is HVIS, the macro of vis used to HTTP style (RFC 1808) - */ -#define HVIS(dst, c, flag, nextc, extra) \ -do \ - if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \ - *dst++ = '%'; \ - *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \ - *dst++ = xtoa((unsigned int)c & 0xf); \ - } else { \ - SVIS(dst, c, flag, nextc, extra); \ - } \ -while (/*CONSTCOND*/0) - -/* - * This is SVIS, the central macro of vis. - * dst: Pointer to the destination buffer - * c: Character to encode - * flag: Flag word - * nextc: The character following 'c' - * extra: Pointer to the list of extra characters to be - * backslash-protected. - */ -#define SVIS(dst, c, flag, nextc, extra) \ -do { \ - int isextra; \ - isextra = strchr(extra, c) != NULL; \ - if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \ - ((flag & VIS_SAFE) && issafe(c)))) { \ - *dst++ = c; \ - break; \ - } \ - if (flag & VIS_CSTYLE) { \ - switch (c) { \ - case '\n': \ - *dst++ = '\\'; *dst++ = 'n'; \ - continue; \ - case '\r': \ - *dst++ = '\\'; *dst++ = 'r'; \ - continue; \ - case '\b': \ - *dst++ = '\\'; *dst++ = 'b'; \ - continue; \ - case BELL: \ - *dst++ = '\\'; *dst++ = 'a'; \ - continue; \ - case '\v': \ - *dst++ = '\\'; *dst++ = 'v'; \ - continue; \ - case '\t': \ - *dst++ = '\\'; *dst++ = 't'; \ - continue; \ - case '\f': \ - *dst++ = '\\'; *dst++ = 'f'; \ - continue; \ - case ' ': \ - *dst++ = '\\'; *dst++ = 's'; \ - continue; \ - case '\0': \ - *dst++ = '\\'; *dst++ = '0'; \ - if (isoctal(nextc)) { \ - *dst++ = '0'; \ - *dst++ = '0'; \ - } \ - continue; \ - default: \ - if (isgraph(c)) { \ - *dst++ = '\\'; *dst++ = c; \ - continue; \ - } \ - } \ - } \ - if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \ - *dst++ = '\\'; \ - *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; \ - *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; \ - *dst++ = (c & 07) + '0'; \ - } else { \ - if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \ - if (c & 0200) { \ - c &= 0177; *dst++ = 'M'; \ - } \ - if (iscntrl(c)) { \ - *dst++ = '^'; \ - if (c == 0177) \ - *dst++ = '?'; \ - else \ - *dst++ = c + '@'; \ - } else { \ - *dst++ = '-'; *dst++ = c; \ - } \ - } \ -} while (/*CONSTCOND*/0) - - -/* - * svis - visually encode characters, also encoding the characters - * pointed to by `extra' - */ -char * -svis(dst, c, flag, nextc, extra) - char *dst; - int c, flag, nextc; - const char *extra; -{ - char *nextra; - _DIAGASSERT(dst != NULL); - _DIAGASSERT(extra != NULL); - MAKEEXTRALIST(flag, nextra, extra); - if (flag & VIS_HTTPSTYLE) - HVIS(dst, c, flag, nextc, nextra); - else - SVIS(dst, c, flag, nextc, nextra); - *dst = '\0'; - return(dst); -} - - -/* - * strsvis, strsvisx - visually encode characters from src into dst - * - * Extra is a pointer to a \0-terminated list of characters to - * be encoded, too. These functions are useful e. g. to - * encode strings in such a way so that they are not interpreted - * by a shell. - * - * Dst must be 4 times the size of src to account for possible - * expansion. The length of dst, not including the trailing NULL, - * is returned. - * - * Strsvisx encodes exactly len bytes from src into dst. - * This is useful for encoding a block of data. - */ -int -strsvis(dst, csrc, flag, extra) - char *dst; - const char *csrc; - int flag; - const char *extra; -{ - int c; - char *start; - char *nextra; - const unsigned char *src = (const unsigned char *)csrc; - - _DIAGASSERT(dst != NULL); - _DIAGASSERT(src != NULL); - _DIAGASSERT(extra != NULL); - MAKEEXTRALIST(flag, nextra, extra); - if (flag & VIS_HTTPSTYLE) { - for (start = dst; (c = *src++) != '\0'; /* empty */) - HVIS(dst, c, flag, *src, nextra); - } else { - for (start = dst; (c = *src++) != '\0'; /* empty */) - SVIS(dst, c, flag, *src, nextra); - } - *dst = '\0'; - return (dst - start); -} - - -int -strsvisx(dst, csrc, len, flag, extra) - char *dst; - const char *csrc; - size_t len; - int flag; - const char *extra; -{ - int c; - char *start; - char *nextra; - const unsigned char *src = (const unsigned char *)csrc; - - _DIAGASSERT(dst != NULL); - _DIAGASSERT(src != NULL); - _DIAGASSERT(extra != NULL); - MAKEEXTRALIST(flag, nextra, extra); - - if (flag & VIS_HTTPSTYLE) { - for (start = dst; len > 0; len--) { - c = *src++; - HVIS(dst, c, flag, len ? *src : '\0', nextra); - } - } else { - for (start = dst; len > 0; len--) { - c = *src++; - SVIS(dst, c, flag, len ? *src : '\0', nextra); - } - } - *dst = '\0'; - return (dst - start); -} -#endif - -#if !HAVE_VIS -/* - * vis - visually encode characters - */ -char * -vis(dst, c, flag, nextc) - char *dst; - int c, flag, nextc; - -{ - char *extra; - - _DIAGASSERT(dst != NULL); - - MAKEEXTRALIST(flag, extra, ""); - if (flag & VIS_HTTPSTYLE) - HVIS(dst, c, flag, nextc, extra); - else - SVIS(dst, c, flag, nextc, extra); - *dst = '\0'; - return (dst); -} - - -/* - * strvis, strvisx - visually encode characters from src into dst - * - * Dst must be 4 times the size of src to account for possible - * expansion. The length of dst, not including the trailing NULL, - * is returned. - * - * Strvisx encodes exactly len bytes from src into dst. - * This is useful for encoding a block of data. - */ -int -strvis(dst, src, flag) - char *dst; - const char *src; - int flag; -{ - char *extra; - - MAKEEXTRALIST(flag, extra, ""); - return (strsvis(dst, src, flag, extra)); -} - - -int -strvisx(dst, src, len, flag) - char *dst; - const char *src; - size_t len; - int flag; -{ - char *extra; - - MAKEEXTRALIST(flag, extra, ""); - return (strsvisx(dst, src, len, flag, extra)); -} -#endif diff --git a/cmd-line-utils/libedit/vis.h b/cmd-line-utils/libedit/vis.h deleted file mode 100644 index 44f6fc7d785..00000000000 --- a/cmd-line-utils/libedit/vis.h +++ /dev/null @@ -1,92 +0,0 @@ -/* $NetBSD: vis.h,v 1.15 2005/02/03 04:39:32 perry Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)vis.h 8.1 (Berkeley) 6/2/93 - */ - -#ifndef _VIS_H_ -#define _VIS_H_ - -#include - -/* - * to select alternate encoding format - */ -#define VIS_OCTAL 0x01 /* use octal \ddd format */ -#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */ - -/* - * to alter set of characters encoded (default is to encode all - * non-graphic except space, tab, and newline). - */ -#define VIS_SP 0x04 /* also encode space */ -#define VIS_TAB 0x08 /* also encode tab */ -#define VIS_NL 0x10 /* also encode newline */ -#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) -#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ - -/* - * other - */ -#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ -#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ - -/* - * unvis return codes - */ -#define UNVIS_VALID 1 /* character valid */ -#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ -#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ -#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ -#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ - -/* - * unvis flags - */ -#define UNVIS_END 1 /* no more characters */ - -__BEGIN_DECLS -char *vis(char *, int, int, int); -char *svis(char *, int, int, int, const char *); -int strvis(char *, const char *, int); -int strsvis(char *, const char *, int, const char *); -int strvisx(char *, const char *, size_t, int); -int strsvisx(char *, const char *, size_t, int, const char *); -int strunvis(char *, const char *); -int strunvisx(char *, const char *, int); -#ifdef __LIBC12_SOURCE__ -int unvis(char *, int, int *, int); -int __unvis13(char *, int, int *, int); -#else -int unvis(char *, int, int *, int); -#endif -__END_DECLS - -#endif /* !_VIS_H_ */ diff --git a/config/ac-macros/character_sets.m4 b/config/ac-macros/character_sets.m4 index a9f7bd73858..24bdd92b083 100644 --- a/config/ac-macros/character_sets.m4 +++ b/config/ac-macros/character_sets.m4 @@ -5,6 +5,9 @@ dnl you must also create strings/ctype-$charset_name.c AC_DIVERT_PUSH(0) +# Any changes to the available character sets must also go into +# include/config-win.h + define(CHARSETS_AVAILABLE0,binary) define(CHARSETS_AVAILABLE1,armscii8 ascii big5 cp1250 cp1251 cp1256 cp1257) define(CHARSETS_AVAILABLE2,cp850 cp852 cp866 cp932 dec8 eucjpms euckr gb2312 gbk geostd8) diff --git a/config/ac-macros/ha_ndbcluster.m4 b/config/ac-macros/ha_ndbcluster.m4 index 9df96a7750b..5ee136f2266 100644 --- a/config/ac-macros/ha_ndbcluster.m4 +++ b/config/ac-macros/ha_ndbcluster.m4 @@ -280,7 +280,7 @@ AC_DEFUN([MYSQL_SETUP_NDBCLUSTER], [ esac # libndbclient versioning when linked with GNU ld. - if $LD --version 2>/dev/null|grep -q GNU; then + if $LD --version 2>/dev/null|grep GNU >/dev/null 2>&1 ; then NDB_LD_VERSION_SCRIPT="-Wl,--version-script=\$(top_builddir)/storage/ndb/src/libndb.ver" AC_CONFIG_FILES(storage/ndb/src/libndb.ver) fi diff --git a/configure.in b/configure.in index 67f58f05d2b..2a56c6554cb 100644 --- a/configure.in +++ b/configure.in @@ -10,7 +10,7 @@ AC_CANONICAL_SYSTEM # # When changing major version number please also check switch statement # in mysqlbinlog::check_master_version(). -AM_INIT_AUTOMAKE(mysql, 5.1.31-pv-0.2.1) +AM_INIT_AUTOMAKE(mysql, 5.1.33-pv-0.2.1) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -378,7 +378,7 @@ fi MYSQL_PROG_AR # libmysqlclient versioning when linked with GNU ld. -if $LD --version 2>/dev/null|grep -q GNU; then +if $LD --version 2>/dev/null| grep GNU >/dev/null 2>&1; then LD_VERSION_SCRIPT="-Wl,--version-script=\$(top_builddir)/libmysql/libmysql.ver" AC_CONFIG_FILES(libmysql/libmysql.ver) fi @@ -450,11 +450,11 @@ AC_SUBST(PERL5) # Enable the abi_check rule only if gcc is available -if expr "$CC" : ".*gcc.*" +if test "$GCC" != "yes" || expr "$CC" : ".*icc.*" then - ABI_CHECK="abi_check" -else ABI_CHECK="" +else + ABI_CHECK="abi_check" fi AC_SUBST(ABI_CHECK) @@ -2091,7 +2091,7 @@ AC_CHECK_FUNCS(alarm bcmp bfill bmove bsearch bzero \ mkstemp mlockall perror poll pread pthread_attr_create mmap mmap64 getpagesize \ pthread_attr_getstacksize pthread_attr_setprio pthread_attr_setschedparam \ pthread_attr_setstacksize pthread_condattr_create pthread_getsequence_np \ - pthread_key_delete pthread_rwlock_rdlock pthread_setprio pthread_setschedprio \ + pthread_key_delete pthread_rwlock_rdlock pthread_setprio \ pthread_setprio_np pthread_setschedparam pthread_sigmask readlink \ realpath rename rint rwlock_init setupterm \ shmget shmat shmdt shmctl sigaction sigemptyset sigaddset \ @@ -2114,6 +2114,15 @@ case "$target" in ;; esac +case "$mysql_cv_sys_os" in + OS400) # i5/OS (OS/400) emits a SIGILL (Function not implemented) when + # unsupported priority values are passed to pthread_setschedprio. + # Since the only supported value is 1, treat it as inexistent. + ;; + *) AC_CHECK_FUNCS(pthread_setschedprio) + ;; +esac + # Check that isinf() is available in math.h and can be used in both C and C++ # code AC_MSG_CHECKING(for isinf in ) @@ -2863,8 +2872,8 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl support-files/MacOSX/Makefile support-files/RHEL4-SElinux/Makefile dnl cmd-line-utils/Makefile cmd-line-utils/libedit/Makefile dnl libmysqld/Makefile libmysqld/examples/Makefile dnl - mysql-test/Makefile dnl - mysql-test/ndb/Makefile netware/Makefile sql-bench/Makefile dnl + mysql-test/Makefile mysql-test/lib/My/SafeProcess/Makefile dnl + netware/Makefile sql-bench/Makefile dnl include/mysql_version.h plugin/Makefile win/Makefile) AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) diff --git a/extra/resolve_stack_dump.c b/extra/resolve_stack_dump.c index 5606c17ecf3..447d63890bd 100644 --- a/extra/resolve_stack_dump.c +++ b/extra/resolve_stack_dump.c @@ -290,7 +290,8 @@ static void do_resolve() char buf[1024], *p; while (fgets(buf, sizeof(buf), fp_dump)) { - p = buf; + /* skip bracket */ + p= (p= strchr(buf, '[')) ? p+1 : buf; /* skip space */ while (my_isspace(&my_charset_latin1,*p)) ++p; diff --git a/extra/yassl/include/openssl/ssl.h b/extra/yassl/include/openssl/ssl.h index de09f1ebe4b..05b34a0dc45 100644 --- a/extra/yassl/include/openssl/ssl.h +++ b/extra/yassl/include/openssl/ssl.h @@ -203,8 +203,8 @@ SSL_CTX* SSL_CTX_new(SSL_METHOD*); SSL* SSL_new(SSL_CTX*); int SSL_set_fd (SSL*, YASSL_SOCKET_T); YASSL_SOCKET_T SSL_get_fd(const SSL*); -int SSL_connect(SSL*); // if you get an error from connect - // see note at top of REAMDE +int SSL_connect(SSL*); /* if you get an error from connect + see note at top of REAMDE */ int SSL_write(SSL*, const void*, int); int SSL_read(SSL*, void*, int); int SSL_accept(SSL*); diff --git a/include/config-win.h b/include/config-win.h index cc2b2767663..ab0926af864 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. 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 @@ -197,11 +197,6 @@ typedef uint rf_SetTimer; #define SIGNAL_WITH_VIO_CLOSE #endif -/* Use all character sets in MySQL */ -#define USE_MB 1 -#define USE_MB_IDENT 1 -#define USE_STRCOLL 1 - /* All windows servers should support .sym files */ #undef USE_SYMDIR #define USE_SYMDIR @@ -250,6 +245,15 @@ inline double ulonglong2double(ulonglong value) #define my_off_t2double(A) ulonglong2double(A) #endif /* _WIN64 */ +inline ulonglong double2ulonglong(double d) +{ + double t= d - (double) 0x8000000000000000ULL; + + if (t >= 0) + return ((ulonglong) t) + 0x8000000000000000ULL; + return (ulonglong) d; +} + #if SIZEOF_OFF_T > 4 #define lseek(A,B,C) _lseeki64((A),(longlong) (B),(C)) #define tell(A) _telli64(A) @@ -362,49 +366,63 @@ inline double ulonglong2double(ulonglong value) #define shared_memory_buffer_length 16000 #define default_shared_memory_base_name "MYSQL" -#define MYSQL_DEFAULT_CHARSET_NAME "latin1" -#define MYSQL_DEFAULT_COLLATION_NAME "latin1_swedish_ci" - #define HAVE_SPATIAL 1 #define HAVE_RTREE_KEYS 1 #define HAVE_OPENSSL 1 #define HAVE_YASSL 1 -/* Define charsets you want */ -/* #undef HAVE_CHARSET_armscii8 */ -/* #undef HAVE_CHARSET_ascii */ +#define COMMUNITY_SERVER 1 +#define ENABLED_PROFILING 1 + +/* + Our Windows binaries include all character sets which MySQL supports. + Any changes to the available character sets must also go into + config/ac-macros/character_sets.m4 +*/ + +#define MYSQL_DEFAULT_CHARSET_NAME "latin1" +#define MYSQL_DEFAULT_COLLATION_NAME "latin1_swedish_ci" + +#define USE_MB 1 +#define USE_MB_IDENT 1 +#define USE_STRCOLL 1 + +#define HAVE_CHARSET_armscii8 +#define HAVE_CHARSET_ascii #define HAVE_CHARSET_big5 1 #define HAVE_CHARSET_cp1250 1 -/* #undef HAVE_CHARSET_cp1251 */ -/* #undef HAVE_CHARSET_cp1256 */ -/* #undef HAVE_CHARSET_cp1257 */ -/* #undef HAVE_CHARSET_cp850 */ -/* #undef HAVE_CHARSET_cp852 */ -/* #undef HAVE_CHARSET_cp866 */ +#define HAVE_CHARSET_cp1251 +#define HAVE_CHARSET_cp1256 +#define HAVE_CHARSET_cp1257 +#define HAVE_CHARSET_cp850 +#define HAVE_CHARSET_cp852 +#define HAVE_CHARSET_cp866 #define HAVE_CHARSET_cp932 1 -/* #undef HAVE_CHARSET_dec8 */ +#define HAVE_CHARSET_dec8 #define HAVE_CHARSET_eucjpms 1 #define HAVE_CHARSET_euckr 1 #define HAVE_CHARSET_gb2312 1 #define HAVE_CHARSET_gbk 1 -/* #undef HAVE_CHARSET_greek */ -/* #undef HAVE_CHARSET_hebrew */ -/* #undef HAVE_CHARSET_hp8 */ -/* #undef HAVE_CHARSET_keybcs2 */ -/* #undef HAVE_CHARSET_koi8r */ -/* #undef HAVE_CHARSET_koi8u */ +#define HAVE_CHARSET_geostd8 +#define HAVE_CHARSET_greek +#define HAVE_CHARSET_hebrew +#define HAVE_CHARSET_hp8 +#define HAVE_CHARSET_keybcs2 +#define HAVE_CHARSET_koi8r +#define HAVE_CHARSET_koi8u #define HAVE_CHARSET_latin1 1 #define HAVE_CHARSET_latin2 1 -/* #undef HAVE_CHARSET_latin5 */ -/* #undef HAVE_CHARSET_latin7 */ -/* #undef HAVE_CHARSET_macce */ -/* #undef HAVE_CHARSET_macroman */ +#define HAVE_CHARSET_latin5 +#define HAVE_CHARSET_latin7 +#define HAVE_CHARSET_macce +#define HAVE_CHARSET_macroman #define HAVE_CHARSET_sjis 1 -/* #undef HAVE_CHARSET_swe7 */ +#define HAVE_CHARSET_swe7 #define HAVE_CHARSET_tis620 1 #define HAVE_CHARSET_ucs2 1 #define HAVE_CHARSET_ujis 1 #define HAVE_CHARSET_utf8 1 + #define HAVE_UCA_COLLATIONS 1 #define HAVE_BOOL 1 diff --git a/include/hash.h b/include/hash.h index 1f094d48585..f4b82454b81 100644 --- a/include/hash.h +++ b/include/hash.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. 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 diff --git a/include/m_ctype.h b/include/m_ctype.h index b85894e94f5..04cf921dfee 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -472,6 +472,7 @@ my_bool my_propagate_complex(CHARSET_INFO *cs, const uchar *str, size_t len); uint my_string_repertoire(CHARSET_INFO *cs, const char *str, ulong len); my_bool my_charset_is_ascii_based(CHARSET_INFO *cs); my_bool my_charset_is_8bit_pure_ascii(CHARSET_INFO *cs); +uint my_charset_repertoire(CHARSET_INFO *cs); #define _MY_U 01 /* Upper case */ diff --git a/include/my_global.h b/include/my_global.h index 4feed96e7d6..4581dd6785c 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -724,7 +724,6 @@ typedef SOCKET_SIZE_TYPE size_socket; #define UNSINT32 /* unsigned int32 */ /* General constants */ -#define SC_MAXWIDTH 256 /* Max width of screen (for error messages) */ #define FN_LEN 256 /* Max file name len */ #define FN_HEADLEN 253 /* Max length of filepart of file name */ #define FN_EXTLEN 20 /* Max length of extension (part of FN_LEN) */ @@ -789,6 +788,9 @@ typedef SOCKET_SIZE_TYPE size_socket; #define ulonglong2double(A) ((double) (ulonglong) (A)) #define my_off_t2double(A) ((double) (my_off_t) (A)) #endif +#ifndef double2ulonglong +#define double2ulonglong(A) ((ulonglong) (double) (A)) +#endif #endif #ifndef offsetof @@ -1130,6 +1132,9 @@ typedef char bool; /* Ordinary boolean values 0 1 */ #define dbug_volatile #endif +/* Some helper macros */ +#define YESNO(X) ((X) ? "yes" : "no") + /* Defines for time function */ #define SCALE_SEC 100 #define SCALE_USEC 10000 diff --git a/include/my_sys.h b/include/my_sys.h index 60122eab94e..5335b65822f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -39,8 +39,6 @@ extern int NEAR my_errno; /* Last error in mysys */ #define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;} #define MY_INIT(name); { my_progname= name; my_init(); } -#define ERRMSGSIZE (SC_MAXWIDTH) /* Max length of a error message */ -#define NRERRBUFFS (2) /* Buffers for parameters */ #define MY_FILE_ERROR ((size_t) -1) /* General bitmaps for my_func's */ @@ -208,7 +206,6 @@ extern void my_large_free(uchar * ptr, myf my_flags); extern int errno; /* declare errno */ #endif #endif /* #ifndef errno */ -extern char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; extern char *home_dir; /* Home directory for user */ extern const char *my_progname; /* program-name (printed in errors) */ extern char NEAR curr_dir[]; /* Current directory for user */ diff --git a/include/myisam.h b/include/myisam.h index 16175547367..d7bfdf7191e 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -185,7 +185,7 @@ typedef struct st_mi_keydef /* Key definition with open & info */ uint16 maxlength; /* max length of (packed) key (auto) */ uint16 block_size_index; /* block_size (auto) */ uint32 version; /* For concurrent read/write */ - uint32 ftparser_nr; /* distinct ftparser number */ + uint32 ftkey_nr; /* full-text index number */ HA_KEYSEG *seg,*end; struct st_mysql_ftparser *parser; /* Fulltext [pre]parser */ diff --git a/include/thr_lock.h b/include/thr_lock.h index 77d428d1805..e409df30972 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. 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 @@ -159,7 +159,8 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count); void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock); my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread); void thr_print_locks(void); /* For debugging */ -my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data); +my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, + enum thr_lock_type new_lock_type); void thr_downgrade_write_lock(THR_LOCK_DATA *data, enum thr_lock_type new_lock_type); my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data); diff --git a/libmysqld/examples/CMakeLists.txt b/libmysqld/examples/CMakeLists.txt index 346278425a7..fa9711b54da 100644 --- a/libmysqld/examples/CMakeLists.txt +++ b/libmysqld/examples/CMakeLists.txt @@ -32,7 +32,7 @@ ADD_EXECUTABLE(mysql_embedded ../../client/completion_hash.cc TARGET_LINK_LIBRARIES(mysql_embedded debug dbug strings mysys vio yassl taocrypt regex ws2_32) ADD_DEPENDENCIES(mysql_embedded libmysqld) -ADD_EXECUTABLE(mysqltest_embedded ../../client/mysqltest.c) +ADD_EXECUTABLE(mysqltest_embedded ../../client/mysqltest.cc) TARGET_LINK_LIBRARIES(mysqltest_embedded debug dbug strings mysys vio yassl taocrypt regex ws2_32) ADD_DEPENDENCIES(mysqltest_embedded libmysqld) diff --git a/libmysqld/examples/Makefile.am b/libmysqld/examples/Makefile.am index 3425b48645f..109d33a85ae 100644 --- a/libmysqld/examples/Makefile.am +++ b/libmysqld/examples/Makefile.am @@ -41,7 +41,7 @@ LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysqld.a @LIBDL@ $(CXXLDFLAGS) \ @NDB_SCI_LIBS@ mysqltest_embedded_LINK = $(CXXLINK) -nodist_mysqltest_embedded_SOURCES = mysqltest.c +nodist_mysqltest_embedded_SOURCES = mysqltest.cc mysqltest_embedded_LDADD = $(LDADD) $(top_builddir)/regex/libregex.a nodist_mysql_SOURCES = mysql.cc readline.cc completion_hash.cc \ diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index 4d321f53ffa..dc9fbbd9aa5 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -17,132 +17,111 @@ ## Process this file with automake to create Makefile.in -SUBDIRS = ndb +testdir = $(prefix)/mysql-test -benchdir_root= $(prefix) -testdir = $(benchdir_root)/mysql-test -EXTRA_SCRIPTS = mysql-test-run-shell.sh install_test_db.sh \ - valgrind.supp $(PRESCRIPTS) -EXTRA_DIST = $(EXTRA_SCRIPTS) suite -GENSCRIPTS = mysql-test-run-shell install_test_db mtr mysql-test-run -PRESCRIPTS = mysql-test-run.pl mysql-stress-test.pl -test_SCRIPTS = $(GENSCRIPTS) $(PRESCRIPTS) -CLEANFILES = $(GENSCRIPTS) +test_SCRIPTS = mtr \ + mysql-test-run \ + mysql-test-run.pl \ + mysql-stress-test.pl -INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I.. +nobase_test_DATA = \ + lib/v1/mysql-test-run.pl \ + lib/v1/mtr_cases.pl \ + lib/v1/mtr_io.pl \ + lib/v1/mtr_report.pl \ + lib/v1/My/Config.pm \ + lib/v1/mtr_gcov.pl \ + lib/v1/mtr_match.pl \ + lib/v1/mtr_stress.pl \ + lib/v1/ndb_config_1_node.ini \ + lib/v1/ndb_config_2_node.ini \ + lib/v1/mtr_gprof.pl \ + lib/v1/mtr_misc.pl \ + lib/v1/mtr_timer.pl \ + lib/v1/mtr_im.pl \ + lib/v1/mtr_process.pl \ + lib/v1/mtr_unique.pl \ +\ + lib/mtr_cases.pm \ + lib/mtr_gcov.pl \ + lib/mtr_gprof.pl \ + lib/mtr_io.pl \ + lib/mtr_match.pm \ + lib/mtr_misc.pl \ + lib/mtr_process.pl \ + lib/mtr_report.pm \ + lib/mtr_stress.pl \ + lib/mtr_unique.pm \ + lib/My/ConfigFactory.pm \ + lib/My/Config.pm \ + lib/My/Find.pm \ + lib/My/Handles.pm \ + lib/My/Options.pm \ + lib/My/Test.pm \ + lib/My/Platform.pm \ + lib/My/SafeProcess.pm \ + lib/My/File/Path.pm \ + lib/My/SysInfo.pm \ + lib/My/CoreDump.pm \ + lib/My/SafeProcess/Base.pm \ + lib/My/SafeProcess/safe_process.pl +SUBDIRS = lib/My/SafeProcess + +EXTRA_DIST = README \ + valgrind.supp \ + $(test_SCRIPTS) \ + $(nobase_test_DATA) + +# List of directories containing test + result files and the +# related test data files that should be copied +TEST_DIRS = t r include std_data std_data/parts \ + std_data/ndb_backup50 std_data/ndb_backup51 \ + std_data/ndb_backup51_data_be std_data/ndb_backup51_data_le \ + std_data/funcs_1 \ + extra/binlog_tests/ extra/rpl_tests \ + suite/binlog suite/binlog/t suite/binlog/r suite/binlog/std_data \ + suite/bugs/data suite/bugs/t suite/bugs/r \ + suite/federated \ + suite/funcs_1 suite/funcs_1/bitdata \ + suite/funcs_1/include suite/funcs_1/lib suite/funcs_1/r \ + suite/funcs_1/t suite/funcs_1/views suite/funcs_1/cursors \ + suite/funcs_1/datadict suite/funcs_1/storedproc suite/funcs_1/triggers \ + suite/funcs_2 suite/funcs_2/charset suite/funcs_2/data \ + suite/funcs_2/include suite/funcs_2/lib suite/funcs_2/r \ + suite/funcs_2/t \ + suite/jp suite/jp/t suite/jp/r suite/jp/std_data \ + suite/manual/t suite/manual/r \ + suite/ndb_team suite/ndb_team/t suite/ndb_team/r \ + suite/rpl suite/rpl/data suite/rpl/include suite/rpl/r \ + suite/rpl/t \ + suite/stress/include suite/stress/t suite/stress/r \ + suite/ndb suite/ndb/t suite/ndb/r \ + suite/rpl_ndb suite/rpl_ndb/t suite/rpl_ndb/r \ + suite/parts suite/parts/t suite/parts/r suite/parts/inc + +# Used by dist-hook and install-data-local to copy all +# test files into either dist or install directory +install_test_files: + @if test -z "$(INSTALL_TO_DIR)"; then \ + echo "Set INSTALL_TO_DIR!" && exit 1; \ + fi + @for dir in $(TEST_DIRS); do \ + from_dir="$(srcdir)/$$dir"; \ + to_dir="$(INSTALL_TO_DIR)/$$dir"; \ + $(mkinstalldirs) "$$to_dir"; \ + for f in `(cd $$from_dir && ls)`; do \ + if test -f "$$from_dir/$$f"; then \ + $(INSTALL_DATA) "$$from_dir/$$f" "$$to_dir/$$f" ; \ + fi; \ + done \ + done dist-hook: - mkdir -p \ - $(distdir)/t \ - $(distdir)/extra/binlog_tests \ - $(distdir)/extra/rpl_tests \ - $(distdir)/r \ - $(distdir)/include \ - $(distdir)/std_data \ - $(distdir)/std_data/ndb_backup50 \ - $(distdir)/std_data/ndb_backup51 \ - $(distdir)/std_data/ndb_backup51_data_be \ - $(distdir)/std_data/ndb_backup51_data_le \ - $(distdir)/std_data/parts \ - $(distdir)/lib \ - $(distdir)/std_data/funcs_1 \ - $(distdir)/lib/My - -$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t - $(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.imtest $(distdir)/t - $(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.opt $(srcdir)/t/*.slave-mi $(distdir)/t - -$(INSTALL_SCRIPT) $(srcdir)/t/*.sh $(distdir)/t - $(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.test $(distdir)/extra/binlog_tests - $(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.test $(distdir)/extra/rpl_tests - -$(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.opt $(distdir)/extra/binlog_tests - -$(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.opt $(distdir)/extra/rpl_tests - $(INSTALL_DATA) $(srcdir)/include/*.inc $(distdir)/include - $(INSTALL_DATA) $(srcdir)/include/*.sql $(distdir)/include - $(INSTALL_DATA) $(srcdir)/include/*.test $(distdir)/include - $(INSTALL_DATA) $(srcdir)/r/*.result $(srcdir)/r/*.require $(distdir)/r - $(INSTALL_DATA) $(srcdir)/std_data/Moscow_leap $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/Index.xml $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.dat $(srcdir)/std_data/*.000001 $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.pem $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.frm $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.MY* $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.cnf $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.txt $(distdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup50/BACKUP* $(distdir)/std_data/ndb_backup50 - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51/BACKUP* $(distdir)/std_data/ndb_backup51 - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51_data_be/BACKUP* $(distdir)/std_data/ndb_backup51_data_be - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51_data_le/BACKUP* $(distdir)/std_data/ndb_backup51_data_le - $(INSTALL_DATA) $(srcdir)/std_data/parts/part_* $(distdir)/std_data/parts - $(INSTALL_DATA) $(srcdir)/std_data/parts/*.MY* $(distdir)/std_data/parts - $(INSTALL_DATA) $(srcdir)/std_data/funcs_1/* $(distdir)/std_data/funcs_1 - $(INSTALL_DATA) $(srcdir)/lib/*.pl $(distdir)/lib - $(INSTALL_DATA) $(srcdir)/lib/My/*.pm $(distdir)/lib/My - -rm -rf `find $(distdir)/suite -type d -name SCCS` $(distdir)/suite/row_lock + $(MAKE) INSTALL_TO_DIR="$(distdir)" install_test_files install-data-local: - $(mkinstalldirs) \ - $(DESTDIR)$(testdir)/t \ - $(DESTDIR)$(testdir)/extra/binlog_tests \ - $(DESTDIR)$(testdir)/extra/rpl_tests \ - $(DESTDIR)$(testdir)/r \ - $(DESTDIR)$(testdir)/include \ - $(DESTDIR)$(testdir)/std_data \ - $(DESTDIR)$(testdir)/std_data/ndb_backup50 \ - $(DESTDIR)$(testdir)/std_data/ndb_backup51 \ - $(DESTDIR)$(testdir)/std_data/ndb_backup51_data_be \ - $(DESTDIR)$(testdir)/std_data/ndb_backup51_data_le \ - $(DESTDIR)$(testdir)/std_data/parts \ - $(DESTDIR)$(testdir)/lib \ - $(DESTDIR)$(testdir)/std_data/funcs_1 \ - $(DESTDIR)$(testdir)/lib/My - $(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir) - -$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t - $(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.imtest $(DESTDIR)$(testdir)/t - $(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.opt $(DESTDIR)$(testdir)/t - -$(INSTALL_SCRIPT) $(srcdir)/t/*.sh $(DESTDIR)$(testdir)/t - -$(INSTALL_DATA) $(srcdir)/t/*.slave-mi $(DESTDIR)$(testdir)/t - $(INSTALL_DATA) $(srcdir)/r/*.result $(DESTDIR)$(testdir)/r - $(INSTALL_DATA) $(srcdir)/r/*.require $(DESTDIR)$(testdir)/r - $(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.test $(DESTDIR)$(testdir)/extra/binlog_tests - $(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.test $(DESTDIR)$(testdir)/extra/rpl_tests - -$(INSTALL_DATA) $(srcdir)/extra/binlog_tests/*.opt $(DESTDIR)$(testdir)/extra/binlog_tests - -$(INSTALL_DATA) $(srcdir)/extra/rpl_tests/*.opt $(DESTDIR)$(testdir)/extra/rpl_tests - $(INSTALL_DATA) $(srcdir)/include/*.inc $(DESTDIR)$(testdir)/include - $(INSTALL_DATA) $(srcdir)/include/*.sql $(DESTDIR)$(testdir)/include - $(INSTALL_DATA) $(srcdir)/include/*.test $(DESTDIR)$(testdir)/include - $(INSTALL_DATA) $(srcdir)/std_data/*.dat $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.*001 $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.cnf $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/Moscow_leap $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/Index.xml $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.pem $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.frm $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.MY* $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.cnf $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/*.txt $(DESTDIR)$(testdir)/std_data - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup50/BACKUP* $(DESTDIR)$(testdir)/std_data/ndb_backup50 - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51/BACKUP* $(DESTDIR)$(testdir)/std_data/ndb_backup51 - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51_data_be/BACKUP* $(DESTDIR)$(testdir)/std_data/ndb_backup51_data_be - $(INSTALL_DATA) $(srcdir)/std_data/ndb_backup51_data_le/BACKUP* $(DESTDIR)$(testdir)/std_data/ndb_backup51_data_le - $(INSTALL_DATA) $(srcdir)/std_data/parts/part_* $(DESTDIR)$(testdir)/std_data/parts - $(INSTALL_DATA) $(srcdir)/std_data/parts/*.MY* $(DESTDIR)$(testdir)/std_data/parts - $(INSTALL_DATA) $(srcdir)/std_data/funcs_1/* $(DESTDIR)$(testdir)/std_data/funcs_1 - $(INSTALL_DATA) $(srcdir)/lib/*.pl $(DESTDIR)$(testdir)/lib - $(INSTALL_DATA) $(srcdir)/lib/My/*.pm $(DESTDIR)$(testdir)/lib/My - for f in `(cd $(srcdir); find suite -type f | egrep -v 'SCCS|row_lock')`; \ - do \ - d=$(DESTDIR)$(testdir)/`dirname $$f`; \ - mkdir -p $$d ; \ - $(INSTALL_DATA) $(srcdir)/$$f $$d ; \ - done + $(MAKE) INSTALL_TO_DIR="$(DESTDIR)$(testdir)" install_test_files uninstall-local: @RM@ -f -r $(DESTDIR)$(testdir) @@ -157,31 +136,5 @@ mysql-test-run: $(RM) -f mysql-test-run $(LN_S) mysql-test-run.pl mysql-test-run -SUFFIXES = .sh - -.sh: - @RM@ -f $@ $@-t - @SED@ \ - -e 's!@''testdir''@!$(testdir)!g' \ - -e 's!@''bindir''@!$(bindir)!g' \ - -e 's!@''scriptdir''@!$(bindir)!g' \ - -e 's!@''prefix''@!$(prefix)!g' \ - -e 's!@''datadir''@!$(datadir)!g' \ - -e 's!@''localstatedir''@!$(localstatedir)!g' \ - -e 's!@''libexecdir''@!$(libexecdir)!g' \ - -e 's!@''PERL''@!@PERL@!' \ - -e 's!@''VERSION''@!@VERSION@!' \ - -e 's!@''MYSQL_TCP_PORT''@!@MYSQL_TCP_PORT@!' \ - -e 's!@''MYSQL_TCP_PORT_DEFAULT''@!@MYSQL_TCP_PORT_DEFAULT@!' \ - -e 's!@''MYSQL_BASE_VERSION''@!@MYSQL_BASE_VERSION@!' \ - -e 's!@''MYSQL_UNIX_ADDR''@!@MYSQL_UNIX_ADDR@!' \ - -e 's!@''MYSQL_TCP_PORT''@!@MYSQL_TCP_PORT@!' \ - -e 's!@''MYSQL_NO_DASH_VERSION''@!@MYSQL_NO_DASH_VERSION@!' \ - -e 's!@''MYSQL_SERVER_SUFFIX''@!@MYSQL_SERVER_SUFFIX@!' \ - -e 's!@''USE_NDBCLUSTER''@!@TEST_NDBCLUSTER@!g' \ - $< > $@-t - @CHMOD@ +x $@-t - @MV@ $@-t $@ - # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/mysql-test/create-test-result b/mysql-test/create-test-result deleted file mode 100755 index ad19cdf08a1..00000000000 --- a/mysql-test/create-test-result +++ /dev/null @@ -1,65 +0,0 @@ -#! /bin/sh - -# This script is a hack for lazy developers who want to get a quick -# start on the result file. The code here is rather dirty, but it works -# If you have a spare moment feel free to improve it - the right way is -# to start mysqld yourself and run mysqltest -r - -RESULT_DIR=r -if [ -z "$EDITOR" ] ; then - EDITOR=vi -fi - -function die() -{ - echo $1 - exit 1 -} - -function usage() -{ - echo "Usage: $0 test_name" - exit 1 -} - -test_name=$1 - -[ -z "$test_name" ] && usage - -result_file=$RESULT_DIR/$test_name.result -reject_file=$RESULT_DIR/$test_name.reject - -[ -f $result_file ] && die "result file $result_file has already been created" - -touch $result_file -echo "Running the test case against empty file, will fail, but don't worry" -./mysql-test-run --local $test_name - -if [ -f $reject_file ] ; then - echo "Below are the contents of the reject file:" - echo "-----start---------------------" - cat $reject_file - echo "-----end-----------------------" - echo "Is this the output you expected from your test case?(y/n)[n]" - read yes_no - if [ x$yes_no = xy ] ; then - echo "Press any key to edit it in $EDITOR, or Ctrl-C to abort" - read junk - $EDITOR $reject_file - edited="edited" - fi - echo "Save $edited file as master result? (y/n)[y]" - read yes_no - if [ x$yes_no != xn ]; then - mv $reject_file $result_file - fi -else - echo "Your test failed so bad, it did not even produce a reject file" - echo "You need to fix your bugs in the test case, the code, or both" - exit 1 -fi - - - - - diff --git a/mysql-test/extra/binlog_tests/binlog_truncate.test b/mysql-test/extra/binlog_tests/binlog_truncate.test new file mode 100644 index 00000000000..dce33b3cef0 --- /dev/null +++ b/mysql-test/extra/binlog_tests/binlog_truncate.test @@ -0,0 +1,27 @@ +# BUG #36763: TRUNCATE TABLE fails to replicate when stmt-based +# binlogging is not supported. + +# This should always be logged as a statement, even when executed as a +# row-by-row deletion. + +# $before_truncate A statement to execute (just) before issuing the +# TRUNCATE TABLE + + +eval CREATE TABLE t1 (a INT) ENGINE=$engine; +eval CREATE TABLE t2 (a INT) ENGINE=$engine; +INSERT INTO t2 VALUES (1),(2),(3); +let $binlog_start = query_get_value("SHOW MASTER STATUS", Position, 1); +if (`select length('$before_truncate') > 0`) { + eval $before_truncate; +} +--echo **** Truncate of empty table shall be logged +TRUNCATE TABLE t1; + +if (`select length('$before_truncate') > 0`) { + eval $before_truncate; +} +TRUNCATE TABLE t2; +source include/show_binlog_events.inc; + +DROP TABLE t1,t2; diff --git a/mysql-test/extra/binlog_tests/blackhole.test b/mysql-test/extra/binlog_tests/blackhole.test index e8671ed2da0..14c15a58e18 100644 --- a/mysql-test/extra/binlog_tests/blackhole.test +++ b/mysql-test/extra/binlog_tests/blackhole.test @@ -7,7 +7,7 @@ -- source include/have_log_bin.inc # The server need to be started in $MYSQLTEST_VARDIR since it -# uses ../std_data_ln/ +# uses ../../std_data/ -- source include/uses_vardir.inc --disable_warnings @@ -114,7 +114,7 @@ insert into t1 values(1); insert ignore into t1 values(1); replace into t1 values(100); create table t2 (a varchar(200)) engine=blackhole; -eval load data infile '../std_data_ln/words.dat' into table t2; +eval load data infile '../../std_data/words.dat' into table t2; alter table t1 add b int; alter table t1 drop b; create table t3 like t1; diff --git a/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test b/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test index fa374502997..b240109f6e6 100644 --- a/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test +++ b/mysql-test/extra/binlog_tests/ctype_ucs_binlog.test @@ -16,7 +16,8 @@ source include/show_binlog_events.inc; # escaped). flush logs; --replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR ---exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000001 +let $MYSQLD_DATADIR= `select @@datadir`; +--exec $MYSQL_BINLOG --short-form $MYSQLD_DATADIR/master-bin.000001 drop table t2; # End of 4.1 tests diff --git a/mysql-test/extra/binlog_tests/database.test b/mysql-test/extra/binlog_tests/database.test index 2e445f98adf..326ecedb60e 100644 --- a/mysql-test/extra/binlog_tests/database.test +++ b/mysql-test/extra/binlog_tests/database.test @@ -28,3 +28,5 @@ enable_warnings; insert into t1 values (1); drop table tt1, t1; source include/show_binlog_events.inc; + +FLUSH STATUS; diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index 6879089a1b7..5db79e4f848 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -317,17 +317,18 @@ connection con4; select get_lock("a",10); # wait for rollback to finish flush logs; +let $MYSQLD_DATADIR= `select @@datadir`; # we check that the error code of the "ROLLBACK" event is 0 and not # ER_SERVER_SHUTDOWN (i.e. disconnection just rolls back transaction # and does not make slave to stop) if (`select @@binlog_format = 'ROW'`) { - --exec $MYSQL_BINLOG --start-position=524 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mix_innodb_myisam_binlog.output + --exec $MYSQL_BINLOG --start-position=524 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mix_innodb_myisam_binlog.output } if (`select @@binlog_format = 'STATEMENT' || @@binlog_format = 'MIXED'`) { - --exec $MYSQL_BINLOG --start-position=555 $MYSQLTEST_VARDIR/log/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mix_innodb_myisam_binlog.output + --exec $MYSQL_BINLOG --start-position=555 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mix_innodb_myisam_binlog.output } --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR @@ -618,7 +619,7 @@ CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; # execute --error ER_DUP_ENTRY - load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); + load data infile '../../std_data/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); # check select * from t4; select count(*) from t1 /* must be 2 */; diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test index 0a0bef4ca4d..26a70c4319e 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_side_effects.test @@ -278,7 +278,7 @@ CREATE TABLE t5 (a int, PRIMARY KEY (a)) ENGINE=InnoDB; # execute --error ER_DUP_ENTRY - load data infile '../std_data_ln/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); + load data infile '../../std_data/rpl_loaddata.dat' into table t4 (a, @b) set b= @b + bug27417(2); # check select * from t4; select count(*) from t1 /* must be 2 */; diff --git a/mysql-test/extra/rpl_tests/rpl_EE_err.test b/mysql-test/extra/rpl_tests/rpl_EE_err.test index b7b393c3d6f..205bbe79dac 100644 --- a/mysql-test/extra/rpl_tests/rpl_EE_err.test +++ b/mysql-test/extra/rpl_tests/rpl_EE_err.test @@ -20,7 +20,8 @@ eval create table t1 (a int) engine=$engine_type; flush tables; -remove_file $MYSQLTEST_VARDIR/master-data/test/t1.MYI ; +let $MYSQLD_DATADIR= `select @@datadir`; +remove_file $MYSQLD_DATADIR/test/t1.MYI ; drop table if exists t1; save_master_pos; connection slave; diff --git a/mysql-test/extra/rpl_tests/rpl_auto_increment.test b/mysql-test/extra/rpl_tests/rpl_auto_increment.test index 1fa3cf034f6..24448a38408 100644 --- a/mysql-test/extra/rpl_tests/rpl_auto_increment.test +++ b/mysql-test/extra/rpl_tests/rpl_auto_increment.test @@ -145,6 +145,23 @@ select * from t3 order by a; connection master; drop table t1,t2,t3; +sync_slave_with_master; + +# +# BUG#41986 Replication slave does not pick up proper AUTO_INCREMENT value for Innodb tables +# +connection master; +set auto_increment_increment=1; +set auto_increment_offset=1; +CREATE TABLE t1 (id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY) ENGINE=innodb; +INSERT INTO t1 VALUES (NULL), (NULL), (NULL); +show create table t1; + +sync_slave_with_master; +show create table t1; + +connection master; +drop table t1; # End cleanup sync_slave_with_master; diff --git a/mysql-test/extra/rpl_tests/rpl_conflicts.test b/mysql-test/extra/rpl_tests/rpl_conflicts.test new file mode 100644 index 00000000000..8a98059b0ad --- /dev/null +++ b/mysql-test/extra/rpl_tests/rpl_conflicts.test @@ -0,0 +1,168 @@ +# ==== Purpose ==== +# +# Test that slave behaves well in some conflict situations. The +# following are tested: +# +# - The slave SQL thread sees an 'INSERT' of a row with a key that +# already exists in the table; +# +# - The slave SQL thread sees a 'DELETE' of a row that does not +# exist in the table. +# +# In statement-logging mode, the first conflict type causes the slave +# to stop with an error and the second conflict is ignored. +# +# In row-logging mode, the slave behavior depends the value of +# @@slave_exec_mode on the slave: if @@slave_exec_mode is IDEMPOTENT, +# the slave should ignore the conflicting statement and continue +# normally. If @@slave_exec_mode is STRICT, the slave should stop +# with an error. +# +# This test was previously named rpl_stm_mystery22/rpl_row_mystery22. +# +# +# ==== Method ==== +# +# Create a table on master and slave, insert a row on slave, and +# insert the same row on master. +# +# Create a table on master and slave, insert a row on master with +# binlogging turned off, and remove the row on master with binlogging +# turned on. +# +# +# ==== Related bugs ==== +# +# BUG#31552: Replication breaks when deleting rows from out-of-sync table without PK +# BUG#31609: Not all RBR slave errors reported as errors +# +# Bug in this test case: +# BUG#37718: rpl.rpl_stm_mystery22 fails sporadically on pushbuild +# +# +# ==== Usage ==== +# +# This file assumes the following: +# +# - The test language variable $slave_is_idempotent is set to 1 if the +# slave is expected to stop on duplicate key errors (i.e., if the +# binlog is in statement mode or +# @@global.slave_exec_mode=STRICT). It is set to 0 otherwise. +# +# - Replication has been initialized by include/master-slave.inc +# +# - The test adds a suppression for the following warning: +# Slave: Can't find record in 't1' Error_code: 1032 + + +--echo ==== Initialize ==== + +--echo [on master] +connection master; +CREATE TABLE t1(a INT PRIMARY KEY); +--echo [on slave] +sync_slave_with_master; + + +--echo ==== Test: SQL thread sees 'INSERT' of existing key ==== + +--echo ---- Prepare slave so that it will get duplicate key error ---- +# This row will be in the way of the row inserted by master. +INSERT INTO t1 VALUES (1); + +--echo ---- Insert rows on master ---- +--echo [on master] +connection master; +# Insert the same row on master +INSERT INTO t1 VALUES (1); +save_master_pos; +SELECT * FROM t1; + +--echo [on slave] +connection slave; + +# If we are statement-logging or if slave_exec_mode=STRICT, we now +# expect to see an error on the slave. Otherwise (i.e., we are +# row-logging and slave_exec_mode=IDEMPOTENT), we expect that the +# duplicate row is ignored by the slave and replication continues. +if (`SELECT @@global.binlog_format != 'ROW' OR @@global.slave_exec_mode = 'STRICT'`) { + --echo ---- Wait until slave stops with an error ---- + # Wait until the slave tries to run the query, fails with duplicate + # key error, and stops the SQL thread. + let $slave_sql_errno= 1062; # ER_DUP_ENTRY + source include/wait_for_slave_sql_error.inc; + let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); + --echo Last_SQL_Error = $err (expected "duplicate key" error) + SELECT * FROM t1; + + --echo ---- Resolve the conflict on the slave and restart SQL thread ---- + DELETE FROM t1 WHERE a = 1; + START SLAVE SQL_THREAD; + source include/wait_for_slave_sql_to_start.inc; +} + +--echo ---- Sync slave and verify that there is no error ---- +sync_with_master; +let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); +--echo Last_SQL_Error = '$err' (expected no error) +SELECT * FROM t1; + + +--echo ==== Test: SQL thread sees 'DELETE' of non-existing row ==== + +--echo ---- On master, insert two rows, the second with binlogging off ---- +--echo [on master] +connection master; +DELETE FROM t1; +INSERT INTO t1 VALUES (1); + +--echo [on slave] +sync_slave_with_master; +DELETE FROM t1 WHERE a = 1; + +--echo ---- On master, remove the row that does not exist on slave ---- +--echo [on master] +connection master; +DELETE FROM t1 WHERE a = 1; +SELECT * FROM t1; +save_master_pos; + +--echo [on slave] +connection slave; + +# If we are row-logging and slave_exec_mode is STRICT, we now expect +# an error since the row to delete does not exist on slave. Otherwise +# (i.e., either we are statement-logging or slave_exec_mode is +# IDEMPOTENT), the absence of the row to delete is ignored and +# replication continues. +if (`SELECT @@global.binlog_format = 'ROW' AND @@global.slave_exec_mode = 'STRICT'`) { + --echo ---- Wait until slave stops with an error ---- + let $slave_sql_errno= 1032; # ER_KEY_NOT_FOUND + source include/wait_for_slave_sql_error.inc; + let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); + --echo Last_SQL_Error = $err (expected "can't find record" error) + SELECT * FROM t1; + + --echo ---- Resolve the conflict on the slave and restart SQL thread ---- + INSERT INTO t1 VALUES (1); + START SLAVE SQL_THREAD; + source include/wait_for_slave_sql_to_start.inc; +} + +--echo ---- Sync slave and verify that there is no error ---- +# The slave should sync ok, and SHOW SLAVE STATUS should give no +# error. +sync_with_master; +let $err= query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1); +--echo Last_SQL_Error = $err (expected no error) +SELECT * FROM t1; + + +--echo ==== Clean up ==== + +--echo [on master] +connection master; +DROP TABLE t1; + +--echo [on slave] +sync_slave_with_master; diff --git a/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test b/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test index 5abd04b98ef..c426ac1fae8 100644 --- a/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test +++ b/mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test @@ -419,7 +419,7 @@ connection master; update t31 set f5=555555555555555 where f3=6; update t31 set f2=2 where f3=2; update t31 set f1=NULL where f3=1; - update t31 set f3=0, f27=NULL, f35='f35 new value' where f3=3; + update t31 set f3=NULL, f27=NULL, f35='f35 new value' where f3=3; --echo --echo ** Delete from Master ** @@ -497,7 +497,7 @@ INSERT INTO t10 () VALUES(1,@b1,DEFAULT,'Kyle',DEFAULT), --echo ******************************************** --echo connection slave; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS @@ -554,7 +554,7 @@ INSERT INTO t11 () VALUES(1,@b1,'Testing is fun','Kyle',DEFAULT), --echo ******************************************** --echo connection slave; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS @@ -697,7 +697,7 @@ SELECT c1,c3,hex(c4),c5,c6 FROM t14 ORDER BY c1; # Remove below once fixed #*************************** connection slave; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS @@ -763,7 +763,7 @@ SELECT c1,hex(c4),c5,c6,c7,c2 FROM t15 ORDER BY c1; --echo ******************************************** --echo connection slave; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS @@ -840,7 +840,7 @@ SELECT c1,hex(c4),c5,c6,c7 FROM t16 ORDER BY c1; --echo ***************** --echo connection slave; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS diff --git a/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test index 7e726828a1e..6890913b7d1 100644 --- a/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test +++ b/mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test @@ -94,7 +94,7 @@ SELECT * FROM t2 ORDER BY a; --echo *** Start Slave *** connection slave; START SLAVE; -wait_for_slave_to_stop; +source include/wait_for_slave_sql_to_stop.inc; --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 16 # 22 # 23 # 33 # 35 # 36 # --query_vertical SHOW SLAVE STATUS @@ -429,6 +429,7 @@ set @b1 = 'b1b1b1b1'; set @b1 = concat(@b1,@b1); INSERT INTO t9 () VALUES(1,@b1,'Kyle'),(2,@b1,'JOE'),(3,@b1,'QA'); +connection slave; --source include/wait_for_slave_sql_to_stop.inc --replace_result $MASTER_MYPORT MASTER_PORT --replace_column 1 # 4 # 7 # 8 # 9 # 16 # 22 # 23 # 33 # 35 # 36 # diff --git a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test index ee6b0ed1426..0baf49c9fac 100644 --- a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test +++ b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test @@ -4,8 +4,7 @@ # Test of FLUSH NO_WRITE_TO_BINLOG by the way. # - --- source include/master-slave.inc +source include/master-slave.inc; let $SERVER_VERSION=`select version()`; @@ -34,26 +33,32 @@ flush tables; --replace_column 2 # 5 # --replace_regex /table_id: [0-9]+/table_id: #/ eval SHOW BINLOG EVENTS FROM $rename_event_pos ; -save_master_pos; -connection slave; -sync_with_master; + +sync_slave_with_master; # Check that the slave is not confused. select * from t3; # Note that all this confusion may cause warnings 'table xx is open on rename' # in the .err files; these are not fatal and are not reported by mysql-test-run. - stop slave; +source include/wait_for_slave_to_stop.inc; + connection master; drop table t1; + connection slave; flush tables with read lock; start slave; -sleep 1; +source include/wait_for_slave_to_start.inc; --error 1192 stop slave; +# Cleanup +unlock tables; + connection master; drop table t3, t4, t5; +sync_slave_with_master; + # End of 4.1 tests diff --git a/mysql-test/extra/rpl_tests/rpl_loaddata.test b/mysql-test/extra/rpl_tests/rpl_loaddata.test index 8f32ee674f8..26916642cae 100644 --- a/mysql-test/extra/rpl_tests/rpl_loaddata.test +++ b/mysql-test/extra/rpl_tests/rpl_loaddata.test @@ -23,12 +23,12 @@ connection master; select last_insert_id(); create table t1(a int not null auto_increment, b int, primary key(a) ); -load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +load data infile '../../std_data/rpl_loaddata.dat' into table t1; # verify that LAST_INSERT_ID() is set by LOAD DATA INFILE select last_insert_id(); create temporary table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60)); -load data infile '../std_data_ln/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; +load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; create table t3 (day date,id int(9),category enum('a','b','c'),name varchar(60)); insert into t3 select * from t2; @@ -56,7 +56,7 @@ sync_with_master; insert into t1 values(1,10); connection master; -load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +load data infile '../../std_data/rpl_loaddata.dat' into table t1; save_master_pos; connection slave; @@ -80,7 +80,7 @@ connection master; set sql_log_bin=0; delete from t1; set sql_log_bin=1; -load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +load data infile '../../std_data/rpl_loaddata.dat' into table t1; save_master_pos; connection slave; # The SQL slave thread should be stopped now. @@ -105,7 +105,7 @@ connection master; set sql_log_bin=0; delete from t1; set sql_log_bin=1; -load data infile '../std_data_ln/rpl_loaddata.dat' into table t1; +load data infile '../../std_data/rpl_loaddata.dat' into table t1; save_master_pos; connection slave; # The SQL slave thread should be stopped now. @@ -125,7 +125,7 @@ reset master; eval create table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60), unique(day)) engine=$engine_type; # no transactions --error ER_DUP_ENTRY -load data infile '../std_data_ln/rpl_loaddata2.dat' into table t2 fields +load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; select * from t2; @@ -141,24 +141,21 @@ alter table t2 drop key day; connection master; delete from t2; --error ER_DUP_ENTRY -load data infile '../std_data_ln/rpl_loaddata2.dat' into table t2 fields +load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; connection slave; --source include/wait_for_slave_sql_to_stop.inc -drop table t2; +drop table t1, t2; connection master; -drop table t2; -drop table t1; +drop table t1, t2; # BUG#17233 LOAD DATA INFILE: failure causes mysqld dbug_assert, binlog not flushed CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=INNODB; --error ER_DUP_ENTRY -LOAD DATA INFILE "../std_data_ln/words.dat" INTO TABLE t1; +LOAD DATA INFILE "../../std_data/words.dat" INTO TABLE t1; ---disable_warnings DROP TABLE IF EXISTS t1; ---enable_warnings # End of 4.1 tests diff --git a/mysql-test/extra/rpl_tests/rpl_log.test b/mysql-test/extra/rpl_tests/rpl_log.test index 60ffab316cf..e4ebfd68761 100644 --- a/mysql-test/extra/rpl_tests/rpl_log.test +++ b/mysql-test/extra/rpl_tests/rpl_log.test @@ -9,10 +9,8 @@ # test the slave immediately writes DROP TEMPORARY TABLE this_old_table). # We wait for the slave to have written all he wants to the binlog # (otherwise RESET MASTER may come too early). -save_master_pos; -connection slave; -sync_with_master; -stop slave; +sync_slave_with_master; +source include/stop_slave.inc; --source include/wait_for_slave_to_stop.inc reset master; reset slave; @@ -25,8 +23,13 @@ connection master; eval create table t1(n int not null auto_increment primary key)ENGINE=$engine_type; insert into t1 values (NULL); drop table t1; +let $LOAD_FILE= ../../std_data/words.dat; +if (!`SELECT length(load_file('$LOAD_FILE'))`){ + let $LOAD_FILE= ../$LOAD_FILE; +} eval create table t1 (word char(20) not null)ENGINE=$engine_type; -load data infile '../std_data_ln/words.dat' into table t1 ignore 1 lines; +--replace_result $LOAD_FILE LOAD_FILE +eval load data infile '$LOAD_FILE' into table t1 ignore 1 lines; select count(*) from t1; --replace_result $VERSION VERSION --replace_column 2 # 5 # @@ -67,19 +70,13 @@ eval create table t3 (a int)ENGINE=$engine_type; connection master; select * from t1 order by 1 asc; -save_master_pos; -connection slave; -# Note that the above 'slave start' will cause a 3rd rotate event (a fake one) -# to go into the relay log (the master always sends a fake one when replication -# starts). -let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%'; ---source include/wait_slave_status.inc -sync_with_master; +sync_slave_with_master; + #check t1 on slave to ensure whether it's identical with on master select * from t1 order by 1 asc; flush logs; -stop slave; ---source include/wait_for_slave_to_stop.inc +source include/stop_slave.inc; +source include/start_slave.inc; connection master; # Create some entries for second log @@ -92,12 +89,11 @@ source include/show_binlog_events.inc; --replace_column 2 # 5 # --replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ show binlog events in 'master-bin.000002'; +--replace_column 2 # show binary logs; -save_master_pos; -connection slave; -start slave; +sync_slave_with_master; --source include/wait_for_slave_to_start.inc -sync_with_master; +--replace_column 2 # show binary logs; --replace_result $MASTER_MYPORT MASTER_PORT $VERSION VERSION --replace_column 2 # 5 # @@ -133,9 +129,13 @@ DROP TABLE t3; # Bug #6880: LAST_INSERT_ID() within a statement # +# Reset binlog so that show binlog events will not show the tests +# above. +source include/master-slave-reset.inc; +connection master; + create table t1(a int auto_increment primary key, b int); insert into t1 values (NULL, 1); -reset master; set insert_id=5; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); source include/show_binlog_events.inc; @@ -145,4 +145,3 @@ drop table t1; # End of 4.1 tests sync_slave_with_master; - diff --git a/mysql-test/extra/rpl_tests/rpl_row_001.test b/mysql-test/extra/rpl_tests/rpl_row_001.test index 6261659c4ce..8eb684e0dff 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_001.test +++ b/mysql-test/extra/rpl_tests/rpl_row_001.test @@ -1,7 +1,9 @@ +let $LOAD_FILE= $MYSQLTEST_VARDIR/std_data/words.dat; CREATE TABLE t1 (word CHAR(20) NOT NULL); -LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1; ---replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR -eval LOAD DATA LOCAL INFILE '$MYSQL_TEST_DIR/std_data/words.dat' INTO TABLE t1; +--replace_result $LOAD_FILE LOAD_FILE +eval LOAD DATA INFILE '$LOAD_FILE' INTO TABLE t1; +--replace_result $LOAD_FILE LOAD_FILE +eval LOAD DATA INFILE '$LOAD_FILE' INTO TABLE t1; SELECT * FROM t1 ORDER BY word LIMIT 10; # diff --git a/mysql-test/extra/rpl_tests/rpl_row_sp002.test b/mysql-test/extra/rpl_tests/rpl_row_sp002.test index 47afcce875b..5a2ab1912b8 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_sp002.test +++ b/mysql-test/extra/rpl_tests/rpl_row_sp002.test @@ -206,7 +206,7 @@ START TRANSACTION; -- disable_query_log -- disable_result_log let $n=50; -while ($n>3) +while ($n) { eval call test.p3($n); dec $n; diff --git a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test index 0e391cb0f37..3b03caee35c 100644 --- a/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test +++ b/mysql-test/extra/rpl_tests/rpl_row_tabledefs.test @@ -122,7 +122,7 @@ INSERT INTO t1_nodef VALUES (1,2); connection slave; --source include/wait_for_slave_sql_to_stop.inc --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -142,7 +142,7 @@ sync_slave_with_master; --echo **** On Slave **** SELECT * FROM t2; --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS connection master; @@ -154,7 +154,7 @@ INSERT INTO t4 VALUES (4); connection slave; --source include/wait_for_slave_sql_to_stop.inc --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -168,7 +168,7 @@ INSERT INTO t5 VALUES (5,10,25); connection slave; --source include/wait_for_slave_sql_to_stop.inc --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -182,7 +182,7 @@ INSERT INTO t6 VALUES (6,12,36); connection slave; --source include/wait_for_slave_sql_to_stop.inc --replace_result $MASTER_MYPORT MASTER_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS SET GLOBAL SQL_SLAVE_SKIP_COUNTER=2; START SLAVE; @@ -191,7 +191,7 @@ connection master; INSERT INTO t9 VALUES (6); sync_slave_with_master; --replace_result $SLAVE_MYPORT SLAVE_PORT ---replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 36 38 +--replace_column 1 # 4 # 7 # 8 # 9 # 20 22 # 23 # 33 # 35 36 38 --query_vertical SHOW SLAVE STATUS # Testing some tables extra field that can be null and cannot be null diff --git a/mysql-test/extra/rpl_tests/rpl_stm_000001.test b/mysql-test/extra/rpl_tests/rpl_stm_000001.test index d007e433ab6..1f5eb5786dd 100644 --- a/mysql-test/extra/rpl_tests/rpl_stm_000001.test +++ b/mysql-test/extra/rpl_tests/rpl_stm_000001.test @@ -2,7 +2,7 @@ -- source include/master-slave.inc create table t1 (word char(20) not null); -load data infile '../std_data_ln/words.dat' into table t1; +load data infile '../../std_data/words.dat' into table t1; --replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR eval load data local infile '$MYSQL_TEST_DIR/std_data/words.dat' into table t1; select * from t1 limit 10; diff --git a/mysql-test/extra/rpl_tests/rpl_stm_EE_err2.test b/mysql-test/extra/rpl_tests/rpl_stm_EE_err2.test index 3304a0ff46d..2c396c9a209 100644 --- a/mysql-test/extra/rpl_tests/rpl_stm_EE_err2.test +++ b/mysql-test/extra/rpl_tests/rpl_stm_EE_err2.test @@ -18,13 +18,18 @@ eval create table t1 (a int, unique(a)) engine=$engine_type; set sql_log_bin=0; insert into t1 values(2); set sql_log_bin=1; -save_master_pos; + --error ER_DUP_ENTRY insert into t1 values(1),(2); drop table t1; -save_master_pos; + connection slave; --source include/wait_for_slave_sql_to_stop.inc +let $error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error, 1); +let $errno= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1); +--echo Error: "$error" (expected different error codes on master and slave) +--echo Errno: "$errno" (expected 0) +drop table t1; # End of 4.1 tests diff --git a/mysql-test/extra/rpl_tests/rpl_truncate.test b/mysql-test/extra/rpl_tests/rpl_truncate.test index bca53336514..7036ab126e1 100644 --- a/mysql-test/extra/rpl_tests/rpl_truncate.test +++ b/mysql-test/extra/rpl_tests/rpl_truncate.test @@ -9,27 +9,8 @@ --source include/master-slave.inc -let $format = STATEMENT; -let $stmt = TRUNCATE TABLE; +let $trunc_stmt = TRUNCATE TABLE; --source extra/rpl_tests/rpl_truncate_helper.test -let $format = MIXED; -let $stmt = TRUNCATE TABLE; +let $trunc_stmt = DELETE FROM; --source extra/rpl_tests/rpl_truncate_helper.test - -let $format = ROW; -let $stmt = TRUNCATE TABLE; ---source extra/rpl_tests/rpl_truncate_helper.test - -let $format = STATEMENT; -let $stmt = DELETE FROM; ---source extra/rpl_tests/rpl_truncate_helper.test - -let $format = MIXED; -let $stmt = DELETE FROM; ---source extra/rpl_tests/rpl_truncate_helper.test - -let $format = ROW; -let $stmt = DELETE FROM; ---source extra/rpl_tests/rpl_truncate_helper.test - diff --git a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test index 76db74acfa1..cd1ce93177a 100644 --- a/mysql-test/extra/rpl_tests/rpl_truncate_helper.test +++ b/mysql-test/extra/rpl_tests/rpl_truncate_helper.test @@ -1,42 +1,35 @@ -connection slave; -STOP SLAVE; -source include/wait_for_slave_to_stop.inc; -connection master; ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings -connection slave; ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings -RESET SLAVE; -START SLAVE; +source include/reset_master_and_slave.inc; --echo **** On Master **** connection master; -eval SET SESSION BINLOG_FORMAT=$format; -eval SET GLOBAL BINLOG_FORMAT=$format; - eval CREATE TABLE t1 (a INT, b LONG) ENGINE=$engine; INSERT INTO t1 VALUES (1,1), (2,2); -SELECT * FROM t1; ---echo **** On Slave **** sync_slave_with_master; -INSERT INTO t1 VALUE (3,3); -SELECT * FROM t1; --echo **** On Master **** connection master; -eval $stmt t1; -SELECT * FROM t1; ---echo **** On Slave **** +eval $trunc_stmt t1; sync_slave_with_master; -# Should be empty -SELECT * FROM t1; + +let $diff_table_1=master:test.t1; +let $diff_table_2=slave:test.t1; +source include/diff_tables.inc; + +--echo ==== Test using a table with delete triggers ==== --echo **** On Master **** connection master; -DROP TABLE t1; -let $SERVER_VERSION=`select version()`; -source include/show_binlog_events.inc; +SET @count := 1; +eval CREATE TABLE t2 (a INT, b LONG) ENGINE=$engine; +CREATE TRIGGER trg1 BEFORE DELETE ON t1 FOR EACH ROW SET @count := @count + 1; +sync_slave_with_master; +--echo **** On Master **** +connection master; +eval $trunc_stmt t1; +sync_slave_with_master; + +let $diff_table_1=master:test.t2; +let $diff_table_2=slave:test.t2; +source include/diff_tables.inc; connection master; -RESET MASTER; +DROP TABLE t1,t2; +sync_slave_with_master; diff --git a/mysql-test/fix-result b/mysql-test/fix-result deleted file mode 100755 index bd380332ff5..00000000000 --- a/mysql-test/fix-result +++ /dev/null @@ -1,22 +0,0 @@ -#! /bin/sh - -# Sasha's hack to fix results generated with mysql-test-run --record -# to be version and test port independent. In some cases, further minor -# manual edititing may be required, but most of the time it should not -# happen - -#It is assumed we are running the script in mysql-test directory - -VERSION=4.0.1-alpha-debug-log -TEST_CASE=$1 - -if [ -z "$TEST_CASE" ] ; -then - echo "usage: $0 test_case_name" - exit 1 -fi - -../extra/replace $VERSION '$VERSION' 9306 '$MASTER_MYPORT' 9307 \ -'$SLAVE_MYPORT' \\ \\\\ -- r/$TEST_CASE.result - - diff --git a/mysql-test/include/analyze-sync_with_master.test b/mysql-test/include/analyze-sync_with_master.test new file mode 100644 index 00000000000..684c0dbbab7 --- /dev/null +++ b/mysql-test/include/analyze-sync_with_master.test @@ -0,0 +1,6 @@ +SHOW PROCESSLIST; + +let $binlog_name= query_get_value("SHOW MASTER STATUS", File, 1); +eval SHOW BINLOG EVENTS IN '$binlog_name'; + +exit; \ No newline at end of file diff --git a/mysql-test/include/analyze-timeout.test b/mysql-test/include/analyze-timeout.test new file mode 100644 index 00000000000..179ad8f748a --- /dev/null +++ b/mysql-test/include/analyze-timeout.test @@ -0,0 +1,3 @@ +SHOW PROCESSLIST; + +exit; diff --git a/mysql-test/include/analyze_failure_sync_with_master.test b/mysql-test/include/analyze_failure_sync_with_master.test deleted file mode 100644 index e6fd32d2f46..00000000000 --- a/mysql-test/include/analyze_failure_sync_with_master.test +++ /dev/null @@ -1,15 +0,0 @@ -# Connect to both master and slave -connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,); -connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,); - -vertical_results; - -echo == MASTER ===========================================================; -connection master; -show master status; -show slave status; - -echo == SLAVE ===========================================================; -connection slave; -show master status; -show slave status; diff --git a/mysql-test/include/check-testcase.test b/mysql-test/include/check-testcase.test index bf70b66e5df..6dcb01c13cf 100644 --- a/mysql-test/include/check-testcase.test +++ b/mysql-test/include/check-testcase.test @@ -1,51 +1,15 @@ + # # This test is executed twice for each test case if mysql-test-run is passed -# the flag --check-testcase. -# Before every testcase it's run with mysqltest in record mode and will -# thus produce an output file -# that can be compared to output from after the tescase. -# In that way it's possible to check that a testcase does not have +# the flag --check-testcase. Before every testcase it is run with mysqltest +# in record mode and will thus produce an output file that can be compared +# to output from after the tescase. +# In that way its possible to check that a testcase does not have # any unwanted side affects. # - -# -# Dump all global variables -# -SHOW GLOBAL VARIABLES WHERE variable_name != 'timestamp'; - -# -# Dump all databases -# -SHOW DATABASES; - -# -# Dump the "test" database, all it's tables and their data -# ---exec $MYSQL_DUMP --skip-comments --skip-lock-tables test - -# -# Dump the "mysql" database and it's tables -# Select data separately to add "order by" -# ---exec $MYSQL_DUMP --skip-comments --skip-lock-tables --no-data mysql -use mysql; -SELECT * FROM columns_priv; -SELECT * FROM db ORDER BY host, db, user; -SELECT * FROM func; -SELECT * FROM help_category; -SELECT * FROM help_keyword; -SELECT * FROM help_relation; -SELECT * FROM help_relation; -SELECT * FROM host; -SELECT * FROM proc; -SELECT * FROM procs_priv; -SELECT * FROM tables_priv; -SELECT * FROM time_zone; -SELECT * FROM time_zone_leap_second; -SELECT * FROM time_zone_name; -SELECT * FROM time_zone_transition; -SELECT * FROM time_zone_transition_type; -SELECT * FROM user; +--disable_query_log +call mtr.check_testcase(); +--enable_query_log diff --git a/mysql-test/include/check-warnings.test b/mysql-test/include/check-warnings.test new file mode 100644 index 00000000000..2144957f742 --- /dev/null +++ b/mysql-test/include/check-warnings.test @@ -0,0 +1,61 @@ +# +# This test is executed once after each test to check the servers +# for unexpected warnings found in the servers error log +# +# NOTE! mysql-test-run.pl has already done a rough filtering +# of the file and written any suspicious lines +# to $error_log.warnings file +# +--disable_query_log + +# Don't write these queries to binlog +set SQL_LOG_BIN=0; + +# Turn off any debug crashes, allow the variable to be +# non existent in release builds +--error 0,1193 +set debug=""; + +use mtr; + +create temporary table error_log ( + row int auto_increment primary key, + suspicious int default 1, + file_name varchar(255), + line varchar(1024) default null +) engine=myisam; + +# Get the name of servers error log +let $log_error= query_get_value(show variables like 'log_error', Value, 1); +let $log_warning= $log_error.warnings; + +# Try tload the warnings into a temporary table, +# it might fail with error saying "The MySQL server is +# running with the --secure-file-priv" in which case +# an attempt to load the file using LOAD DATA LOCAL is made +--error 0,1290 +eval load data infile '$log_warning' into table error_log + fields terminated by 'xykls37' escaped by '' + ignore 1 lines + (line) + set file_name='$log_error'; + +if ($mysql_errno) +{ + # Try LOAD DATA LOCAL + eval load data local infile '$log_warning' into table error_log + fields terminated by 'xykls37' escaped by '' + ignore 1 lines + (line) + set file_name='$log_error'; +} + +# Call check_warnings to filter out any warning in +# the error_log table +call mtr.check_warnings(@result); +if (`select @result = 0`){ + skip OK; +} +--enable_query_log +echo ^ Found warnings!!; +exit; diff --git a/mysql-test/include/check_events_off.inc b/mysql-test/include/check_events_off.inc new file mode 100644 index 00000000000..599a4b9e504 --- /dev/null +++ b/mysql-test/include/check_events_off.inc @@ -0,0 +1,31 @@ +########## include/check_events_off.inc #################################### +# # +# Purpose: # +# Wait till we can expect that we have no event activity till the scheduler is # +# switched on again. # +# = There will be no modifications of user tables by existing events # +# except they use "INSERT DELAYED" or the server system variable # +# "concurrent_inserts" is not switched off. # +# Only some storage engines support concurrent_inserts" or "INSERT DELAYED". # +# # +# Creation: # +# 2008-12-19 mleich Implement this check needed for bug fixes in tests # +# # +################################################################################ + +# 1. Check that the server system variable shows the state needed +if (`SELECT @@global.event_scheduler <> 'OFF'`) +{ + --echo # Error: We expect here that the event scheduler is switched off. + SELECT @@global.event_scheduler; + --echo # Thinkable reasons: + --echo # 1. SET GLOBAL event_scheduler = OFF had not the expected effect. + --echo # 2. Use of the current routine (include/check_events_off.inc) + --echo # within the wrong situation + --die +} +# 2. Wait till we have no event_scheduler session within the processlist +--source include/no_running_event_scheduler.inc +# 3. Wait till we have no event executor sessions within the processlist +--source include/no_running_events.inc + diff --git a/mysql-test/include/circular_rpl_for_4_hosts_init.inc b/mysql-test/include/circular_rpl_for_4_hosts_init.inc new file mode 100644 index 00000000000..ac6654777db --- /dev/null +++ b/mysql-test/include/circular_rpl_for_4_hosts_init.inc @@ -0,0 +1,130 @@ +############################################################# +# +# Author: Serge Kozlov +# Date: 03/11/2008 +# Purpose: Set up circular replication based on schema +# A->B->C->D->A +# +# Notes: +# 1. --slave-num=3 must be added to *-master.opt file +# 2. Even the test uses new names for servers but file names +# of log files are still old: +# master_a -> master.[log|err] +# master_b -> slave.[log|err] +# master_c -> slave1.[log|err] +# master_d -> slave2.[log|err] +# +############################################################# +--source include/master-slave.inc + +# +# Set up circular ring by schema A->B->C->D->A +# + +--connection slave +STOP SLAVE; +RESET SLAVE; + +# master a +--connection master +--disconnect master +connect (master_a,127.0.0.1,root,,test,$MASTER_MYPORT,); +RESET MASTER; +--disable_warnings +STOP SLAVE; +--enable_warnings +RESET SLAVE; +SET auto_increment_increment = 4; +SET auto_increment_offset = 1; +let $_binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); + +# master b +--connection slave +--disconnect slave +connect (master_b,127.0.0.1,root,,test,$SLAVE_MYPORT,); +RESET MASTER; +RESET SLAVE; +--replace_result $MASTER_MYPORT MASTER_A_PORT $_binlog_file MASTER_A_LOG_FILE +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$MASTER_MYPORT,master_user='root',MASTER_LOG_FILE='$_binlog_file' +SET auto_increment_increment = 4; +SET auto_increment_offset = 2; +let $_binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); + +# master c +--connection slave1 +--disconnect slave1 +connect (master_c,127.0.0.1,root,,test,$SLAVE_MYPORT1,); +RESET MASTER; +--disable_warnings +STOP SLAVE; +--enable_warnings +RESET SLAVE; +--replace_result $SLAVE_MYPORT MASTER_B_PORT $_binlog_file MASTER_B_LOG_FILE +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SLAVE_MYPORT,master_user='root',MASTER_LOG_FILE='$_binlog_file' +SET auto_increment_increment = 4; +SET auto_increment_offset = 3; +let $_binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); + +# master d +connect (master_d,127.0.0.1,root,,test,$SLAVE_MYPORT2,); +RESET MASTER; +--disable_warnings +STOP SLAVE; +--enable_warnings +RESET SLAVE; +--replace_result $SLAVE_MYPORT1 MASTER_C_PORT $_binlog_file MASTER_C_LOG_FILE +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SLAVE_MYPORT1,master_user='root',MASTER_LOG_FILE='$_binlog_file' +SET auto_increment_increment = 4; +SET auto_increment_offset = 4; +let $_binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); + +# master a +--connection master_a +--replace_result $SLAVE_MYPORT2 MASTER_D_PORT $_binlog_file MASTER_D_LOG_FILE +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SLAVE_MYPORT2,master_user='root',MASTER_LOG_FILE='$_binlog_file' + + + +# Check server_ids: they should be different +--connection master_a +let $_id_a= query_get_value(SHOW VARIABLES LIKE 'server_id', Value, 1); +SHOW VARIABLES LIKE 'auto_increment_%'; +--connection master_b +let $_id_b= query_get_value(SHOW VARIABLES LIKE 'server_id', Value, 1); +SHOW VARIABLES LIKE 'auto_increment_%'; +--connection master_c +let $_id_c= query_get_value(SHOW VARIABLES LIKE 'server_id', Value, 1); +SHOW VARIABLES LIKE 'auto_increment_%'; +--connection master_d +let $_id_d= query_get_value(SHOW VARIABLES LIKE 'server_id', Value, 1); +SHOW VARIABLES LIKE 'auto_increment_%'; +--connection master_a +let $_compared_ids= (($_id_a <> $_id_b) AND ($_id_a <> $_id_c) AND ($_id_a <> $_id_d) AND ($_id_b <> $_id_c) AND ($_id_b <> $_id_d) AND ($_id_c <> $_id_d)) AS a; +let $_compared_ids_result= query_get_value(SELECT $_compared_ids, a, 1); +--echo $_compared_ids_result + +# Start ring +--connection master_a +connect(slave,127.0.0.1,root,,test,$MASTER_MYPORT); +START SLAVE; +--source include/wait_for_slave_to_start.inc +--disconnect slave + +--connection master_b +connect(slave,127.0.0.1,root,,test,$SLAVE_MYPORT1); +START SLAVE; +--source include/wait_for_slave_to_start.inc +--disconnect slave + +--connection master_c +connect(slave,127.0.0.1,root,,test,$SLAVE_MYPORT); +START SLAVE; +--source include/wait_for_slave_to_start.inc +--disconnect slave + +--connection master_d +connect(slave,127.0.0.1,root,,test,$SLAVE_MYPORT2); +START SLAVE; +--source include/wait_for_slave_to_start.inc +--disconnect slave + diff --git a/mysql-test/include/circular_rpl_for_4_hosts_sync.inc b/mysql-test/include/circular_rpl_for_4_hosts_sync.inc new file mode 100644 index 00000000000..68aede76913 --- /dev/null +++ b/mysql-test/include/circular_rpl_for_4_hosts_sync.inc @@ -0,0 +1,23 @@ +############################################################# +# +# Author: Serge Kozlov +# Date: 03/11/2008 +# Purpose: Sync all hosts for circular replication based on +# schema A->B->C->D->A +# +# Notes: see include/circular_rpl_for_4_hosts_init.inc +# +############################################################# + +# Make the full loop of sync +--connection master_a +--disable_query_log +--sync_slave_with_master master_b +--sync_slave_with_master master_c +--sync_slave_with_master master_d +--sync_slave_with_master master_a +--sync_slave_with_master master_b +--sync_slave_with_master master_c +--save_master_pos +--connection master_a +--enable_query_log diff --git a/mysql-test/include/cleanup_fake_relay_log.inc b/mysql-test/include/cleanup_fake_relay_log.inc new file mode 100644 index 00000000000..43aa46cb657 --- /dev/null +++ b/mysql-test/include/cleanup_fake_relay_log.inc @@ -0,0 +1,16 @@ +# ==== Purpose ==== +# +# Clean up files create by setup_fake_relay_log.inc. +# +# ==== Usage ==== +# +# See setup_fake_relay_log.inc + +--echo Cleaning up after setup_fake_relay_log.inc + +# Remove files. +remove_file $_fake_relay_log; +remove_file $_fake_relay_index; +--disable_query_log +eval SET @@global.relay_log_purge= $_fake_relay_log_purge; +--enable_query_log diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index 98f9c93b25a..a4e7d9ae601 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -267,7 +267,7 @@ select * from t2; insert into t2 (a) values (1026); --replace_result $MYSQLTEST_VARDIR .. --error ER_DUP_ENTRY -eval load data infile "../std_data_ln/words.dat" into table t1 (a) set a:=f2(26); +eval load data infile "../../std_data/words.dat" into table t1 (a) set a:=f2(26); select * from t2; rollback; @@ -617,10 +617,10 @@ call p_verify_status_increment(0, 0, 0, 0); --echo --echo # No test because of Bug#8729 "rename table fails on temporary table" ---echo # 24. DDL: TRUNCATE TEMPORARY TABLE, does not start a transaction +--echo # 24. DDL: TRUNCATE TEMPORARY TABLE --echo truncate table t2; -call p_verify_status_increment(2, 0, 2, 0); +call p_verify_status_increment(4, 0, 4, 0); commit; --echo # There is nothing left to commit call p_verify_status_increment(0, 0, 0, 0); @@ -671,8 +671,11 @@ call p_verify_status_increment(2, 2, 2, 2); savepoint a; call p_verify_status_increment(0, 0, 0, 0); insert t1 set a=4; ---echo # Sic: a bug. Binlog did not register itself this time. -call p_verify_status_increment(1, 0, 1, 0); +--echo # Binlog does not register itself this time for other than the 1st +--echo # statement of the transaction with MIXED/STATEMENT binlog_format. +--echo # It needs registering with the ROW format. Therefore 1,0,2,2 are +--echo # the correct arguments to this test after bug#40221 fixed. +call p_verify_status_increment(1, 0, 2, 2); release savepoint a; rollback; call p_verify_status_increment(0, 0, 0, 0); @@ -730,7 +733,7 @@ call p_verify_status_increment(1, 0, 1, 0); rename table t4 to t3; call p_verify_status_increment(1, 0, 1, 0); truncate table t3; -call p_verify_status_increment(2, 2, 2, 2); +call p_verify_status_increment(4, 4, 4, 4); create view v1 as select * from t2; call p_verify_status_increment(1, 0, 1, 0); check table t1; diff --git a/mysql-test/include/concurrent.inc b/mysql-test/include/concurrent.inc index febab1eceb4..3b34a5b1ede 100644 --- a/mysql-test/include/concurrent.inc +++ b/mysql-test/include/concurrent.inc @@ -637,7 +637,7 @@ drop table t1; --echo ** connection default connection default; drop table t1; - +drop user mysqltest@localhost; disconnect thread1; disconnect thread2; diff --git a/mysql-test/include/count_sessions.inc b/mysql-test/include/count_sessions.inc new file mode 100644 index 00000000000..4728e39ce74 --- /dev/null +++ b/mysql-test/include/count_sessions.inc @@ -0,0 +1,21 @@ +# include/count_sessions.inc +# +# SUMMARY +# +# Stores the number of current sessions in $count_sessions. +# +# +# USAGE +# +# Please look into include/wait_until_count_sessions.inc +# for examples of typical usage. +# +# +# EXAMPLE +# backup.test, grant3.test +# +# +# Created: 2009-01-14 mleich +# + +let $count_sessions= query_get_value(SHOW STATUS LIKE 'Threads_connected', Value, 1); diff --git a/mysql-test/include/default_my.cnf b/mysql-test/include/default_my.cnf new file mode 100644 index 00000000000..d77fee0e200 --- /dev/null +++ b/mysql-test/include/default_my.cnf @@ -0,0 +1,25 @@ +# Use default setting for mysqld processes +!include default_mysqld.cnf + +[mysqld.1] + +# Run the master.sh script before starting this process +#!run-master-sh + +log-bin= master-bin + + +[mysqlbinlog] +disable-force-if-open + +# mysql_fix_privilege_tables.sh does not read from [client] so it +# need its own section +[mysql_fix_privilege_tables] +socket= @client.socket +port= @client.port +user= @client.user +password= @client.password + +[ENV] +MASTER_MYPORT= @mysqld.1.port +MASTER_MYSOCK= @mysqld.1.socket diff --git a/mysql-test/include/default_mysqld.cnf b/mysql-test/include/default_mysqld.cnf new file mode 100644 index 00000000000..ad0090aaf36 --- /dev/null +++ b/mysql-test/include/default_mysqld.cnf @@ -0,0 +1,21 @@ +# Default values that applies to all MySQL Servers +[mysqld] +open-files-limit= 1024 +local-infile +default-character-set= latin1 + +# Increase default connect_timeout to avoid intermittent +# disconnects when test servers are put under load see BUG#28359 +connect-timeout= 60 + +log-bin-trust-function-creators=1 +key_buffer_size= 1M +sort_buffer= 256K +max_heap_table_size= 1M + +loose-innodb_data_file_path= ibdata1:10M:autoextend + +slave-net-timeout=120 + +log-bin=mysqld-bin + diff --git a/mysql-test/include/default_ndbd.cnf b/mysql-test/include/default_ndbd.cnf new file mode 100644 index 00000000000..9a88a5936aa --- /dev/null +++ b/mysql-test/include/default_ndbd.cnf @@ -0,0 +1,27 @@ + +[cluster_config] +MaxNoOfSavedMessages= 1000 +MaxNoOfConcurrentTransactions= 128 +MaxNoOfConcurrentOperations= 10000 +DataMemory= 20M +IndexMemory= 1M +Diskless= 0 +TimeBetweenWatchDogCheck= 30000 +MaxNoOfOrderedIndexes= 32 +MaxNoOfAttributes= 2048 +TimeBetweenGlobalCheckpoints= 500 +NoOfFragmentLogFiles= 4 +FragmentLogFileSize= 12M +DiskPageBufferMemory= 4M + +# O_DIRECT has issues on 2.4 whach have not been handled, Bug #29612 +#ODirect= 1 +# the following parametes just function as a small regression +# test that the parameter exists +InitialNoOfOpenFiles= 27 + +# Increase timeouts for slow test-machines +HeartbeatIntervalDbDb= 30000 +HeartbeatIntervalDbApi= 30000 + +#TransactionDeadlockDetectionTimeout= 7500 diff --git a/mysql-test/include/federated.inc b/mysql-test/include/federated.inc deleted file mode 100644 index 925ecdd9682..00000000000 --- a/mysql-test/include/federated.inc +++ /dev/null @@ -1,28 +0,0 @@ ---source include/have_log_bin.inc ---source include/not_embedded.inc ---source ./include/have_federated_db.inc - -source ./include/master-slave.inc; - -# remote table creation - -# We have to sync with master, to ensure slave had time to start properly -# before we stop it. If not, we get errors about UNIX_TIMESTAMP() in the log. -connection master; -sync_slave_with_master; - -connection slave; -#--replicate-ignore-db=federated -stop slave; - ---disable_warnings -# at this point, we are connected to master -DROP DATABASE IF EXISTS federated; ---enable_warnings -CREATE DATABASE federated; - -connection master; ---disable_warnings -DROP DATABASE IF EXISTS federated; ---enable_warnings -CREATE DATABASE federated; diff --git a/mysql-test/include/have_32bit.inc b/mysql-test/include/have_32bit.inc index b09a813967d..66684fb5325 100644 --- a/mysql-test/include/have_32bit.inc +++ b/mysql-test/include/have_32bit.inc @@ -11,6 +11,6 @@ eval SET @@global.sort_buffer_size = $save; --enable_query_log if (!$mach32) { - skip Need a 32 bit machine; + skip Need a 32 bit machine/binary; } diff --git a/mysql-test/include/have_64bit.inc b/mysql-test/include/have_64bit.inc index 31529a0c9d4..cbba5e1d338 100644 --- a/mysql-test/include/have_64bit.inc +++ b/mysql-test/include/have_64bit.inc @@ -9,6 +9,6 @@ eval SET @@session.sort_buffer_size = $save; --enable_query_log if (!$mach64) { - skip Need a 64 bit machine; + skip Need a 64 binary ; } diff --git a/mysql-test/include/have_blackhole.inc b/mysql-test/include/have_blackhole.inc index 6c4da01d61d..146f5afb0d5 100644 --- a/mysql-test/include/have_blackhole.inc +++ b/mysql-test/include/have_blackhole.inc @@ -1,4 +1,5 @@ -disable_query_log; ---require r/true.require -select (support = 'YES' or support = 'DEFAULT') as `TRUE` from information_schema.engines where engine = 'blackhole'; -enable_query_log; +if (!`SELECT count(*) FROM information_schema.engines WHERE + (support = 'YES' OR support = 'DEFAULT') AND + engine = 'blackhole'`){ + skip Need blackhole engine; +} diff --git a/mysql-test/include/have_bug25714.inc b/mysql-test/include/have_bug25714.inc deleted file mode 100644 index 1d957bec2ec..00000000000 --- a/mysql-test/include/have_bug25714.inc +++ /dev/null @@ -1,7 +0,0 @@ -# -# Check if the variable MYSQL_BUG25714 is set -# ---require r/have_bug25714.require -disable_query_log; -eval select LENGTH('$MYSQL_BUG25714') > 0 as 'have_bug25714_exe'; -enable_query_log; diff --git a/mysql-test/include/have_federated_db.inc b/mysql-test/include/have_federated_db.inc deleted file mode 100644 index 041a29f460b..00000000000 --- a/mysql-test/include/have_federated_db.inc +++ /dev/null @@ -1,4 +0,0 @@ -disable_query_log; ---require r/true.require -select (support = 'YES' or support = 'DEFAULT') as `TRUE` from information_schema.engines where engine = 'federated'; -enable_query_log; diff --git a/mysql-test/include/have_log_bin.inc b/mysql-test/include/have_log_bin.inc index 5bcdb30b3e0..369af9b8e1d 100644 --- a/mysql-test/include/have_log_bin.inc +++ b/mysql-test/include/have_log_bin.inc @@ -1,3 +1,11 @@ +# ==== Purpose ==== +# +# Ensure that the server is running with binlogging on +# +# ==== Usage ==== +# +# source include/have_log_bin.inc; + -- require r/have_log_bin.require disable_query_log; show variables like 'log_bin'; diff --git a/mysql-test/include/have_ndbapi_examples.inc b/mysql-test/include/have_ndbapi_examples.inc index df7aa41ec5d..88499d555c0 100644 --- a/mysql-test/include/have_ndbapi_examples.inc +++ b/mysql-test/include/have_ndbapi_examples.inc @@ -1,4 +1,4 @@ --require r/have_ndbapi_examples.require disable_query_log; -eval select LENGTH('$MY_NDB_EXAMPLES_BINARY') > 0 as 'have_ndb_example'; +eval select LENGTH('$NDB_EXAMPLES_BINARY') > 0 as 'have_ndb_example'; enable_query_log; diff --git a/mysql-test/include/have_simple_parser.inc b/mysql-test/include/have_simple_parser.inc new file mode 100644 index 00000000000..c85786bd524 --- /dev/null +++ b/mysql-test/include/have_simple_parser.inc @@ -0,0 +1,16 @@ +# +# Check if server has support for loading udf's +# i.e it will support dlopen +# +--require r/have_dynamic_loading.require +disable_query_log; +show variables like 'have_dynamic_loading'; +enable_query_log; + +# +# Check if the variable SIMPLE_PARSER is set +# +--require r/have_simple_parser.require +disable_query_log; +eval select LENGTH('$SIMPLE_PARSER') > 0 as 'have_simple_parser'; +enable_query_log; diff --git a/mysql-test/include/index_merge1.inc b/mysql-test/include/index_merge1.inc index 023d582065e..5837df67a75 100644 --- a/mysql-test/include/index_merge1.inc +++ b/mysql-test/include/index_merge1.inc @@ -501,4 +501,30 @@ SELECT b,a from t1 WHERE (b!='c' AND b!='f' && b!='h') OR DROP TABLE t1; +--echo # +--echo # BUG#40974: Incorrect query results when using clause evaluated using range check +--echo # +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1 (a int); +insert into t1 values (1),(2); +create table t2(a int, b int); +insert into t2 values (1,1), (2, 1000); +create table t3 (a int, b int, filler char(100), key(a), key(b)); + +insert into t3 select 1000, 1000,'filler' from t0 A, t0 B, t0 C; +insert into t3 values (1,1,'data'); +insert into t3 values (1,1,'data'); +-- echo The plan should be ALL/ALL/ALL(Range checked for each record (index map: 0x3) +explain select * from t1 +where exists (select 1 from t2, t3 + where t2.a=t1.a and (t3.a=t2.b or t3.b=t2.b or t3.b=t2.b+1)); + +select * from t1 +where exists (select 1 from t2, t3 + where t2.a=t1.a and (t3.a=t2.b or t3.b=t2.b or t3.b=t2.b+1)); + +drop table t0, t1, t2, t3; + --echo End of 5.0 tests diff --git a/mysql-test/include/linux_sys_vars.inc b/mysql-test/include/linux_sys_vars.inc index 4401a1e057a..85d7c6df7dc 100644 --- a/mysql-test/include/linux_sys_vars.inc +++ b/mysql-test/include/linux_sys_vars.inc @@ -9,7 +9,6 @@ SET @min_flush_time = 0; #SET @max_flush_time = 0; SET @default_key_buffer_size = 131072; -SET @min_key_buffer_size = 36; #SET @default_join_buffer_size = 131072; #SET @min_join_buffer_size = 8200; diff --git a/mysql-test/include/loaddata_autocom.inc b/mysql-test/include/loaddata_autocom.inc index cca56709331..bb286fb4169 100644 --- a/mysql-test/include/loaddata_autocom.inc +++ b/mysql-test/include/loaddata_autocom.inc @@ -7,16 +7,20 @@ eval SET SESSION STORAGE_ENGINE = $engine_type; drop table if exists t1; --enable_warnings +let $load_file= $MYSQLTEST_VARDIR/std_data/loaddata2.dat; + # NDB does not support the create option 'Binlog of table with BLOB attribute and no PK' # So use a dummy PK here. create table t1 (id int unsigned not null auto_increment primary key, a text, b text); start transaction; -load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by '''' (a, b); +--replace_result $load_file LOAD_FILE +eval load data infile '$load_file' into table t1 fields terminated by ',' enclosed by '''' (a, b); commit; select count(*) from t1; truncate table t1; start transaction; -load data infile '../std_data_ln/loaddata2.dat' into table t1 fields terminated by ',' enclosed by '''' (a, b); +--replace_result $load_file LOAD_FILE +eval load data infile '$load_file' into table t1 fields terminated by ',' enclosed by '''' (a, b); rollback; select count(*) from t1; diff --git a/mysql-test/include/master-slave-reset.inc b/mysql-test/include/master-slave-reset.inc index 1363fab236a..938eb2c074a 100644 --- a/mysql-test/include/master-slave-reset.inc +++ b/mysql-test/include/master-slave-reset.inc @@ -1,6 +1,13 @@ +# Reset the master and the slave to start fresh. +# +# It is necessary to execute RESET MASTER and RESET SLAVE on both +# master and slave since the replication setup might be circular. +# +# Since we expect STOP SLAVE to produce a warning as the slave is +# stopped (the server was started with skip-slave-start), we disable +# warnings when doing STOP SLAVE. + connection slave; -#we expect STOP SLAVE to produce a warning as the slave is stopped -#(the server was started with skip-slave-start) --disable_warnings stop slave; source include/wait_for_slave_to_stop.inc; @@ -13,11 +20,17 @@ use test; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; --enable_warnings reset master; +--disable_query_log +reset slave; +--enable_query_log connection slave; reset slave; # Clean up old test tables --disable_warnings drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; --enable_warnings +--disable_query_log +reset master; +--enable_query_log start slave; source include/wait_for_slave_to_start.inc; diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index d2332edd5dc..cc9183205be 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -623,7 +623,8 @@ DROP TABLE t1,t2,t3; # create table t1 (a int) engine=innodb; -copy_file $MYSQLTEST_VARDIR/master-data/test/t1.frm $MYSQLTEST_VARDIR/master-data/test/bug29807.frm; +let $MYSQLD_DATADIR= `select @@datadir`; +copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/bug29807.frm; --error 1146 select * from bug29807; drop table t1; diff --git a/mysql-test/include/mtr_check.sql b/mysql-test/include/mtr_check.sql new file mode 100644 index 00000000000..12cb2c870a2 --- /dev/null +++ b/mysql-test/include/mtr_check.sql @@ -0,0 +1,59 @@ +delimiter ||; + +use mtr|| + +-- +-- Procedure used to check if server has been properly +-- restored after testcase has been run +-- +CREATE DEFINER=root@localhost PROCEDURE check_testcase() +BEGIN + + -- Dump all global variables except those + -- that are supposed to change + SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES + WHERE variable_name != 'timestamp' ORDER BY VARIABLE_NAME; + + -- Dump all databases, there should be none + -- except those that was created during bootstrap + SELECT * FROM INFORMATION_SCHEMA.SCHEMATA; + + -- The test database should not contain any tables + SELECT table_name AS tables_in_test FROM INFORMATION_SCHEMA.TABLES + WHERE table_schema='test'; + + -- Show "mysql" database, tables and columns + SELECT CONCAT(table_schema, '.', table_name) AS tables_in_mysql + FROM INFORMATION_SCHEMA.TABLES + WHERE table_schema='mysql' AND table_name != 'ndb_apply_status' + ORDER BY tables_in_mysql; + SELECT CONCAT(table_schema, '.', table_name) AS columns_in_mysql, + column_name, ordinal_position, column_default, is_nullable, + data_type, character_maximum_length, character_octet_length, + numeric_precision, numeric_scale, character_set_name, + collation_name, column_type, column_key, extra, column_comment + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_schema='mysql' AND table_name != 'ndb_apply_status' + ORDER BY columns_in_mysql; + + -- Checksum system tables to make sure they have been properly + -- restored after test + checksum table + mysql.columns_priv, + mysql.db, + mysql.func, + mysql.help_category, + mysql.help_keyword, + mysql.help_relation, + mysql.host, + mysql.proc, + mysql.procs_priv, + mysql.tables_priv, + mysql.time_zone, + mysql.time_zone_leap_second, + mysql.time_zone_name, + mysql.time_zone_transition, + mysql.time_zone_transition_type, + mysql.user; + +END|| diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql new file mode 100644 index 00000000000..73287900f3c --- /dev/null +++ b/mysql-test/include/mtr_warnings.sql @@ -0,0 +1,238 @@ +delimiter ||; + +use mtr|| + +-- +-- Create table where testcases can insert patterns to +-- be suppressed +-- +CREATE TABLE test_suppressions ( + pattern VARCHAR(255) +) ENGINE=MyISAM|| + + +-- +-- Declare a trigger that makes sure +-- no invalid patterns can be inserted +-- into test_suppressions +-- +/*!50002 +CREATE DEFINER=root@localhost TRIGGER ts_insert +BEFORE INSERT ON test_suppressions +FOR EACH ROW BEGIN + DECLARE dummy INT; + SELECT "" REGEXP NEW.pattern INTO dummy; +END +*/|| + + +-- +-- Load table with patterns that will be suppressed globally(always) +-- +CREATE TABLE global_suppressions ( + pattern VARCHAR(255) +) ENGINE=MyISAM|| + + +-- Declare a trigger that makes sure +-- no invalid patterns can be inserted +-- into global_suppressions +-- +/*!50002 +CREATE DEFINER=root@localhost TRIGGER gs_insert +BEFORE INSERT ON global_suppressions +FOR EACH ROW BEGIN + DECLARE dummy INT; + SELECT "" REGEXP NEW.pattern INTO dummy; +END +*/|| + + + +-- +-- Insert patterns that should always be suppressed +-- +INSERT INTO global_suppressions VALUES + ("'SELECT UNIX_TIMESTAMP\\(\\)' failed on master"), + ("Aborted connection"), + ("Client requested master to start replication from impossible position"), + ("Could not find first log file name in binary log"), + ("Enabling keys got errno"), + ("Error reading master configuration"), + ("Error reading packet"), + ("Event Scheduler"), + ("Failed to open log"), + ("Failed to open the existing master info file"), + ("Forcing shutdown of [0-9]* plugins"), + ("Forcing close of thread"), + + /* + Due to timing issues, it might be that this warning + is printed when the server shuts down and the + computer is loaded. + */ + + ("Got error [0-9]* when reading table"), + ("Incorrect definition of table"), + ("Incorrect information in file"), + ("InnoDB: Warning: we did not need to do crash recovery"), + ("Invalid \\(old\\?\\) table or database name"), + ("Lock wait timeout exceeded"), + ("Log entry on master is longer than max_allowed_packet"), + ("unknown option '--loose-"), + ("unknown variable 'loose-"), + ("You have forced lower_case_table_names to 0 through a command-line option"), + ("Setting lower_case_table_names=2"), + ("NDB Binlog:"), + ("NDB: failed to setup table"), + ("NDB: only row based binary logging"), + ("Neither --relay-log nor --relay-log-index were used"), + ("Query partially completed"), + ("Slave I.O thread aborted while waiting for relay log"), + ("Slave SQL thread is stopped because UNTIL condition"), + ("Slave SQL thread retried transaction"), + ("Slave \\(additional info\\)"), + ("Slave: .*Duplicate column name"), + ("Slave: .*master may suffer from"), + ("Slave: According to the master's version"), + ("Slave: Column [0-9]* type mismatch"), + ("Slave: Error .* doesn't exist"), + ("Slave: Error .*Unknown table"), + ("Slave: Error in Write_rows event: "), + ("Slave: Field .* of table .* has no default value"), + ("Slave: Field .* doesn't have a default value"), + ("Slave: Query caused different errors on master and slave"), + ("Slave: Table .* doesn't exist"), + ("Slave: Table width mismatch"), + ("Slave: The incident LOST_EVENTS occured on the master"), + ("Slave: Unknown error.* 1105"), + ("Slave: Can't drop database.* database doesn't exist"), + ("Slave SQL:.*(Error_code: \[\[:digit:\]\]+|Query:.*)"), + ("Sort aborted"), + ("Time-out in NDB"), + ("Warning:\s+One can only use the --user.*root"), + ("Warning:\s+Setting lower_case_table_names=2"), + ("Warning:\s+Table:.* on (delete|rename)"), + ("You have an error in your SQL syntax"), + ("deprecated"), + ("description of time zone"), + ("equal MySQL server ids"), + ("error .*connecting to master"), + ("error reading log entry"), + ("lower_case_table_names is set"), + ("skip-name-resolve mode"), + ("slave SQL thread aborted"), + ("Slave: .*Duplicate entry"), + + /* + Special case, made as specific as possible, for: + Bug #28436: Incorrect position in SHOW BINLOG EVENTS causes + server coredump + */ + + ("Error in Log_event::read_log_event\\\(\\\): 'Sanity check failed', data_len: 258, event_type: 49"), + + ("Statement is not safe to log in statement format"), + + /* test case for Bug#bug29807 copies a stray frm into database */ + ("InnoDB: Error: table `test`.`bug29807` does not exist in the InnoDB internal"), + ("Cannot find or open table test\/bug29807 from"), + + /* innodb foreign key tests that fail in ALTER or RENAME produce this */ + ("InnoDB: Error: in ALTER TABLE `test`.`t[12]`"), + ("InnoDB: Error: in RENAME TABLE table `test`.`t1`"), + ("InnoDB: Error: table `test`.`t[12]` does not exist in the InnoDB internal"), + + /* Test case for Bug#14233 produces the following warnings: */ + ("Stored routine 'test'.'bug14233_1': invalid value in column mysql.proc"), + ("Stored routine 'test'.'bug14233_2': invalid value in column mysql.proc"), + ("Stored routine 'test'.'bug14233_3': invalid value in column mysql.proc"), + + /* + BUG#32080 - Excessive warnings on Solaris: setrlimit could not + change the size of core files + */ + ("setrlimit could not change the size of core files to 'infinity'"), + + /* + rpl_extrColmaster_*.test, the slave thread produces warnings + when it get updates to a table that has more columns on the + master + */ + ("Slave: Unknown column 'c7' in 't15' Error_code: 1054"), + ("Slave: Can't DROP 'c7'.* 1091"), + ("Slave: Key column 'c6'.* 1072"), + + /* Test case for Bug#31590 in order_by.test produces the following error */ + ("Out of sort memory; increase server sort buffer size"), + + /* Special case for Bug #26402 in show_check.test + - Question marks are not valid file name parts on Windows. Ignore + this error message. + */ + ("Can't find file: '.\\\\test\\\\\\?{8}.frm'"), + + ("THE_LAST_SUPPRESSION")|| + + +-- +-- Procedure that uses the above created tables to check +-- the servers error log for warnings +-- +CREATE DEFINER=root@localhost PROCEDURE check_warnings(OUT result INT) +BEGIN + DECLARE `pos` bigint unsigned; + + -- Don't write these queries to binlog + SET SQL_LOG_BIN=0; + + -- + -- Remove mark from lines that are suppressed by global suppressions + -- + UPDATE error_log el, global_suppressions gs + SET suspicious=0 + WHERE el.suspicious=1 AND el.line REGEXP gs.pattern; + + -- + -- Remove mark from lines that are suppressed by test specific suppressions + -- + UPDATE error_log el, test_suppressions ts + SET suspicious=0 + WHERE el.suspicious=1 AND el.line REGEXP ts.pattern; + + -- + -- Get the number of marked lines and return result + -- + SELECT COUNT(*) INTO @num_warnings FROM error_log + WHERE suspicious=1; + + IF @num_warnings > 0 THEN + SELECT file_name, line + FROM error_log WHERE suspicious=1; + --SELECT * FROM test_suppressions; + -- Return 2 -> check failed + SELECT 2 INTO result; + ELSE + -- Return 0 -> OK + SELECT 0 INTO RESULT; + END IF; + + -- Cleanup for next test + TRUNCATE test_suppressions; + DROP TABLE error_log; + +END|| + +-- +-- Declare a procedure testcases can use to insert test +-- specific suppressions +-- +/*!50001 +CREATE DEFINER=root@localhost +PROCEDURE add_suppression(pattern VARCHAR(255)) +BEGIN + INSERT INTO test_suppressions (pattern) VALUES (pattern); +END +*/|| + + diff --git a/mysql-test/include/mysqlbinlog_row_engine.inc b/mysql-test/include/mysqlbinlog_row_engine.inc index 8211baea22c..95440ab04a0 100644 --- a/mysql-test/include/mysqlbinlog_row_engine.inc +++ b/mysql-test/include/mysqlbinlog_row_engine.inc @@ -1651,7 +1651,7 @@ FLUSH LOGS; let $MYSQLD_DATADIR= `select @@datadir`; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /(@[0-9]*=[0-9-]*[.][0-9]{1,3})[0-9e+-]*[^ ]*[ ]*(.*(FLOAT|DOUBLE).*[*].)/\1... \2/ ---exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLTEST_VARDIR/log/master-bin.000001 +--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --echo # --echo # Cleanup. @@ -1731,7 +1731,7 @@ FLUSH LOGS; let $MYSQLD_DATADIR= `select @@datadir`; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /(@[0-9]*=[0-9-]*[.][0-9]{1,3})[0-9e+-]*[^ ]*[ ]*(.*(FLOAT|DOUBLE).*[*].)/\1... \2/ ---exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLTEST_VARDIR/log/master-bin.000001 +--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --echo # --echo # Cleanup. @@ -1854,7 +1854,7 @@ FLUSH LOGS; let $MYSQLD_DATADIR= `select @@datadir`; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /(@[0-9]*=[0-9-]*[.][0-9]{1,3})[0-9e+-]*[^ ]*[ ]*(.*(FLOAT|DOUBLE).*[*].)/\1... \2/ ---exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLTEST_VARDIR/log/master-bin.000001 +--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --echo # --echo # Cleanup. @@ -1887,7 +1887,7 @@ eval CREATE TABLE t1 ( --echo # --echo # Load data. --echo # -LOAD DATA INFILE '../std_data_ln/loaddata5.dat' +LOAD DATA INFILE '../../std_data/loaddata5.dat' INTO TABLE t1 FIELDS TERMINATED BY '' ENCLOSED BY '' (c1,c2) SET c3 = 'Wow'; @@ -1912,7 +1912,7 @@ FLUSH LOGS; let $MYSQLD_DATADIR= `select @@datadir`; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_regex /SQL_LOAD_MB-[0-9]-[0-9]/SQL_LOAD_MB-#-#/ /exec_time=[0-9]*/exec_time=#/ /end_log_pos [0-9]*/end_log_pos #/ /# at [0-9]*/# at #/ /Xid = [0-9]*/Xid = #/ /thread_id=[0-9]*/thread_id=#/ /table id [0-9]*/table id #/ /mapped to number [0-9]*/mapped to number #/ /server v [^ ]*/server v #.##.##/ /(@[0-9]*=[0-9-]*[.][0-9]{1,3})[0-9e+-]*[^ ]*[ ]*(.*(FLOAT|DOUBLE).*[*].)/\1... \2/ ---exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLTEST_VARDIR/log/master-bin.000001 +--exec $MYSQL_BINLOG --base64-output=decode-rows -v -v $MYSQLD_DATADIR/master-bin.000001 --echo # --echo # Cleanup. diff --git a/mysql-test/include/mysqltest-x.inc b/mysql-test/include/mysqltest-x.inc index dd1468aed07..797c5c39f3f 100644 --- a/mysql-test/include/mysqltest-x.inc +++ b/mysql-test/include/mysqltest-x.inc @@ -1,2 +1,3 @@ echo Output from mysqltest-x.inc; +exit; diff --git a/mysql-test/include/ndb_backup.inc b/mysql-test/include/ndb_backup.inc index f0a883d4e11..e6780788fe9 100644 --- a/mysql-test/include/ndb_backup.inc +++ b/mysql-test/include/ndb_backup.inc @@ -2,23 +2,21 @@ # By JBM 2006-02-16 So that the code is not repeated # # in test cases and can be reused. # ###################################################### ---exec $NDB_MGM --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -e "start backup" >> $NDB_TOOLS_OUTPUT +--exec $NDB_MGM --no-defaults --ndb-connectstring="$NDB_CONNECTSTRING" -e "start backup" >> $NDB_TOOLS_OUTPUT # there is no neat way to find the backupid, this is a hack to find it... +let $dump_file= $MYSQLTEST_VARDIR/tmp/tmp.dat; +--exec $NDB_TOOLS_DIR/ndb_select_all --ndb-connectstring="$NDB_CONNECTSTRING" -d sys --delimiter=',' SYSTAB_0 | grep 520093696 > $dump_file ---exec $NDB_TOOLS_DIR/ndb_select_all --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -d sys --delimiter=',' SYSTAB_0 | grep 520093696 > $MYSQLTEST_VARDIR/tmp.dat +CREATE TEMPORARY TABLE test.backup_info (id INT, backup_id INT) ENGINE = HEAP; -CREATE TEMPORARY TABLE IF NOT EXISTS test.backup_info (id INT, backup_id INT) ENGINE = HEAP; - -DELETE FROM test.backup_info; - -LOAD DATA INFILE '../tmp.dat' INTO TABLE test.backup_info FIELDS TERMINATED BY ','; - ---replace_column 1 - -SELECT @the_backup_id:=backup_id FROM test.backup_info; - -let the_backup_id=`select @the_backup_id`; +--replace_result $dump_file DUMP_FILE +eval LOAD DATA INFILE '$dump_file' INTO TABLE test.backup_info FIELDS TERMINATED BY ','; +# Load backup id into environment variable +let the_backup_id=`SELECT backup_id from test.backup_info`; DROP TABLE test.backup_info; +remove_file $dump_file; + + diff --git a/mysql-test/include/ndb_master-slave_2ch.inc b/mysql-test/include/ndb_master-slave_2ch.inc new file mode 100644 index 00000000000..52a06c01d86 --- /dev/null +++ b/mysql-test/include/ndb_master-slave_2ch.inc @@ -0,0 +1,136 @@ +############################################################# +# Author: Serge Kozlov +# Date: 03/17/2008 +# Purpose: Set up circular cluster replication where each +# cluster has two mysqlds and replication directions are +# following: +# master ---> slave +# / \ +# cluster A cluster B +# \ / +# master1 <--- slave1 +############################################################# + +--source include/have_log_bin.inc + +# Make connections to mysqlds + +connect (master,127.0.0.1,root,,test,$MASTER_MYPORT,); +connect (master1,127.0.0.1,root,,test,$MASTER_MYPORT1,); +connect (slave,127.0.0.1,root,,test,$SLAVE_MYPORT,); +connect (slave1,127.0.0.1,root,,test,$SLAVE_MYPORT1,); + +# Check that all mysqld compiled with ndb support + +--connection master +--disable_query_log +--require r/true.require +SELECT (support = 'YES' or support = 'DEFAULT') AS `TRUE` FROM information_schema.engines WHERE engine = 'ndbcluster'; +--source include/ndb_not_readonly.inc +--enable_query_log + +--connection master1 +--disable_query_log +--require r/true.require +SELECT (support = 'YES' or support = 'DEFAULT') AS `TRUE` FROM information_schema.engines WHERE engine = 'ndbcluster'; +--source include/ndb_not_readonly.inc +--enable_query_log + +--connection slave +--disable_query_log +--require r/true.require +SELECT (support = 'YES' or support = 'DEFAULT') AS `TRUE` FROM information_schema.engines WHERE engine = 'ndbcluster'; +--source include/ndb_not_readonly.inc +--enable_query_log + +--connection slave1 +--disable_query_log +--require r/true.require +SELECT (support = 'YES' or support = 'DEFAULT') AS `TRUE` FROM information_schema.engines WHERE engine = 'ndbcluster'; +--source include/ndb_not_readonly.inc +--enable_query_log + +# Stop slaves + +--connection master +--disable_warnings +STOP SLAVE; +--wait_for_slave_to_stop +--enable_warnings + +--connection master1 +--disable_warnings +STOP SLAVE; +--wait_for_slave_to_stop +--enable_warnings + +--connection slave +--disable_warnings +STOP SLAVE; +--wait_for_slave_to_stop +--enable_warnings + +--connection slave1 +--disable_warnings +STOP SLAVE; +--wait_for_slave_to_stop +--enable_warnings + +# Reset masters + +--connection master +--disable_warnings +--disable_query_log +USE test; +--enable_query_log +DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +RESET MASTER; + +--connection master1 +--disable_warnings +--disable_query_log +USE test; +--enable_query_log +DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +RESET MASTER; + +--connection slave +--disable_warnings +--disable_query_log +USE test; +--enable_query_log +DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +RESET MASTER; + +--connection slave1 +--disable_warnings +--disable_query_log +USE test; +--enable_query_log +DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8,t9; +--enable_warnings +RESET MASTER; + +# Start slaves + +--connection slave +RESET SLAVE; +--replace_result $MASTER_MYPORT MASTER_MYPORT +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$MASTER_MYPORT,master_user='root' +START SLAVE; +--source include/wait_for_slave_to_start.inc + +--connection master1 +RESET SLAVE; +--replace_result $SLAVE_MYPORT1 SLAVE_MYPORT1 +--eval CHANGE MASTER TO master_host='127.0.0.1',master_port=$SLAVE_MYPORT1,master_user='root' +START SLAVE; +--source include/wait_for_slave_to_start.inc + + +# Set the default connection to 'master' (cluster A) +connection master; + diff --git a/mysql-test/include/ndb_not_readonly.inc b/mysql-test/include/ndb_not_readonly.inc index a87ba66f1ef..f50ca0cab66 100644 --- a/mysql-test/include/ndb_not_readonly.inc +++ b/mysql-test/include/ndb_not_readonly.inc @@ -17,7 +17,7 @@ while ($mysql_errno) { if (!$counter) { - die("Failed while waiting for mysqld to come out of readonly mode"); + die Failed while waiting for mysqld to come out of readonly mode; } dec $counter; --sleep 0.1 diff --git a/mysql-test/include/ndb_restore_master.inc b/mysql-test/include/ndb_restore_master.inc index b01a0b58ee9..ae5f055b442 100644 --- a/mysql-test/include/ndb_restore_master.inc +++ b/mysql-test/include/ndb_restore_master.inc @@ -3,6 +3,6 @@ # in test cases and can be reused. # ###################################################### ---exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="$NDB_CONNECTSTRING" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT ---exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -p 8 -b $the_backup_id -n 2 -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="$NDB_CONNECTSTRING" -p 8 -b $the_backup_id -n 2 -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT diff --git a/mysql-test/include/ndb_restore_slave_eoption.inc b/mysql-test/include/ndb_restore_slave_eoption.inc index f1f6cf96881..a8657f68c8d 100644 --- a/mysql-test/include/ndb_restore_slave_eoption.inc +++ b/mysql-test/include/ndb_restore_slave_eoption.inc @@ -3,9 +3,9 @@ # in test cases and can be reused. # ###################################################### ---exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="$NDB_CONNECTSTRING_SLAVE" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT ---exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 2 -r -e --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="$NDB_CONNECTSTRING_SLAVE" -p 8 -b $the_backup_id -n 2 -r -e --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT diff --git a/mysql-test/include/no_running_event_scheduler.inc b/mysql-test/include/no_running_event_scheduler.inc new file mode 100644 index 00000000000..92813df9f96 --- /dev/null +++ b/mysql-test/include/no_running_event_scheduler.inc @@ -0,0 +1,23 @@ +########## include/no_running_event_scheduler.inc ########################## +# # +# Wait till the event scheduler disappeared from processlist. # +# # +# The characteristics of the event_scheduler entry within the processlist is # +# user = 'event_scheduler' and command = 'Daemon'. I am not 100% sure if # +# ther is no short phase with command <> 'Daemon'. # +# A query with WHERE user = 'event_scheduler' only will also catch events in # +# startup phase. This is no problem since this phase is very short. # +# # +# A wait_timeout of >= 3 seconds was within experiments sufficient even on a # +# testing box with heavy parallel load. Therefore 5 seconds should be enough. # +# # +# Creation: # +# 2008-12-19 mleich Implement this check needed for test bug fixes # +# # +################################################################################ + +let $wait_timeout= 5; +let $wait_condition= + SELECT COUNT(*) = 0 FROM information_schema.processlist + WHERE user = 'event_scheduler'; +--source include/wait_condition.inc diff --git a/mysql-test/include/no_running_events.inc b/mysql-test/include/no_running_events.inc new file mode 100644 index 00000000000..8090b2706b8 --- /dev/null +++ b/mysql-test/include/no_running_events.inc @@ -0,0 +1,25 @@ +########## include/no_running_events.inc ################################### +# # +# Wait till all event executors have finished their work. # +# # +# Different event executors share the characteristics that their entry within # +# processlist contains command = 'Connect'. # +# Of course the corresponding query will also catch other connections being # +# within the connect phase. This is no problem since the connect phase is # +# usually very short. # +# # +# A wait_timeout of >= 3 seconds was during experiments in case of "simple" # +# SQL commands sufficient even on a testing box with heavy parallel load. # +# "simple" = no sleeps, no long running commands, no waiting for lock ... # +# We use here the default of 30 seconds because this wastes some time only in # +# case of unexpected situations. # +# # +# Creation: # +# 2008-12-19 mleich Implement this check needed for test bug fixes # +# # +################################################################################ + +let $wait_condition= + SELECT COUNT(*) = 0 FROM information_schema.processlist + WHERE command = 'Connect'; +--source include/wait_condition.inc diff --git a/mysql-test/include/not_as_root.inc b/mysql-test/include/not_as_root.inc index e0277ea593e..6c88051d632 100644 --- a/mysql-test/include/not_as_root.inc +++ b/mysql-test/include/not_as_root.inc @@ -1,4 +1,3 @@ --- require r/not_as_root.require -disable_query_log; -eval select "$MYSQL_TEST_ROOT" as running_as_root; -enable_query_log; +if ($MYSQL_TEST_ROOT){ + skip Not as root; +} diff --git a/mysql-test/include/ps_modify.inc b/mysql-test/include/ps_modify.inc index 4cde18b97d1..f66f888261d 100644 --- a/mysql-test/include/ps_modify.inc +++ b/mysql-test/include/ps_modify.inc @@ -108,7 +108,6 @@ execute stmt1 using @arg00, @arg01; select a,b from t1 where a=@arg00; set @arg00=NULL; set @arg01=2; ---error 1048 execute stmt1 using @arg00, @arg01; select a,b from t1 order by a; set @arg00=0; diff --git a/mysql-test/include/query_cache.inc b/mysql-test/include/query_cache.inc index fdd6bc50eae..77ea0021a5d 100644 --- a/mysql-test/include/query_cache.inc +++ b/mysql-test/include/query_cache.inc @@ -176,6 +176,7 @@ show status like "Qcache_queries_in_cache"; show status like "Qcache_hits"; # Final cleanup +eval set GLOBAL query_cache_size=$save_query_cache_size; connection default; drop table t2; disconnect connection1; diff --git a/mysql-test/include/report-features.test b/mysql-test/include/report-features.test index df395f6e3f0..1e4ab232490 100644 --- a/mysql-test/include/report-features.test +++ b/mysql-test/include/report-features.test @@ -9,3 +9,4 @@ show engines; show variables; --echo ===== STOP ===== --enable_query_log +exit; \ No newline at end of file diff --git a/mysql-test/include/reset_master_and_slave.inc b/mysql-test/include/reset_master_and_slave.inc index c2d4120ddc9..30ba1f07a40 100644 --- a/mysql-test/include/reset_master_and_slave.inc +++ b/mysql-test/include/reset_master_and_slave.inc @@ -1,10 +1,8 @@ --echo **** Resetting master and slave **** connection slave; -STOP SLAVE; -source include/wait_for_slave_to_stop.inc; +source include/stop_slave.inc; RESET SLAVE; connection master; RESET MASTER; connection slave; -START SLAVE; -source include/wait_for_slave_to_start.inc; +source include/start_slave.inc; diff --git a/mysql-test/include/restart_mysqld.inc b/mysql-test/include/restart_mysqld.inc new file mode 100644 index 00000000000..0f363ff1ee3 --- /dev/null +++ b/mysql-test/include/restart_mysqld.inc @@ -0,0 +1,25 @@ + +# Write file to make mysql-test-run.pl expect the "crash", but don't start +# it until it's told to +--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +wait +EOF + +# Send shutdown to the connected server and give +# it 10 seconds to die before zapping it +shutdown_server 10; + +# Write file to make mysql-test-run.pl start up the server again +--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +restart +EOF + +# Turn on reconnect +--enable_reconnect + +# Call script that will poll the server waiting for it to be back online again +--source include/wait_until_connected_again.inc + +# Turn off reconnect again +--disable_reconnect + diff --git a/mysql-test/include/rpl_events.inc b/mysql-test/include/rpl_events.inc index 34ceba81a38..0effa8c4e5c 100644 --- a/mysql-test/include/rpl_events.inc +++ b/mysql-test/include/rpl_events.inc @@ -62,7 +62,9 @@ SELECT db, name, status, originator FROM mysql.event WHERE db = 'test' AND name DROP EVENT IF EXISTS test.slave_once; --enable_warnings -CREATE EVENT test.slave_once ON SCHEDULE EVERY 5 MINUTE DO +# Create an event on slave and check its state. An event shouldn't be executed +# so set start time in 1 hour. +CREATE EVENT test.slave_once ON SCHEDULE EVERY 5 MINUTE STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO INSERT IGNORE INTO t1(id, c) VALUES (3, 'from slave_once'); --echo "Checking event status on the slave for originator value = slave's server_id" @@ -81,8 +83,11 @@ connection master; DROP EVENT IF EXISTS test.justonce; --enable_warnings +# Create an event on master and check its state on slave. An event shouldn't be executed +# so set start time in 1 hour. Check that changes of event statement replicated to slave + --echo "Creating event test.er on the master" -CREATE EVENT test.er ON SCHEDULE EVERY 3 SECOND DO +CREATE EVENT test.er ON SCHEDULE EVERY 3 SECOND STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO INSERT IGNORE INTO t1(id, c) VALUES (4, 'from er'); --echo "Checking event status on the master" @@ -95,7 +100,7 @@ SELECT db, name, status, originator, body FROM mysql.event WHERE db = 'test' AND connection master; --echo "Altering event test.er on the master" -ALTER EVENT test.er ON SCHEDULE EVERY 5 SECOND DO +ALTER EVENT test.er ON SCHEDULE EVERY 5 SECOND STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO INSERT IGNORE INTO t1(id, c) VALUES (5, 'from alter er'); --echo "Checking event status on the master" @@ -123,8 +128,11 @@ SELECT db, name, status, originator FROM mysql.event WHERE db = 'test'; # test the DISABLE ON SLAVE for setting event SLAVESIDE_DISABLED as status # on CREATE EVENT +# Create an event on slave and check its status. An event shouldn't be executed +# so set start time in 1 hour. + --echo "Creating event test.slave_terminate on the slave" -CREATE EVENT test.slave_terminate ON SCHEDULE EVERY 3 SECOND DO +CREATE EVENT test.slave_terminate ON SCHEDULE EVERY 3 SECOND STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR DO INSERT IGNORE INTO t1(id, c) VALUES (6, 'from slave_terminate'); --echo "Checking event status on the slave" diff --git a/mysql-test/include/rpl_multi_engine.inc b/mysql-test/include/rpl_multi_engine.inc index f2c837ef90b..b2d1a9c1cef 100644 --- a/mysql-test/include/rpl_multi_engine.inc +++ b/mysql-test/include/rpl_multi_engine.inc @@ -9,7 +9,7 @@ select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; sync_slave_with_master; select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; connection master; -DELETE FROM mysqltest1.t1 WHERE id = 42; +DELETE FROM t1 WHERE id = 42; select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; sync_slave_with_master; select id,hex(b1),vc,bc,d,f,total,y,t from t1 order by id; diff --git a/mysql-test/include/running_event_scheduler.inc b/mysql-test/include/running_event_scheduler.inc new file mode 100644 index 00000000000..296bf842e82 --- /dev/null +++ b/mysql-test/include/running_event_scheduler.inc @@ -0,0 +1,30 @@ +############# include/running_event_scheduler.inc ########################## +# # +# Wait till the event scheduler reached its final state within the processlist.# +# # +# The characteristics of the event_scheduler entry within the processlist is # +# user = 'event_scheduler' and command = 'Daemon'. I am not 100% sure if # +# ther is no short phase with command <> 'Daemon'. # +# A query with WHERE user = 'event_scheduler' only will also catch events in # +# startup phase. # +# # +# Creation: # +# 2008-12-19 mleich Implement this check needed for test bug fixes # +# # +################################################################################ + +# 1. Check that the server system variable shows the state needed +if (`SELECT @@global.event_scheduler <> 'ON'`) +{ + --echo # Error: We expect here that the event scheduler is switched on. + SELECT @@global.event_scheduler; + --echo # Thinkable reasons: + --echo # 1. SET GLOBAL event_scheduler = ON had not the expected effect. + --echo # 2. Use of the current routine (include/running_event_scheduler.inc) + --echo # within the wrong situation + --die +} +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE user = 'event_scheduler' AND command = 'Daemon'; +--source include/wait_condition.inc diff --git a/mysql-test/include/setup_fake_relay_log.inc b/mysql-test/include/setup_fake_relay_log.inc new file mode 100644 index 00000000000..79ff7429466 --- /dev/null +++ b/mysql-test/include/setup_fake_relay_log.inc @@ -0,0 +1,77 @@ +# ==== Purpose ==== +# +# Setup replication from an existing relay log in the current +# connection. +# +# ==== Usage ==== +# +# Make sure the slave is not running and issue: +# +# let $fake_relay_log= /path/to/fake-relay-log-file.000001 +# source include/setup_fake_relay_log.inc; +# START SLAVE SQL_THREAD; # setup_fake_relay_log doesn't start slave +# ... +# source include/cleanup_fake_relay_log.inc +# +# You must run the server with --relay-log=FILE. You probably want to +# run with --replicate-same-server-id too. +# +# ==== Implementation ==== +# +# First makes a sanity check, ensuring that the slave threads are not +# running. Then copies the $fake_relay_log to RELAY_BIN-fake.000001, +# where RELAY_BIN is the basename of the relay log, and updates +# RELAY_BIN.index. Finally issues CHANGE MASTER so that it uses the +# given files. +# +# ==== Side effects ==== +# +# Creates a binlog file and a binlog index file, and sets +# @@global.relay_log_purge=1. All this is restored when you call +# cleanup_fake_relay_log.inc. +# +# Enables the query log. + + +--disable_query_log + +# Print message. +let $_fake_relay_log_printable= `SELECT REPLACE('$fake_relay_log', '$MYSQL_TEST_DIR', 'MYSQL_TEST_DIR')`; +--echo Setting up fake replication from $_fake_relay_log_printable + +# Sanity check. +let $_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1); +let $_io_running= query_get_value(SHOW SLAVE STATUS, Slave_IO_Running, 1); +if (`SELECT "$_sql_running" = "Yes" OR "$_io_running" = "Yes"`) { + --echo Error: Slave was running when test case sourced + --echo include/setup_fake_replication.inc + --echo Slave_IO_Running = $_io_running; Slave_SQL_Running = $_sql_running + --echo Printing some debug info: + SHOW SLAVE STATUS; + SHOW MASTER STATUS; + SHOW BINLOG EVENTS; + SHOW PROCESSLIST; +} + +# Read server variables. +let $MYSQLD_DATADIR= `SELECT @@datadir`; +let $_fake_filename= query_get_value(SHOW VARIABLES LIKE 'relay_log', Value, 1); +if (`SELECT '$_fake_filename' = ''`) { + --echo Badly written test case: relay_log variable is empty. Please use the + --echo server option --relay-log=FILE. +} +let $_fake_relay_log= $MYSQLD_DATADIR/$_fake_filename-fake.000001; +let $_fake_relay_index= $MYSQLD_DATADIR/$_fake_filename.index; +# Need to restore relay_log_purge in cleanup_fake_relay_log.inc, since +# CHANGE MASTER modifies it (see the manual for CHANGE MASTER). +let $_fake_relay_log_purge= `SELECT @@global.relay_log_purge`; + +# Create relay log file. +copy_file $fake_relay_log $_fake_relay_log; +# Create relay log index. +--exec echo $_fake_relay_log > $_fake_relay_index + +# Setup replication from existing relay log. +eval CHANGE MASTER TO MASTER_HOST='dummy.localdomain', RELAY_LOG_FILE='$_fake_relay_log', RELAY_LOG_POS=4; + +--enable_query_log diff --git a/mysql-test/include/show_rpl_debug_info.inc b/mysql-test/include/show_rpl_debug_info.inc new file mode 100644 index 00000000000..252d63f1bf4 --- /dev/null +++ b/mysql-test/include/show_rpl_debug_info.inc @@ -0,0 +1,87 @@ +# ==== Purpose ==== +# +# Print status information for replication, typically used to debug +# test failures. +# +# First, the following is printed on slave: +# +# SHOW SLAVE STATUS +# SHOW PROCESSLIST +# SHOW BINLOG EVENTS IN +# +# Where is the currently active binlog. +# +# Then, the following is printed on master: +# +# SHOW MASTER STATUS +# SHOW PROCESSLIST +# SHOW BINLOG EVENTS IN +# SHOW BINLOG EVENTS IN +# +# Where is the binlog name that the slave sql thread +# is currently reading from and is the binlog that +# the slave IO thread is currently reading from. +# +# ==== Usage ==== +# +# [let $master_connection= ;] +# source include/show_rpl_debug_info.inc; +# +# If $master_connection is set, debug info will be retrieved from the +# connection named $master_connection. Otherwise, it will be +# retrieved from the 'master' connection if the current connection is +# 'slave'. + +let $_con= $CURRENT_CONNECTION; +--echo +--echo [on $_con] +--echo +--echo **** SHOW SLAVE STATUS on $_con **** +query_vertical SHOW SLAVE STATUS; +--echo +--echo **** SHOW PROCESSLIST on $_con **** +SHOW PROCESSLIST; +--echo +--echo **** SHOW BINLOG EVENTS on $_con **** +let $binlog_name= query_get_value("SHOW MASTER STATUS", File, 1); +eval SHOW BINLOG EVENTS IN '$binlog_name'; + +let $_master_con= $master_connection; +if (`SELECT '$_master_con' = ''`) +{ + if (`SELECT '$_con' = 'slave'`) + { + let $_master_con= master; + } + if (`SELECT '$_master_con' = ''`) + { + --echo Unable to determine master connection. No debug info printed for master. + --echo Please fix the test case by setting $master_connection before sourcing + --echo show_rpl_debug_info.inc. + } +} + +if (`SELECT '$_master_con' != ''`) +{ + + let $master_binlog_name_io= query_get_value("SHOW SLAVE STATUS", Master_Log_File, 1); + let $master_binlog_name_sql= query_get_value("SHOW SLAVE STATUS", Relay_Master_Log_File, 1); + --echo + --echo [on $_master_con] + connection $_master_con; + --echo + --echo **** SHOW MASTER STATUS on $_master_con **** + query_vertical SHOW MASTER STATUS; + --echo + --echo **** SHOW PROCESSLIST on $_master_con **** + SHOW PROCESSLIST; + --echo + --echo **** SHOW BINLOG EVENTS on $_master_con **** + eval SHOW BINLOG EVENTS IN '$master_binlog_name_sql'; + if (`SELECT '$master_binlog_name_io' != '$master_binlog_name_sql'`) + { + eval SHOW BINLOG EVENTS IN '$master_binlog_name_io'; + } + + connection $_con; +} diff --git a/mysql-test/include/start_slave.inc b/mysql-test/include/start_slave.inc new file mode 100644 index 00000000000..78a02736de8 --- /dev/null +++ b/mysql-test/include/start_slave.inc @@ -0,0 +1,21 @@ +# ==== Purpose ==== +# +# Issues START SLAVE on the current connection. Then waits until both +# the IO and SQL threads have started, or until a timeout is reached. +# +# Please use this instead of 'START SLAVE', to reduce the risk of test +# case bugs. +# +# ==== Usage ==== +# +# source include/wait_for_slave_to_start.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. + +--disable_query_log +START SLAVE; +--enable_query_log +--echo include/start_slave.inc +source include/wait_for_slave_to_start.inc; diff --git a/mysql-test/include/stop_slave.inc b/mysql-test/include/stop_slave.inc new file mode 100644 index 00000000000..7161e6fe739 --- /dev/null +++ b/mysql-test/include/stop_slave.inc @@ -0,0 +1,21 @@ +# ==== Purpose ==== +# +# Issues STOP SLAVE on the current connection. Then waits until both +# the IO and SQL threads have stopped, or until a timeout is reached. +# +# Please use this instead of 'STOP SLAVE', to reduce the risk of test +# case bugs. +# +# ==== Usage ==== +# +# source include/wait_for_slave_to_start.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. + +--disable_query_log +STOP SLAVE; +--enable_query_log +--echo include/stop_slave.inc +source include/wait_for_slave_to_stop.inc; diff --git a/mysql-test/include/sync_slave_io_with_master.inc b/mysql-test/include/sync_slave_io_with_master.inc new file mode 100644 index 00000000000..f7dd563039c --- /dev/null +++ b/mysql-test/include/sync_slave_io_with_master.inc @@ -0,0 +1,36 @@ +# ==== Purpose ==== +# +# Waits until the slave IO thread has been synced, i.e., all events +# have been copied over to slave. Does not care if the SQL thread is +# in sync. +# +# +# ==== Usage ==== +# +# source include/sync_slave_io_with_master.inc; +# +# Syncs to the current position on master, as found by SHOW MASTER +# STATUS. +# +# Must be called on the master. Will change connection to the slave. +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. + +let $_master_file= query_get_value("SHOW MASTER STATUS", File, 1); +let $_master_pos= query_get_value("SHOW MASTER STATUS", Position, 1); + +connection slave; + +let $slave_error_message= Failed while waiting for slave IO thread to sync; + +let $slave_param= Master_Log_File; +let $slave_param_value= $_master_file; +source include/wait_for_slave_param.inc; + +let $slave_param= Read_Master_Log_Pos; +let $slave_param_value= $_master_pos; +source include/wait_for_slave_param.inc; + +let $slave_error_message= ; diff --git a/mysql-test/include/testdb_only.inc b/mysql-test/include/testdb_only.inc index ddc3f123d45..528e2f3eb9d 100644 --- a/mysql-test/include/testdb_only.inc +++ b/mysql-test/include/testdb_only.inc @@ -19,12 +19,6 @@ # # ################################################################### ---disable_query_log -eval set @USE_RUNNING_SERVER= '$USE_RUNNING_SERVER'; ---require r/testdb_only.require -SELECT 'use extern server' - AS "Variable_name ", - IF(@USE_RUNNING_SERVER= '1','YES', - IF(@USE_RUNNING_SERVER= '0','NO','UNEXPECTED')) - AS "Value" ; ---enable_query_log +if ($USE_RUNNING_SERVER){ + skip Not with extern server; +} diff --git a/mysql-test/include/wait_condition_sp.inc b/mysql-test/include/wait_condition_sp.inc new file mode 100644 index 00000000000..66301da557c --- /dev/null +++ b/mysql-test/include/wait_condition_sp.inc @@ -0,0 +1,62 @@ +# include/wait_condition.inc +# +# SUMMARY +# +# Waits until the passed statement returns true, or the operation +# times out. +# +# USAGE +# +# let $wait_condition= +# SELECT c = 3 FROM t; +# --source include/wait_condition.inc +# +# OR +# +# let $wait_timeout= 60; # Override default 30 seconds with 60. +# let $wait_condition= +# SELECT c = 3 FROM t; +# --source include/wait_condition.inc +# --echo Executed the test condition $wait_condition_reps times +# +# EXAMPLE +# events_bugs.test, events_time_zone.test +# + +--disable_query_log + +let $wait_counter= 300; +if ($wait_timeout) +{ + let $wait_counter= `SELECT $wait_timeout * 10`; +} +# Reset $wait_timeout so that its value won't be used on subsequent +# calls, and default will be used instead. +let $wait_timeout= 0; + +# Keep track of how many times the wait condition is tested +# This is used by some tests (e.g., main.status) +let $wait_condition_reps= 0; +while ($wait_counter) +{ + let $success= `$wait_condition`; + inc $wait_condition_reps; + if ($success) + { + let $wait_counter= 0; + } + if (!$success) + { + real_sleep 0.1; + dec $wait_counter; + } +} +if (!$success) +{ + echo Timeout in wait_condition.inc for $wait_condition; + show master status; + show slave status; +} + +--enable_query_log + diff --git a/mysql-test/include/wait_for_slave_io_to_start.inc b/mysql-test/include/wait_for_slave_io_to_start.inc new file mode 100644 index 00000000000..abdc8339290 --- /dev/null +++ b/mysql-test/include/wait_for_slave_io_to_start.inc @@ -0,0 +1,19 @@ +# ==== Purpose ==== +# +# Waits until the IO thread of the current connection has started and +# connected to the master (i.e., until SHOW SLAVE STATUS returns Yes +# in the Slave_IO_Running field), or until a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_io_to_start.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. + +let $slave_param= Slave_IO_Running; +let $slave_param_value= Yes; +let $slave_error_message= Failed while waiting for slave IO thread to start; +source include/wait_for_slave_param.inc; +let $slave_error_message= ; diff --git a/mysql-test/include/wait_for_slave_io_to_stop.inc b/mysql-test/include/wait_for_slave_io_to_stop.inc index 6e66d4e7521..f61b0db1ed7 100644 --- a/mysql-test/include/wait_for_slave_io_to_stop.inc +++ b/mysql-test/include/wait_for_slave_io_to_stop.inc @@ -1,33 +1,24 @@ -################################################### -#Author: Jeb -#Date: 2007-06-11 -#Purpose: used for io errors on the slave. If Slave gets an io -# error, the io trhead should stop -#Details: -# 1) Fill in and setup variables -# 2) loop through looking for -# sql threads to stop -# 3) If loops too long die. -#################################################### -connection slave; -let $my_show= SHOW SLAVE STATUS; -let $sql_running= Slave_IO_Running; -let $row_number= 1; -let $run= 1; -let $counter= 300; +# ==== Purpose ==== +# +# Waits until the IO thread of the current connection has stopped, or +# until a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_io_to_stop.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. -while ($run) +# if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE +# STATUS will return an empty set. +let $_slave_io_running= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, 1); +if (`SELECT '$_slave_io_running' != 'No such row'`) { - let $io_result= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, $row_number); - if (`SELECT '$io_result' = 'No'`){ - let $run= 0; - } - sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave IO thread to stop" - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; + let $slave_param= Slave_IO_Running; + let $slave_param_value= No; + let $slave_error_message= Failed while waiting for slave IO thread to stop; + source include/wait_for_slave_param.inc; + let $slave_error_message= ; } - diff --git a/mysql-test/include/wait_for_slave_param.inc b/mysql-test/include/wait_for_slave_param.inc index fed97195aba..82e57922913 100644 --- a/mysql-test/include/wait_for_slave_param.inc +++ b/mysql-test/include/wait_for_slave_param.inc @@ -1,26 +1,82 @@ -# include/wait_for_slave_param.inc +# ==== Purpose ==== # -# SUMMARY +# Waits until SHOW SLAVE STATUS has returned a specified value, or +# until a timeout is reached. # -# Waits until SHOW SLAVE STATUS has returned a spicified value. +# ==== Usage ==== # -# USAGE +# let $slave_param= Slave_SQL_Running; +# let $slave_param_value= No; +# source include/slave_wait_param.inc; # -# let $slave_param= Slave_SQL_Running; -# let $slave_param_value= No; -# --source include/slave_wait_param.inc +# Parameters: +# +# $slave_param, $slave_param_value +# This macro will wait until the column of the output of SHOW SLAVE +# STATUS named $slave_param gets the value $slave_param_value. See +# the example above. +# +# $slave_param_comparison +# By default, this file waits until $slave_param becomes equal to +# $slave_param_value. If you want to wait until $slave_param +# becomes *unequal* to $slave_param_value, set this parameter to the +# string '!=', like this: +# let $slave_param_comparison= !=; +# +# $slave_timeout +# The default timeout is 5 minutes. You can change the timeout by +# setting $slave_timeout. The unit is tenths of seconds. +# +# $master_connection +# If the timeout is reached, debug info is given by calling SHOW +# SLAVE STATUS, SHOW PROCESSLIST, and SHOW BINLOG EVENTS. Then, a +# 'connection master' is then issued, and more debug info is given +# by calling SHOW MASTER STATUS, SHOW PROCESSLIST, and SHOW BINLOG +# EVENTS. If $master_connection is set, the latter three commands +# will be issued on $master_connection instead of on the host named +# 'master'. See also show_rpl_debug_info.inc +# +# $slave_error_message +# If set, this is printed when a timeout occurs. This is primarily +# intended to be used by other wait_for_slave_* macros, to indicate +# what the purpose of the wait was. (A very similar error message is +# given by default, but the wait_for_slave_* macros use this to give +# an error message identical to that in previous versions, so that +# errors are easier searchable in the pushbuild history.) -let $slave_wait_param_counter= 300; -let $slave_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); -while (`select "$slave_value" != "$slave_param_value"`) +let $_slave_timeout_counter= $slave_timeout; +if (!$_slave_timeout_counter) { - dec $slave_wait_param_counter; - if (!$slave_wait_param_counter) - { - --echo ERROR: failed while waiting for slave parameter $slave_param: $slave_param_value - query_vertical show slave status; - exit; - } - sleep 0.1; - let $slave_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); + let $_slave_timeout_counter= 3000; +} + +let $_slave_param_comparison= $slave_param_comparison; +if (`SELECT '$_slave_param_comparison' = ''`) +{ + let $_slave_param_comparison= =; +} + +let $_show_slave_status_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); +while (`SELECT NOT('$_show_slave_status_value' $_slave_param_comparison '$slave_param_value') AND $_slave_timeout_counter > 0`) +{ + dec $_slave_timeout_counter; + if ($_slave_timeout_counter) + { + sleep 0.1; + let $_show_slave_status_value= query_get_value("SHOW SLAVE STATUS", $slave_param, 1); + } +} + +# This has to be outside the loop until BUG#41913 has been fixed +if (!$_slave_timeout_counter) +{ + --echo **** ERROR: timeout after $slave_timeout seconds while waiting for slave parameter $slave_param $_slave_param_comparison $slave_param_value **** + if (`SELECT '$slave_error_message' != ''`) + { + --echo Message: $slave_error_message + } + --echo Current connection is '$CURRENT_CONNECTION' + echo Note: the following output may have changed since the failure was detected; + source include/show_rpl_debug_info.inc; + exit; } diff --git a/mysql-test/include/wait_for_slave_sql_error.inc b/mysql-test/include/wait_for_slave_sql_error.inc index 6780edbe2f0..ad1d7a9e639 100644 --- a/mysql-test/include/wait_for_slave_sql_error.inc +++ b/mysql-test/include/wait_for_slave_sql_error.inc @@ -1,33 +1,39 @@ -################################################### -#Author: Sven -#Date: 2007-10-09 -#Purpose: Wait until the slave has an error in the -# sql thread, as indicated by -# "SHOW SLAVE STATUS", or at most 30 -# seconds. -#Details: -# 1) Fill in and setup variables -# 2) loop, looking for sql error on slave -# 3) If it loops too long, die. -#################################################### -connection slave; -let $row_number= 1; -let $run= 1; -let $counter= 300; +# ==== Purpose ==== +# +# Waits until the SQL thread of the current connection has got an +# error, or until a timeout is reached. Also waits until the SQL +# thread has completely stopped. +# +# ==== Usage ==== +# +# source include/wait_for_slave_sql_error.inc; +# +# Parameters: +# +# $slave_sql_errno +# The expected SQL error number. This is required. +# (After BUG#41956 has been fixed, this will be required to be a +# symbolic name instead of a number.) +# +# $slave_timeout +# See wait_for_slave_param.inc for description. +# +# $master_connection +# See wait_for_slave_param.inc for description. -while ($run) -{ - let $sql_result= query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, $row_number); - let $run= `SELECT '$sql_result' = '0'`; - if ($run) { - real_sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave to produce an error in its sql thread" - --replace_result $MASTER_MYPORT MASTER_PORT - --replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; - } +if (`SELECT '$slave_sql_errno' = ''`) { + --echo !!!ERROR IN TEST: you must set \$slave_sql_errno before sourcing wait_fro_slave_sql_error.inc + exit; +} + +let $slave_param= Slave_SQL_Running; +let $slave_param_value= No; +let $slave_error_message= Failed while waiting for slave to stop the SQL thread (expecting error in the SQL thread); +source include/wait_for_slave_param.inc; + +let $_error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1); +if (`SELECT '$_error' != '$slave_sql_errno'`) { + --echo Slave stopped with wrong error code: $_error (expected $slave_sql_errno) + source include/show_rpl_debug_info.inc; + exit; } diff --git a/mysql-test/include/wait_for_slave_sql_error_and_skip.inc b/mysql-test/include/wait_for_slave_sql_error_and_skip.inc index 4b4776d2923..247de3a41a1 100644 --- a/mysql-test/include/wait_for_slave_sql_error_and_skip.inc +++ b/mysql-test/include/wait_for_slave_sql_error_and_skip.inc @@ -5,8 +5,23 @@ # # ==== Usage ==== # -# let show_sql_error=0|1; +# let $slave_sql_error= ; # source include/wait_for_slave_sql_error_and_skip.inc; +# +# Parameters: +# +# $slave_sql_errno +# The error number to wait for. This is required. (See +# wait_for_slave_sql_error.inc) +# +# $show_sql_error +# If set, will print the error to the query log. +# +# $slave_timeout +# See wait_for_slave_param.inc for description. +# +# $master_connection +# See wait_for_slave_param.inc for description. echo --source include/wait_for_slave_sql_error_and_skip.inc; connection slave; @@ -17,11 +32,7 @@ if ($show_sql_error) echo Last_SQL_Error = $error; } -# wait for SQL thread to stop after the error -source include/wait_for_slave_sql_to_stop.inc; - # skip the erroneous statement set global sql_slave_skip_counter=1; -start slave; -source include/wait_for_slave_to_start.inc; +source include/start_slave.inc; connection master; diff --git a/mysql-test/include/wait_for_slave_sql_to_start.inc b/mysql-test/include/wait_for_slave_sql_to_start.inc index 14134725da4..48744f5dd13 100644 --- a/mysql-test/include/wait_for_slave_sql_to_start.inc +++ b/mysql-test/include/wait_for_slave_sql_to_start.inc @@ -1,31 +1,17 @@ -################################################### -#Author: Mats (based on file written by Jeb) -#Date: 2008-05-06 -#Purpose: To wait for slave SQL thread to start -#Details: -# 1) Fill in and setup variables -# 2) loop through looking for both -# io and sql threads to start -# 3) If loops too long die. -#################################################### -connection slave; -let $row_number= 1; -let $run= 1; -let $counter= 300; - -while ($run) -{ - let $sql_result= query_get_value("SHOW SLAVE STATUS", Slave_SQL_Running, $row_number); - if (`SELECT '$sql_result' = 'Yes'`){ - let $run= 0; - } - sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave SQL to start" - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; -} - +# ==== Purpose ==== +# +# Waits the SQL thread of the current connection has started, or until +# a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_sql_to_start.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. +let $slave_param= Slave_SQL_Running; +let $slave_param_value= Yes; +let $slave_error_message= Failed while waiting for slave SQL to start; +source include/wait_for_slave_param.inc; diff --git a/mysql-test/include/wait_for_slave_sql_to_stop.inc b/mysql-test/include/wait_for_slave_sql_to_stop.inc index cb5c437a586..6992613b646 100644 --- a/mysql-test/include/wait_for_slave_sql_to_stop.inc +++ b/mysql-test/include/wait_for_slave_sql_to_stop.inc @@ -1,33 +1,24 @@ -################################################### -#Author: Jeb -#Date: 2007-06-11 -#Purpose: used for SQL errors on the slave. If Slave gets a sql -# error, the SQL trhead should stop -#Details: -# 1) Fill in and setup variables -# 2) loop through looking for -# sql threads to stop -# 3) If loops too long die. -#################################################### -if (!$keep_connection) -{ - connection slave; -} -let $row_number= 1; -let $run= 1; -let $counter= 300; +# ==== Purpose ==== +# +# Waits the SQL thread of the current connection has stopped, or until +# a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_sql_to_stop.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. -while ($run) +# if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE +# STATUS will return an empty set. +let $_slave_io_running= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, 1); +if (`SELECT '$_slave_io_running' != 'No such row'`) { - let $sql_result= query_get_value("SHOW SLAVE STATUS", Slave_SQL_Running, $row_number); - if (`SELECT '$sql_result' = 'No'`){ - let $run= 0; - } - sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave SQL thread to stop" - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; + let $slave_param= Slave_SQL_Running; + let $slave_param_value= No; + let $slave_error_message= Failed while waiting for slave SQL thread to stop; + source include/wait_for_slave_param.inc; + let $slave_error_message= ; } diff --git a/mysql-test/include/wait_for_slave_to_start.inc b/mysql-test/include/wait_for_slave_to_start.inc index 29d87b58a3c..567950cc6d7 100644 --- a/mysql-test/include/wait_for_slave_to_start.inc +++ b/mysql-test/include/wait_for_slave_to_start.inc @@ -1,35 +1,24 @@ -################################################### -#Author: Jeb -#Date: 2007-06-11 -#Purpose: To wait a brief time for slave to start -#Details: -# 1) Fill in and setup variables -# 2) loop through looking for both -# io and sql threads to start -# 3) If loops too long die. -#################################################### -connection slave; -let $row_number= 1; -let $run= 1; -let $counter= 300; +# ==== Purpose ==== +# +# Waits until both the IO and SQL threads of the current connection +# have started, or until a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_to_start.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. -while ($run) -{ - let $io_result= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, $row_number); - if (`SELECT '$io_result' = 'Yes'`){ +let $slave_error_message= Failed while waiting for slave to start; - let $sql_result= query_get_value("SHOW SLAVE STATUS", Slave_SQL_Running, $row_number); - if (`SELECT '$sql_result' = 'Yes'`){ - let $run= 0; - } - } - sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave to start" - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; -} +let $slave_param= Slave_IO_Running; +let $slave_param_value= Yes; +source include/wait_for_slave_param.inc; +let $slave_param= Slave_SQL_Running; +let $slave_param_value= Yes; +source include/wait_for_slave_param.inc; +let $slave_error_message= ; diff --git a/mysql-test/include/wait_for_slave_to_stop.inc b/mysql-test/include/wait_for_slave_to_stop.inc index 5bd2d0338f8..56d0e7b0c91 100644 --- a/mysql-test/include/wait_for_slave_to_stop.inc +++ b/mysql-test/include/wait_for_slave_to_stop.inc @@ -1,39 +1,30 @@ -################################################### -#Author: Jeb -#Date: 2007-06-11 -#Purpose: To replace the mysqltest.c executable -# wait_for_slave_to_stop function and -# return this to the test language. -#Details: -# 1) Fill in and setup variables -# 2) loop through looking for both -# io and sql threads to stop -# 3) If loops too long die. -#################################################### -connection slave; -let $row_number= 1; -let $run= 1; -let $counter= 300; +# ==== Purpose ==== +# +# Waits until both the IO and SQL threads of the current connection +# have stopped, or until a timeout is reached. +# +# ==== Usage ==== +# +# source include/wait_for_slave_to_stop.inc; +# +# Parameters to this macro are $slave_timeout and +# $master_connection. See wait_for_slave_param.inc for +# descriptions. -while ($run) +# if server has not used CHANGE MASTER to initiate slave, SHOW SLAVE +# STATUS will return an empty set. +let $_slave_io_running= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, 1); +if (`SELECT '$_slave_io_running' != 'No such row'`) { - let $io_result= query_get_value("SHOW SLAVE STATUS", Slave_IO_Running, $row_number); - if (`SELECT '$io_result' = 'No'`){ + let $slave_error_message= Failed while waiting for slave to stop; - let $sql_result= query_get_value("SHOW SLAVE STATUS", Slave_SQL_Running, $row_number); - if (`SELECT '$sql_result' = 'No'`){ - let $run= 0; - } - } - sleep 0.1; - if (!$counter){ - --echo "Failed while waiting for slave to stop" - --replace_result $MASTER_MYPORT MASTER_PORT - --replace_column 1 # 7 # 8 # 9 # 22 # 23 # 33 # - query_vertical SHOW SLAVE STATUS; - exit; - } - dec $counter; + let $slave_param= Slave_IO_Running; + let $slave_param_value= No; + source include/wait_for_slave_param.inc; + + let $slave_param= Slave_SQL_Running; + let $slave_param_value= No; + source include/wait_for_slave_param.inc; + + let $slave_error_message= ; } - - diff --git a/mysql-test/include/wait_show_pattern.inc b/mysql-test/include/wait_show_pattern.inc deleted file mode 100644 index c9f84ce7f08..00000000000 --- a/mysql-test/include/wait_show_pattern.inc +++ /dev/null @@ -1,51 +0,0 @@ -# include/wait_show_pattern.inc -# -# SUMMARY -# -# Waits until output produced by SHOW statement which particular type is -# specified as parameter matches certain pattern or maximum time reached. -# -# NOTES -# -# Only the first row produced by the parameter statement is checked. -# -# USAGE -# -# let $show_type= ; -# let $show_pattern= 'Pattern to be used for LIKE matching'; -# --source wait_show_pattern.inc -# -# EXAMPLES -# -# alter_table-big.test, wait_slave_status.inc -# -# SEE ALSO -# -# wait_slave_status.inc, wait_condition.inc (>=5.1) -# -############################################################################### - ---disable_query_log - -# We accept to wait maximum 30 seconds (0.2 sec/loop). -let $wait_counter= 150; -while ($wait_counter) -{ - let $result= `SHOW $show_type`; - let $success= `SELECT '$result' LIKE $show_pattern`; - if ($success) - { - let $wait_counter= 0; - } - if (!$success) - { - real_sleep 0.2; - dec $wait_counter; - } -} -if (!$success) -{ - echo Timeout in wait_show_pattern.inc \$show_type= $show_type \$show_pattern= $show_pattern (\$result= '$result'); -} - ---enable_query_log diff --git a/mysql-test/include/wait_slave_status.inc b/mysql-test/include/wait_slave_status.inc deleted file mode 100644 index d8d048527cf..00000000000 --- a/mysql-test/include/wait_slave_status.inc +++ /dev/null @@ -1,129 +0,0 @@ -# include/wait_slave_status.inc -# -# Created by Matthias Leich -# -# SUMMARY -# -# Waits until slave has reached certain state or maximum time reached. -# -# (This script will not work, when the SHOW command delivers more than one -# result record, because only the first record will be caught.) -# -# USAGE -# -# Set $result_pattern in test file and source this file: -# -# let $result_pattern= -# --include wait_slave_status.inc -# -# EXAMPLE -# -# The script rpl_until.test: -# ... -# --replace_result $MASTER_MYPORT MASTER_MYPORT -# --replace_column 1 # 9 # 23 # 33 # -# --vertical_results show slave status; -# -# outputs -# show slave status; -# Slave_IO_State # -# Master_Host 127.0.0.1 -# Master_User root -# Master_Port MASTER_MYPORT -# Connect_Retry 1 -# Master_Log_File master-bin.000001 -# Read_Master_Log_Pos 776 -# Relay_Log_File slave-relay-bin.000004 -# Relay_Log_Pos # -# Relay_Master_Log_File master-bin.000001 -# Slave_IO_Running Yes -# Slave_SQL_Running No -# Replicate_Do_DB -# Replicate_Ignore_DB -# Replicate_Do_Table -# Replicate_Ignore_Table -# Replicate_Wild_Do_Table -# Replicate_Wild_Ignore_Table -# Last_Errno 0 -# Last_Error -# Skip_Counter 0 -# Exec_Master_Log_Pos 319 -# Relay_Log_Space # -# Until_Condition Master -# Until_Log_File master-bin.000001 -# Until_Log_Pos 319 -# Master_SSL_Allowed No -# Master_SSL_CA_File -# Master_SSL_CA_Path -# Master_SSL_Cert -# Master_SSL_Cipher -# Master_SSL_Key -# Seconds_Behind_Master # -# -# The main problem with the "show slave status;" in rpl_until is, that -# depending on the total test engine power and the current load caused by -# other processes, the expected slave status might be not reached though -# it will happen in maybe some seconds. -# -# The typical problem with rpl_until is that Slave_IO_Running is "No" -# instead of "Yes". -# -# The expected result follows the LIKE pattern: -# -# let $result_pattern= '%127.0.0.1%root%1%master-bin.000001%776%slave-relay-bin.000004%master-bin.000001%Yes%No%0%0%319%Master%master-bin.000001%319%No%'; -# -# The Slave_IO_Running value is the "Yes" just after the "master-bin.000001". -# -# How to get this pattern ? -# -# Any lines "--replace_result ..." and "--replace_colum ..." just before -# the SHOW TABLE STATUS and of course the expected result itself -# show us columns where the content must be unified, because it is non -# deterministic or it depends on the current test environment. -# -# Unfortunately "--replace_result ..." and "--replace_colum ..." do not -# affect the result of our assignment let $my_val= `SHOW SLAVE STATUS`; -# Therefore such content must be covered by '%'. -# -# Please be careful. A more simple pattern might be dangerous, because we -# might get "wrong" matches. Example: There might be several "Yes" and "No" -# within one result row. -# -############################################################################### - -# We do not want to print the auxiliary commands, because they are not of -# interest and their amount will vary depending how fast we get the -# desired state. ---disable_query_log - -# The protocol should show -# - the setting of $result_pattern and -# - that this file is sourced , -# because this increases the chance to use the protocol as replay script. -eval SELECT "let \$result_pattern= $result_pattern ;" AS ""; -SELECT '--source include/wait_slave_status.inc' AS ""; - -let $show_type= SLAVE STATUS; -let $show_pattern= $result_pattern; ---enable_query_log - ---source include/wait_show_pattern.inc - -if (!$success) -{ -let $message= ! Attention: Timeout in wait_slave_status.inc. - | Possible reasons with decreasing probability: - | - The LIKE pattern is wrong, because the - | testcase was altered or the layout of the - | SHOW SLAVE STATUS result set changed. - | - There is a new bug within the replication. - | - We met an extreme testing environment and timeout is - | too small.; ---source include/show_msg80.inc ---echo DEBUG INFO START (wait_slave_status.inc): ---echo $result_pattern ---vertical_results -show slave status; ---echo DEBUG INFO END -} diff --git a/mysql-test/include/wait_until_connected_again.inc b/mysql-test/include/wait_until_connected_again.inc index dc96f646cb3..c7bb774929a 100644 --- a/mysql-test/include/wait_until_connected_again.inc +++ b/mysql-test/include/wait_until_connected_again.inc @@ -4,9 +4,13 @@ --disable_result_log --disable_query_log let $counter= 500; +let $mysql_errno= 9999; while ($mysql_errno) { - --error 0,2002,2006 + # Strangely enough, the server might return "Too many connections" + # while being shutdown, thus 1040 is an "allowed" error + # See BUG#36228 + --error 0,1040,1053,2002,2003,2006,2013 show status; dec $counter; diff --git a/mysql-test/include/wait_until_count_sessions.inc b/mysql-test/include/wait_until_count_sessions.inc new file mode 100644 index 00000000000..36fa9accafe --- /dev/null +++ b/mysql-test/include/wait_until_count_sessions.inc @@ -0,0 +1,112 @@ +# include/wait_until_count_sessions.inc +# +# SUMMARY +# +# Waits until the passed number ($count_sessions) of concurrent sessions was +# observed via +# SHOW STATUS LIKE 'Threads_connected' +# or the operation times out. +# Note: Starting with 5.1 we could also use +# SELECT COUNT(*) FROM information_schema.processlist +# I stay with "SHOW STATUS LIKE 'Threads_connected'" because this +# runs in all versions 5.0+ +# +# +# USAGE +# +# let $count_sessions= 3; +# --source include/wait_until_count_sessions.inc +# +# OR typical example of a test which uses more than one session +# Such a test could harm successing tests if there is no server shutdown +# and start between.cw +# +# If the testing box is slow than the disconnect of sessions belonging to +# the current test might happen when the successing test gets executed. +# This means the successing test might see activities like unexpected +# rows within the general log or the PROCESSLIST. +# Example from bug http://bugs.mysql.com/bug.php?id=40377 +# --- bzr_mysql-6.0-rpl/.../r/log_state.result +# +++ bzr_mysql-6.0-rpl/.../r/log_state.reject +# @@ -25,6 +25,7 @@ +# event_time user_host ... command_type argument +# TIMESTAMP USER_HOST ... Query create table t1(f1 int) +# TIMESTAMP USER_HOST ... Query select * from mysql.general_log +# +TIMESTAMP USER_HOST ... Quit +# .... +# +# What to do? +# ----------- +# +# # Determine initial number of connections (set $count_sessions) +# --source include/count_sessions.inc +# ... +# connect (con1,.....) +# ... +# connection default; +# ... +# disconnect con1; +# ... +# # Wait until we have reached the initial number of connections +# # or more than the sleep time above (10 seconds) has passed. +# # $count_sessions +# --source include/wait_until_count_sessions.inc +# +# +# Important note about tests with unfortunate (= not cooperative +# to successing tests) architecture: +# connection con1; +# send SELECT ..., sleep(10) +# connection default; +# ... +# disconnect con1; +# +# should be fixed by +# connection con1; +# send SELECT ..., sleep(10) +# connection default; +# ... +# connect con1; +# reap; +# connection default; +# disconnect con1; +# +# and not only by appending include/wait_until_count_sessions.inc etc. +# +# +# EXAMPLE +# +# backup.test, grant3.test +# +# +# Created: 2009-01-14 mleich +# + +let $wait_counter= 50; +if ($wait_timeout) +{ + let $wait_counter= `SELECT $wait_timeout * 10`; +} +# Reset $wait_timeout so that its value won't be used on subsequent +# calls, and default will be used instead. +let $wait_timeout= 0; +while ($wait_counter) +{ + let $current_sessions= query_get_value(SHOW STATUS LIKE 'Threads_connected', Value, 1); + let $success= `SELECT $current_sessions = $count_sessions`; + if ($success) + { + let $wait_counter= 0; + } + if (!$success) + { + real_sleep 0.1; + dec $wait_counter; + } +} +if (!$success) +{ + --echo # Timeout in wait_until_count_sessions.inc + --echo # Number of sessions expected: $count_sessions found: $current_sessions +} + diff --git a/mysql-test/include/wait_until_disconnected.inc b/mysql-test/include/wait_until_disconnected.inc new file mode 100644 index 00000000000..a4362e52d01 --- /dev/null +++ b/mysql-test/include/wait_until_disconnected.inc @@ -0,0 +1,21 @@ +# +# Include this script to wait until the connection to the +# server has been dropped +--disable_result_log +--disable_query_log +let $counter= 500; +let $mysql_errno= 0; +while (!$mysql_errno) +{ + --error 0,1053,2002,2006,2013 + show status; + + dec $counter; + if (!$counter) + { + --die Server failed to dissapear + } + --sleep 0.1 +} +--enable_query_log +--enable_result_log diff --git a/mysql-test/include/windows_sys_vars.inc b/mysql-test/include/windows_sys_vars.inc index 90ff86fefd1..1d51ddb52f9 100644 --- a/mysql-test/include/windows_sys_vars.inc +++ b/mysql-test/include/windows_sys_vars.inc @@ -9,7 +9,6 @@ SET @min_flush_time = 0; #SET @max_flush_time = 0; SET @default_key_buffer_size= 131072; -SET @min_key_buffer_size= 8; #SET @default_join_buffer_size = 131072; #SET @min_join_buffer_size = 8200; diff --git a/mysql-test/install_test_db.sh b/mysql-test/install_test_db.sh deleted file mode 100644 index e4df8f619cc..00000000000 --- a/mysql-test/install_test_db.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# Copyright (C) 1997-2006 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; version 2 of the License. -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# This scripts creates the privilege tables db, host, user, tables_priv, -# columns_priv in the mysql database, as well as the func table. - -if [ x$1 = x"--bin" ]; then - shift 1 - BINARY_DIST=1 - - bindir=../bin - scriptdir=bin - libexecdir=../libexec - - # Check if it's a binary distribution or a 'make install' - if test -x ../libexec/mysqld - then - execdir=../libexec - elif test -x ../../sbin/mysqld # RPM installation - then - execdir=../../sbin - bindir=../../bin - scriptdir=../bin - libexecdir=../../libexec - else - execdir=../bin - fi - fix_bin=mysql-test -else - execdir=../sql - bindir=../client - fix_bin=. - scriptdir=scripts - libexecdir=../libexec -fi - -vardir=var -logdir=$vardir/log -if [ x$1 = x"-slave" ] -then - shift 1 - data=var/slave-data -else - if [ x$1 = x"-1" ] - then - data=var/master-data1 - else - data=var/master-data - fi -fi -ldata=$fix_bin/$data - -mdata=$data/mysql -EXTRA_ARG="" - -mysqld= -if test -x $execdir/mysqld -then - mysqld=$execdir/mysqld -else - if test ! -x $libexecdir/mysqld - then - echo "mysqld is missing - looked in $execdir and in $libexecdir" - exit 1 - else - mysqld=$libexecdir/mysqld - fi -fi - -# On IRIX hostname is in /usr/bsd so add this to the path -PATH=$PATH:/usr/bsd -hostname=`hostname` # Install this too in the user table -hostname="$hostname%" # Fix if not fully qualified hostname - - -#create the directories -[ -d $vardir ] || mkdir $vardir -[ -d $logdir ] || mkdir $logdir - -# Create database directories mysql & test -if [ -d $data ] ; then rm -rf $data ; fi -mkdir $data $data/mysql $data/test - -#for error messages -if [ x$BINARY_DIST = x1 ] ; then -basedir=.. -else -basedir=. -EXTRA_ARG="--windows" -fi - -INSTALL_CMD="$scriptdir/mysql_install_db --no-defaults $EXTRA_ARG --basedir=$basedir --datadir=mysql-test/$ldata --srcdir=." -echo "running $INSTALL_CMD" - -cd .. -if $INSTALL_CMD -then - exit 0 -else - echo "Error executing mysqld --bootstrap" - exit 1 -fi diff --git a/mysql-test/lib/My/Config.pm b/mysql-test/lib/My/Config.pm index 5491e341ddc..f8416e3df3a 100644 --- a/mysql-test/lib/My/Config.pm +++ b/mysql-test/lib/My/Config.pm @@ -4,6 +4,7 @@ package My::Config::Option; use strict; use warnings; +use Carp; sub new { @@ -26,12 +27,22 @@ sub value { return $self->{value}; } +sub option { + my ($self)= @_; + my $name= $self->{name}; + my $value= $self->{value}; + + my $opt= $name; + $opt= "$name=$value" if ($value); + $opt= "--$opt" unless ($opt =~ /^--/); + return $opt; +} package My::Config::Group; use strict; use warnings; - +use Carp; sub new { my ($class, $group_name)= @_; @@ -68,7 +79,7 @@ sub remove { return undef unless defined $option; # Remove from the hash - delete($self->{options_by_name}->{$option_name}) or die; + delete($self->{options_by_name}->{$option_name}) or croak; # Remove from the array @{$self->{options}}= grep { $_->name ne $option_name } @{$self->{options}}; @@ -88,6 +99,33 @@ sub name { return $self->{name}; } +sub suffix { + my ($self)= @_; + # Everything in name from the last . + my @parts= split(/\./, $self->{name}); + my $suffix= pop(@parts); + return ".$suffix"; +} + +sub after { + my ($self, $prefix)= @_; + die unless defined $prefix; + + # everything after $prefix + my $name= $self->{name}; + if ($name =~ /^\Q$prefix\E(.*)$/) + { + return $1; + } + die "Failed to extract the value after '$prefix' in $name"; +} + + +sub split { + my ($self)= @_; + # Return an array with name parts + return split(/\./, $self->{name}); +} # # Return a specific option in the group @@ -100,23 +138,37 @@ sub option { # -# Return a specific value for an option in the group +# Return value for an option in the group, fail if it does not exist # sub value { my ($self, $option_name)= @_; my $option= $self->option($option_name); - die "No option named '$option_name' in this group" + croak "No option named '$option_name' in group '$self->{name}'" if ! defined($option); return $option->value(); } +# +# Return value for an option if it exist +# +sub if_exist { + my ($self, $option_name)= @_; + my $option= $self->option($option_name); + + return undef if ! defined($option); + + return $option->value(); +} + + package My::Config; use strict; use warnings; +use Carp; use IO::File; use File::Basename; @@ -132,13 +184,13 @@ sub new { my $self= bless { groups => [] }, $class; my $F= IO::File->new($path, "<") - or die "Could not open '$path': $!"; + or croak "Could not open '$path': $!"; while ( my $line= <$F> ) { chomp($line); # [group] - if ( $line =~ /\[(.*)\]/ ) { + if ( $line =~ /^\[(.*)\]/ ) { # New group found $group_name= $1; #print "group: $group_name\n"; @@ -149,7 +201,7 @@ sub new { # Magic #! comments elsif ( $line =~ /^#\!/) { my $magic= $line; - die "Found magic comment '$magic' outside of group" + croak "Found magic comment '$magic' outside of group" unless $group_name; #print "$magic\n"; @@ -171,8 +223,13 @@ sub new { # !include elsif ( $line =~ /^\!include\s*(.*?)\s*$/ ) { my $include_file_name= dirname($path)."/".$1; - # Check that the file exists - die "The include file '$include_file_name' does not exist" + + # Check that the file exists relative to path of first config file + if (! -f $include_file_name){ + # Try to include file relativ to current dir + $include_file_name= $1; + } + croak "The include file '$include_file_name' does not exist" unless -f $include_file_name; $self->append(My::Config->new($include_file_name)); @@ -182,7 +239,7 @@ sub new { elsif ( $line =~ /^([\@\w-]+)\s*$/ ) { my $option= $1; - die "Found option '$option' outside of group" + croak "Found option '$option' outside of group" unless $group_name; #print "$option\n"; @@ -194,13 +251,13 @@ sub new { my $option= $1; my $value= $2; - die "Found option '$option=$value' outside of group" + croak "Found option '$option=$value' outside of group" unless $group_name; #print "$option=$value\n"; $self->insert($group_name, $option, $value); } else { - die "Unexpected line '$line' found in '$path'"; + croak "Unexpected line '$line' found in '$path'"; } } @@ -231,6 +288,7 @@ sub insert { # Add the option to the group $group->insert($option, $value, $if_not_exist); } + return $group; } # @@ -240,11 +298,11 @@ sub remove { my ($self, $group_name, $option_name)= @_; my $group= $self->group($group_name); - die "group '$group_name' does not exist" + croak "group '$group_name' does not exist" unless defined($group); $group->remove($option_name) or - die "option '$option_name' does not exist"; + croak "option '$option_name' does not exist"; } @@ -267,10 +325,10 @@ sub group_exists { # sub _group_insert { my ($self, $group_name)= @_; - caller eq __PACKAGE__ or die; + caller eq __PACKAGE__ or croak; # Check that group does not already exist - die "Group already exists" if $self->group_exists($group_name); + croak "Group already exists" if $self->group_exists($group_name); my $group= My::Config::Group->new($group_name); push(@{$self->{groups}}, $group); @@ -354,11 +412,11 @@ sub value { my ($self, $group_name, $option_name)= @_; my $group= $self->group($group_name); - die "group '$group_name' does not exist" + croak "group '$group_name' does not exist" unless defined($group); my $option= $group->option($option_name); - die "option '$option_name' does not exist" + croak "option '$option_name' does not exist" unless defined($option); return $option->value(); @@ -372,7 +430,7 @@ sub exists { my ($self, $group_name, $option_name)= @_; my $group= $self->group($group_name); - die "group '$group_name' does not exist" + croak "group '$group_name' does not exist" unless defined($group); my $option= $group->option($option_name); @@ -412,11 +470,11 @@ sub stringify { # Save the config to named file # sub save { - my ($self, $path)= @_; - my $F= IO::File->new($path, ">") - or die "Could not open '$path': $!"; - print $F $self; - undef $F; # Close the file + my ($self, $path)= @_; + my $F= IO::File->new($path, ">") + or croak "Could not open '$path': $!"; + print $F $self; + undef $F; # Close the file } 1; diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm new file mode 100644 index 00000000000..567a05ac7a1 --- /dev/null +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -0,0 +1,655 @@ +# -*- cperl -*- +package My::ConfigFactory; + +use strict; +use warnings; +use Carp; + +use My::Config; +use My::Find; + +use File::Basename; + + +# +# Rules to run first of all +# +my @pre_rules= +( +); + + +my @share_locations= ("share/mysql", "sql/share", "share"); + + +sub get_basedir { + my ($self, $group)= @_; + my $basedir= $group->if_exist('basedir') || + $self->{ARGS}->{basedir}; + return $basedir; +} + + +sub fix_charset_dir { + my ($self, $config, $group_name, $group)= @_; + return my_find_dir($self->get_basedir($group), + \@share_locations, "charsets"); +} + +sub fix_language { + my ($self, $config, $group_name, $group)= @_; + return my_find_dir($self->get_basedir($group), + \@share_locations, "english"); +} + +sub fix_datadir { + my ($self, $config, $group_name)= @_; + my $vardir= $self->{ARGS}->{vardir}; + return "$vardir/$group_name/data"; +} + +sub fix_pidfile { + my ($self, $config, $group_name, $group)= @_; + my $vardir= $self->{ARGS}->{vardir}; + return "$vardir/run/$group_name.pid"; +} + +sub fix_port { + my ($self, $config, $group_name, $group)= @_; + my $hostname= $group->value('#host'); + return $self->{HOSTS}->{$hostname}++; +} + +sub fix_host { + my ($self)= @_; + # Get next host from HOSTS array + my @hosts= keys(%{$self->{HOSTS}});; + my $host_no= $self->{NEXT_HOST}++ % @hosts; + return $hosts[$host_no]; +} + +sub is_unique { + my ($config, $name, $value)= @_; + + foreach my $group ( $config->groups() ) { + if ($group->option($name)) { + if ($group->value($name) eq $value){ + return 0; + } + } + } + return 1; +} + +sub fix_server_id { + my ($self, $config, $group_name, $group)= @_; +#define in the order that mysqlds are listed in my.cnf + + my $server_id= $group->if_exist('server-id'); + if (defined $server_id){ + if (!is_unique($config, 'server-id', $server_id)) { + croak "The server-id($server_id) for '$group_name' is not unique"; + } + return $server_id; + } + + do { + $server_id= $self->{SERVER_ID}++; + } while(!is_unique($config, 'server-id', $server_id)); + + #print "$group_name: server_id: $server_id\n"; + return $server_id; +} + +sub fix_socket { + my ($self, $config, $group_name, $group)= @_; + # Put socket file in tmpdir + my $dir= $self->{ARGS}->{tmpdir}; + return "$dir/$group_name.sock"; +} + +sub fix_tmpdir { + my ($self, $config, $group_name, $group)= @_; + my $dir= $self->{ARGS}->{tmpdir}; + return "$dir/$group_name"; +} + +sub fix_log_error { + my ($self, $config, $group_name, $group)= @_; + my $dir= dirname($group->value('datadir')); + return "$dir/mysqld.err"; +} + +sub fix_log { + my ($self, $config, $group_name, $group)= @_; + my $dir= dirname($group->value('datadir')); + return "$dir/mysqld.log"; +} + +sub fix_log_slow_queries { + my ($self, $config, $group_name, $group)= @_; + my $dir= dirname($group->value('datadir')); + return "$dir/mysqld-slow.log"; +} + +sub fix_secure_file_priv { + my ($self)= @_; + my $vardir= $self->{ARGS}->{vardir}; + # By default, prevent the started mysqld to access files outside of vardir + return $vardir; +} + +sub fix_std_data { + my ($self, $config, $group_name, $group)= @_; + my $basedir= $self->get_basedir($group); + return "$basedir/mysql-test/std_data"; +} + +sub ssl_supported { + my ($self)= @_; + return $self->{ARGS}->{ssl}; +} + +sub fix_skip_ssl { + return if !ssl_supported(@_); + # Add skip-ssl if ssl is supported to avoid + # that mysqltest connects with SSL by default + return 1; +} + +sub fix_ssl_ca { + return if !ssl_supported(@_); + my $std_data= fix_std_data(@_); + return "$std_data/cacert.pem" +} + +sub fix_ssl_server_cert { + return if !ssl_supported(@_); + my $std_data= fix_std_data(@_); + return "$std_data/server-cert.pem" +} + +sub fix_ssl_client_cert { + return if !ssl_supported(@_); + my $std_data= fix_std_data(@_); + return "$std_data/client-cert.pem" +} + +sub fix_ssl_server_key { + return if !ssl_supported(@_); + my $std_data= fix_std_data(@_); + return "$std_data/server-key.pem" +} + +sub fix_ssl_client_key { + return if !ssl_supported(@_); + my $std_data= fix_std_data(@_); + return "$std_data/client-key.pem" +} + + +# +# Rules to run for each mysqld in the config +# - will be run in order listed here +# +my @mysqld_rules= + ( + { 'basedir' => sub { return shift->{ARGS}->{basedir}; } }, + { 'tmpdir' => \&fix_tmpdir }, + { 'character-sets-dir' => \&fix_charset_dir }, + { 'language' => \&fix_language }, + { 'datadir' => \&fix_datadir }, + { 'pid-file' => \&fix_pidfile }, + { '#host' => \&fix_host }, + { 'port' => \&fix_port }, + { 'socket' => \&fix_socket }, + { 'log-error' => \&fix_log_error }, + { 'log' => \&fix_log }, + { 'log-slow-queries' => \&fix_log_slow_queries }, + { '#user' => sub { return shift->{ARGS}->{user} || ""; } }, + { '#password' => sub { return shift->{ARGS}->{password} || ""; } }, + { 'server-id' => \&fix_server_id, }, + # By default, prevent the started mysqld to access files outside of vardir + { 'secure-file-priv' => sub { return shift->{ARGS}->{vardir}; } }, + { 'ssl-ca' => \&fix_ssl_ca }, + { 'ssl-cert' => \&fix_ssl_server_cert }, + { 'ssl-key' => \&fix_ssl_server_key }, + ); + + +sub fix_ndb_mgmd_port { + my ($self, $config, $group_name, $group)= @_; + my $hostname= $group->value('HostName'); + return $self->{HOSTS}->{$hostname}++; +} + + +sub fix_cluster_dir { + my ($self, $config, $group_name, $group)= @_; + my $vardir= $self->{ARGS}->{vardir}; + my (undef, $process_type, $idx, $suffix)= split(/\./, $group_name); + return "$vardir/mysql_cluster.$suffix/$process_type.$idx"; +} + + +sub fix_cluster_backup_dir { + my ($self, $config, $group_name, $group)= @_; + my $vardir= $self->{ARGS}->{vardir}; + my (undef, $process_type, $idx, $suffix)= split(/\./, $group_name); + return "$vardir/mysql_cluster.$suffix/"; +} + + +# +# Rules to run for each ndb_mgmd in the config +# - will be run in order listed here +# +my @ndb_mgmd_rules= +( + { 'PortNumber' => \&fix_ndb_mgmd_port }, + { 'DataDir' => \&fix_cluster_dir }, +); + + +# +# Rules to run for each ndbd in the config +# - will be run in order listed here +# +my @ndbd_rules= +( + { 'HostName' => \&fix_host }, + { 'DataDir' => \&fix_cluster_dir }, + { 'BackupDataDir' => \&fix_cluster_backup_dir }, +); + + +# +# Rules to run for each cluster_config section +# - will be run in order listed here +# +my @cluster_config_rules= +( + { 'ndb_mgmd' => \&fix_host }, + { 'ndbd' => \&fix_host }, + { 'mysqld' => \&fix_host }, + { 'ndbapi' => \&fix_host }, +); + + +# +# Rules to run for [client] section +# - will be run in order listed here +# +my @client_rules= +( +); + + +# +# Rules to run for [mysqltest] section +# - will be run in order listed here +# +my @mysqltest_rules= +( + { 'ssl-ca' => \&fix_ssl_ca }, + { 'ssl-cert' => \&fix_ssl_client_cert }, + { 'ssl-key' => \&fix_ssl_client_key }, + { 'skip-ssl' => \&fix_skip_ssl }, +); + + +# +# Rules to run for [mysqlbinlog] section +# - will be run in order listed here +# +my @mysqlbinlog_rules= +( + { 'character-sets-dir' => \&fix_charset_dir }, +); + + +# +# Rules to run for [mysql_upgrade] section +# - will be run in order listed here +# +my @mysql_upgrade_rules= +( + { 'tmpdir' => sub { return shift->{ARGS}->{tmpdir}; } }, +); + + +# +# Generate a [client.] group to be +# used for connecting to [mysqld.] +# +sub post_check_client_group { + my ($self, $config, $client_group_name, $mysqld_group_name)= @_; + + # Settings needed for client, copied from its "mysqld" + my %client_needs= + ( + port => 'port', + socket => 'socket', + host => '#host', + user => '#user', + password => '#password', + ); + + my $group_to_copy_from= $config->group($mysqld_group_name); + while (my ($name_to, $name_from)= each( %client_needs )) { + my $option= $group_to_copy_from->option($name_from); + + if (! defined $option){ + #print $config; + croak "Could not get value for '$name_from'"; + } + $config->insert($client_group_name, $name_to, $option->value()) + } +} + + +sub post_check_client_groups { + my ($self, $config)= @_; + + my $first_mysqld= $config->first_like('mysqld.'); + + return unless $first_mysqld; + + # Always generate [client] pointing to the first + # [mysqld.] + $self->post_check_client_group($config, + 'client', + $first_mysqld->name()); + + # Then generate [client.] for each [mysqld.] + foreach my $mysqld ( $config->like('mysqld.') ) { + $self->post_check_client_group($config, + 'client'.$mysqld->after('mysqld'), + $mysqld->name()) + } + +} + + +# +# Generate [embedded] by copying the values +# needed from the default [mysqld] section +# and from first [mysqld.] +# +sub post_check_embedded_group { + my ($self, $config)= @_; + + return unless $self->{ARGS}->{embedded}; + + my $mysqld= $config->group('mysqld') or + croak "Can't run with embedded, config has no default mysqld section"; + + my $first_mysqld= $config->first_like('mysqld.') or + croak "Can't run with embedded, config has no mysqld"; + + my @no_copy = + ( + 'log-error', # Embedded server writes stderr to mysqltest's log file + 'slave-net-timeout', # Embedded server are not build with replication + ); + + foreach my $option ( $mysqld->options(), $first_mysqld->options() ) { + # Don't copy options whose name is in "no_copy" list + next if grep ( $option->name() eq $_, @no_copy); + + $config->insert('embedded', $option->name(), $option->value()) + } + +} + + +sub resolve_at_variable { + my ($self, $config, $group, $option)= @_; + + # Split the options value on last . + my @parts= split(/\./, $option->value()); + my $option_name= pop(@parts); + my $group_name= join('.', @parts); + + $group_name =~ s/^\@//; # Remove at + + my $from_group= $config->group($group_name) + or croak "There is no group named '$group_name' that ", + "can be used to resolve '$option_name'"; + + my $from= $from_group->value($option_name); + $config->insert($group->name(), $option->name(), $from) +} + + +sub post_fix_resolve_at_variables { + my ($self, $config)= @_; + + foreach my $group ( $config->groups() ) { + foreach my $option ( $group->options()) { + next unless defined $option->value(); + + $self->resolve_at_variable($config, $group, $option) + if ($option->value() =~ /^\@/); + } + } +} + +sub post_fix_mysql_cluster_section { + my ($self, $config)= @_; + + # Add a [mysl_cluster.] section for each + # defined [cluster_config.] section + foreach my $group ( $config->like('cluster_config\.\w*$') ) + { + my @urls; + # Generate ndb_connectstring for this cluster + foreach my $ndb_mgmd ( $config->like('cluster_config.ndb_mgmd.')) { + if ($ndb_mgmd->suffix() eq $group->suffix()) { + my $host= $ndb_mgmd->value('HostName'); + my $port= $ndb_mgmd->value('PortNumber'); + push(@urls, "$host:$port"); + } + } + croak "Could not generate valid ndb_connectstring for '$group'" + unless @urls > 0; + my $ndb_connectstring= join(";", @urls); + + # Add ndb_connectstring to [mysql_cluster.] + $config->insert('mysql_cluster'.$group->suffix(), + 'ndb_connectstring', $ndb_connectstring); + + # Add ndb_connectstring to each mysqld connected to this + # cluster + foreach my $mysqld ( $config->like('cluster_config.mysqld.')) { + if ($mysqld->suffix() eq $group->suffix()) { + my $after= $mysqld->after('cluster_config.mysqld'); + $config->insert("mysqld$after", + 'ndb_connectstring', $ndb_connectstring); + } + } + } +} + +# +# Rules to run last of all +# +my @post_rules= +( + \&post_check_client_groups, + \&post_fix_mysql_cluster_section, + \&post_fix_resolve_at_variables, + \&post_check_embedded_group, +); + + +sub run_rules_for_group { + my ($self, $config, $group, @rules)= @_; + foreach my $hash ( @rules ) { + while (my ($option, $rule)= each( %{$hash} )) { + # Only run this rule if the value is not already defined + if (!$config->exists($group->name(), $option)) { + my $value; + if (ref $rule eq "CODE") { + # Call the rule function + $value= &$rule($self, $config, $group->name(), + $config->group($group->name())); + } else { + $value= $rule; + } + if (defined $value) { + $config->insert($group->name(), $option, $value, 1); + } + } + } + } +} + + +sub run_section_rules { + my ($self, $config, $name, @rules)= @_; + + foreach my $group ( $config->like($name) ) { + $self->run_rules_for_group($config, $group, @rules); + } +} + + +sub run_generate_sections_from_cluster_config { + my ($self, $config)= @_; + + my @options= ('ndb_mgmd', 'ndbd', + 'mysqld', 'ndbapi'); + + foreach my $group ( $config->like('cluster_config\.\w*$') ) { + + # Keep track of current index per process type + my %idxes; + map { $idxes{$_}= 1; } @options; + + foreach my $option_name ( @options ) { + my $value= $group->value($option_name); + my @hosts= split(/,/, $value, -1); # -1 => return also empty strings + + # Add at least one host + push(@hosts, undef) unless scalar(@hosts); + + # Assign hosts unless already fixed + @hosts= map { $self->fix_host() unless $_; } @hosts; + + # Write the hosts value back + $group->insert($option_name, join(",", @hosts)); + + # Generate sections for each host + foreach my $host ( @hosts ){ + my $idx= $idxes{$option_name}++; + + my $suffix= $group->suffix(); + # Generate a section for ndb_mgmd to read + $config->insert("cluster_config.$option_name.$idx$suffix", + "HostName", $host); + + if ($option_name eq 'mysqld'){ + my $datadir= + $self->fix_cluster_dir($config, + "cluster_config.mysqld.$idx$suffix", + $group); + $config->insert("mysqld.$idx$suffix", + 'datadir', "$datadir/data"); + } + } + } + } +} + + +sub new_config { + my ($class, $args)= @_; + + my @required_args= ('basedir', 'baseport', 'vardir', 'template_path'); + + foreach my $required ( @required_args ) { + croak "you must pass '$required'" unless defined $args->{$required}; + } + + # Fill in hosts/port hash + my $hosts= {}; + my $baseport= $args->{baseport}; + $args->{hosts}= [ 'localhost' ] unless exists($args->{hosts}); + foreach my $host ( @{$args->{hosts}} ) { + $hosts->{$host}= $baseport; + } + + # Open the config template + my $config= My::Config->new($args->{'template_path'}); + my $extra_template_path= $args->{'extra_template_path'}; + if ($extra_template_path){ + $config->append(My::Config->new($extra_template_path)); + } + my $self= bless { + CONFIG => $config, + ARGS => $args, + HOSTS => $hosts, + NEXT_HOST => 0, + SERVER_ID => 1, + }, $class; + + + { + # Run pre rules + foreach my $rule ( @pre_rules ) { + &$rule($self, $config); + } + } + + + $self->run_section_rules($config, + 'cluster_config\.\w*$', + @cluster_config_rules); + $self->run_generate_sections_from_cluster_config($config); + + $self->run_section_rules($config, + 'cluster_config.ndb_mgmd.', + @ndb_mgmd_rules); + $self->run_section_rules($config, + 'cluster_config.ndbd', + @ndbd_rules); + + $self->run_section_rules($config, + 'mysqld.', + @mysqld_rules); + + # [mysqlbinlog] need additional settings + $self->run_rules_for_group($config, + $config->insert('mysqlbinlog'), + @mysqlbinlog_rules); + + # [mysql_upgrade] need additional settings + $self->run_rules_for_group($config, + $config->insert('mysql_upgrade'), + @mysql_upgrade_rules); + + # Additional rules required for [client] + $self->run_rules_for_group($config, + $config->insert('client'), + @client_rules); + + + # Additional rules required for [mysqltest] + $self->run_rules_for_group($config, + $config->insert('mysqltest'), + @mysqltest_rules); + + { + # Run post rules + foreach my $rule ( @post_rules ) { + &$rule($self, $config); + } + } + + return $config; +} + + +1; + diff --git a/mysql-test/lib/My/CoreDump.pm b/mysql-test/lib/My/CoreDump.pm new file mode 100644 index 00000000000..599f9ccbfca --- /dev/null +++ b/mysql-test/lib/My/CoreDump.pm @@ -0,0 +1,131 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +package My::CoreDump; + +use strict; +use Carp; +use My::Platform; + +use File::Temp qw/ tempfile tempdir /; + +sub _gdb { + my ($core_name)= @_; + + print "\nTrying 'gdb' to get a backtrace\n"; + + return unless -f $core_name; + + # Find out name of binary that generated core + `gdb -c '$core_name' --batch 2>&1` =~ + /Core was generated by `([^\s\'\`]+)/; + my $binary= $1 or return; + print "Core generated by '$binary'\n"; + + # Create tempfile containing gdb commands + my ($tmp, $tmp_name) = tempfile(); + print $tmp + "bt\n", + "thread apply all bt\n", + "quit\n"; + close $tmp or die "Error closing $tmp_name: $!"; + + # Run gdb + my $gdb_output= + `gdb '$binary' -c '$core_name' -x '$tmp_name' --batch 2>&1`; + + unlink $tmp_name or die "Error removing $tmp_name: $!"; + + return if $? >> 8; + return unless $gdb_output; + + print <&1` =~ + /Corefile specified executable: "([^"]+)"/; + my $binary= $1 or return; + print "Core generated by '$binary'\n"; + + # Find all threads + my @thr_ids = `echo threads | dbx '$binary' '$core_name' 2>&1` =~ /t@\d+/g; + + # Create tempfile containing dbx commands + my ($tmp, $tmp_name) = tempfile(); + foreach my $thread (@thr_ids) { + print $tmp "where $thread\n"; + } + print $tmp "exit\n"; + close $tmp or die "Error closing $tmp_name: $!"; + + # Run dbx + my $dbx_output= + `cat '$tmp_name' | dbx '$binary' '$core_name' 2>&1`; + + unlink $tmp_name or die "Error removing $tmp_name: $!"; + + return if $? >> 8; + return unless $dbx_output; + + print <($core_name)){ + return; + } + } + return; +} + + +1; diff --git a/mysql-test/lib/My/File/Path.pm b/mysql-test/lib/My/File/Path.pm new file mode 100644 index 00000000000..99edeecdaf7 --- /dev/null +++ b/mysql-test/lib/My/File/Path.pm @@ -0,0 +1,177 @@ +# -*- cperl -*- +package My::File::Path; +use strict; + + +# +# File::Path::rmtree has a problem with deleting files +# and directories where it hasn't got read permission +# +# Patch this by installing a 'rmtree' function in local +# scope that first chmod all files to 0777 before calling +# the original rmtree function. +# +# This is almost gone in version 1.08 of File::Path - +# but unfortunately some hosts still suffers +# from this also in 1.08 +# + +use Exporter; +use base "Exporter"; +our @EXPORT= qw / rmtree mkpath copytree /; + +use File::Find; +use File::Copy; +use File::Spec; +use Carp; +use My::Handles; +use My::Platform; + +sub rmtree { + my ($dir)= @_; + find( { + bydepth => 1, + no_chdir => 1, + wanted => sub { + my $name= $_; + if (!-l $name && -d _){ + return if (rmdir($name) == 1); + + chmod(0777, $name) or carp("couldn't chmod(0777, $name): $!"); + + return if (rmdir($name) == 1); + + # Failed to remove the directory, analyze + carp("Couldn't remove directory '$name': $!"); + My::Handles::show_handles($name); + } else { + return if (unlink($name) == 1); + + chmod(0777, $name) or carp("couldn't chmod(0777, $name): $!"); + + return if (unlink($name) == 1); + + carp("Couldn't delete file '$name': $!"); + My::Handles::show_handles($name); + } + } + }, $dir ); +}; + + +use File::Basename; +sub _mkpath_debug { + my ($message, $path, $dir, $err)= @_; + + print "=" x 40, "\n"; + print $message, "\n"; + print "err: '$err'\n"; + print "path: '$path'\n"; + print "dir: '$dir'\n"; + + print "-" x 40, "\n"; + my $dirname= dirname($path); + print "ls -l $dirname\n"; + print `ls -l $dirname`, "\n"; + print "-" x 40, "\n"; + print "dir $dirname\n"; + print `dir $dirname`, "\n"; + print "-" x 40, "\n"; + my $dirname2= dirname($dirname); + print "ls -l $dirname2\n"; + print `ls -l $dirname2`, "\n"; + print "-" x 40, "\n"; + print "dir $dirname2\n"; + print `dir $dirname2`, "\n"; + print "-" x 40, "\n"; + print "file exists\n" if (-e $path); + print "file is a plain file\n" if (-f $path); + print "file is a directory\n" if (-d $path); + print "-" x 40, "\n"; + print "showing handles for $path\n"; + My::Handles::show_handles($path); + + print "=" x 40, "\n"; + +} + + +sub mkpath { + my $path; + + die "Usage: mkpath()" unless @_ == 1; + + foreach my $dir ( File::Spec->splitdir( @_ ) ) { + #print "dir: $dir\n"; + if ($dir =~ /^[a-z]:/i){ + # Found volume ie. C: + $path= $dir; + next; + } + + $path= File::Spec->catdir($path, $dir); + #print "path: $path\n"; + + next if -d $path; # Path already exists and is a directory + croak("File already exists but is not a directory: '$path'") if -e $path; + next if mkdir($path); + _mkpath_debug("mkdir failed", $path, $dir, $!); + + # mkdir failed, try one more time + next if mkdir($path); + _mkpath_debug("mkdir failed, second time", $path, $dir, $!); + + # mkdir failed again, try two more time after sleep(s) + sleep(1); + next if mkdir($path); + _mkpath_debug("mkdir failed, third time", $path, $dir, $!); + + sleep(1); + next if mkdir($path); + _mkpath_debug("mkdir failed, fourth time", $path, $dir, $!); + + # Report failure and die + croak("Couldn't create directory '$path' ", + " after 4 attempts and 2 sleep(1): $!"); + } +}; + + +sub copytree { + my ($from_dir, $to_dir, $use_umask) = @_; + + die "Usage: copytree(, , [])" + unless @_ == 2 or @_ == 3; + + my $orig_umask; + if ($use_umask){ + # Set new umask and remember the original + $orig_umask= umask(oct($use_umask)); + } + + mkpath("$to_dir"); + opendir(DIR, "$from_dir") + or croak("Can't find $from_dir$!"); + for(readdir(DIR)) { + + next if "$_" eq "." or "$_" eq ".."; + + # Skip SCCS/ directories + next if "$_" eq "SCCS"; + + if ( -d "$from_dir/$_" ) + { + copytree("$from_dir/$_", "$to_dir/$_"); + next; + } + copy("$from_dir/$_", "$to_dir/$_"); + } + closedir(DIR); + + if ($orig_umask){ + # Set the original umask + umask($orig_umask); + } +} + +1; diff --git a/mysql-test/lib/My/Find.pm b/mysql-test/lib/My/Find.pm new file mode 100644 index 00000000000..8557584bbc8 --- /dev/null +++ b/mysql-test/lib/My/Find.pm @@ -0,0 +1,245 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +package My::Find; + +# +# Utility functions to find files in a MySQL source or bindist +# + +use strict; +use Carp; +use My::Platform; + +use base qw(Exporter); +our @EXPORT= qw(my_find_bin my_find_dir my_find_file NOT_REQUIRED); + +our $vs_config_dir; + +my $bin_extension= ".exe" if IS_WINDOWS; + +# Helper function to be used for fourth parameter to find functions +sub NOT_REQUIRED { return 0; } + +# +# my_find_bin - find an executable with "name_1...name_n" in +# paths "path_1...path_n" and return the full path +# +# Example: +# my $mysqld_exe= my_find_bin($basedir. +# ["sql", "bin"], +# ["mysqld", "mysqld-debug"]); +# my $mysql_exe= my_find_bin($basedir, +# ["client", "bin"], +# "mysql"); +# +# +# To check if something exists, use the required parameter +# set to 0, the function will return an empty string if the +# binary is not found +# my $mysql_exe= my_find_bin($basedir, +# ["client", "bin"], +# "mysql", NOT_REQUIRED); +# +# NOTE: The function honours MTR_VS_CONFIG environment variable +# +# +sub my_find_bin { + my ($base, $paths, $names, $required)= @_; + croak "usage: my_find_bin(, , , [])" + unless @_ == 4 or @_ == 3; + + # ------------------------------------------------------- + # Find and return the first executable + # ------------------------------------------------------- + foreach my $path (my_find_paths($base, $paths, $names, $bin_extension)) { + return $path if ( -x $path or (IS_WINDOWS and -f $path) ); + } + if (defined $required and $required == NOT_REQUIRED){ + # Return empty string to indicate not found + return ""; + } + find_error($base, $paths, $names); +} + + +# +# my_find_file - find a file with "name_1...name_n" in +# paths "path_1...path_n" and return the full path +# +# Example: +# my $mysqld_exe= my_find_file($basedir. +# ["sql", "bin"], +# "filename"); +# +# +# Also supports NOT_REQUIRED flag +# +# NOTE: The function honours MTR_VS_CONFIG environment variable +# +# +sub my_find_file { + my ($base, $paths, $names, $required)= @_; + croak "usage: my_find_file(, , , [])" + unless @_ == 4 or @_ == 3; + + # ------------------------------------------------------- + # Find and return the first executable + # ------------------------------------------------------- + foreach my $path (my_find_paths($base, $paths, $names, $bin_extension)) { + return $path if ( -f $path ); + } + if (defined $required and $required == NOT_REQUIRED){ + # Return empty string to indicate not found + return ""; + } + find_error($base, $paths, $names); +} + + +# +# my_find_dir - find the first existing directory in one of +# the given paths +# +# Example: +# my $charset_set= my_find_dir($basedir, +# ["mysql/share","sql/share", "share"], +# ["charset"]); +# or +# my $charset_set= my_find_dir($basedir, +# ['client_release', 'client_debug', +# 'client', 'bin']); +# +# NOTE: The function honours MTR_VS_CONFIG environment variable +# +# +sub my_find_dir { + my ($base, $paths, $dirs, $required)= @_; + croak "usage: my_find_dir(, [, ])" + unless (@_ == 3 or @_ == 2); + + # ------------------------------------------------------- + # Find and return the first directory + # ------------------------------------------------------- + foreach my $path (my_find_paths($base, $paths, $dirs)) { + return $path if ( -d $path ); + } + find_error($base, $paths, $dirs); +} + + +sub my_find_paths { + my ($base, $paths, $names, $extension)= @_; + + # Convert the arguments into two normal arrays to ease + # further mappings + my (@names, @paths); + push(@names, ref $names eq "ARRAY" ? @$names : $names); + push(@paths, ref $paths eq "ARRAY" ? @$paths : $paths); + + #print "base: $base\n"; + #print "names: @names\n"; + #print "paths: @paths\n"; + + # User can select to look in a special build dir + # which is a subdirectory of any of the paths + my @extra_dirs; + my $build_dir= $vs_config_dir || $ENV{MTR_VS_CONFIG} || $ENV{MTR_BUILD_DIR}; + push(@extra_dirs, $build_dir) if defined $build_dir; + + if (defined $extension){ + # Append extension to names, if name does not already have extension + map { $_.=$extension unless /\.(.*)+$/ } @names; + } + + # ------------------------------------------------------- + # Windows specific + # ------------------------------------------------------- + if (IS_WINDOWS) { + # Add the default extra build dirs unless a specific one has + # already been selected + push(@extra_dirs, + ("release", + "relwithdebinfo", + "debug")) if @extra_dirs == 0; + } + + #print "extra_build_dir: @extra_dirs\n"; + + # ------------------------------------------------------- + # Build cross product of "paths * extra_build_dirs" + # ------------------------------------------------------- + push(@paths, map { my $path= $_; + map { "$path/$_" } @extra_dirs + } @paths); + #print "paths: @paths\n"; + + # ------------------------------------------------------- + # Build cross product of "paths * names" + # ------------------------------------------------------- + @paths= map { my $path= $_; + map { "$path/$_" } @names + } @paths; + #print "paths: @paths\n"; + + # ------------------------------------------------------- + # Prepend base to all paths + # ------------------------------------------------------- + @paths= map { "$base/$_" } @paths; + #print "paths: @paths\n"; + + # ------------------------------------------------------- + # Glob all paths to expand wildcards + # ------------------------------------------------------- + @paths= map { glob("$_") } @paths; + #print "paths: @paths\n"; + + # ------------------------------------------------------- + # Return the list of paths + # ------------------------------------------------------- + return @paths; +} + + +sub commify { + return + (@_ == 0) ? '' : + (@_ == 1) ? $_[0] : + (@_ == 2) ? join(" or ", @_) : + join(", ", @_[0..($#_-1)], "or $_[-1]"); + +} + + +sub fnuttify { + return map('\''.$_.'\'', @_); +} + + +sub find_error { + my ($base, $paths, $names)= @_; + + my (@names, @paths); + push(@names, ref $names eq "ARRAY" ? @$names : $names); + push(@paths, ref $paths eq "ARRAY" ? @$paths : $paths); + + croak "** ERROR: Could not find ", + commify(fnuttify(@names)), " in ", + commify(fnuttify(my_find_paths($base, $paths, $names))), "\n"; +} + +1; diff --git a/mysql-test/lib/My/Handles.pm b/mysql-test/lib/My/Handles.pm new file mode 100755 index 00000000000..66ee22b403f --- /dev/null +++ b/mysql-test/lib/My/Handles.pm @@ -0,0 +1,69 @@ +# -*- cperl -*- +# Copyright (C) 2008 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +package My::Handles; + + +use strict; +use Carp; + +use My::Platform; + +my $handle_exe; + + +if (IS_WINDOWS){ + # Check if handle.exe is available + # Pass switch to accept the EULA to avoid hanging + # if the program hasn't been run before. + my $list= `handle.exe -? -accepteula 2>&1`; + foreach my $line (split('\n', $list)) + { + $handle_exe= "$1.$2" + if ($line =~ /Handle v([0-9]*)\.([0-9]*)/); + } + if ($handle_exe){ + print "Found handle.exe version $handle_exe\n"; + } +} + + +sub show_handles +{ + my ($dir)= @_; + return unless $handle_exe; + return unless $dir; + + $dir= native_path($dir); + + # Get a list of open handles in a particular directory + my $list= `handle.exe "$dir" 2>&1` or return; + + foreach my $line (split('\n', $list)) + { + return if ($line =~ /No matching handles found/); + } + + print "\n"; + print "=" x 50, "\n"; + print "Open handles in '$dir':\n"; + print "$list\n"; + print "=" x 50, "\n\n"; + + return; +} + +1; diff --git a/mysql-test/lib/My/Options.pm b/mysql-test/lib/My/Options.pm new file mode 100644 index 00000000000..40f05c41d1c --- /dev/null +++ b/mysql-test/lib/My/Options.pm @@ -0,0 +1,199 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +package My::Options; + +# +# Utility functions to work with list of options +# + +use strict; + + +sub same($$) { + my $l1= shift; + my $l2= shift; + return compare($l1,$l2) == 0; +} + + +sub compare ($$) { + my $l1= shift; + my $l2= shift; + + my @l1= @$l1; + my @l2= @$l2; + + return -1 if @l1 < @l2; + return 1 if @l1 > @l2; + + while ( @l1 ) # Same length + { + my $e1= shift @l1; + my $e2= shift @l2; + my $cmp= ($e1 cmp $e2); + return $cmp if $cmp != 0; + } + + return 0; # They are the same +} + + +sub _split_option { + my ($option)= @_; + if ($option=~ /^--(.*)=(.*)$/){ + return ($1, $2); + } + elsif ($option=~ /^--(.*)$/){ + return ($1, undef) + } + elsif ($option=~ /^\$(.*)$/){ # $VAR + return ($1, undef) + } + elsif ($option=~ /^(.*)=(.*)$/){ + return ($1, $2) + } + elsif ($option=~ /^-O$/){ + return (undef, undef); + } + die "Unknown option format '$option'"; +} + + +sub _build_option { + my ($name, $value)= @_; + if ($name =~ /^O, /){ + return "-".$name."=".$value; + } + elsif ($value){ + return "--".$name."=".$value; + } + return "--".$name; +} + + +# +# Compare two list of options and return what would need +# to be done to get the server running with the new settings +# +sub diff { + my ($from_opts, $to_opts)= @_; + + my %from; + foreach my $from (@$from_opts) + { + my ($opt, $value)= _split_option($from); + next unless defined($opt); + $from{$opt}= $value; + } + + #print "from: ", %from, "\n"; + + my %to; + foreach my $to (@$to_opts) + { + my ($opt, $value)= _split_option($to); + next unless defined($opt); + $to{$opt}= $value; + } + + #print "to: ", %to, "\n"; + + # Remove the ones that are in both lists + foreach my $name (keys %from){ + if (exists $to{$name} and $to{$name} eq $from{$name}){ + #print "removing '$name' from both lists\n"; + delete $to{$name}; + delete $from{$name}; + } + } + + #print "from: ", %from, "\n"; + #print "to: ", %to, "\n"; + + # Add all keys in "to" to result + my @result; + foreach my $name (keys %to){ + push(@result, _build_option($name, $to{$name})); + } + + # Add all keys in "from" that are not in "to" + # to result as "set to default" + foreach my $name (keys %from){ + if (not exists $to{$name}) { + push(@result, _build_option($name, "default")); + } + } + + return @result; +} + + +sub is_set { + my ($opts, $set_opts)= @_; + + foreach my $opt (@$opts){ + + my ($opt_name1, $value1)= _split_option($opt); + + foreach my $set_opt (@$set_opts){ + my ($opt_name2, $value2)= _split_option($set_opt); + + if ($opt_name1 eq $opt_name2){ + # Option already set + return 1; + } + } + } + + return 0; +} + + +sub toSQL { + my (@options)= @_; + my @sql; + + foreach my $option (@options) { + my ($name, $value)= _split_option($option); + #print "name: $name\n"; + #print "value: $value\n"; + if ($name =~ /^O, (.*)/){ + push(@sql, "SET GLOBAL $1=$value"); + } + elsif ($name =~ /^set-variable=(.*)/){ + push(@sql, "SET GLOBAL $1=$value"); + } + else { + my $sql_name= $name; + $sql_name=~ s/-/_/g; + push(@sql, "SET GLOBAL $sql_name=$value"); + } + } + return join("; ", @sql); +} + + +sub toStr { + my $name= shift; + return "$name: ", + "['", join("', '", @_), "']\n"; +} + + +1; + diff --git a/mysql-test/lib/My/Platform.pm b/mysql-test/lib/My/Platform.pm new file mode 100644 index 00000000000..3dd5c552b10 --- /dev/null +++ b/mysql-test/lib/My/Platform.pm @@ -0,0 +1,156 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +package My::Platform; + +use strict; +use File::Basename; +use File::Path; + +use base qw(Exporter); +our @EXPORT= qw(IS_CYGWIN IS_WINDOWS IS_WIN32PERL + native_path posix_path mixed_path + check_socket_path_length process_alive); + +BEGIN { + if ($^O eq "cygwin") { + # Make sure cygpath works + if ((system("cygpath > /dev/null 2>&1") >> 8) != 1){ + die "Could not execute 'cygpath': $!"; + } + eval 'sub IS_CYGWIN { 1 }'; + } + else { + eval 'sub IS_CYGWIN { 0 }'; + } + if ($^O eq "MSWin32") { + eval 'sub IS_WIN32PERL { 1 }'; + } + else { + eval 'sub IS_WIN32PERL { 0 }'; + } +} + +BEGIN { + if (IS_CYGWIN or IS_WIN32PERL) { + eval 'sub IS_WINDOWS { 1 }'; + } + else { + eval 'sub IS_WINDOWS { 0 }'; + } +} + + +# +# native_path +# Convert from path format used by perl to the underlying +# operating systems format +# +# NOTE +# Used when running windows binaries (that expect windows paths) +# in cygwin perl (that uses unix paths) +# + +use Memoize; +if (!IS_WIN32PERL){ + memoize('mixed_path'); + memoize('native_path'); + memoize('posix_path'); +} + +sub mixed_path { + my ($path)= @_; + if (IS_CYGWIN){ + return unless defined $path; + my $cmd= "cygpath -m $path"; + $path= `$cmd` or + print "Failed to run: '$cmd', $!\n"; + chomp $path; + } + return $path; +} + +sub native_path { + my ($path)= @_; + $path=~ s/\//\\/g + if (IS_CYGWIN or IS_WIN32PERL); + return $path; +} + +sub posix_path { + my ($path)= @_; + if (IS_CYGWIN){ + return unless defined $path; + $path= `cygpath $path`; + chomp $path; + } + return $path; +} + +use File::Temp qw /tempdir/; + +sub check_socket_path_length { + my ($path)= @_; + + return 0 if IS_WINDOWS; + + require IO::Socket::UNIX; + + my $truncated= 1; # Be negative + + # Create a tempfile name with same length as "path" + my $tmpdir = tempdir( CLEANUP => 0); + my $len = length($path) - length($tmpdir); + my $testfile = $tmpdir . "x" x ($len > 0 ? $len : 1); + my $sock; + eval { + $sock= new IO::Socket::UNIX + ( + Local => $testfile, + Listen => 1, + ); + + die "Could not create UNIX domain socket: $!" + unless defined $sock; + + die "UNIX domain socket patch was truncated" + unless ($testfile eq $sock->hostpath()); + + $truncated= 0; # Yes, it worked! + + }; + #print "check_socket_path_length, failed: ", $@, '\n' if ($@); + + $sock= undef; # Close socket + unlink($testfile); # Remove the physical file + rmdir($tmpdir); # Remove the tempdir + return $truncated; +} + + +sub process_alive { + my ($pid)= @_; + die "usage: process_alive(pid)" unless $pid; + + return kill(0, $pid) unless IS_WINDOWS; + + my @list= split(/,/, `tasklist /FI "PID eq $pid" /NH /FO CSV`); + my $ret_pid= eval($list[1]); + return ($ret_pid == $pid); +} + + +1; diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm new file mode 100644 index 00000000000..0e3aa968052 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess.pm @@ -0,0 +1,580 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +package My::SafeProcess; + +# +# Class that encapsulates process creation, monitoring and cleanup +# +# Spawns a monitor process which spawns a new process locally or +# remote using subclasses My::Process::Local or My::Process::Remote etc. +# +# The monitor process runs a simple event loop more or less just +# waiting for a reason to zap the process it monitors. Thus the user +# of this class does not need to care about process cleanup, it's +# handled automatically. +# +# The monitor process wait for: +# - the parent process to close the pipe, in that case it +# will zap the "monitored process" and exit +# - the "monitored process" to exit, in which case it will exit +# itself with same exit code as the "monitored process" +# - the parent process to send the "shutdown" signal in wich case +# monitor will kill the "monitored process" hard and exit +# +# +# When used it will look something like this: +# $> ps +# [script.pl] +# - [monitor for `mysqld`] +# - [mysqld] +# - [monitor for `mysqld`] +# - [mysqld] +# - [monitor for `mysqld`] +# - [mysqld] +# +# + +use strict; +use Carp; +use POSIX qw(WNOHANG); + +use My::SafeProcess::Base; +use base 'My::SafeProcess::Base'; + +use My::Find; +use My::Platform; + +my %running; +my $_verbose= 0; + +END { + # Kill any children still running + for my $proc (values %running){ + if ( $proc->is_child($$) ){ + #print "Killing: $proc\n"; + if ($proc->wait_one(0)){ + $proc->kill(); + } + } + } +} + + +sub is_child { + my ($self, $parent_pid)= @_; + croak "usage: \$safe_proc->is_child()" unless (@_ == 2 and ref $self); + return ($self->{PARENT} == $parent_pid); +} + + +# Find the safe process binary or script +my @safe_process_cmd; +my $safe_kill; +if (IS_WIN32PERL or IS_CYGWIN){ + # Use my_safe_process.exe + my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + "my_safe_process"); + push(@safe_process_cmd, $exe); + + # Use my_safe_kill.exe + $safe_kill= my_find_bin(".", "lib/My/SafeProcess", "my_safe_kill"); +} +else +{ + # Use my_safe_process + my $exe= my_find_bin(".", ["lib/My/SafeProcess", "My/SafeProcess"], + "my_safe_process"); + push(@safe_process_cmd, $exe); +} + + +sub new { + my $class= shift; + + my %opts= + ( + verbose => 0, + @_ + ); + + my $path = delete($opts{'path'}) or croak "path required @_"; + my $args = delete($opts{'args'}) or croak "args required @_"; + my $input = delete($opts{'input'}); + my $output = delete($opts{'output'}); + my $error = delete($opts{'error'}); + my $verbose = delete($opts{'verbose'}); + my $host = delete($opts{'host'}); + my $shutdown = delete($opts{'shutdown'}); + my $user_data= delete($opts{'user_data'}); + +# if (defined $host) { +# $safe_script= "lib/My/SafeProcess/safe_process_cpcd.pl"; +# } + + if (IS_CYGWIN){ + $path= mixed_path($path); + $input= mixed_path($input); + $output= mixed_path($output); + $error= mixed_path($error); + } + + my @safe_args; + my ($safe_path, $safe_script)= @safe_process_cmd; + push(@safe_args, $safe_script) if defined $safe_script; + + push(@safe_args, "--verbose") if $verbose > 0; + + # Point the safe_process at the right parent if running on cygwin + push(@safe_args, "--parent-pid=".Cygwin::pid_to_winpid($$)) if IS_CYGWIN; + + push(@safe_args, "--"); + push(@safe_args, $path); # The program safe_process should execute + push(@safe_args, @$$args); + + print "### safe_path: ", $safe_path, " ", join(" ", @safe_args), "\n" + if $verbose > 1; + + my $pid= create_process( + path => $safe_path, + input => $input, + output => $output, + error => $error, + append => $opts{append}, + args => \@safe_args, + ); + + my $name = delete($opts{'name'}) || "SafeProcess$pid"; + my $proc= bless + ({ + SAFE_PID => $pid, + SAFE_WINPID => $pid, # Inidicates this is always a real process + SAFE_NAME => $name, + SAFE_SHUTDOWN => $shutdown, + PARENT => $$, + SAFE_USER_DATA => $user_data, + }, $class); + + # Put the new process in list of running + $running{$pid}= $proc; + return $proc; + +} + + +sub run { + my $proc= new(@_); + $proc->wait_one(); + return $proc->exit_status(); +} + +# +# Start a process that returns after "duration" seconds +# or when it's parent process does not exist anymore +# +sub timer { + my $class= shift; + my $duration= shift or croak "duration required"; + my $parent_pid= $$; + + my $pid= My::SafeProcess::Base::_safe_fork(); + if ($pid){ + # Parent + my $proc= bless + ({ + SAFE_PID => $pid, + SAFE_NAME => "timer", + PARENT => $$, + }, $class); + + # Put the new process in list of running + $running{$pid}= $proc; + return $proc; + } + + # Child, install signal handlers and sleep for "duration" + $SIG{INT}= 'IGNORE'; + + $SIG{TERM}= sub { + #print STDERR "timer $$: woken up, exiting!\n"; + exit(0); + }; + + $0= "safe_timer($duration)"; + + if (IS_WIN32PERL){ + # Just a thread in same process + sleep($duration); + print STDERR "timer $$: expired after $duration seconds\n"; + exit(0); + } + + my $count_down= $duration; + while($count_down--){ + + # Check that parent is still alive + if (kill(0, $parent_pid) == 0){ + #print STDERR "timer $$: parent gone, exiting!\n"; + exit(0); + } + + sleep(1); + } + print STDERR "timer $$: expired after $duration seconds\n"; + exit(0); +} + + +# +# Shutdown process nicely, and wait for shutdown_timeout seconds +# If processes hasn't shutdown, kill them hard and wait for return +# +sub shutdown { + my $shutdown_timeout= shift; + my @processes= @_; + _verbose("shutdown, timeout: $shutdown_timeout, @processes"); + + return if (@processes == 0); + + # Call shutdown function if process has one, else + # use kill + foreach my $proc (@processes){ + _verbose(" proc: $proc"); + my $shutdown= $proc->{SAFE_SHUTDOWN}; + if ($shutdown_timeout > 0 and defined $shutdown){ + $shutdown->(); + $proc->{WAS_SHUTDOWN}= 1; + } + else { + $proc->start_kill(); + } + } + + my @kill_processes= (); + + # Wait max shutdown_timeout seconds for those process + # that has been shutdown + foreach my $proc (@processes){ + next unless $proc->{WAS_SHUTDOWN}; + my $ret= $proc->wait_one($shutdown_timeout); + if ($ret != 0) { + push(@kill_processes, $proc); + } + # Only wait for the first process with shutdown timeout + $shutdown_timeout= 0; + } + + # Wait infinitely for those process + # that has been killed + foreach my $proc (@processes){ + next if $proc->{WAS_SHUTDOWN}; + my $ret= $proc->wait_one(undef); + if ($ret != 0) { + warn "Wait for killed process failed!"; + push(@kill_processes, $proc); + # Try one more time, best option... + } + } + + # Return if all servers has exited + return if (@kill_processes == 0); + + foreach my $proc (@kill_processes){ + $proc->start_kill(); + } + + foreach my $proc (@kill_processes){ + $proc->wait_one(undef); + } + + return; +} + + +sub _winpid ($) { + my ($pid)= @_; + + # In win32 perl, the pid is already the winpid + return $pid unless IS_CYGWIN; + + # In cygwin, the pid is the pseudo process -> + # get the real winpid of my_safe_process + return Cygwin::pid_to_winpid($pid); +} + + +# +# Tell the process to die as fast as possible +# +sub start_kill { + my ($self)= @_; + croak "usage: \$safe_proc->start_kill()" unless (@_ == 1 and ref $self); + _verbose("start_kill: $self"); + my $ret= 1; + + my $pid= $self->{SAFE_PID}; + die "INTERNAL ERROR: no pid" unless defined $pid; + + if (IS_WINDOWS and defined $self->{SAFE_WINPID}) + { + die "INTERNAL ERROR: no safe_kill" unless defined $safe_kill; + + my $winpid= _winpid($pid); + $ret= system($safe_kill, $winpid) >> 8; + + if ($ret == 3){ + print "Couldn't open the winpid: $winpid ", + "for pid: $pid, try one more time\n"; + sleep(1); + $winpid= _winpid($pid); + $ret= system($safe_kill, $winpid) >> 8; + print "Couldn't open the winpid: $winpid ", + "for pid: $pid, continue and see what happens...\n"; + } + } + else + { + $pid= $self->{SAFE_PID}; + die "Can't kill not started process" unless defined $pid; + $ret= kill("TERM", $pid); + } + + return $ret; +} + + +sub dump_core { + my ($self)= @_; + return if IS_WINDOWS; + my $pid= $self->{SAFE_PID}; + die "Can't cet core from not started process" unless defined $pid; + _verbose("Sending ABRT to $self"); + kill ("ABRT", $pid); + return 1; +} + + +# +# Kill the process as fast as possible +# and wait for it to return +# +sub kill { + my ($self)= @_; + croak "usage: \$safe_proc->kill()" unless (@_ == 1 and ref $self); + + $self->start_kill(); + $self->wait_one(); + return 1; +} + + +sub _collect { + my ($self)= @_; + + $self->{EXIT_STATUS}= $?; + _verbose("_collect: $self"); + + # Take the process out of running list + my $pid= $self->{SAFE_PID}; + die unless delete($running{$pid}); +} + + +# Wait for process to exit +# optionally with a timeout +# +# timeout +# undef -> wait blocking infinitely +# 0 -> just poll with WNOHANG +# >0 -> wait blocking for max timeout seconds +# +# RETURN VALUES +# 0 Not running +# 1 Still running +# +sub wait_one { + my ($self, $timeout)= @_; + croak "usage: \$safe_proc->wait_one([timeout])" unless ref $self; + + _verbose("wait_one $self, $timeout"); + + if ( ! defined($self->{SAFE_PID}) ) { + # No pid => not running + _verbose("No pid => not running"); + return 0; + } + + if ( defined $self->{EXIT_STATUS} ) { + # Exit status already set => not running + _verbose("Exit status already set => not running"); + return 0; + } + + my $pid= $self->{SAFE_PID}; + + my $use_alarm; + my $blocking; + if (defined $timeout) + { + if ($timeout == 0) + { + # 0 -> just poll with WNOHANG + $blocking= 0; + $use_alarm= 0; + } + else + { + # >0 -> wait blocking for max timeout seconds + $blocking= 1; + $use_alarm= 1; + } + } + else + { + # undef -> wait blocking infinitely + $blocking= 1; + $use_alarm= 0; + } + #_verbose("blocking: $blocking, use_alarm: $use_alarm"); + + my $retpid; + eval + { + # alarm should break the wait + local $SIG{ALRM}= sub { die "waitpid timeout"; }; + + alarm($timeout) if $use_alarm; + + $retpid= waitpid($pid, $blocking ? 0 : &WNOHANG); + + alarm(0) if $use_alarm; + }; + + if ($@) + { + die "Got unexpected: $@" if ($@ !~ /waitpid timeout/); + if (!defined $retpid) { + # Got timeout + _verbose("Got timeout"); + return 1; + } + # Got pid _and_ alarm, continue + _verbose("Got pid and alarm, continue"); + } + + if ( $retpid == 0 ) { + # 0 => still running + _verbose("0 => still running"); + return 1; + } + + if ( not $blocking and $retpid == -1 ) { + # still running + _verbose("still running"); + return 1; + } + + #warn "wait_one: expected pid $pid but got $retpid" + # unless( $retpid == $pid ); + + $self->_collect(); + return 0; +} + + +# +# Wait for any process to exit +# +# Returns a reference to the SafeProcess that +# exited or undefined +# +sub wait_any { + my $ret_pid; + if (IS_WIN32PERL) { + # Can't wait for -1 => use a polling loop + do { + Win32::Sleep(10); # 10 milli seconds + foreach my $pid (keys %running){ + $ret_pid= waitpid($pid, &WNOHANG); + last if $pid == $ret_pid; + } + } while ($ret_pid == 0); + } + else + { + $ret_pid= waitpid(-1, 0); + if ($ret_pid <= 0){ + # No more processes to wait for + print STDERR "wait_any, got invalid pid: $ret_pid\n"; + return undef; + } + } + + # Look it up in "running" table + my $proc= $running{$ret_pid}; + unless (defined $proc){ + print STDERR "Could not find pid: $ret_pid in running list\n"; + print STDERR "running: ". join(", ", keys(%running)). "\n"; + return undef; + } + $proc->_collect; + return $proc; +} + +# +# Overload string operator +# and fallback to default functions if no +# overloaded function is found +# +use overload + '""' => \&self2str, + fallback => 1; + + +# +# Return the process as a nicely formatted string +# +sub self2str { + my ($self)= @_; + my $pid= $self->{SAFE_PID}; + my $winpid= $self->{SAFE_WINPID}; + my $name= $self->{SAFE_NAME}; + my $exit_status= $self->{EXIT_STATUS}; + + my $str= "[$name - pid: $pid"; + $str.= ", winpid: $winpid" if defined $winpid; + $str.= ", exit: $exit_status" if defined $exit_status; + $str.= "]"; +} + +sub _verbose { + return unless $_verbose; + print STDERR " ## ", @_, "\n"; +} + + +sub pid { + my ($self)= @_; + return $self->{SAFE_PID}; +} + +sub user_data { + my ($self)= @_; + return $self->{SAFE_USER_DATA}; +} + + +1; diff --git a/mysql-test/lib/My/SafeProcess/Base.pm b/mysql-test/lib/My/SafeProcess/Base.pm new file mode 100644 index 00000000000..3fc1b1be017 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/Base.pm @@ -0,0 +1,212 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +use strict; + +package My::SafeProcess::Base; + +# +# Utility functions for Process management +# + +use Carp; +use IO::Pipe; + +use base qw(Exporter); +our @EXPORT= qw(create_process); + + + +# +# safe_fork +# Retry a couple of times if fork returns EAGAIN +# +sub _safe_fork { + my $retries= 5; + my $pid; + + FORK: + { + $pid= fork; + if ( not defined($pid)) { + + croak("fork failed after: $!") if (!$retries--); + + warn("fork failed sleep 1 second and redo: $!"); + sleep(1); + redo FORK; + } + } + + return $pid; +}; + + +# +# Decode exit status +# +sub exit_status { + my $self= shift; + my $raw= $self->{EXIT_STATUS}; + + croak("Can't call exit_status before process has died") + unless defined $raw; + + if ($raw & 127) + { + # Killed by signal + my $signal_num= $raw & 127; + my $dumped_core= $raw & 128; + return 1; # Return error code + } + else + { + # Normal process exit + return $raw >> 8; + }; +} + + +# +# Create a new process +# Return pid of the new process +# +sub create_process { + my %opts= + ( + @_ + ); + + my $path = delete($opts{'path'}) or die "path required"; + my $args = delete($opts{'args'}) or die "args required"; + my $input = delete($opts{'input'}); + my $output = delete($opts{'output'}); + my $error = delete($opts{'error'}); + + my $open_mode= $opts{append} ? ">>" : ">"; + + if ($^O eq "MSWin32"){ + + #printf STDERR "stdin %d, stdout %d, stderr %d\n", + # fileno STDIN, fileno STDOUT, fileno STDERR; + + # input output redirect + my ($oldin, $oldout, $olderr); + open $oldin, '<&', \*STDIN or die "Failed to save old stdin: $!"; + open $oldout, '>&', \*STDOUT or die "Failed to save old stdout: $!"; + open $olderr, '>&', \*STDERR or die "Failed to save old stderr: $!"; + + if ( $input ) { + if ( ! open(STDIN, "<", $input) ) { + croak("can't redirect STDIN to '$input': $!"); + } + } + + if ( $output ) { + if ( ! open(STDOUT, $open_mode, $output) ) { + croak("can't redirect STDOUT to '$output': $!"); + } + } + + if ( $error ) { + if ( $output eq $error ) { + if ( ! open(STDERR, ">&STDOUT") ) { + croak("can't dup STDOUT: $!"); + } + } + elsif ( ! open(STDERR, $open_mode, $error) ) { + croak("can't redirect STDERR to '$error': $!"); + } + } + + + # Magic use of 'system(1, @args)' to spawn a process + # and get a proper Win32 pid + unshift (@$args, $path); + my $pid= system(1, @$args); + if ( $pid == 0 ){ + print $olderr "create_process failed: $^E\n"; + die "create_process failed: $^E"; + } + + # Retore IO redirects + open STDERR, '>&', $olderr + or croak("unable to reestablish STDERR"); + open STDOUT, '>&', $oldout + or croak("unable to reestablish STDOUT"); + open STDIN, '<&', $oldin + or croak("unable to reestablish STDIN"); + #printf STDERR "stdin %d, stdout %d, stderr %d\n", + # fileno STDIN, fileno STDOUT, fileno STDERR; + return $pid; + + } + + local $SIG{PIPE}= sub { print STDERR "Got signal $@\n"; }; + my $pipe= IO::Pipe->new(); + my $pid= _safe_fork(); + if ($pid){ + # Parent + $pipe->reader(); + my $line= <$pipe>; # Wait for child to say it's ready + return $pid; + } + + $SIG{INT}= 'DEFAULT'; + + # Make this process it's own process group to be able to kill + # it and any childs(that hasn't changed group themself) + setpgrp(0,0) if $opts{setpgrp}; + + if ( $output and !open(STDOUT, $open_mode, $output) ) { + croak("can't redirect STDOUT to '$output': $!"); + } + + if ( $error ) { + if ( defined $output and $output eq $error ) { + if ( ! open(STDERR, ">&STDOUT") ) { + croak("can't dup STDOUT: $!"); + } + } + elsif ( ! open(STDERR, $open_mode, $error) ) { + croak("can't redirect STDERR to '$error': $!"); + } + } + + if ( $input ) { + if ( ! open(STDIN, "<", $input) ) { + croak("can't redirect STDIN to '$input': $!"); + } + } + + # Tell parent to continue + $pipe->writer(); + print $pipe "ready\n"; + + if ( !exec($path, @$args) ){ + croak("Failed to exec '$path': $!"); + } + + croak("Should never come here"); + +} + +1; + diff --git a/mysql-test/lib/My/SafeProcess/CMakeLists.txt b/mysql-test/lib/My/SafeProcess/CMakeLists.txt new file mode 100644 index 00000000000..97fab820f95 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +ADD_EXECUTABLE(my_safe_process safe_process_win.cc) +ADD_EXECUTABLE(my_safe_kill safe_kill_win.cc) diff --git a/mysql-test/lib/My/SafeProcess/Makefile.am b/mysql-test/lib/My/SafeProcess/Makefile.am new file mode 100644 index 00000000000..623c0e9a87a --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/Makefile.am @@ -0,0 +1,28 @@ +# Copyright (C) 2000-2006 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; version 2 of the License. +# +# 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 + +safedir = $(prefix)/mysql-test/lib/My/SafeProcess +#nobase_bin_PROGRAMS = ... +safe_PROGRAMS = my_safe_process + +my_safe_process_SOURCES = safe_process.cc + +EXTRA_DIST = safe_kill_win.cc \ + safe_process_win.cc \ + CMakeLists.txt + + +# Don't update the files from bitkeeper +%::SCCS/s.% diff --git a/mysql-test/lib/My/SafeProcess/safe_kill_win.cc b/mysql-test/lib/My/SafeProcess/safe_kill_win.cc new file mode 100755 index 00000000000..c6256fd92e1 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/safe_kill_win.cc @@ -0,0 +1,85 @@ +/* Copyright (C) 2004 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; version 2 of the License. + + 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 */ + + +/* + Utility program used to signal a safe_process it's time to shutdown + + Usage: + safe_kill +*/ + +#include +#include +#include + +int main(int argc, const char** argv ) +{ + DWORD pid= -1; + HANDLE shutdown_event; + char safe_process_name[32]= {0}; + int retry_open_event= 100; + /* Ignore any signals */ + signal(SIGINT, SIG_IGN); + signal(SIGBREAK, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + if (argc != 2) { + fprintf(stderr, "safe_kill \n"); + exit(2); + } + pid= atoi(argv[1]); + + _snprintf(safe_process_name, sizeof(safe_process_name), + "safe_process[%d]", pid); + + /* Open the event to signal */ + while ((shutdown_event= + OpenEvent(EVENT_MODIFY_STATE, FALSE, safe_process_name)) == NULL) + { + /* + Check if the process is alive, otherwise there is really + no idea to retry the open of the event + */ + HANDLE process; + if ((process= OpenProcess(SYNCHRONIZE, FALSE, pid)) == NULL) + { + fprintf(stderr, "Could not open event or process %d, error: %d\n", + pid, GetLastError()); + exit(3); + } + CloseHandle(process); + + if (retry_open_event--) + Sleep(100); + else + { + fprintf(stderr, "Failed to open shutdown_event '%s', error: %d\n", + safe_process_name, GetLastError()); + exit(3); + } + } + + if(SetEvent(shutdown_event) == 0) + { + fprintf(stderr, "Failed to signal shutdown_event '%s', error: %d\n", + safe_process_name, GetLastError()); + CloseHandle(shutdown_event); + exit(4); + } + CloseHandle(shutdown_event); + exit(0); +} + diff --git a/mysql-test/lib/My/SafeProcess/safe_process.cc b/mysql-test/lib/My/SafeProcess/safe_process.cc new file mode 100644 index 00000000000..7932f3fd2d6 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/safe_process.cc @@ -0,0 +1,277 @@ +/* Copyright (C) 2008 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; version 2 of the License. + + 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 */ + + +/* + Utility program that encapsulates process creation, monitoring + and bulletproof process cleanup + + Usage: + safe_process [options to safe_process] -- progname arg1 ... argn + + To safeguard mysqld you would invoke safe_process with a few options + for safe_process itself followed by a double dash to indicate start + of the command line for the program you really want to start + + $> safe_process --output=output.log -- mysqld --datadir=var/data1 ... + + This would redirect output to output.log and then start mysqld, + once it has done that it will continue to monitor the child as well + as the parent. + + The safe_process then checks the follwing things: + 1. Child exits, propagate the childs return code to the parent + by exiting with the same return code as the child. + + 2. Parent dies, immediately kill the child and exit, thus the + parent does not need to properly cleanup any child, it is handled + automatically. + + 3. Signal's recieced by the process will trigger same action as 2) + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int verbose= 0; +int terminated= 0; +pid_t child_pid= -1; +char safe_process_name[32]= {0}; + + +static void message(const char* fmt, ...) +{ + if (!verbose) + return; + va_list args; + fprintf(stderr, "%s: ", safe_process_name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + fflush(stderr); +} + + +static void die(const char* fmt, ...) +{ + va_list args; + fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + if (int last_err= errno) + fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err)); + exit(1); +} + + +static void kill_child (void) +{ + int status= 0; + + message("Killing child: %d", child_pid); + // Terminate whole process group + kill(-child_pid, SIGKILL); + + pid_t ret_pid= waitpid(child_pid, &status, 0); + if (ret_pid == child_pid) + { + int exit_code= 1; + if (WIFEXITED(status)) + { + // Process has exited, collect return status + exit_code= WEXITSTATUS(status); + message("Child exit: %d", exit_code); + // Exit with exit status of the child + exit(exit_code); + } + + if (WIFSIGNALED(status)) + message("Child killed by signal: %d", WTERMSIG(status)); + + exit(exit_code); + } + exit(1); +} + + +static void handle_abort (int sig) +{ + message("Got signal %d, child_pid: %d, sending ABRT", sig, child_pid); + + if (child_pid > 0) { + kill (-child_pid, SIGABRT); // Don't wait for it to terminate + } +} + + +static void handle_signal (int sig) +{ + message("Got signal %d, child_pid: %d", sig, child_pid); + terminated= 1; + + if (child_pid > 0) + kill_child(); + + // Ignore further signals + signal(SIGTERM, SIG_IGN); + signal(SIGINT, SIG_IGN); + + // Continune execution, allow the child to be started and + // finally terminated by monitor loop +} + + +int main(int argc, char* const argv[] ) +{ + char* const* child_argv= 0; + pid_t own_pid= getpid(); + pid_t parent_pid= getppid(); + + /* Install signal handlers */ + signal(SIGTERM, handle_signal); + signal(SIGINT, handle_signal); + signal(SIGCHLD, handle_signal); + signal(SIGABRT, handle_abort); + + sprintf(safe_process_name, "safe_process[%d]", own_pid); + + message("Started"); + + /* Parse arguments */ + for (int i= 1; i < argc; i++) { + const char* arg= argv[i]; + if (strcmp(arg, "--") == 0 && strlen(arg) == 2) { + /* Got the "--" delimiter */ + if (i >= argc) + die("No real args -> nothing to do"); + child_argv= &argv[i+1]; + break; + } else { + if ( strcmp(arg, "--verbose") == 0 ) + verbose++; + else if ( strncmp(arg, "--parent-pid", 10) == 0 ) + { + /* Override parent_pid with a value provided by user */ + const char* start; + if ((start= strstr(arg, "=")) == NULL) + die("Could not find start of option value in '%s'", arg); + start++; /* Step past = */ + if ((parent_pid= atoi(start)) == 0) + die("Invalid value '%s' passed to --parent-id", start); + } + else + die("Unknown option: %s", arg); + } + } + if (!child_argv || *child_argv == 0) + die("nothing to do"); + + message("parent_pid: %d", parent_pid); + if (parent_pid == own_pid) + die("parent_pid is equal to own pid!"); + + char buf; + int pfd[2]; + if (pipe(pfd) == -1) + die("Failed to create pipe"); + + /* Create the child process */ + while((child_pid= fork()) == -1) + { + message("fork failed"); + sleep(1); + } + + if (child_pid == 0) + { + close(pfd[0]); // Close unused read end + + // Use default signal handlers in child + signal(SIGTERM, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + + // Make this process it's own process group to be able to kill + // it and any childs(that hasn't changed group themself) + setpgid(0, 0); + + // Signal that child is ready + buf= 37; + write(pfd[1], &buf, 1); + // Close write end + close(pfd[1]); + + if (execvp(child_argv[0], child_argv) < 0) + die("Failed to exec child"); + } + + close(pfd[1]); // Close unused write end + + // Wait for child to signal it's ready + read(pfd[0], &buf, 1); + if(buf != 37) + die("Didn't get 37 from pipe"); + close(pfd[0]); // Close read end + + /* Monitor loop */ + message("Started child %d, terminated: %d", child_pid, terminated); + + while(!terminated) + { + // Check if parent is still alive + if (kill(parent_pid, 0) != 0){ + message("Parent is not alive anymore"); + break; + } + + // Check if child has exited, normally this will be + // detected immediately with SIGCHLD handler + int status= 0; + pid_t ret_pid= waitpid(child_pid, &status, WNOHANG); + if (ret_pid == child_pid) + { + int ret_code= 2; + if (WIFEXITED(status)) + { + // Process has exited, collect return status + int ret_code= WEXITSTATUS(status); + message("Child exit: %d", ret_code); + // Exit with exit status of the child + exit(ret_code); + } + + if (WIFSIGNALED(status)) + message("Child killed by signal: %d", WTERMSIG(status)); + + exit(ret_code); + } + sleep(1); + } + kill_child(); + + exit(1); +} + diff --git a/mysql-test/lib/My/SafeProcess/safe_process.pl b/mysql-test/lib/My/SafeProcess/safe_process.pl new file mode 100644 index 00000000000..e3114a749d3 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/safe_process.pl @@ -0,0 +1,151 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use warnings; + +use lib 'lib'; +use My::SafeProcess::Base; +use POSIX qw(WNOHANG); + +########################################################################### +# Util functions +########################################################################### + +# +#Print message to stderr +# +my $verbose= 0; +sub message { + if ($verbose > 0){ + use Time::localtime; + my $tm= localtime(); + my $timestamp= sprintf("%02d%02d%02d %2d:%02d:%02d", + $tm->year % 100, $tm->mon+1, $tm->mday, + $tm->hour, $tm->min, $tm->sec); + print STDERR $timestamp, " monitor[$$]: ", @_, "\n"; + } +} + + +########################################################################### +# Main program +########################################################################### + +my $terminated= 0; + +# Protect against being killed in the middle +# of child creation, just set the terminated flag +# to make sure the child will be killed off +# when program is ready to do that +$SIG{TERM}= sub { message("!Got signal @_"); $terminated= 1; }; +$SIG{INT}= sub { message("!Got signal @_"); $terminated= 1; }; + +my $parent_pid= getppid(); + +my $found_double_dash= 0; +while (my $arg= shift(@ARGV)){ + + if ($arg =~ /^--$/){ + $found_double_dash= 1; + last; + } + elsif ($arg =~ /^--verbose$/){ + $verbose= 1; + } + else { + die "Unknown option: $arg"; + } +} + +my $path= shift(@ARGV); # Executable + +die "usage:\n" . + " safe_process.pl [opts] -- [ [...]]" + unless defined $path || $found_double_dash; + + +message("started"); +#message("path: '$path'"); +message("parent: $parent_pid"); + +# Start process to monitor +my $child_pid= + create_process( + path => $path, + args => \@ARGV, + setpgrp => 1, + ); +message("Started child $child_pid"); + +eval { + sub handle_signal { + $terminated= 1; + message("Got signal @_"); + + # Ignore all signals + foreach my $name (keys %SIG){ + $SIG{$name}= 'IGNORE'; + } + + die "signaled\n"; + }; + local $SIG{TERM}= \&handle_signal; + local $SIG{INT}= \&handle_signal; + local $SIG{CHLD}= sub { + message("Got signal @_"); + kill(9, -$child_pid); + my $ret= waitpid($child_pid, 0); + if ($? & 127){ + exit(65); # Killed by signal + } + exit($? >> 8); + }; + + # Monitoring loop + while(!$terminated) { + + # Check if parent is still alive + if (kill(0, $parent_pid) < 1){ + message("Parent is not alive anymore"); + last; + } + + # Wait for child to terminate but wakeup every + # second to also check that parent is still alive + my $ret_pid; + $ret_pid= waitpid($child_pid, &WNOHANG); + if ($ret_pid == $child_pid) { + # Process has exited, collect return status + my $ret_code= $? >> 8; + message("Child exit: $ret_code"); + # Exit with exit status of the child + exit ($ret_code); + } + sleep(1); + } +}; +if ( $@ ) { + # The monitoring loop should have been + # broken by handle_signal + warn "Unexpected: $@" unless ( $@ =~ /signaled/ ); +} + +# Use negative pid in order to kill the whole +# process group +# +my $ret= kill(9, -$child_pid); +message("Killed child: $child_pid, ret: $ret"); +if ($ret > 0) { + message("Killed child: $child_pid"); + # Wait blocking for the child to return + my $ret_pid= waitpid($child_pid, 0); + if ($ret_pid != $child_pid){ + message("unexpected pid $ret_pid returned from waitpid($child_pid)"); + } +} + +message("DONE!"); +exit (1); + + diff --git a/mysql-test/lib/My/SafeProcess/safe_process_win.cc b/mysql-test/lib/My/SafeProcess/safe_process_win.cc new file mode 100755 index 00000000000..640254875c9 --- /dev/null +++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc @@ -0,0 +1,323 @@ +/* Copyright (C) 2004 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; version 2 of the License. + + 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 */ + + +/* + Utility program that encapsulates process creation, monitoring + and bulletproof process cleanup + + Usage: + safe_process [options to safe_process] -- progname arg1 ... argn + + To safeguard mysqld you would invoke safe_process with a few options + for safe_process itself followed by a double dash to indicate start + of the command line for the program you really want to start + + $> safe_process --output=output.log -- mysqld --datadir=var/data1 ... + + This would redirect output to output.log and then start mysqld, + once it has done that it will continue to monitor the child as well + as the parent. + + The safe_process then checks the follwing things: + 1. Child exits, propagate the childs return code to the parent + by exiting with the same return code as the child. + + 2. Parent dies, immediately kill the child and exit, thus the + parent does not need to properly cleanup any child, it is handled + automatically. + + 3. Signal's recieced by the process will trigger same action as 2) + + 4. The named event "safe_process[pid]" can be signaled and will + trigger same action as 2) + + WARNING! Be careful when using ProcessExplorer, since it will open + a handle to each process(and maybe also the Job), the process + spawned by safe_process will not be closed off when safe_process + is killed. +*/ + +/* Requires Windows 2000 or higher */ +#define _WIN32_WINNT 0x0500 + +#include +#include +#include +#include + +static int verbose= 0; +static char safe_process_name[32]= {0}; + +static void message(const char* fmt, ...) +{ + if (!verbose) + return; + va_list args; + fprintf(stderr, "%s: ", safe_process_name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + fflush(stderr); +} + + +static void die(const char* fmt, ...) +{ + va_list args; + fprintf(stderr, "%s: FATAL ERROR, ", safe_process_name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + if (int last_err= GetLastError()) + fprintf(stderr, "error: %d, %s\n", last_err, strerror(last_err)); + fflush(stderr); + exit(1); +} + + +DWORD get_parent_pid(DWORD pid) +{ + HANDLE snapshot; + DWORD parent_pid= -1; + PROCESSENTRY32 pe32; + pe32.dwSize= sizeof(PROCESSENTRY32); + + snapshot= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + die("CreateToolhelp32Snapshot failed"); + + if (!Process32First(snapshot, &pe32)) + { + CloseHandle(snapshot); + die("Process32First failed"); + } + + do + { + if (pe32.th32ProcessID == pid) + parent_pid= pe32.th32ParentProcessID; + } while(Process32Next( snapshot, &pe32)); + CloseHandle(snapshot); + + if (parent_pid == -1) + die("Could not find parent pid"); + + return parent_pid; +} + + +enum { + PARENT, + CHILD, + EVENT, + NUM_HANDLES +}; + + +HANDLE shutdown_event; +void handle_signal (int signal) +{ + message("Got signal: %d", signal); + if(SetEvent(shutdown_event) == 0) { + /* exit safe_process and (hopefully) kill off the child */ + die("Failed to SetEvent"); + } +} + + +int main(int argc, const char** argv ) +{ + char child_args[4096]= {0}; + DWORD pid= GetCurrentProcessId(); + DWORD parent_pid= get_parent_pid(pid); + HANDLE job_handle; + HANDLE wait_handles[NUM_HANDLES]= {0}; + PROCESS_INFORMATION process_info= {0}; + + sprintf(safe_process_name, "safe_process[%d]", pid); + + /* Create an event for the signal handler */ + if ((shutdown_event= + CreateEvent(NULL, TRUE, FALSE, safe_process_name)) == NULL) + die("Failed to create shutdown_event"); + wait_handles[EVENT]= shutdown_event; + + signal(SIGINT, handle_signal); + signal(SIGBREAK, handle_signal); + signal(SIGTERM, handle_signal); + + message("Started"); + + /* Parse arguments */ + for (int i= 1; i < argc; i++) { + const char* arg= argv[i]; + char* to= child_args; + if (strcmp(arg, "--") == 0 && strlen(arg) == 2) { + /* Got the "--" delimiter */ + if (i >= argc) + die("No real args -> nothing to do"); + /* Copy the remaining args to child_arg */ + for (int j= i+1; j < argc; j++) { + to+= _snprintf(to, child_args + sizeof(child_args) - to, "%s ", argv[j]); + } + break; + } else { + if ( strcmp(arg, "--verbose") == 0 ) + verbose++; + else if ( strncmp(arg, "--parent-pid", 10) == 0 ) + { + /* Override parent_pid with a value provided by user */ + const char* start; + if ((start= strstr(arg, "=")) == NULL) + die("Could not find start of option value in '%s'", arg); + start++; /* Step past = */ + if ((parent_pid= atoi(start)) == 0) + die("Invalid value '%s' passed to --parent-id", start); + } + else + die("Unknown option: %s", arg); + } + } + if (*child_args == '\0') + die("nothing to do"); + + /* Open a handle to the parent process */ + message("parent_pid: %d", parent_pid); + if (parent_pid == pid) + die("parent_pid is equal to own pid!"); + + if ((wait_handles[PARENT]= + OpenProcess(SYNCHRONIZE, FALSE, parent_pid)) == NULL) + die("Failed to open parent process with pid: %d", parent_pid); + + /* Create the child process in a job */ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 }; + STARTUPINFO si = { 0 }; + si.cb = sizeof(si); + + /* + Create the job object to make it possible to kill the process + and all of it's children in one go + */ + if ((job_handle= CreateJobObject(NULL, NULL)) == NULL) + die("CreateJobObject failed"); + + /* + Make all processes associated with the job terminate when the + last handle to the job is closed. + */ + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, + &jeli, sizeof(jeli)) == 0) + message("SetInformationJobObject failed, continue anyway..."); + +#if 0 + /* Setup stdin, stdout and stderr redirect */ + si.dwFlags= STARTF_USESTDHANDLES; + si.hStdInput= GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput= GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError= GetStdHandle(STD_ERROR_HANDLE); +#endif + + /* + Create the process suspended to make sure it's assigned to the + Job before it creates any process of it's own + + Allow the new process to break away from any job that this + process is part of so that it can be assigned to the new JobObject + we just created. This is safe since the new JobObject is created with + the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag, making sure it will be + terminated when the last handle to it is closed(which is owned by + this process). + */ + if (CreateProcess(NULL, (LPSTR)child_args, + NULL, + NULL, + TRUE, /* inherit handles */ + CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, + NULL, + NULL, + &si, + &process_info) == 0) + die("CreateProcess failed"); + + if (AssignProcessToJobObject(job_handle, process_info.hProcess) == 0) + { + TerminateProcess(process_info.hProcess, 200); + die("AssignProcessToJobObject failed"); + } + ResumeThread(process_info.hThread); + CloseHandle(process_info.hThread); + + wait_handles[CHILD]= process_info.hProcess; + + message("Started child %d", process_info.dwProcessId); + + /* Monitor loop */ + DWORD child_exit_code= 1; + DWORD wait_res= WaitForMultipleObjects(NUM_HANDLES, wait_handles, + FALSE, INFINITE); + switch (wait_res) + { + case WAIT_OBJECT_0 + PARENT: + message("Parent exit"); + break; + case WAIT_OBJECT_0 + CHILD: + if (GetExitCodeProcess(wait_handles[CHILD], &child_exit_code) == 0) + message("Child exit: could not get exit_code"); + else + message("Child exit: exit_code: %d", child_exit_code); + break; + case WAIT_OBJECT_0 + EVENT: + message("Wake up from shutdown_event"); + break; + + default: + message("Unexpected result %d from WaitForMultipleObjects", wait_res); + break; + } + message("Exiting, child: %d", process_info.dwProcessId); + + if (TerminateJobObject(job_handle, 201) == 0) + message("TerminateJobObject failed"); + CloseHandle(job_handle); + message("Job terminated and closed"); + if (wait_res != WAIT_OBJECT_0 + CHILD) + { + /* The child has not yet returned, wait for it */ + message("waiting for child to exit"); + if ((wait_res= WaitForSingleObject(wait_handles[CHILD], INFINITE)) + != WAIT_OBJECT_0) + { + message("child wait failed: %d", wait_res); + } + else + { + message("child wait succeeded"); + } + /* Child's exit code should now be 201, no need to get it */ + } + + message("Closing handles"); + for (int i= 0; i < NUM_HANDLES; i++) + CloseHandle(wait_handles[i]); + + message("Exiting, exit_code: %d", child_exit_code); + exit(child_exit_code); +} + diff --git a/mysql-test/lib/My/SysInfo.pm b/mysql-test/lib/My/SysInfo.pm new file mode 100644 index 00000000000..f1ba5fb610f --- /dev/null +++ b/mysql-test/lib/My/SysInfo.pm @@ -0,0 +1,211 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +package My::SysInfo; + +use strict; +use Carp; +use My::Platform; + +use constant DEFAULT_BOGO_MIPS => 2000; + +sub _cpuinfo { + my ($self)= @_; + + my $info_file= "/proc/cpuinfo"; + if ( !( -e $info_file and -f $info_file) ) { + return undef; + } + + my $F= IO::File->new($info_file) or return undef; + + # Set input separator to blank line + local $/ = ''; + + while ( my $cpu_chunk= <$F>) { + chomp($cpu_chunk); + + my $cpuinfo = {}; + + foreach my $cpuline ( split(/\n/, $cpu_chunk) ) { + my ( $attribute, $value ) = split(/\s*:\s*/, $cpuline); + + $attribute =~ s/\s+/_/; + $attribute = lc($attribute); + + if ( $value =~ /^(no|not available|yes)$/ ) { + $value = $value eq 'yes' ? 1 : 0; + } + + if ( $attribute eq 'flags' ) { + @{ $cpuinfo->{flags} } = split / /, $value; + } else { + $cpuinfo->{$attribute} = $value; + } + } + + # Make sure bogomips is set to some value + $cpuinfo->{bogomips} |= DEFAULT_BOGO_MIPS; + + # Cpus reported once, but with 'cpu_count' set to the actual number + my $cpu_count= $cpuinfo->{cpu_count} || 1; + for(1..$cpu_count){ + push(@{$self->{cpus}}, $cpuinfo); + } + } + $F= undef; # Close file + return $self; +} + + +sub _kstat { + my ($self)= @_; + while (1){ + my $instance_num= $self->{cpus} ? @{$self->{cpus}} : 0; + my $list= `kstat -p -m cpu_info -i $instance_num 2> /dev/null`; + my @lines= split('\n', $list) or last; # Break loop + + my $cpuinfo= {}; + foreach my $line (@lines) + { + my ($module, $instance, $name, $statistic, $value)= + $line=~ /(\w*):(\w*):(\w*):(\w*)\t(.*)/; + + $cpuinfo->{$statistic}= $value; + } + + # Default value, the actual cpu values can be used to decrease this + # on slower cpus + $cpuinfo->{bogomips}= DEFAULT_BOGO_MIPS; + + push(@{$self->{cpus}}, $cpuinfo); + } + + # At least one cpu should have been found + # if this method worked + if ( $self->{cpus} ) { + return $self; + } + return undef; +} + + +sub _unamex { + my ($self)= @_; + # TODO + return undef; +} + + +sub new { + my ($class)= @_; + + + my $self= bless { + cpus => (), + }, $class; + + my @info_methods = + ( + \&_cpuinfo, + \&_kstat, + \&_unamex, + ); + + # Detect virtual machines + my $isvm= 0; + + if (IS_WINDOWS) { + # Detect vmware service + $isvm= `tasklist` =~ /vmwareservice/i; + } + $self->{isvm}= $isvm; + + foreach my $method (@info_methods){ + if ($method->($self)){ + return $self; + } + } + + # Push a dummy cpu + push(@{$self->{cpus}}, + { + bogomips => DEFAULT_BOGO_MIPS, + model_name => "unknown", + }); + + return $self; +} + + +# Return the list of cpus found +sub cpus { + my ($self)= @_; + return @{$self->{cpus}} or + confess "INTERNAL ERROR: No cpus in list"; +} + + +# Return the number of cpus found +sub num_cpus { + my ($self)= @_; + return int(@{$self->{cpus}}) or + confess "INTERNAL ERROR: No cpus in list"; +} + + +# Return the smallest bogomips value amongst the processors +sub min_bogomips { + my ($self)= @_; + + my $bogomips; + + foreach my $cpu (@{$self->{cpus}}) { + if (!defined $bogomips or $bogomips > $cpu->{bogomips}) { + $bogomips= $cpu->{bogomips}; + } + } + + return $bogomips; +} + +sub isvm { + my ($self)= @_; + + return $self->{isvm}; +} + + +# Prit the cpuinfo +sub print_info { + my ($self)= @_; + + foreach my $cpu (@{$self->{cpus}}) { + while ((my ($key, $value)) = each(%$cpu)) { + print " ", $key, "= "; + if (ref $value eq "ARRAY") { + print "[", join(", ", @$value), "]"; + } else { + print $value; + } + print "\n"; + } + print "\n"; + } +} + +1; diff --git a/mysql-test/lib/My/Test.pm b/mysql-test/lib/My/Test.pm new file mode 100644 index 00000000000..68b100f91af --- /dev/null +++ b/mysql-test/lib/My/Test.pm @@ -0,0 +1,123 @@ +# -*- cperl -*- + + +# +# One test +# +package My::Test; + +use strict; +use warnings; +use Carp; + + +sub new { + my $class= shift; + my $self= bless { + @_, + }, $class; + return $self; +} + + +# +# Return a unique key that can be used to +# identify this test in a hash +# +sub key { + my ($self)= @_; + return $self->{key}; +} + + +sub _encode { + my ($value)= @_; + $value =~ s/([|\\\x{0a}\x{0d}])/sprintf('\%02X', ord($1))/eg; + return $value; +} + +sub _decode { + my ($value)= @_; + $value =~ s/\\([0-9a-fA-F]{2})/chr(hex($1))/ge; + return $value; +} + +sub is_failed { + my ($self)= @_; + my $result= $self->{result}; + croak "'is_failed' can't be called until test has been run!" + unless defined $result; + + return ($result eq 'MTR_RES_FAILED'); +} + + +sub write_test { + my ($test, $sock, $header)= @_; + + # Give the test a unique key before serializing it + $test->{key}= "$test" unless defined $test->{key}; + + print $sock $header, "\n"; + while ((my ($key, $value)) = each(%$test)) { + print $sock $key, "= "; + if (ref $value eq "ARRAY") { + print $sock "[", _encode(join(", ", @$value)), "]"; + } else { + print $sock _encode($value); + } + print $sock "\n"; + } + print $sock "\n"; +} + + +sub read_test { + my ($sock)= @_; + my $test= My::Test->new(); + # Read the : separated key value pairs until a + # single newline on it's own line + my $line; + while (defined($line= <$sock>)) { + # List is terminated by newline on it's own + if ($line eq "\n") { + # Correctly terminated reply + # print "Got newline\n"; + last; + } + chomp($line); + + # Split key/value on the first "=" + my ($key, $value)= split("= ", $line, 2); + + if ($value =~ /^\[(.*)\]/){ + my @values= split(", ", _decode($1)); + push(@{$test->{$key}}, @values); + } + else + { + $test->{$key}= _decode($value); + } + } + return $test; +} + + +sub print_test { + my ($self)= @_; + + print "[", $self->{name}, "]", "\n"; + while ((my ($key, $value)) = each(%$self)) { + print " ", $key, "= "; + if (ref $value eq "ARRAY") { + print "[", join(", ", @$value), "]"; + } else { + print $value; + } + print "\n"; + } + print "\n"; +} + + +1; diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm new file mode 100644 index 00000000000..23a85ef7ecc --- /dev/null +++ b/mysql-test/lib/mtr_cases.pm @@ -0,0 +1,1138 @@ +# -*- cperl -*- +# Copyright (C) 2005-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +package mtr_cases; +use strict; + +use base qw(Exporter); +our @EXPORT= qw(collect_option collect_test_cases); + +use mtr_report; +use mtr_match; + +# Options used for the collect phase +our $start_from; +our $print_testcases; +our $skip_rpl; +our $do_test; +our $skip_test; +our $opt_skip_combination; +our $binlog_format; +our $enable_disabled; +our $default_storage_engine; +our $opt_with_ndbcluster_only; +our $defaults_file; +our $defaults_extra_file; +our $reorder= 1; + +sub collect_option { + my ($opt, $value)= @_; + + # Evaluate $opt as string to use "Getopt::Long::Callback legacy API" + my $opt_name = "$opt"; + + # Convert - to _ in option name + $opt_name =~ s/-/_/g; + no strict 'refs'; + ${$opt_name}= $value; +} + +use File::Basename; +use File::Spec::Functions qw / splitdir /; +use IO::File(); +use My::Config; +use My::Platform; +use My::Test; +use My::Find; + +require "mtr_misc.pl"; + +# Precompiled regex's for tests to do or skip +my $do_test_reg; +my $skip_test_reg; + +sub init_pattern { + my ($from, $what)= @_; + return undef unless defined $from; + if ( $from =~ /^[a-z0-9\.]*$/ ) { + # Does not contain any regex (except . that we allow as + # separator betwen suite and testname), make the pattern match + # beginning of string + $from= "^$from"; + mtr_verbose("$what='$from'"); + } + # Check that pattern is a valid regex + eval { "" =~/$from/; 1 } or + mtr_error("Invalid regex '$from' passed to $what\nPerl says: $@"); + return $from; +} + + +############################################################################## +# +# Collect information about test cases to be run +# +############################################################################## + +sub collect_test_cases ($$) { + my $suites= shift; # Semicolon separated list of test suites + my $opt_cases= shift; + my $cases= []; # Array of hash(one hash for each testcase) + + $do_test_reg= init_pattern($do_test, "--do-test"); + $skip_test_reg= init_pattern($skip_test, "--skip-test"); + + foreach my $suite (split(",", $suites)) + { + push(@$cases, collect_one_suite($suite, $opt_cases)); + } + + if ( @$opt_cases ) + { + # A list of tests was specified on the command line + # Check that the tests specified was found + # in at least one suite + foreach my $test_name_spec ( @$opt_cases ) + { + my $found= 0; + my ($sname, $tname, $extension)= split_testname($test_name_spec); + foreach my $test ( @$cases ) + { + # test->{name} is always in suite.name format + if ( $test->{name} =~ /.*\.$tname/ ) + { + $found= 1; + } + } + if ( not $found ) + { + mtr_error("Could not find '$tname' in '$suites' suite(s)"); + } + } + } + + if ( $reorder ) + { + # Reorder the test cases in an order that will make them faster to run + my %sort_criteria; + + # Make a mapping of test name to a string that represents how that test + # should be sorted among the other tests. Put the most important criterion + # first, then a sub-criterion, then sub-sub-criterion, etc. + foreach my $tinfo (@$cases) + { + my @criteria = (); + + # + # Append the criteria for sorting, in order of importance. + # + push(@criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "A" : "B")); + # Group test with equal options together. + # Ending with "~" makes empty sort later than filled + my $opts= $tinfo->{'master_opt'} ? $tinfo->{'master_opt'} : []; + push(@criteria, join("!", sort @{$opts}) . "~"); + + $sort_criteria{$tinfo->{name}} = join(" ", @criteria); + } + + @$cases = sort { + $sort_criteria{$a->{'name'}} . $a->{'name'} cmp + $sort_criteria{$b->{'name'}} . $b->{'name'}; } @$cases; + + # For debugging the sort-order + # foreach my $tinfo (@$cases) + # { + # print("$sort_criteria{$tinfo->{'name'}} -> \t$tinfo->{'name'}\n"); + # } + + } + + if (defined $print_testcases){ + print_testcases(@$cases); + exit(1); + } + + return $cases; + +} + + +# Returns (suitename, testname, extension) +sub split_testname { + my ($test_name)= @_; + + # Get rid of directory part and split name on .'s + my @parts= split(/\./, basename($test_name)); + + if (@parts == 1){ + # Only testname given, ex: alias + return (undef , $parts[0], undef); + } elsif (@parts == 2) { + # Either testname.test or suite.testname given + # Ex. main.alias or alias.test + + if ($parts[1] eq "test") + { + return (undef , $parts[0], $parts[1]); + } + else + { + return ($parts[0], $parts[1], undef); + } + + } elsif (@parts == 3) { + # Fully specified suitename.testname.test + # ex main.alias.test + return ( $parts[0], $parts[1], $parts[2]); + } + + mtr_error("Illegal format of test name: $test_name"); +} + + +sub collect_one_suite($) +{ + my $suite= shift; # Test suite name + my $opt_cases= shift; + my @cases; # Array of hash + + mtr_verbose("Collecting: $suite"); + + my $suitedir= "$::glob_mysql_test_dir"; # Default + if ( $suite ne "main" ) + { + # Allow suite to be path to "some dir" if $suite has at least + # one directory part + if ( -d $suite and splitdir($suite) > 1 ){ + $suitedir= $suite; + mtr_report(" - from '$suitedir'"); + + } + else + { + $suitedir= my_find_dir($::basedir, + ["mysql-test/suite", + "mysql-test", + # Look in storage engine specific suite dirs + "storage/*/mysql-test-suites" + ], + [$suite]); + } + mtr_verbose("suitedir: $suitedir"); + } + + my $testdir= "$suitedir/t"; + my $resdir= "$suitedir/r"; + + # Check if t/ exists + if (-d $testdir){ + # t/ exists + + if ( -d $resdir ) + { + # r/exists + } + else + { + # No r/, use t/ as result dir + $resdir= $testdir; + } + + } + else { + # No t/ dir => there can' be any r/ dir + mtr_error("Can't have r/ dir without t/") if -d $resdir; + + # No t/ or r/ => use suitedir + $resdir= $testdir= $suitedir; + } + + mtr_verbose("testdir: $testdir"); + mtr_verbose("resdir: $resdir"); + + # ---------------------------------------------------------------------- + # Build a hash of disabled testcases for this suite + # ---------------------------------------------------------------------- + my %disabled; + if ( open(DISABLED, "$testdir/disabled.def" ) ) + { + while ( ) + { + chomp; + if ( /^\s*(\S+)\s*:\s*(.*?)\s*$/ ) + { + $disabled{$1}= $2; + } + } + close DISABLED; + } + + # Read suite.opt file + my $suite_opt_file= "$testdir/suite.opt"; + my $suite_opts= []; + if ( -f $suite_opt_file ) + { + $suite_opts= opts_from_file($suite_opt_file); + } + + if ( @$opt_cases ) + { + # Collect in specified order + foreach my $test_name_spec ( @$opt_cases ) + { + my ($sname, $tname, $extension)= split_testname($test_name_spec); + + # The test name parts have now been defined + #print " suite_name: $sname\n"; + #print " tname: $tname\n"; + #print " extension: $extension\n"; + + # Check cirrect suite if suitename is defined + next if (defined $sname and $suite ne $sname); + + if ( defined $extension ) + { + my $full_name= "$testdir/$tname.$extension"; + # Extension was specified, check if the test exists + if ( ! -f $full_name) + { + # This is only an error if suite was specified, otherwise it + # could exist in another suite + mtr_error("Test '$full_name' was not found in suite '$sname'") + if $sname; + + next; + } + } + else + { + # No extension was specified, use default + $extension= "test"; + my $full_name= "$testdir/$tname.$extension"; + + # Test not found here, could exist in other suite + next if ( ! -f $full_name ); + } + + push(@cases, + collect_one_test_case($suitedir, + $testdir, + $resdir, + $suite, + $tname, + "$tname.$extension", + \%disabled, + $suite_opts)); + } + } + else + { + opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); + + foreach my $elem ( sort readdir(TESTDIR) ) + { + my $tname= mtr_match_extension($elem, 'test'); + + next unless defined $tname; + + # Skip tests that does not match the --do-test= filter + next if ($do_test_reg and not $tname =~ /$do_test_reg/o); + + push(@cases, + collect_one_test_case($suitedir, + $testdir, + $resdir, + $suite, + $tname, + $elem, + \%disabled, + $suite_opts)); + } + closedir TESTDIR; + } + + # Return empty list if no testcases found + return if (@cases == 0); + + # ---------------------------------------------------------------------- + # Read combinations for this suite and build testcases x combinations + # if any combinations exists + # ---------------------------------------------------------------------- + if ( ! $opt_skip_combination ) + { + my @combinations; + my $combination_file= "$suitedir/combinations"; + #print "combination_file: $combination_file\n"; + if (@::opt_combinations) + { + # take the combination from command-line + mtr_verbose("Take the combination from command line"); + foreach my $combination (@::opt_combinations) { + my $comb= {}; + $comb->{name}= $combination; + push(@{$comb->{comb_opt}}, $combination); + push(@combinations, $comb); + } + } + elsif (-f $combination_file ) + { + # Read combinations file in my.cnf format + mtr_verbose("Read combinations file"); + my $config= My::Config->new($combination_file); + foreach my $group ($config->groups()) { + my $comb= {}; + $comb->{name}= $group->name(); + foreach my $option ( $group->options() ) { + push(@{$comb->{comb_opt}}, $option->option()); + } + push(@combinations, $comb); + } + } + + if (@combinations) + { + print " - adding combinations for $suite\n"; + #print_testcases(@cases); + + my @new_cases; + foreach my $comb (@combinations) + { + foreach my $test (@cases) + { + + next if ( $test->{'skip'} ); + + # Skip this combination if the values it provides + # already are set in master_opt or slave_opt + if (My::Options::is_set($test->{master_opt}, $comb->{comb_opt}) && + My::Options::is_set($test->{slave_opt}, $comb->{comb_opt}) ){ + next; + } + + # Copy test options + my $new_test= My::Test->new(); + while (my ($key, $value) = each(%$test)) { + if (ref $value eq "ARRAY") { + push(@{$new_test->{$key}}, @$value); + } else { + $new_test->{$key}= $value; + } + } + + # Append the combination options to master_opt and slave_opt + push(@{$new_test->{master_opt}}, @{$comb->{comb_opt}}); + push(@{$new_test->{slave_opt}}, @{$comb->{comb_opt}}); + + # Add combination name short name + $new_test->{combination}= $comb->{name}; + + # Add the new test to new test cases list + push(@new_cases, $new_test); + } + } + + # Add the plain test if it was not already added + # as part of a combination + my %added; + foreach my $new_test (@new_cases){ + $added{$new_test->{name}}= 1; + } + foreach my $test (@cases){ + push(@new_cases, $test) unless $added{$test->{name}}; + } + + + #print_testcases(@new_cases); + @cases= @new_cases; + #print_testcases(@cases); + } + } + optimize_cases(\@cases); + #print_testcases(@cases); + + return @cases; +} + + + +# +# Loop through all test cases +# - optimize which test to run by skipping unnecessary ones +# - update settings if necessary +# +sub optimize_cases { + my ($cases)= @_; + + foreach my $tinfo ( @$cases ) + { + # Skip processing if already marked as skipped + next if $tinfo->{skip}; + + # ======================================================= + # If a special binlog format was selected with + # --mysqld=--binlog-format=x, skip all test that does not + # support it + # ======================================================= + #print "binlog_format: $binlog_format\n"; + if (defined $binlog_format ) + { + # ======================================================= + # Fixed --binlog-format=x specified on command line + # ======================================================= + if ( defined $tinfo->{'binlog_formats'} ) + { + #print "binlog_formats: ". join(", ", @{$tinfo->{binlog_formats}})."\n"; + + # The test supports different binlog formats + # check if the selected one is ok + my $supported= + grep { $_ eq $binlog_format } @{$tinfo->{'binlog_formats'}}; + if ( !$supported ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= + "Doesn't support --binlog-format='$binlog_format'"; + } + } + } + else + { + # ======================================================= + # Use dynamic switching of binlog format + # ======================================================= + + # Get binlog-format used by this test from master_opt + my $test_binlog_format; + foreach my $opt ( @{$tinfo->{master_opt}} ) { + $test_binlog_format= + mtr_match_prefix($opt, "--binlog-format=") || $test_binlog_format; + } + + if (defined $test_binlog_format and + defined $tinfo->{binlog_formats} ) + { + my $supported= + grep { $_ eq $test_binlog_format } @{$tinfo->{'binlog_formats'}}; + if ( !$supported ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= + "Doesn't support --binlog-format='$test_binlog_format'"; + next; + } + } + } + + # ======================================================= + # Check that engine selected by + # --default-storage-engine= is supported + # ======================================================= + my %builtin_engines = ('myisam' => 1, 'memory' => 1); + + foreach my $opt ( @{$tinfo->{master_opt}} ) { + my $default_engine= + mtr_match_prefix($opt, "--default-storage-engine="); + + if (defined $default_engine){ + + #print " $tinfo->{name}\n"; + #print " - The test asked to use '$default_engine'\n"; + + #my $engine_value= $::mysqld_variables{$default_engine}; + #print " - The mysqld_variables says '$engine_value'\n"; + + if ( ! exists $::mysqld_variables{$default_engine} and + ! exists $builtin_engines{$default_engine} ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= + "'$default_engine' not supported"; + } + + $tinfo->{'ndb_test'}= 1 + if ( $default_engine =~ /^ndb/i ); + $tinfo->{'innodb_test'}= 1 + if ( $default_engine =~ /^innodb/i ); + } + } + } +} + + +# +# Read options from the given opt file and append them as an array +# to $tinfo->{$opt_name} +# +sub process_opts_file { + my ($tinfo, $opt_file, $opt_name)= @_; + + if ( -f $opt_file ) + { + my $opts= opts_from_file($opt_file); + + foreach my $opt ( @$opts ) + { + my $value; + + # The opt file is used both to send special options to the mysqld + # as well as pass special test case specific options to this + # script + + $value= mtr_match_prefix($opt, "--timezone="); + if ( defined $value ) + { + $tinfo->{'timezone'}= $value; + next; + } + + $value= mtr_match_prefix($opt, "--result-file="); + if ( defined $value ) + { + # Specifies the file mysqltest should compare + # output against + $tinfo->{'result_file'}= "r/$value.result"; + next; + } + + $value= mtr_match_prefix($opt, "--config-file-template="); + if ( defined $value) + { + # Specifies the configuration file to use for this test + $tinfo->{'template_path'}= dirname($tinfo->{path})."/$value"; + next; + } + + # If we set default time zone, remove the one we have + $value= mtr_match_prefix($opt, "--default-time-zone="); + if ( defined $value ) + { + # Set timezone for this test case to something different + $tinfo->{'timezone'}= "GMT-8"; + # Fallthrough, add the --default-time-zone option + } + + # The --restart option forces a restart even if no special + # option is set. If the options are the same as next testcase + # there is no need to restart after the testcase + # has completed + if ( $opt eq "--force-restart" ) + { + $tinfo->{'force_restart'}= 1; + next; + } + + # Ok, this was a real option, add it + push(@{$tinfo->{$opt_name}}, $opt); + } + } +} + +############################################################################## +# +# Collect information about a single test case +# +############################################################################## + +sub collect_one_test_case { + my $suitedir= shift; + my $testdir= shift; + my $resdir= shift; + my $suitename= shift; + my $tname= shift; + my $filename= shift; + my $disabled= shift; + my $suite_opts= shift; + + #print "collect_one_test_case\n"; + #print " suitedir: $suitedir\n"; + #print " testdir: $testdir\n"; + #print " resdir: $resdir\n"; + #print " suitename: $suitename\n"; + #print " tname: $tname\n"; + #print " filename: $filename\n"; + + # ---------------------------------------------------------------------- + # Check --start-from + # ---------------------------------------------------------------------- + if ( $start_from ) + { + # start_from can be specified as [suite.].testname_prefix + my ($suite, $test, $ext)= split_testname($start_from); + + if ( $suite and $suitename lt $suite){ + return; # Skip silently + } + if ( $tname lt $test ){ + return; # Skip silently + } + } + + # ---------------------------------------------------------------------- + # Set defaults + # ---------------------------------------------------------------------- + my $tinfo= My::Test->new + ( + name => "$suitename.$tname", + shortname => $tname, + path => "$testdir/$filename", + + ); + + my $result_file= "$resdir/$tname.result"; + if (-f $result_file) { + # Allow nonexistsing result file + # in that case .test must issue "exit" otherwise test + # should fail by default + $tinfo->{result_file}= $result_file; + } + else { + # No .result file exist + # Remember the path where it should be + # saved in case of --record + $tinfo->{record_file}= $result_file; + } + + # ---------------------------------------------------------------------- + # Skip some tests but include in list, just mark them as skipped + # ---------------------------------------------------------------------- + if ( $skip_test_reg and $tname =~ /$skip_test_reg/o ) + { + $tinfo->{'skip'}= 1; + return $tinfo; + } + + # ---------------------------------------------------------------------- + # Check for disabled tests + # ---------------------------------------------------------------------- + my $marked_as_disabled= 0; + if ( $disabled->{$tname} ) + { + # Test was marked as disabled in suites disabled.def file + $marked_as_disabled= 1; + $tinfo->{'comment'}= $disabled->{$tname}; + } + + my $disabled_file= "$testdir/$tname.disabled"; + if ( -f $disabled_file ) + { + $marked_as_disabled= 1; + $tinfo->{'comment'}= mtr_fromfile($disabled_file); + } + + if ( $marked_as_disabled ) + { + if ( $enable_disabled ) + { + # User has selected to run all disabled tests + mtr_report(" - $tinfo->{name} wil be run although it's been disabled\n", + " due to '$tinfo->{comment}'"); + } + else + { + $tinfo->{'skip'}= 1; + $tinfo->{'disable'}= 1; # Sub type of 'skip' + return $tinfo; + } + } + + # ---------------------------------------------------------------------- + # Append suite extra options to both master and slave + # ---------------------------------------------------------------------- + push(@{$tinfo->{'master_opt'}}, @$suite_opts); + push(@{$tinfo->{'slave_opt'}}, @$suite_opts); + + #----------------------------------------------------------------------- + # Check for test specific config file + #----------------------------------------------------------------------- + my $test_cnf_file= "$testdir/$tname.cnf"; + if ( -f $test_cnf_file) { + # Specifies the configuration file to use for this test + $tinfo->{'template_path'}= $test_cnf_file; + } + + # ---------------------------------------------------------------------- + # Check for test specific config file + # ---------------------------------------------------------------------- + my $test_cnf_file= "$testdir/$tname.cnf"; + if ( -f $test_cnf_file ) { + # Specifies the configuration file to use for this test + $tinfo->{'template_path'}= $test_cnf_file; + } + + # ---------------------------------------------------------------------- + # master sh + # ---------------------------------------------------------------------- + my $master_sh= "$testdir/$tname-master.sh"; + if ( -f $master_sh ) + { + if ( IS_WIN32PERL ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return $tinfo; + } + else + { + $tinfo->{'master_sh'}= $master_sh; + } + } + + # ---------------------------------------------------------------------- + # slave sh + # ---------------------------------------------------------------------- + my $slave_sh= "$testdir/$tname-slave.sh"; + if ( -f $slave_sh ) + { + if ( IS_WIN32PERL ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No tests with sh scripts on Windows"; + return $tinfo; + } + else + { + $tinfo->{'slave_sh'}= $slave_sh; + } + } + + # ---------------------------------------------------------------------- + # .slave-mi + # ---------------------------------------------------------------------- + mtr_error("$tname: slave-mi not supported anymore") + if ( -f "$testdir/$tname.slave-mi"); + + + tags_from_test_file($tinfo,"$testdir/${tname}.test"); + + if ( defined $default_storage_engine ) + { + # Different default engine is used + # tag test to require that engine + $tinfo->{'ndb_test'}= 1 + if ( $default_storage_engine =~ /^ndb/i ); + + $tinfo->{'innodb_test'}= 1 + if ( $default_storage_engine =~ /^innodb/i ); + + } + + if ( $tinfo->{'big_test'} and ! $::opt_big_test ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need 'big-test' option"; + return $tinfo + } + + if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need debug binaries"; + return $tinfo + } + + if ( $tinfo->{'ndb_test'} ) + { + # This is a NDB test + if ( $::opt_skip_ndbcluster == 2 ) + { + # Ndb is not supported, skip it + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster support"; + return $tinfo; + } + elsif ( $::opt_skip_ndbcluster ) + { + # All ndb test's should be skipped + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No ndbcluster tests(--skip-ndbcluster)"; + return $tinfo; + } + } + else + { + # This is not a ndb test + if ( $opt_with_ndbcluster_only ) + { + # Only the ndb test should be run, all other should be skipped + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Only ndbcluster tests"; + return $tinfo; + } + } + + if ($tinfo->{'federated_test'}) + { + # This is a test that need federated, enable it + push(@{$tinfo->{'master_opt'}}, "--loose-federated"); + push(@{$tinfo->{'slave_opt'}}, "--loose-federated"); + } + + if ( $tinfo->{'innodb_test'} ) + { + # This is a test that need innodb + if ( $::mysqld_variables{'innodb'} ne "TRUE" ) + { + # innodb is not supported, skip it + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No innodb support"; + return $tinfo; + } + } + else + { + push(@{$tinfo->{'master_opt'}}, "--loose-skip-innodb"); + push(@{$tinfo->{'slave_opt'}}, "--loose-skip-innodb"); + } + + if ( $tinfo->{'need_binlog'} ) + { + if (grep(/^--skip-log-bin/, @::opt_extra_mysqld_opt) ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Test need binlog"; + return $tinfo; + } + } + else + { + # Test does not need binlog, add --skip-binlog to + # the options used when starting + push(@{$tinfo->{'master_opt'}}, "--loose-skip-log-bin"); + push(@{$tinfo->{'slave_opt'}}, "--loose-skip-log-bin"); + } + + if ( $tinfo->{'rpl_test'} ) + { + if ( $skip_rpl ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "No replication tests(--skip-rpl)"; + return $tinfo; + } + } + + if ( $::opt_embedded_server ) + { + if ( $tinfo->{'not_embedded'} ) + { + $tinfo->{'skip'}= 1; + $tinfo->{'comment'}= "Not run for embedded server"; + return $tinfo; + } + } + + # ---------------------------------------------------------------------- + # Find config file to use if not already selected in .opt file + # ---------------------------------------------------------------------- + if (defined $defaults_file) { + # Using same config file for all tests + $tinfo->{template_path}= $defaults_file; + } + elsif (! $tinfo->{template_path} ) + { + my $config= "$suitedir/my.cnf"; + if (! -f $config ) + { + # assume default.cnf will be used + $config= "include/default_my.cnf"; + + # Suite has no config, autodetect which one to use + if ( $tinfo->{rpl_test} ){ + $config= "suite/rpl/my.cnf"; + if ( $tinfo->{ndb_test} ){ + $config= "suite/rpl_ndb/my.cnf"; + } + } + elsif ( $tinfo->{ndb_test} ){ + $config= "suite/ndb/my.cnf"; + } + } + $tinfo->{template_path}= $config; + } + + # Set extra config file to use + if (defined $defaults_extra_file) { + $tinfo->{extra_template_path}= $defaults_extra_file; + } + + # ---------------------------------------------------------------------- + # Append mysqld extra options to both master and slave + # ---------------------------------------------------------------------- + push(@{$tinfo->{'master_opt'}}, @::opt_extra_mysqld_opt); + push(@{$tinfo->{'slave_opt'}}, @::opt_extra_mysqld_opt); + + # ---------------------------------------------------------------------- + # Add master opts, extra options only for master + # ---------------------------------------------------------------------- + process_opts_file($tinfo, "$testdir/$tname-master.opt", 'master_opt'); + + # ---------------------------------------------------------------------- + # Add slave opts, list of extra option only for slave + # ---------------------------------------------------------------------- + process_opts_file($tinfo, "$testdir/$tname-slave.opt", 'slave_opt'); + + return $tinfo; +} + + +# List of tags in the .test files that if found should set +# the specified value in "tinfo" +my @tags= +( + ["include/have_binlog_format_row.inc", "binlog_formats", ["row"]], + ["include/have_binlog_format_statement.inc", "binlog_formats", ["statement"]], + ["include/have_binlog_format_mixed.inc", "binlog_formats", ["mixed"]], + ["include/have_binlog_format_mixed_or_row.inc", + "binlog_formats", ["mixed", "row"]], + ["include/have_binlog_format_mixed_or_statement.inc", + "binlog_formats", ["mixed", "statement"]], + ["include/have_binlog_format_row_or_statement.inc", + "binlog_formats", ["row", "statement"]], + + ["include/have_log_bin.inc", "need_binlog", 1], + + ["include/have_innodb.inc", "innodb_test", 1], + ["include/big_test.inc", "big_test", 1], + ["include/have_debug.inc", "need_debug", 1], + ["include/have_ndb.inc", "ndb_test", 1], + ["include/have_multi_ndb.inc", "ndb_test", 1], + ["include/master-slave.inc", "rpl_test", 1], + ["include/ndb_master-slave.inc", "rpl_test", 1], + ["include/ndb_master-slave.inc", "ndb_test", 1], + ["federated.inc", "federated_test", 1], + ["include/not_embedded.inc", "not_embedded", 1], +); + + +sub tags_from_test_file { + my $tinfo= shift; + my $file= shift; + #mtr_verbose("$file"); + my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!"); + + while ( my $line= <$F> ) + { + + # Skip line if it start's with # + next if ( $line =~ /^#/ ); + + # Match this line against tag in "tags" array + foreach my $tag (@tags) + { + if ( index($line, $tag->[0]) >= 0 ) + { + # Tag matched, assign value to "tinfo" + $tinfo->{"$tag->[1]"}= $tag->[2]; + } + } + + # If test sources another file, open it as well + if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ or + $line =~ /^([[:space:]]*)source(.*);$/ ) + { + my $value= $2; + $value =~ s/^\s+//; # Remove leading space + $value =~ s/[[:space:]]+$//; # Remove ending space + + # Sourced file may exist relative to test or + # in global location + foreach my $sourced_file (dirname($file). "/$value", + "$::glob_mysql_test_dir/$value") + { + if ( -f $sourced_file ) + { + # Only source the file if it exists, we may get + # false positives in the regexes above if someone + # writes "source nnnn;" in a test case(such as mysqltest.test) + tags_from_test_file($tinfo, $sourced_file); + last; + } + } + } + + } +} + +sub unspace { + my $string= shift; + my $quote= shift; + $string =~ s/[ \t]/\x11/g; + return "$quote$string$quote"; +} + + +sub opts_from_file ($) { + my $file= shift; + + open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); + my @args; + while ( ) + { + chomp; + + # --set-variable=init_connect=set @a='a\\0c' + s/^\s+//; # Remove leading space + s/\s+$//; # Remove ending space + + # This is strange, but we need to fill whitespace inside + # quotes with something, to remove later. We do this to + # be able to split on space. Else, we have trouble with + # options like + # + # --someopt="--insideopt1 --insideopt2" + # + # But still with this, we are not 100% sure it is right, + # we need a shell to do it right. + + s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; + s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; + s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; + s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; + + foreach my $arg (split(/[ \t]+/)) + { + $arg =~ tr/\x11\x0a\x0b/ \'\"/; # Put back real chars + # The outermost quotes has to go + $arg =~ s/^([^\'\"]*)\'(.*)\'([^\'\"]*)$/$1$2$3/ + or $arg =~ s/^([^\'\"]*)\"(.*)\"([^\'\"]*)$/$1$2$3/; + $arg =~ s/\\\\/\\/g; + + # Do not pass empty string since my_getopt is not capable to handle it. + if (length($arg)) { + push(@args, $arg); + } + } + } + close FILE; + return \@args; +} + +sub print_testcases { + my (@cases)= @_; + + print "=" x 60, "\n"; + foreach my $test (@cases){ + $test->print_test(); + } + print "=" x 60, "\n"; +} + + +1; diff --git a/mysql-test/lib/mtr_diff.pl b/mysql-test/lib/mtr_diff.pl deleted file mode 100644 index 26e556de5e8..00000000000 --- a/mysql-test/lib/mtr_diff.pl +++ /dev/null @@ -1,297 +0,0 @@ -# -*- cperl -*- -# Copyright (C) 2005 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; version 2 of the License. -# -# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -# This is a library file used by the Perl version of mysql-test-run, -# and is part of the translation of the Bourne shell script with the -# same name. - -#use Data::Dumper; -use strict; - -# $Data::Dumper::Indent= 1; - -sub mtr_diff($$); - -############################################################################## -# -# This is a simplified unified diff, with some special handling -# of unsorted result sets -# -############################################################################## - -# FIXME replace die with mtr_error - -#require "mtr_report.pl"; -#mtr_diff("a.txt","b.txt"); - -sub mtr_diff ($$) { - my $file1 = shift; - my $file2 = shift; - - # ---------------------------------------------------------------------- - # We read in all of the files at once - # ---------------------------------------------------------------------- - - unless ( open(FILE1, $file1) ) - { - mtr_warning("can't open \"$file1\": $!"); - return; - } - - unless ( open(FILE2, $file2) ) - { - mtr_warning("can't open \"$file2\": $!"); - return; - } - - my $lines1= collect_lines(); - my $lines2= collect_lines(); - close FILE1; - close FILE2; - -# print Dumper($lines1); -# print Dumper($lines2); - - # ---------------------------------------------------------------------- - # We compare line by line, but don't shift off elements until we know - # what to do. This way we use the "restart" method, do simple change - # and restart by entering the diff loop from the beginning again. - # ---------------------------------------------------------------------- - - my @context; - my @info; # Collect information, and output later - my $lno1= 1; - my $lno2= 1; - - while ( @$lines1 or @$lines2 ) - { - unless ( @$lines1 ) - { - push(@info, map {['+',$lno1,$lno2++,$_]} @$lines2); - last; - } - unless ( @$lines2 ) - { - push(@info, map {['-',$lno1++,$lno2,$_]} @$lines1); - last; - } - - # ---------------------------------------------------------------------- - # We know both have lines - # ---------------------------------------------------------------------- - - if ( $lines1->[0] eq $lines2->[0] ) - { - # Simple case, first line match and all is well - push(@info, ['',$lno1++,$lno2++,$lines1->[0]]); - shift @$lines1; - shift @$lines2; - next; - } - - # ---------------------------------------------------------------------- - # Now, we know they differ - # ---------------------------------------------------------------------- - - # How far in the other one, is there a match? - - my $idx2= find_next_match($lines1->[0], $lines2); - my $idx1= find_next_match($lines2->[0], $lines1); - - # Here we could test "if ( !defined $idx2 or !defined $idx1 )" and - # use a more complicated diff algorithm in the case both contains - # each others lines, just dislocated. But for this application, there - # should be no need. - - if ( !defined $idx2 ) - { - push(@info, ['-',$lno1++,$lno2,$lines1->[0]]); - shift @$lines1; - } - else - { - push(@info, ['+',$lno1,$lno2++,$lines2->[0]]); - shift @$lines2; - } - } - - # ---------------------------------------------------------------------- - # Try to output nicely - # ---------------------------------------------------------------------- - -# print Dumper(\@info); - - # We divide into "chunks" to output - # We want at least three lines of context - - my @chunks; - my @chunk; - my $state= 'pre'; # 'pre', 'in' and 'post' difference - my $post_count= 0; - - foreach my $info ( @info ) - { - if ( $info->[0] eq '' and $state eq 'pre' ) - { - # Collect no more than three lines of context before diff - push(@chunk, $info); - shift(@chunk) if @chunk > 3; - next; - } - - if ( $info->[0] =~ /(\+|\-)/ and $state =~ /(pre|in)/ ) - { - # Start/continue collecting diff - $state= 'in'; - push(@chunk, $info); - next; - } - - if ( $info->[0] eq '' and $state eq 'in' ) - { - # Stop collecting diff, and collect context after diff - $state= 'post'; - $post_count= 1; - push(@chunk, $info); - next; - } - - if ( $info->[0] eq '' and $state eq 'post' and $post_count < 6 ) - { - # We might find a new diff sequence soon, continue to collect - # non diffs but five up on 6. - $post_count++; - push(@chunk, $info); - next; - } - - if ( $info->[0] eq '' and $state eq 'post' ) - { - # We put an end to this, giving three non diff lines to - # the old chunk, and three to the new one. - my @left= splice(@chunk, -3, 3); - push(@chunks, [@chunk]); - $state= 'pre'; - $post_count= 0; - @chunk= @left; - next; - } - - if ( $info->[0] =~ /(\+|\-)/ and $state eq 'post' ) - { - # We didn't split, continue collect diff - $state= 'in'; - push(@chunk, $info); - next; - } - - } - - if ( $post_count > 3 ) - { - $post_count -= 3; - splice(@chunk, -$post_count, $post_count); - } - push(@chunks, [@chunk]) if @chunk and $state ne 'pre'; - - foreach my $chunk ( @chunks ) - { - my $from_file_start= $chunk->[0]->[1]; - my $to_file_start= $chunk->[0]->[2]; - my $from_file_offset= $chunk->[$#$chunk]->[1] - $from_file_start; - my $to_file_offset= $chunk->[$#$chunk]->[2] - $to_file_start; - print "\@\@ -$from_file_start,$from_file_offset ", - "+$to_file_start,$to_file_offset \@\@\n"; - - foreach my $info ( @$chunk ) - { - if ( $info->[0] eq '' ) - { - print " $info->[3]\n"; - } - elsif ( $info->[0] eq '-' ) - { - print "- $info->[3]\n"; - } - elsif ( $info->[0] eq '+' ) - { - print "+ $info->[3]\n"; - } - } - } - -# print Dumper(\@chunks); - -} - - -############################################################################## -# Find if the string is found in the array, return the index if found, -# if not found, return "undef" -############################################################################## - -sub find_next_match { - my $line= shift; - my $lines= shift; - - for ( my $idx= 0; $idx < @$lines; $idx++ ) - { - return $idx if $lines->[$idx] eq $line; - } - - return undef; # No match found -} - - -############################################################################## -# Just read the lines, but handle "sets" of lines that are unordered -############################################################################## - -sub collect_lines { - - my @recordset; - my @lines; - - while (@_) - { - my $line= shift @_; - chomp($line); - - if ( $line =~ /^\Q%unordered%\E\t/ ) - { - push(@recordset, $line); - } - elsif ( @recordset ) - { - push(@lines, sort @recordset); - @recordset= (); # Clear it - } - else - { - push(@lines, $line); - } - } - - if ( @recordset ) - { - push(@lines, sort @recordset); - @recordset= (); # Clear it - } - - return \@lines; -} - -1; diff --git a/mysql-test/lib/mtr_gcov.pl b/mysql-test/lib/mtr_gcov.pl index a2de1fcbdff..5049fdd6063 100644 --- a/mysql-test/lib/mtr_gcov.pl +++ b/mysql-test/lib/mtr_gcov.pl @@ -20,25 +20,14 @@ use strict; -# These are not to be prefixed with "mtr_" +sub gcov_prepare ($) { + my ($dir)= @_; -sub gcov_prepare (); -sub gcov_collect (); - -############################################################################## -# -# -# -############################################################################## - -sub gcov_prepare () { - - `find $::glob_basedir -name \*.gcov \ + `find $dir -name \*.gcov \ -or -name \*.da | xargs rm`; } -# Used by gcov -our @mysqld_src_dirs= +my @mysqld_src_dirs= ( "strings", "mysys", @@ -53,21 +42,24 @@ our @mysqld_src_dirs= "sql", ); -sub gcov_collect () { +sub gcov_collect ($$$) { + my ($dir, $gcov, $gcov_msg, $gcov_err)= @_; + + my $start_dir= cwd(); print "Collecting source coverage info...\n"; - -f $::opt_gcov_msg and unlink($::opt_gcov_msg); - -f $::opt_gcov_err and unlink($::opt_gcov_err); + -f $gcov_msg and unlink($gcov_msg); + -f $gcov_err and unlink($gcov_err); foreach my $d ( @mysqld_src_dirs ) { - chdir("$::glob_basedir/$d"); + chdir("$dir/$d"); foreach my $f ( (glob("*.h"), glob("*.cc"), glob("*.c")) ) { - `$::opt_gcov $f 2>>$::opt_gcov_err >>$::opt_gcov_msg`; + `$gcov $f 2>>$gcov_err >>$gcov_msg`; } - chdir($::glob_mysql_test_dir); + chdir($start_dir); } - print "gcov info in $::opt_gcov_msg, errors in $::opt_gcov_err\n"; + print "gcov info in $gcov_msg, errors in $gcov_err\n"; } diff --git a/mysql-test/lib/mtr_io.pl b/mysql-test/lib/mtr_io.pl index aa671c0f4f7..21581798ddc 100644 --- a/mysql-test/lib/mtr_io.pl +++ b/mysql-test/lib/mtr_io.pl @@ -19,135 +19,15 @@ # same name. use strict; +use Carp; -sub mtr_get_pid_from_file ($); -sub mtr_get_opts_from_file ($); sub mtr_fromfile ($); sub mtr_tofile ($@); sub mtr_tonewfile($@); -sub mtr_lastlinefromfile($); sub mtr_appendfile_to_file ($$); sub mtr_grab_file($); - - -############################################################################## -# -# -# -############################################################################## - -sub mtr_get_pid_from_file ($) { - my $pid_file_path= shift; - my $TOTAL_ATTEMPTS= 30; - my $timeout= 1; - - # We should read from the file until we get correct pid. As it is - # stated in BUG#21884, pid file can be empty at some moment. So, we should - # read it until we get valid data. - - for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt) - { - mtr_debug("Reading pid file '$pid_file_path' " . - "($cur_attempt of $TOTAL_ATTEMPTS)..."); - - open(FILE, '<', $pid_file_path) - or mtr_error("can't open file \"$pid_file_path\": $!"); - - # Read pid number from file - my $pid= ; - chomp $pid; - close FILE; - - return $pid if $pid=~ /^(\d+)/; - - mtr_debug("Pid file '$pid_file_path' does not yet contain pid number.\n" . - "Sleeping $timeout second(s) more..."); - - sleep($timeout); - } - - mtr_error("Pid file '$pid_file_path' is corrupted. " . - "Can not retrieve PID in " . - ($timeout * $TOTAL_ATTEMPTS) . " seconds."); -} - -sub mtr_get_opts_from_file ($) { - my $file= shift; - - open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); - my @args; - while ( ) - { - chomp; - - # --set-variable=init_connect=set @a='a\\0c' - s/^\s+//; # Remove leading space - s/\s+$//; # Remove ending space - - # This is strange, but we need to fill whitespace inside - # quotes with something, to remove later. We do this to - # be able to split on space. Else, we have trouble with - # options like - # - # --someopt="--insideopt1 --insideopt2" - # - # But still with this, we are not 100% sure it is right, - # we need a shell to do it right. - -# print STDERR "\n"; -# print STDERR "AAA: $_\n"; - - s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; - s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; - s/\'([^\'\"]*)\'/unspace($1,"\x0a")/ge; - s/\"([^\'\"]*)\"/unspace($1,"\x0b")/ge; - -# print STDERR "BBB: $_\n"; - -# foreach my $arg (/(--?\w.*?)(?=\s+--?\w|$)/) - - # FIXME ENV vars should be expanded!!!! - - foreach my $arg (split(/[ \t]+/)) - { - $arg =~ tr/\x11\x0a\x0b/ \'\"/; # Put back real chars - # The outermost quotes has to go - $arg =~ s/^([^\'\"]*)\'(.*)\'([^\'\"]*)$/$1$2$3/ - or $arg =~ s/^([^\'\"]*)\"(.*)\"([^\'\"]*)$/$1$2$3/; - $arg =~ s/\\\\/\\/g; - - $arg =~ s/\$\{(\w+)\}/envsubst($1)/ge; - $arg =~ s/\$(\w+)/envsubst($1)/ge; - -# print STDERR "ARG: $arg\n"; - # Do not pass empty string since my_getopt is not capable to handle it. - if (length($arg)) - { - push(@args, $arg) - } - } - } - close FILE; - return \@args; -} - -sub envsubst { - my $string= shift; - - if ( ! defined $ENV{$string} ) - { - mtr_error("opt file referense \$$string that is unknown"); - } - - return $ENV{$string}; -} - -sub unspace { - my $string= shift; - my $quote= shift; - $string =~ s/[ \t]/\x11/g; - return "$quote$string$quote"; -} +sub mtr_printfile($); +sub mtr_lastlinesfromfile ($$); # Read a whole file, stripping leading and trailing whitespace. sub mtr_fromfile ($) { @@ -161,19 +41,6 @@ sub mtr_fromfile ($) { return $text; } -sub mtr_lastlinefromfile ($) { - my $file= shift; - my $text; - - open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); - while (my $line= ) - { - $text= $line; - } - close FILE; - return $text; -} - sub mtr_tofile ($@) { my $file= shift; @@ -183,6 +50,7 @@ sub mtr_tofile ($@) { close FILE; } + sub mtr_tonewfile ($@) { my $file= shift; @@ -191,6 +59,7 @@ sub mtr_tonewfile ($@) { close FILE; } + sub mtr_appendfile_to_file ($$) { my $from_file= shift; my $to_file= shift; @@ -203,6 +72,7 @@ sub mtr_appendfile_to_file ($$) { close TOFILE; } + # Read a whole file verbatim. sub mtr_grab_file($) { my $file= shift; @@ -215,4 +85,26 @@ sub mtr_grab_file($) { } +# Print the file to STDOUT +sub mtr_printfile($) { + my $file= shift; + open(FILE, '<', $file) + or warn $!; + print while(); + close FILE; + return; +} + +sub mtr_lastlinesfromfile ($$) { + croak "usage: mtr_lastlinesfromfile(file,numlines)" unless (@_ == 2); + my ($file, $num_lines)= @_; + my $text; + open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!"); + my @lines= reverse ; + close FILE; + my $size= scalar(@lines); + $num_lines= $size unless ($size >= $num_lines); + return join("", reverse(splice(@lines, 0, $num_lines))); +} + 1; diff --git a/mysql-test/lib/mtr_match.pm b/mysql-test/lib/mtr_match.pm new file mode 100644 index 00000000000..40afd4e0336 --- /dev/null +++ b/mysql-test/lib/mtr_match.pm @@ -0,0 +1,97 @@ +# -*- cperl -*- +# Copyright (C) 2004-2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +package mtr_match; +use strict; + +use base qw(Exporter); +our @EXPORT= qw(mtr_match_prefix + mtr_match_extension + mtr_match_substring); + +# +# Match a prefix and return what is after the prefix +# +sub mtr_match_prefix ($$) { + my $string= shift; + my $prefix= shift; + + if ( $string =~ /^\Q$prefix\E(.*)$/ ) # strncmp + { + return $1; + } + else + { + return undef; # NULL + } +} + + +# +# Match extension and return the name without extension +# +sub mtr_match_extension ($$) { + my $file= shift; + my $ext= shift; + + if ( $file =~ /^(.*)\.\Q$ext\E$/ ) # strchr+strcmp or something + { + return $1; + } + else + { + return undef; # NULL + } +} + + +# +# Match a substring anywere in a string +# +sub mtr_match_substring ($$) { + my $string= shift; + my $substring= shift; + + if ( $string =~ /(.*)\Q$substring\E(.*)$/ ) # strncmp + { + return $1; + } + else + { + return undef; # NULL + } +} + + +sub mtr_match_any_exact ($$) { + my $string= shift; + my $mlist= shift; + + foreach my $m (@$mlist) + { + if ( $string eq $m ) + { + return 1; + } + } + return 0; +} + +1; diff --git a/mysql-test/lib/mtr_misc.pl b/mysql-test/lib/mtr_misc.pl index 0173e8b8572..658eb270535 100644 --- a/mysql-test/lib/mtr_misc.pl +++ b/mysql-test/lib/mtr_misc.pl @@ -19,45 +19,25 @@ # same name. use strict; -use File::Find; -sub mtr_native_path($); +use My::Platform; + sub mtr_init_args ($); sub mtr_add_arg ($$@); +sub mtr_args2str($@); sub mtr_path_exists(@); sub mtr_script_exists(@); sub mtr_file_exists(@); sub mtr_exe_exists(@); sub mtr_exe_maybe_exists(@); -sub mtr_copy_dir($$); -sub mtr_rmtree($); -sub mtr_same_opts($$); -sub mtr_cmp_opts($$); + ############################################################################## # -# Misc +# Args # ############################################################################## -# Convert path to OS native format -sub mtr_native_path($) -{ - my $path= shift; - - # MySQL version before 5.0 still use cygwin, no need - # to convert path - return $path - if ($::mysql_version_id < 50000); - - $path=~ s/\//\\/g - if ($::glob_win32); - return $path; -} - - -# FIXME move to own lib - sub mtr_init_args ($) { my $args = shift; $$args = []; # Empty list @@ -68,9 +48,18 @@ sub mtr_add_arg ($$@) { my $format= shift; my @fargs = @_; + # Quote args if args contain space + $format= "\"$format\"" + if (IS_WINDOWS and grep(/\s/, @fargs)); + push(@$args, sprintf($format, @fargs)); } +sub mtr_args2str($@) { + my $exe= shift or die; + return join(" ", native_path($exe), @_); +} + ############################################################################## # @@ -100,7 +89,7 @@ sub mtr_path_exists (@) { sub mtr_script_exists (@) { foreach my $path ( @_ ) { - if($::glob_win32) + if(IS_WINDOWS) { return $path if -f $path; } @@ -140,11 +129,10 @@ sub mtr_file_exists (@) { sub mtr_exe_maybe_exists (@) { my @path= @_; - map {$_.= ".exe"} @path if $::glob_win32; - map {$_.= ".nlm"} @path if $::glob_netware; + map {$_.= ".exe"} @path if IS_WINDOWS; foreach my $path ( @path ) { - if($::glob_win32) + if(IS_WINDOWS) { return $path if -f $path; } @@ -179,134 +167,11 @@ sub mtr_exe_exists (@) { } -sub mtr_copy_dir($$) { - my $from_dir= shift; - my $to_dir= shift; +sub mtr_milli_sleep { + die "usage: mtr_milli_sleep(milliseconds)" unless @_ == 1; + my ($millis)= @_; - # mtr_verbose("Copying from $from_dir to $to_dir"); - - mkpath("$to_dir"); - opendir(DIR, "$from_dir") - or mtr_error("Can't find $from_dir$!"); - for(readdir(DIR)) { - next if "$_" eq "." or "$_" eq ".."; - if ( -d "$from_dir/$_" ) - { - mtr_copy_dir("$from_dir/$_", "$to_dir/$_"); - next; - } - copy("$from_dir/$_", "$to_dir/$_"); - } - closedir(DIR); - -} - - -sub mtr_rmtree($) { - my ($dir)= @_; - mtr_verbose("mtr_rmtree: $dir"); - - # Try to use File::Path::rmtree. Recent versions - # handles removal of directories and files that don't - # have full permissions, while older versions - # may have a problem with that and we use our own version - - eval { rmtree($dir); }; - if ( $@ ) { - mtr_warning("rmtree($dir) failed, trying with File::Find..."); - - my $errors= 0; - - # chmod - find( { - no_chdir => 1, - wanted => sub { - chmod(0777, $_) - or mtr_warning("couldn't chmod(0777, $_): $!") and $errors++; - } - }, - $dir - ); - - # rm - finddepth( { - no_chdir => 1, - wanted => sub { - my $file= $_; - # Use special underscore (_) filehandle, caches stat info - if (!-l $file and -d _ ) { - rmdir($file) or - mtr_warning("couldn't rmdir($file): $!") and $errors++; - } else { - unlink($file) - or mtr_warning("couldn't unlink($file): $!") and $errors++; - } - } - }, - $dir - ); - - mtr_error("Failed to remove '$dir'") if $errors; - - mtr_report("OK, that worked!"); - } -} - - -sub mtr_same_opts ($$) { - my $l1= shift; - my $l2= shift; - return mtr_cmp_opts($l1,$l2) == 0; -} - -sub mtr_cmp_opts ($$) { - my $l1= shift; - my $l2= shift; - - my @l1= @$l1; - my @l2= @$l2; - - return -1 if @l1 < @l2; - return 1 if @l1 > @l2; - - while ( @l1 ) # Same length - { - my $e1= shift @l1; - my $e2= shift @l2; - my $cmp= ($e1 cmp $e2); - return $cmp if $cmp != 0; - } - - return 0; # They are the same -} - -# -# Compare two arrays and put all unequal elements into a new one -# -sub mtr_diff_opts ($$) { - my $l1= shift; - my $l2= shift; - my $f; - my $l= []; - foreach my $e1 (@$l1) - { - $f= undef; - foreach my $e2 (@$l2) - { - $f= 1 unless ($e1 ne $e2); - } - push(@$l, $e1) unless (defined $f); - } - foreach my $e2 (@$l2) - { - $f= undef; - foreach my $e1 (@$l1) - { - $f= 1 unless ($e1 ne $e2); - } - push(@$l, $e2) unless (defined $f); - } - return $l; + select(undef, undef, undef, ($millis/1000)); } 1; diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 8fd900330da..a99119a199d 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -18,985 +18,13 @@ # and is part of the translation of the Bourne shell script with the # same name. +use strict; use Socket; use Errno; -use strict; -use POSIX qw(WNOHANG SIGHUP); - -sub mtr_run ($$$$$$;$); -sub mtr_spawn ($$$$$$;$); -sub mtr_check_stop_servers ($); -sub mtr_kill_leftovers (); -sub mtr_wait_blocking ($); -sub mtr_record_dead_children (); -sub mtr_ndbmgm_start($$); -sub mtr_mysqladmin_start($$$); -sub mtr_exit ($); sub sleep_until_file_created ($$$); -sub mtr_kill_processes ($); -sub mtr_ping_with_timeout($); sub mtr_ping_port ($); -# Local function -sub spawn_impl ($$$$$$$); - -############################################################################## -# -# Execute an external command -# -############################################################################## - -sub mtr_run ($$$$$$;$) { - my $path= shift; - my $arg_list_t= shift; - my $input= shift; - my $output= shift; - my $error= shift; - my $pid_file= shift; # Not used - my $spawn_opts= shift; - - return spawn_impl($path,$arg_list_t,'run',$input,$output,$error, - $spawn_opts); -} - -sub mtr_run_test ($$$$$$;$) { - my $path= shift; - my $arg_list_t= shift; - my $input= shift; - my $output= shift; - my $error= shift; - my $pid_file= shift; # Not used - my $spawn_opts= shift; - - return spawn_impl($path,$arg_list_t,'test',$input,$output,$error, - $spawn_opts); -} - -sub mtr_spawn ($$$$$$;$) { - my $path= shift; - my $arg_list_t= shift; - my $input= shift; - my $output= shift; - my $error= shift; - my $pid_file= shift; # Not used - my $spawn_opts= shift; - - return spawn_impl($path,$arg_list_t,'spawn',$input,$output,$error, - $spawn_opts); -} - - - -sub spawn_impl ($$$$$$$) { - my $path= shift; - my $arg_list_t= shift; - my $mode= shift; - my $input= shift; - my $output= shift; - my $error= shift; - my $spawn_opts= shift; - - if ( $::opt_script_debug ) - { - mtr_report(""); - mtr_debug("-" x 73); - mtr_debug("STDIN $input") if $input; - mtr_debug("STDOUT $output") if $output; - mtr_debug("STDERR $error") if $error; - mtr_debug("$mode: $path ", join(" ",@$arg_list_t)); - mtr_debug("spawn options:"); - if ($spawn_opts) - { - foreach my $key (sort keys %{$spawn_opts}) - { - mtr_debug(" - $key: $spawn_opts->{$key}"); - } - } - else - { - mtr_debug(" none"); - } - mtr_debug("-" x 73); - mtr_report(""); - } - - mtr_error("Can't spawn with empty \"path\"") unless defined $path; - - - FORK: - { - my $pid= fork(); - - if ( ! defined $pid ) - { - if ( $! == $!{EAGAIN} ) # See "perldoc Errno" - { - mtr_warning("Got EAGAIN from fork(), sleep 1 second and redo"); - sleep(1); - redo FORK; - } - - mtr_error("$path ($pid) can't be forked, error: $!"); - - } - - if ( $pid ) - { - select(STDOUT) if $::glob_win32_perl; - return spawn_parent_impl($pid,$mode,$path); - } - else - { - # Child, redirect output and exec - - $SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't - - my $log_file_open_mode = '>'; - - if ($spawn_opts and $spawn_opts->{'append_log_file'}) - { - $log_file_open_mode = '>>'; - } - - if ( $output ) - { - if ( $::glob_win32_perl ) - { - # Don't redirect stdout on ActiveState perl since this is - # just another thread in the same process. - } - elsif ( ! open(STDOUT,$log_file_open_mode,$output) ) - { - mtr_child_error("can't redirect STDOUT to \"$output\": $!"); - } - } - - if ( $error ) - { - if ( !$::glob_win32_perl and $output eq $error ) - { - if ( ! open(STDERR,">&STDOUT") ) - { - mtr_child_error("can't dup STDOUT: $!"); - } - } - else - { - if ( ! open(STDERR,$log_file_open_mode,$error) ) - { - mtr_child_error("can't redirect STDERR to \"$error\": $!"); - } - } - } - - if ( $input ) - { - if ( ! open(STDIN,"<",$input) ) - { - mtr_child_error("can't redirect STDIN to \"$input\": $!"); - } - } - - if ( ! exec($path,@$arg_list_t) ) - { - mtr_child_error("failed to execute \"$path\": $!"); - } - mtr_error("Should never come here 1!"); - } - mtr_error("Should never come here 2!"); - } - mtr_error("Should never come here 3!"); -} - - -sub spawn_parent_impl { - my $pid= shift; - my $mode= shift; - my $path= shift; - - if ( $mode eq 'run' or $mode eq 'test' ) - { - if ( $mode eq 'run' ) - { - # Simple run of command, wait blocking for it to return - my $ret_pid= waitpid($pid,0); - if ( $ret_pid != $pid ) - { - # The "simple" waitpid has failed, print debug info - # and try to handle the error - mtr_warning("waitpid($pid, 0) returned $ret_pid " . - "when waiting for '$path', error: '$!'"); - if ( $ret_pid == -1 ) - { - # waitpid returned -1, that would indicate the process - # no longer exist and waitpid couldn't wait for it. - return 1; - } - mtr_error("Error handling failed"); - } - - return mtr_process_exit_status($?); - } - else - { - # We run mysqltest and wait for it to return. But we try to - # catch dying mysqld processes as well. - # - # We do blocking waitpid() until we get the return from the - # "mysqltest" call. But if a mysqld process dies that we - # started, we take this as an error, and kill mysqltest. - - - my $exit_value= -1; - my $saved_exit_value; - my $ret_pid; # What waitpid() returns - - while ( ($ret_pid= waitpid(-1,0)) != -1 ) - { - # Someone terminated, don't know who. Collect - # status info first before $? is lost, - # but not $exit_value, this is flagged from - - my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid); - if ( $timer_name ) - { - if ( $timer_name eq "suite" ) - { - # We give up here - # FIXME we should only give up the suite, not all of the run? - print STDERR "\n"; - mtr_error("Test suite timeout"); - } - elsif ( $timer_name eq "testcase" ) - { - $saved_exit_value= 63; # Mark as timeout - kill(9, $pid); # Kill mysqltest - next; # Go on and catch the termination - } - } - - if ( $ret_pid == $pid ) - { - # We got termination of mysqltest, we are done - $exit_value= mtr_process_exit_status($?); - last; - } - - # One of the child processes died, unless this was expected - # mysqltest should be killed and test aborted - - check_expected_crash_and_restart($ret_pid); - } - - if ( $ret_pid != $pid ) - { - # We terminated the waiting because a "mysqld" process died. - # Kill the mysqltest process. - mtr_verbose("Kill mysqltest because another process died"); - kill(9,$pid); - - $ret_pid= waitpid($pid,0); - - if ( $ret_pid != $pid ) - { - mtr_error("$path ($pid) got lost somehow"); - } - } - - return $saved_exit_value || $exit_value; - } - } - else - { - # We spawned a process we don't wait for - return $pid; - } -} - - -# ---------------------------------------------------------------------- -# We try to emulate how an Unix shell calculates the exit code -# ---------------------------------------------------------------------- - -sub mtr_process_exit_status { - my $raw_status= shift; - - if ( $raw_status & 127 ) - { - return ($raw_status & 127) + 128; # Signal num + 128 - } - else - { - return $raw_status >> 8; # Exit code - } -} - - -############################################################################## -# -# Kill processes left from previous runs -# -############################################################################## - - -# Kill all processes(mysqld, ndbd, ndb_mgmd and im) that would conflict with -# this run -# Make sure to remove the PID file, if any. -# kill IM manager first, else it will restart the servers -sub mtr_kill_leftovers () { - - mtr_report("Killing Possible Leftover Processes"); - mtr_debug("mtr_kill_leftovers(): started."); - - my @kill_pids; - my %admin_pids; - - foreach my $srv (@{$::master}, @{$::slave}) - { - mtr_debug(" - mysqld " . - "(pid: $srv->{pid}; " . - "pid file: '$srv->{path_pid}'; " . - "socket: '$srv->{path_sock}'; ". - "port: $srv->{port})"); - - my $pid= mtr_mysqladmin_start($srv, "shutdown", 20); - - # Save the pid of the mysqladmin process - $admin_pids{$pid}= 1; - - push(@kill_pids,{ - pid => $srv->{'pid'}, - pidfile => $srv->{'path_pid'}, - sockfile => $srv->{'path_sock'}, - port => $srv->{'port'}, - }); - $srv->{'pid'}= 0; # Assume we are done with it - } - - if ( ! $::opt_skip_ndbcluster ) - { - - foreach my $cluster (@{$::clusters}) - { - - # Don't shut down a "running" cluster - next if $cluster->{'use_running'}; - - mtr_debug(" - cluster " . - "(pid: $cluster->{pid}; " . - "pid file: '$cluster->{path_pid})"); - - my $pid= mtr_ndbmgm_start($cluster, "shutdown"); - - # Save the pid of the ndb_mgm process - $admin_pids{$pid}= 1; - - push(@kill_pids,{ - pid => $cluster->{'pid'}, - pidfile => $cluster->{'path_pid'} - }); - - $cluster->{'pid'}= 0; # Assume we are done with it - - foreach my $ndbd (@{$cluster->{'ndbds'}}) - { - mtr_debug(" - ndbd " . - "(pid: $ndbd->{pid}; " . - "pid file: '$ndbd->{path_pid})"); - - push(@kill_pids,{ - pid => $ndbd->{'pid'}, - pidfile => $ndbd->{'path_pid'}, - }); - $ndbd->{'pid'}= 0; # Assume we are done with it - } - } - } - - # Wait for all the admin processes to complete - mtr_wait_blocking(\%admin_pids); - - # If we trusted "mysqladmin --shutdown_timeout= ..." we could just - # terminate now, but we don't (FIXME should be debugged). - # So we try again to ping and at least wait the same amount of time - # mysqladmin would for all to die. - - mtr_ping_with_timeout(\@kill_pids); - - # We now have tried to terminate nice. We have waited for the listen - # port to be free, but can't really tell if the mysqld process died - # or not. We now try to find the process PID from the PID file, and - # send a kill to that process. Note that Perl let kill(0,@pids) be - # a way to just return the numer of processes the kernel can send - # signals to. So this can be used (except on Cygwin) to determine - # if there are processes left running that we cound out might exists. - # - # But still after all this work, all we know is that we have - # the ports free. - - # We scan the "var/run/" directory for other process id's to kill - - my $rundir= "$::opt_vardir/run"; - - mtr_debug("Processing PID files in directory '$rundir'..."); - - if ( -d $rundir ) - { - opendir(RUNDIR, $rundir) - or mtr_error("can't open directory \"$rundir\": $!"); - - my @pids; - - while ( my $elem= readdir(RUNDIR) ) - { - # Only read pid from files that end with .pid - if ( $elem =~ /.*[.]pid$/) - { - my $pidfile= "$rundir/$elem"; - - if ( -f $pidfile ) - { - mtr_debug("Processing PID file: '$pidfile'..."); - - my $pid= mtr_get_pid_from_file($pidfile); - - mtr_debug("Got pid: $pid from file '$pidfile'"); - - if ( $::glob_cygwin_perl or kill(0, $pid) ) - { - mtr_debug("There is process with pid $pid -- scheduling for kill."); - push(@pids, $pid); # We know (cygwin guess) it exists - } - else - { - mtr_debug("There is no process with pid $pid -- skipping."); - } - } - } - else - { - mtr_warning("Found non pid file $elem in $rundir") - if -f "$rundir/$elem"; - next; - } - } - closedir(RUNDIR); - - if ( @pids ) - { - mtr_debug("Killing the following processes with PID files: " . - join(' ', @pids) . "..."); - - start_reap_all(); - - if ( $::glob_cygwin_perl ) - { - # We have no (easy) way of knowing the Cygwin controlling - # process, in the PID file we only have the Windows process id. - system("kill -f " . join(" ",@pids)); # Hope for the best.... - mtr_debug("Sleep 5 seconds waiting for processes to die"); - sleep(5); - } - else - { - my $retries= 10; # 10 seconds - do - { - mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids)); - kill(9, @pids); - mtr_report("Sleep 1 second waiting for processes to die"); - sleep(1) # Wait one second - } while ( $retries-- and kill(0, @pids) ); - - if ( kill(0, @pids) ) # Check if some left - { - mtr_warning("can't kill process(es) " . join(" ", @pids)); - } - } - - stop_reap_all(); - } - } - else - { - mtr_debug("Directory for PID files ($rundir) does not exist."); - } - - # We may have failed everything, but we now check again if we have - # the listen ports free to use, and if they are free, just go for it. - - mtr_debug("Checking known mysqld servers..."); - - foreach my $srv ( @kill_pids ) - { - if ( defined $srv->{'port'} and mtr_ping_port($srv->{'port'}) ) - { - mtr_warning("can't kill old process holding port $srv->{'port'}"); - } - } - - mtr_debug("mtr_kill_leftovers(): finished."); -} - - -# -# Check that all processes in "spec" are shutdown gracefully -# else kill them off hard -# -sub mtr_check_stop_servers ($) { - my $spec= shift; - - # Return if no processes are defined - return if ! @$spec; - - mtr_verbose("mtr_check_stop_servers"); - - # ---------------------------------------------------------------------- - # Wait until servers in "spec" has stopped listening - # to their ports or timeout occurs - # ---------------------------------------------------------------------- - mtr_ping_with_timeout(\@$spec); - - # ---------------------------------------------------------------------- - # Use waitpid() nonblocking for a little while, to see how - # many process's will exit sucessfully. - # This is the normal case. - # ---------------------------------------------------------------------- - my $wait_counter= 50; # Max number of times to redo the loop - foreach my $srv ( @$spec ) - { - my $pid= $srv->{'pid'}; - my $ret_pid; - if ( $pid ) - { - $ret_pid= waitpid($pid,&WNOHANG); - if ($ret_pid == $pid) - { - mtr_verbose("Caught exit of process $ret_pid"); - $srv->{'pid'}= 0; - } - elsif ($ret_pid == 0) - { - mtr_verbose("Process $pid is still alive"); - if ($wait_counter-- > 0) - { - # Give the processes more time to exit - select(undef, undef, undef, (0.1)); - redo; - } - } - else - { - mtr_warning("caught exit of unknown child $ret_pid"); - } - } - } - - # ---------------------------------------------------------------------- - # The processes that haven't yet exited need to - # be killed hard, put them in "kill_pids" hash - # ---------------------------------------------------------------------- - my %kill_pids; - foreach my $srv ( @$spec ) - { - my $pid= $srv->{'pid'}; - if ( $pid ) - { - # Server is still alive, put it in list to be hard killed - if ($::glob_win32_perl) - { - # Kill the real process if it's known - $pid= $srv->{'real_pid'} if ($srv->{'real_pid'}); - } - $kill_pids{$pid}= 1; - - # Write a message to the process's error log (if it has one) - # that it's being killed hard. - if ( defined $srv->{'errfile'} ) - { - mtr_tofile($srv->{'errfile'}, "Note: Forcing kill of process $pid\n"); - } - mtr_warning("Forcing kill of process $pid"); - - } - else - { - # Server is dead, remove the pidfile if it exists - # - # Race, could have been removed between test with -f - # and the unlink() below, so better check again with -f - if ( -f $srv->{'pidfile'} and ! unlink($srv->{'pidfile'}) and - -f $srv->{'pidfile'} ) - { - mtr_error("can't remove $srv->{'pidfile'}"); - } - } - } - - if ( ! keys %kill_pids ) - { - # All processes has exited gracefully - return; - } - - mtr_kill_processes(\%kill_pids); - - # ---------------------------------------------------------------------- - # All processes are killed, cleanup leftover files - # ---------------------------------------------------------------------- - { - my $errors= 0; - foreach my $srv ( @$spec ) - { - if ( $srv->{'pid'} ) - { - # Server has been hard killed, clean it's resources - foreach my $file ($srv->{'pidfile'}, $srv->{'sockfile'}) - { - # Know it is dead so should be no race, careful anyway - if ( defined $file and -f $file and ! unlink($file) and -f $file ) - { - $errors++; - mtr_warning("couldn't delete $file"); - } - } - - if ($::glob_win32_perl and $srv->{'real_pid'}) - { - # Wait for the pseudo pid - if the real_pid was known - # the pseudo pid has not been waited for yet, wai blocking - # since it's "such a simple program" - mtr_verbose("Wait for pseudo process $srv->{'pid'}"); - my $ret_pid= waitpid($srv->{'pid'}, 0); - mtr_verbose("Pseudo process $ret_pid died"); - } - - $srv->{'pid'}= 0; - } - } - if ( $errors ) - { - # There where errors killing processes - # do one last attempt to ping the servers - # and if they can't be pinged, assume they are dead - if ( ! mtr_ping_with_timeout( \@$spec ) ) - { - mtr_error("we could not kill or clean up all processes"); - } - else - { - mtr_verbose("All ports were free, continuing"); - } - } - } -} - - -# Wait for all the process in the list to terminate -sub mtr_wait_blocking($) { - my $admin_pids= shift; - - - # Return if no processes defined - return if ! %$admin_pids; - - mtr_verbose("mtr_wait_blocking"); - - # Wait for all the started processes to exit - # As mysqladmin is such a simple program, we trust it to terminate itself. - # I.e. we wait blocking, and wait for them all before we go on. - foreach my $pid (keys %{$admin_pids}) - { - my $ret_pid= waitpid($pid,0); - - } -} - -# Start "mysqladmin " for a specific mysqld -sub mtr_mysqladmin_start($$$) { - my $srv= shift; - my $command= shift; - my $adm_shutdown_tmo= shift; - - my $args; - mtr_init_args(\$args); - - mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--user=%s", $::opt_user); - mtr_add_arg($args, "--password="); - mtr_add_arg($args, "--silent"); - if ( -e $srv->{'path_sock'} ) - { - mtr_add_arg($args, "--socket=%s", $srv->{'path_sock'}); - } - if ( $srv->{'port'} ) - { - mtr_add_arg($args, "--port=%s", $srv->{'port'}); - } - if ( $srv->{'port'} and ! -e $srv->{'path_sock'} ) - { - mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket - } - mtr_add_arg($args, "--connect_timeout=5"); - - # Shutdown time must be high as slave may be in reconnect - mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); - mtr_add_arg($args, "$command"); - my $pid= mtr_spawn($::exe_mysqladmin, $args, - "", "", "", "", - { append_log_file => 1 }); - mtr_verbose("mtr_mysqladmin_start, pid: $pid"); - return $pid; - -} - -# Start "ndb_mgm shutdown" for a specific cluster, it will -# shutdown all data nodes and leave the ndb_mgmd running -sub mtr_ndbmgm_start($$) { - my $cluster= shift; - my $command= shift; - - my $args; - - mtr_init_args(\$args); - - mtr_add_arg($args, "--no-defaults"); - mtr_add_arg($args, "--core"); - mtr_add_arg($args, "--try-reconnect=1"); - mtr_add_arg($args, "--ndb_connectstring=%s", $cluster->{'connect_string'}); - mtr_add_arg($args, "-e"); - mtr_add_arg($args, "$command"); - - my $pid= mtr_spawn($::exe_ndb_mgm, $args, - "", "/dev/null", "/dev/null", "", - {}); - mtr_verbose("mtr_ndbmgm_start, pid: $pid"); - return $pid; - -} - - -# Ping all servers in list, exit when none of them answers -# or when timeout has passed -sub mtr_ping_with_timeout($) { - my $spec= shift; - my $timeout= 200; # 20 seconds max - my $res= 1; # If we just fall through, we are done - # in the sense that the servers don't - # listen to their ports any longer - - mtr_debug("Waiting for mysqld servers to stop..."); - - TIME: - while ( $timeout-- ) - { - foreach my $srv ( @$spec ) - { - $res= 1; # We are optimistic - if ( $srv->{'pid'} and defined $srv->{'port'} ) - { - if ( mtr_ping_port($srv->{'port'}) ) - { - mtr_verbose("waiting for process $srv->{'pid'} to stop ". - "using port $srv->{'port'}"); - - # Millisceond sleep emulated with select - select(undef, undef, undef, (0.1)); - $res= 0; - next TIME; - } - else - { - # Process was not using port - } - } - } - last; # If we got here, we are done - } - - if ($res) - { - mtr_debug("mtr_ping_with_timeout(): All mysqld instances are down."); - } - else - { - mtr_report("mtr_ping_with_timeout(): At least one server is alive."); - } - - return $res; -} - - -# -# Loop through our list of processes and look for and entry -# with the provided pid -# Set the pid of that process to 0 if found -# -sub mark_process_dead($) -{ - my $ret_pid= shift; - - foreach my $mysqld (@{$::master}, @{$::slave}) - { - if ( $mysqld->{'pid'} eq $ret_pid ) - { - mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); - $mysqld->{'pid'}= 0; - return; - } - } - - foreach my $cluster (@{$::clusters}) - { - if ( $cluster->{'pid'} eq $ret_pid ) - { - mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); - $cluster->{'pid'}= 0; - return; - } - - foreach my $ndbd (@{$cluster->{'ndbds'}}) - { - if ( $ndbd->{'pid'} eq $ret_pid ) - { - mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); - $ndbd->{'pid'}= 0; - return; - } - } - } - mtr_warning("mark_process_dead couldn't find an entry for pid: $ret_pid"); - -} - -# -# Loop through our list of processes and look for and entry -# with the provided pid, if found check for the file indicating -# expected crash and restart it. -# -sub check_expected_crash_and_restart($) -{ - my $ret_pid= shift; - - foreach my $mysqld (@{$::master}, @{$::slave}) - { - if ( $mysqld->{'pid'} eq $ret_pid ) - { - mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid"); - $mysqld->{'pid'}= 0; - - # Check if crash expected and restart if it was - my $expect_file= "$::opt_vardir/tmp/" . "$mysqld->{'type'}" . - "$mysqld->{'idx'}" . ".expect"; - if ( -f $expect_file ) - { - mtr_verbose("Crash was expected, file $expect_file exists"); - mysqld_start($mysqld, $mysqld->{'start_opts'}, - $mysqld->{'start_slave_master_info'}); - unlink($expect_file); - } - - return; - } - } - - foreach my $cluster (@{$::clusters}) - { - if ( $cluster->{'pid'} eq $ret_pid ) - { - mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid"); - $cluster->{'pid'}= 0; - - # Check if crash expected and restart if it was - my $expect_file= "$::opt_vardir/tmp/ndb_mgmd_" . "$cluster->{'type'}" . - ".expect"; - if ( -f $expect_file ) - { - mtr_verbose("Crash was expected, file $expect_file exists"); - ndbmgmd_start($cluster); - unlink($expect_file); - } - return; - } - - foreach my $ndbd (@{$cluster->{'ndbds'}}) - { - if ( $ndbd->{'pid'} eq $ret_pid ) - { - mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid"); - $ndbd->{'pid'}= 0; - - # Check if crash expected and restart if it was - my $expect_file= "$::opt_vardir/tmp/ndbd_" . "$cluster->{'type'}" . - "$ndbd->{'idx'}" . ".expect"; - if ( -f $expect_file ) - { - mtr_verbose("Crash was expected, file $expect_file exists"); - ndbd_start($cluster, $ndbd->{'idx'}, - $ndbd->{'start_extra_args'}); - unlink($expect_file); - } - return; - } - } - } - - if ($::instance_manager->{'spawner_pid'} eq $ret_pid) - { - return; - } - - mtr_warning("check_expected_crash_and_restart couldn't find an entry for pid: $ret_pid"); - -} - -############################################################################## -# -# The operating system will keep information about dead children, -# we read this information here, and if we have records the process -# is alive, we mark it as dead. -# -############################################################################## - -sub mtr_record_dead_children () { - - my $process_died= 0; - my $ret_pid; - - # Wait without blockinng to see if any processes had died - # -1 or 0 means there are no more procesess to wait for - while ( ($ret_pid= waitpid(-1,&WNOHANG)) != 0 and $ret_pid != -1) - { - mtr_warning("mtr_record_dead_children: $ret_pid"); - mark_process_dead($ret_pid); - $process_died= 1; - } - return $process_died; -} - -sub start_reap_all { - # This causes terminating processes to not become zombies, avoiding - # the need for (or possibility of) explicit waitpid(). - $SIG{CHLD}= 'IGNORE'; - - # On some platforms (Linux, QNX, OSX, ...) there is potential race - # here. If a process terminated before setting $SIG{CHLD} (but after - # any attempt to waitpid() it), it will still be a zombie. So we - # have to handle any such process here. - my $pid; - while(($pid= waitpid(-1, &WNOHANG)) != 0 and $pid != -1) - { - mtr_warning("start_reap_all pid: $pid"); - mark_process_dead($pid); - }; -} - -sub stop_reap_all { - $SIG{CHLD}= 'DEFAULT'; -} - - sub mtr_ping_port ($) { my $port= shift; @@ -1041,7 +69,7 @@ sub mtr_ping_port ($) { sub sleep_until_file_created ($$$) { my $pidfile= shift; my $timeout= shift; - my $pid= shift; + my $proc= shift; my $sleeptime= 100; # Milliseconds my $loops= ($timeout * 1000) / $sleeptime; @@ -1053,9 +81,9 @@ sub sleep_until_file_created ($$$) { } # Check if it died after the fork() was successful - if ( $pid != 0 && waitpid($pid,&WNOHANG) == $pid ) + if ( defined $proc and ! $proc->wait_one(0) ) { - mtr_warning("Process $pid died"); + mtr_warning("Process $proc died"); return 0; } @@ -1070,73 +98,12 @@ sub sleep_until_file_created ($$$) { "still waiting for $left seconds..."); } - # Millisceond sleep emulated with select - select(undef, undef, undef, ($sleeptime/1000)); + mtr_milli_sleep($sleeptime); + } return 0; } -sub mtr_kill_processes ($) { - my $pids = shift; - - mtr_verbose("mtr_kill_processes (" . join(" ", keys %{$pids}) . ")"); - - foreach my $pid (keys %{$pids}) - { - - if ($pid <= 0) - { - mtr_warning("Trying to kill illegal pid: $pid"); - next; - } - - my $signaled_procs= kill(9, $pid); - if ($signaled_procs == 0) - { - # No such process existed, assume it's killed - mtr_verbose("killed $pid(no such process)"); - } - else - { - my $ret_pid= waitpid($pid,0); - if ($ret_pid == $pid) - { - mtr_verbose("killed $pid(got the pid)"); - } - elsif ($ret_pid == -1) - { - mtr_verbose("killed $pid(got -1)"); - } - } - } - mtr_verbose("done killing processes"); -} - - -############################################################################## -# -# When we exit, we kill off all children -# -############################################################################## - -sub mtr_exit ($) { - my $code= shift; - mtr_timer_stop_all($::glob_timers); - local $SIG{HUP} = 'IGNORE'; - # ToDo: Signalling -$$ will only work if we are the process group - # leader (in fact on QNX it will signal our session group leader, - # which might be Do-compile or Pushbuild, causing tests to be - # aborted). So we only do it if we are the group leader. We might - # set ourselves as the group leader at startup (with - # POSIX::setpgrp(0,0)), but then care must be needed to always do - # proper child process cleanup. - POSIX::kill(SIGHUP, -$$) if !$::glob_win32_perl and $$ == getpgrp(); - - exit($code); -} - -########################################################################### - 1; diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm new file mode 100644 index 00000000000..ce3fba87385 --- /dev/null +++ b/mysql-test/lib/mtr_report.pm @@ -0,0 +1,465 @@ +# -*- cperl -*- +# Copyright 2004-2008 MySQL AB, 2008 Sun Microsystems, Inc. +# +# 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# This is a library file used by the Perl version of mysql-test-run, +# and is part of the translation of the Bourne shell script with the +# same name. + +package mtr_report; +use strict; + +use base qw(Exporter); +our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line + mtr_print_header mtr_report mtr_report_stats + mtr_warning mtr_error mtr_debug mtr_verbose + mtr_verbose_restart mtr_report_test_passed + mtr_report_test_skipped mtr_print + mtr_report_test); + +use mtr_match; +require "mtr_io.pl"; + +my $tot_real_time= 0; + +our $timestamp= 0; +our $timediff= 0; +our $name; +our $verbose; +our $verbose_restart= 0; +our $timer= 1; + +sub report_option { + my ($opt, $value)= @_; + + # Evaluate $opt as string to use "Getopt::Long::Callback legacy API" + my $opt_name = "$opt"; + + # Convert - to _ in option name + $opt_name =~ s/-/_/g; + no strict 'refs'; + ${$opt_name}= $value; +} + +sub _name { + return $name ? $name." " : undef; +} + +sub _mtr_report_test_name ($) { + my $tinfo= shift; + my $tname= $tinfo->{name}; + + return unless defined $verbose; + + # Add combination name if any + $tname.= " '$tinfo->{combination}'" + if defined $tinfo->{combination}; + + print _name(), _timestamp(); + printf "%-40s ", $tname; +} + + +sub mtr_report_test_skipped ($) { + my ($tinfo)= @_; + $tinfo->{'result'}= 'MTR_RES_SKIPPED'; + + mtr_report_test($tinfo); +} + + +sub mtr_report_test_passed ($) { + my ($tinfo)= @_; + + # Save the timer value + my $timer_str= ""; + if ( $timer and -f "$::opt_vardir/log/timer" ) + { + $timer_str= mtr_fromfile("$::opt_vardir/log/timer"); + $tinfo->{timer}= $timer_str; + } + + # Big warning if status already set + if ( $tinfo->{'result'} ){ + mtr_warning("mtr_report_test_passed: Test result", + "already set to '", $tinfo->{'result'}, ","); + } + + $tinfo->{'result'}= 'MTR_RES_PASSED'; + + mtr_report_test($tinfo); +} + + +sub mtr_report_test ($) { + my ($tinfo)= @_; + _mtr_report_test_name($tinfo); + + my $comment= $tinfo->{'comment'}; + my $logfile= $tinfo->{'logfile'}; + my $warnings= $tinfo->{'warnings'}; + my $result= $tinfo->{'result'}; + + if ($result eq 'MTR_RES_FAILED'){ + + my $timest = format_time(); + + if ( $warnings ) + { + mtr_report("[ fail ] Found warnings/errors in server log file!"); + mtr_report(" Test ended at $timest"); + mtr_report($warnings); + return; + } + my $timeout= $tinfo->{'timeout'}; + if ( $timeout ) + { + mtr_report("[ fail ] timeout after $timeout seconds"); + mtr_report(" Test ended at $timest"); + mtr_report("\n$tinfo->{'comment'}"); + return; + } + else + { + mtr_report("[ fail ]\n Test ended at $timest"); + } + + if ( $logfile ) + { + # Test failure was detected by test tool and its report + # about what failed has been saved to file. Display the report. + mtr_report("\n$logfile\n"); + } + if ( $comment ) + { + # The test failure has been detected by mysql-test-run.pl + # when starting the servers or due to other error, the reason for + # failing the test is saved in "comment" + mtr_report("\n$comment\n"); + } + + if ( !$logfile and !$comment ) + { + # Neither this script or the test tool has recorded info + # about why the test has failed. Should be debugged. + mtr_report("\nUnknown result, neither 'comment' or 'logfile' set"); + } + } + elsif ($result eq 'MTR_RES_SKIPPED') + { + if ( $tinfo->{'disable'} ) + { + mtr_report("[ disabled ] $comment"); + } + elsif ( $comment ) + { + mtr_report("[ skipped ] $comment"); + } + else + { + mtr_report("[ skipped ]"); + } + } + elsif ($result eq 'MTR_RES_PASSED') + { + my $timer_str= $tinfo->{timer} || ""; + $tot_real_time += ($timer_str/1000); + mtr_report("[ pass ] ", sprintf("%5s", $timer_str)); + + # Show any problems check-testcase found + if ( defined $tinfo->{'check'} ) + { + mtr_report($tinfo->{'check'}); + } + } +} + + +sub mtr_report_stats ($) { + my $tests= shift; + + # ---------------------------------------------------------------------- + # Find out how we where doing + # ---------------------------------------------------------------------- + + my $tot_skiped= 0; + my $tot_passed= 0; + my $tot_failed= 0; + my $tot_tests= 0; + my $tot_restarts= 0; + my $found_problems= 0; + + foreach my $tinfo (@$tests) + { + if ( $tinfo->{failures} ) + { + # Test has failed at least one time + $tot_tests++; + $tot_failed++; + } + elsif ( $tinfo->{'result'} eq 'MTR_RES_SKIPPED' ) + { + # Test was skipped + $tot_skiped++; + } + elsif ( $tinfo->{'result'} eq 'MTR_RES_PASSED' ) + { + # Test passed + $tot_tests++; + $tot_passed++; + } + + if ( $tinfo->{'restarted'} ) + { + # Servers was restarted + $tot_restarts++; + } + + # Look for warnings produced by mysqltest + my $base_file= mtr_match_extension($tinfo->{'result_file'}, + "result"); # Trim extension + my $warning_file= "$base_file.warnings"; + if ( -f $warning_file ) + { + $found_problems= 1; + mtr_warning("Check myqltest warnings in '$warning_file'"); + } + } + + # ---------------------------------------------------------------------- + # Print out a summary report to screen + # ---------------------------------------------------------------------- + print "The servers were restarted $tot_restarts times\n"; + + if ( $timer ) + { + use English; + + mtr_report("Spent", sprintf("%.3f", $tot_real_time),"of", + time - $BASETIME, "seconds executing testcases"); + } + + + my $warnlog= "$::opt_vardir/log/warnings"; + if ( -f $warnlog ) + { + mtr_warning("Got errors/warnings while running tests, please examine", + "'$warnlog' for details."); + } + + print "\n"; + + # Print a list of check_testcases that failed(if any) + if ( $::opt_check_testcases ) + { + my %check_testcases; + + foreach my $tinfo (@$tests) + { + if ( defined $tinfo->{'check_testcase_failed'} ) + { + $check_testcases{$tinfo->{'name'}}= 1; + } + } + + if ( keys %check_testcases ) + { + print "Check of testcase failed for: "; + print join(" ", keys %check_testcases); + print "\n\n"; + } + } + + # Print a list of testcases that failed + if ( $tot_failed != 0 ) + { + + # Print each failed test, again + #foreach my $test ( @$tests ){ + # if ( $test->{failures} ) { + # mtr_report_test($test); + # } + #} + + my $ratio= $tot_passed * 100 / $tot_tests; + print "Failed $tot_failed/$tot_tests tests, "; + printf("%.2f", $ratio); + print "\% were successful.\n\n"; + + # Print the list of test that failed in a format + # that can be copy pasted to rerun only failing tests + print "Failing test(s):"; + + my %seen= (); + foreach my $tinfo (@$tests) + { + my $tname= $tinfo->{'name'}; + if ( $tinfo->{failures} and ! $seen{$tname}) + { + print " $tname"; + $seen{$tname}= 1; + } + } + print "\n\n"; + + # Print info about reporting the error + print + "The log files in var/log may give you some hint of what went wrong.\n\n", + "If you want to report this error, please read first ", + "the documentation\n", + "at http://dev.mysql.com/doc/mysql/en/mysql-test-suite.html\n\n"; + + } + else + { + print "All $tot_tests tests were successful.\n\n"; + } + + if ( $tot_failed != 0 || $found_problems) + { + mtr_error("there were failing test cases"); + } +} + + +############################################################################## +# +# Text formatting +# +############################################################################## + +sub mtr_print_line () { + print '-' x 60, "\n"; +} + + +sub mtr_print_thick_line { + my $char= shift || '='; + print $char x 78, "\n"; +} + + +sub mtr_print_header () { + print "\n"; + printf "TEST"; + print " " x 38; + print "RESULT "; + print "TIME (ms)" if $timer; + print "\n"; + mtr_print_line(); + print "\n"; +} + + +############################################################################## +# +# Log and reporting functions +# +############################################################################## + +use Time::localtime; + +use Time::HiRes qw(gettimeofday); + +sub format_time { + my $tm= localtime(); + return sprintf("%4d-%02d-%02d %02d:%02d:%02d", + $tm->year + 1900, $tm->mon+1, $tm->mday, + $tm->hour, $tm->min, $tm->sec); +} + +my $t0= gettimeofday(); + +sub _timestamp { + return "" unless $timestamp; + + my $diff; + if ($timediff){ + my $t1= gettimeofday(); + my $elapsed= $t1 - $t0; + + $diff= sprintf(" +%02.3f", $elapsed); + + # Save current time for next lap + $t0= $t1; + + } + + my $tm= localtime(); + return sprintf("%02d%02d%02d %2d:%02d:%02d%s ", + $tm->year % 100, $tm->mon+1, $tm->mday, + $tm->hour, $tm->min, $tm->sec, $diff); +} + +# Always print message to screen +sub mtr_print (@) { + print _name(), join(" ", @_), "\n"; +} + + +# Print message to screen if verbose is defined +sub mtr_report (@) { + if (defined $verbose) + { + print _name(), join(" ", @_), "\n"; + } +} + + +# Print warning to screen +sub mtr_warning (@) { + print STDERR _name(), _timestamp(), + "mysql-test-run: WARNING: ", join(" ", @_), "\n"; +} + + +# Print error to screen and then exit +sub mtr_error (@) { + print STDERR _name(), _timestamp(), + "mysql-test-run: *** ERROR: ", join(" ", @_), "\n"; + exit(1); +} + + +sub mtr_debug (@) { + if ( $verbose > 2 ) + { + print STDERR _name(), + _timestamp(), "####: ", join(" ", @_), "\n"; + } +} + + +sub mtr_verbose (@) { + if ( $verbose ) + { + print STDERR _name(), _timestamp(), + "> ",join(" ", @_),"\n"; + } +} + + +sub mtr_verbose_restart (@) { + my ($server, @args)= @_; + my $proc= $server->{proc}; + if ( $verbose_restart ) + { + print STDERR _name(),_timestamp(), + "> Restart $proc - ",join(" ", @args),"\n"; + } +} + + +1; diff --git a/mysql-test/lib/mtr_stress.pl b/mysql-test/lib/mtr_stress.pl index 93b06b32c5f..cd5c7b0dbb7 100644 --- a/mysql-test/lib/mtr_stress.pl +++ b/mysql-test/lib/mtr_stress.pl @@ -135,7 +135,7 @@ sub run_stress_test () } mtr_init_args(\$args); - + mtr_add_args($args, "$::glob_mysql_test_dir/mysql-stress-test.pl"); mtr_add_arg($args, "--server-socket=%s", $::master->[0]->{'path_sock'}); mtr_add_arg($args, "--server-user=%s", $::opt_user); mtr_add_arg($args, "--server-database=%s", "test"); @@ -181,7 +181,13 @@ sub run_stress_test () } #Run stress test - mtr_run("$::glob_mysql_test_dir/mysql-stress-test.pl", $args, "", "", "", ""); + My::SafeProcess->run + ( + name => "stress test", + path => $^X, + args => \$args, + ); + if ( ! $::glob_use_embedded_server ) { stop_all_servers(); diff --git a/mysql-test/lib/mtr_unique.pm b/mysql-test/lib/mtr_unique.pm new file mode 100644 index 00000000000..2ac172883a2 --- /dev/null +++ b/mysql-test/lib/mtr_unique.pm @@ -0,0 +1,195 @@ +# -*- cperl -*- +# Copyright (C) 2006 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; version 2 of the License. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +package mtr_unique; + +use strict; +use Fcntl ':flock'; + +use base qw(Exporter); +our @EXPORT= qw(mtr_get_unique_id mtr_release_unique_id); + +use My::Platform; + +sub msg { + # print "### unique($$) - ", join(" ", @_), "\n"; +} + +my $file; + +if(!IS_WINDOWS) +{ + $file= "/tmp/mysql-test-ports"; +} +else +{ + $file= $ENV{'TEMP'}."/mysql-test-ports"; +} + + +my %mtr_unique_ids; + +END { + my $allocated_id= $mtr_unique_ids{$$}; + if (defined $allocated_id) + { + mtr_release_unique_id($allocated_id); + } + delete $mtr_unique_ids{$$}; +} + +# +# Get a unique, numerical ID, given a file name (where all +# requested IDs are stored), a minimum and a maximum value. +# +# If no unique ID within the specified parameters can be +# obtained, return undef. +# +sub mtr_get_unique_id($$) { + my ($min, $max)= @_;; + + msg("get, '$file', $min-$max"); + + die "Can only get one unique id per process!" if $mtr_unique_ids{$$}; + + my $ret = undef; + my $changed = 0; + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + + chmod 0777, "$file.sem"; + open SEM, ">", "$file.sem" or die "can't write to $file.sem"; + flock SEM, LOCK_EX or die "can't lock $file.sem"; + if(! -e $file) { + open FILE, ">", $file or die "can't create $file"; + close FILE; + } + + msg("HAVE THE LOCK"); + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + + chmod 0777, $file; + open FILE, "+<", $file or die "can't open $file"; + #select undef,undef,undef,0.2; + seek FILE, 0, 0; + my %taken = (); + while() { + chomp; + my ($id, $pid) = split / /; + $taken{$id} = $pid; + msg("taken: $id, $pid"); + # Check if process with given pid is alive + if(!process_alive($pid)) { + print "Removing slot $id used by missing process $pid\n"; + msg("Removing slot $id used by missing process $pid"); + delete $taken{$id}; + $changed++; + } + } + for(my $i=$min; $i<=$max; ++$i) { + if(! exists $taken{$i}) { + $ret = $i; + $taken{$i} = $$; + $changed++; + # Remember the id this process got + $mtr_unique_ids{$$}= $i; + msg(" got $i"); + last; + } + } + if($changed) { + seek FILE, 0, 0; + truncate FILE, 0 or die "can't truncate $file"; + for my $k (keys %taken) { + print FILE $k . ' ' . $taken{$k} . "\n"; + } + } + close FILE; + + msg("RELEASING THE LOCK"); + flock SEM, LOCK_UN or warn "can't unlock $file.sem"; + close SEM; + + return $ret; +} + + +# +# Release a unique ID. +# +sub mtr_release_unique_id($) { + my ($myid)= @_; + + msg("release, $myid"); + + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + + open SEM, ">", "$file.sem" or die "can't write to $file.sem"; + flock SEM, LOCK_EX or die "can't lock $file.sem"; + + msg("HAVE THE LOCK"); + + if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { + die 'lock file is a symbolic link'; + } + + if(! -e $file) { + open FILE, ">", $file or die "can't create $file"; + close FILE; + } + open FILE, "+<", $file or die "can't open $file"; + #select undef,undef,undef,0.2; + seek FILE, 0, 0; + my %taken = (); + while() { + chomp; + my ($id, $pid) = split / /; + msg(" taken, $id $pid"); + $taken{$id} = $pid; + } + + if ($taken{$myid} != $$) + { + msg(" The unique id for this process does not match pid"); + } + + + msg(" removing $myid"); + delete $taken{$myid}; + seek FILE, 0, 0; + truncate FILE, 0 or die "can't truncate $file"; + for my $k (keys %taken) { + print FILE $k . ' ' . $taken{$k} . "\n"; + } + close FILE; + + msg("RELEASE THE LOCK"); + + flock SEM, LOCK_UN or warn "can't unlock $file.sem"; + close SEM; +} + + +1; + diff --git a/mysql-test/lib/t/Base.t b/mysql-test/lib/t/Base.t new file mode 100644 index 00000000000..6ca7657d421 --- /dev/null +++ b/mysql-test/lib/t/Base.t @@ -0,0 +1,27 @@ +# -*- cperl -*- +use Test::More qw(no_plan); +use strict; + +use_ok ("My::SafeProcess::Base"); + + +my $count= 0; +for (1..100){ + my $pid= My::SafeProcess::Base::_safe_fork(); + exit unless $pid; + (waitpid($pid, 0) == $pid) and $count++; +} +ok($count == 100, "safe_fork"); + +# A nice little forkbomb +SKIP: { + skip("forkbomb", 1); + eval { + while(1){ + my $pid= My::SafeProcess::Base::_safe_fork(); + exit unless $pid; + } + }; + ok($@, "forkbomb"); +} + diff --git a/mysql-test/lib/t/Find.t b/mysql-test/lib/t/Find.t new file mode 100644 index 00000000000..90489ba06dd --- /dev/null +++ b/mysql-test/lib/t/Find.t @@ -0,0 +1,33 @@ +# -*- cperl -*- +use Test::More qw(no_plan); +use strict; + +use_ok ("My::Find"); +my $basedir= "../.."; + +print "=" x 40, "\n"; +my $mysqld_exe= my_find_bin($basedir, + ["sql", "bin"], + ["mysqld", "mysqld-debug"]); +print "mysqld_exe: $mysqld_exe\n"; +print "=" x 40, "\n"; +my $mysql_exe= my_find_bin($basedir, + ["client", "bin"], + "mysql"); +print "mysql_exe: $mysql_exe\n"; +print "=" x 40, "\n"; + +my $mtr_build_dir= $ENV{MTR_BUILD_DIR}; +$ENV{MTR_BUILD_DIR}= "debug"; +my $mysql_exe= my_find_bin($basedir, + ["client", "bin"], + "mysql"); +print "mysql_exe: $mysql_exe\n"; +$ENV{MTR_BUILD_DIR}= $mtr_build_dir; +print "=" x 40, "\n"; + +my $charset_dir= my_find_dir($basedir, + ["share/mysql", "sql/share", "share"], + "charsets"); +print "charset_dir: $charset_dir\n"; +print "=" x 40, "\n"; diff --git a/mysql-test/lib/t/Options.t b/mysql-test/lib/t/Options.t new file mode 100644 index 00000000000..7012f3da8de --- /dev/null +++ b/mysql-test/lib/t/Options.t @@ -0,0 +1,127 @@ + +# -*- cperl -*- +use Test::More qw(no_plan); +use strict; + +use_ok("My::Options"); + +my @tests= +( + [ + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=ms'], + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=statement'], + ['--binlog-format=statement'] + ], + + [ + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=statement'], + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=mixed'], + ['--binlog-format=mixed'] + ], + + [ + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=mixed'], + ['--binlog-format=row', '--loose-skip-innodb', '--binlog-format=statement'], + ['--binlog-format=statement'] + ], + + [ + ['--binlog-format=mixed', '--loose-skip-innodb', '--binlog-format=row'], + ['--binlog-format=statement', '--loose-skip-innodb', '--binlog-format=row'], + [ ] + ], + + [ + ['--binlog-format=row'], + [ ], + ['--binlog-format=default'] + ], + + [ + [ ], + ['--binlog-format=row'], + ['--binlog-format=row'] + ], + + [ + [ ], + ['-O', 'max_binlog_size=1' ], + ['--max_binlog_size=1' ] + ], + + [ + ['-O', 'max_binlog_size=1' ], + ['-O', 'max_binlog_size=1' ], + [ ], + ], + + [ + ['-O', 'max_binlog_size=1' ], + [ ], + ['--max_binlog_size=default' ] + ], + + [ + [ ], + ['-O', 'max_binlog_size=1', '--binlog-format=row' ], + ['--max_binlog_size=1', '--binlog-format=row' ] + ], + [ + ['--binlog-format=statement' ], + ['-O', 'max_binlog_size=1', '--binlog-format=row' ], + ['--max_binlog_size=1', '--binlog-format=row'] + ], + + [ + [ '--binlog-format=statement' ], + ['-O', 'max_binlog_size=1', '--binlog-format=statement' ], + ['--max_binlog_size=1' ] + ], + + [ + [ '--binlog-format=statement' ], + ['-O', 'max_binlog_size=1', '--binlog-format=statement' ], + ['--max_binlog_size=1' ] + ], + + [ + [ '--binlog-format=statement' ], + ['--relay-log=/path/to/a/relay-log', '--binlog-format=row'], + ['--relay-log=/path/to/a/relay-log', '--binlog-format=row' ] + ], + + + [ + [ '--binlog-format=statement' ], + ['--relay-log=/path/to/a/relay-log', '-O', 'max_binlog_size=1'], + ['--max_binlog_size=1', '--relay-log=/path/to/a/relay-log', '--binlog-format=default' ] + ], + + [ + [ '--slow-query-log=0' ], + [ '--slow-query-log' ], + [ '--slow-query-log' ] + ], + + +); + + +my $test_no= 0; +foreach my $test (@tests){ + print "test", $test_no++, "\n"; + foreach my $opts (@$test){ + print My::Options::toStr("", @$opts); + } + my $from= $test->[0]; + my $to= $test->[1]; + my @result= My::Options::diff($from, $to); + ok(My::Options::same(\@result, $test->[2])); + if (!My::Options::same(\@result, $test->[2])){ + print "failed\n"; + print My::Options::toStr("result", @result); + print My::Options::toStr("expect", @{$test->[2]}); + } + print My::Options::toSQL(@result), "\n"; + print "\n"; +} diff --git a/mysql-test/lib/t/Platform.t b/mysql-test/lib/t/Platform.t new file mode 100644 index 00000000000..a8cb7751925 --- /dev/null +++ b/mysql-test/lib/t/Platform.t @@ -0,0 +1,18 @@ +# -*- cperl -*- +use Test::More qw(no_plan); +use strict; + +use_ok ("My::Platform"); +use My::Platform; + +use File::Temp qw / tempdir /; +my $dir = tempdir( CLEANUP => 1 ); + +print "Running on Windows\n" if (IS_WINDOWS); +print "Using ActiveState perl\n" if (IS_WIN32PERL); +print "Using cygwin perl\n" if (IS_CYGWIN); + +print "dir: '$dir'\n"; +print "native: '".native_path($dir)."'\n"; +print "mixed: '".mixed_path($dir)."'\n"; +print "posix: '".posix_path($dir)."'\n"; diff --git a/mysql-test/lib/t/SafeProcess.t b/mysql-test/lib/t/SafeProcess.t new file mode 100644 index 00000000000..d4a62ff8cca --- /dev/null +++ b/mysql-test/lib/t/SafeProcess.t @@ -0,0 +1,102 @@ +# -*- cperl -*- + +use strict; +use FindBin; +use IO::File; + +use Test::More qw(no_plan); +use_ok ("My::SafeProcess"); + + +my $perl_path= $^X; + +{ + # Test exit codes + my $count= 32; + my $ok_count= 0; + for my $code (0..$count-1) { + + my $args= [ "$FindBin::Bin/test_child.pl", "--exit-code=$code" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + output => "/dev/null", + error => "/dev/null", + ); + # Wait max 10 seconds for the process to finish + $ok_count++ if ($proc->wait_one(10) == 0 and + $proc->exit_status() == $code); + } + ok($count == $ok_count, "check exit_status, $ok_count"); +} + + +{ + # spawn a number of concurrent processes + my $count= 16; + my $ok_count= 0; + my %procs; + for my $code (0..$count-1) { + + my $args= [ "$FindBin::Bin/test_child.pl", "--exit-code=$code" ]; + $procs{$code}= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + output => "/dev/null", + error => "/dev/null", + ); + } + + for my $code (0..$count-1) { + $ok_count++ if ($procs{$code}->wait_one(10) == 0 and + $procs{$code}->exit_status() == $code); + } + ok($count == $ok_count, "concurrent, $ok_count"); +} + + +# +# Test stdout, stderr +# +{ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my $args= [ "$FindBin::Bin/test_child.pl" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + output => "$dir/output.txt", + error => "$dir/error.txt", + ); + + $proc->wait_one(2); # Wait max 2 seconds for the process to finish + + my $fh= IO::File->new("$dir/output.txt"); + my @text= <$fh>; + ok(grep(/Hello stdout/, @text), "check stdout"); + $fh= IO::File->new("$dir/error.txt"); + my @text= <$fh>; + ok(grep(/Hello stderr/, @text), "check stderr"); + + # To same file + $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + output => "$dir/output.txt", + error => "$dir/output.txt", + debug => 1, + ); + + $proc->wait_one(2); # Wait max 2 seconds for the process to finish + + my $fh= IO::File->new("$dir/output.txt"); + my @text= <$fh>; + ok((grep(/Hello stdout/, @text) and grep(/Hello stderr/, @text)), + "check stdout and stderr"); + +} diff --git a/mysql-test/lib/t/SafeProcessStress.pl b/mysql-test/lib/t/SafeProcessStress.pl new file mode 100755 index 00000000000..0f7a59d67f0 --- /dev/null +++ b/mysql-test/lib/t/SafeProcessStress.pl @@ -0,0 +1,149 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use FindBin; +use My::SafeProcess; + +# +# Test longterm running of SafeProcess +# + +my $perl_path= $^X; +my $verbose= 0; +my $loops= 100; + +print "kill one and wait for one\n"; +for (1...$loops){ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my @procs; + for (1..10){ + + my $args= [ "$FindBin::Bin/dummyd.pl", "--vardir=$dir" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + verbose => $verbose, + ); + push(@procs, $proc); + } + + foreach my $proc (@procs) { + $proc->kill(); + # dummyd will always be killed and thus + # exit_status should have been set to 1 + die "oops, exit_status: ", $proc->exit_status() + unless $proc->exit_status() == 1; + } + + print "=" x 60, "\n"; +} + + +print "With 1 second sleep in dummyd\n"; +for (1...$loops){ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my @procs; + for (1..10){ + + my $args= [ "$FindBin::Bin/dummyd.pl", + "--vardir=$dir", + "--sleep=1" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + verbose => $verbose, + ); + push(@procs, $proc); + } + + foreach my $proc (@procs) { + $proc->kill(); + } + + print "=" x 60, "\n"; +} + +print "kill all and wait for one\n"; +for (1...$loops){ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my @procs; + for (1..10){ + + my $args= [ "$FindBin::Bin/dummyd.pl", "--vardir=$dir" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + verbose => $verbose, + ); + push(@procs, $proc); + } + + foreach my $proc (@procs) { + $proc->start_kill(); + } + + foreach my $proc (@procs) { + $proc->wait_one(); + } + + print "=" x 60, "\n"; +} + +print "kill all using shutdown without callback\n"; +for (1...$loops){ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my @procs; + for (1..10){ + + my $args= [ "$FindBin::Bin/dummyd.pl", "--vardir=$dir" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + verbose => $verbose, + ); + push(@procs, $proc); + } + + My::SafeProcess::shutdown(2, @procs); + + print "=" x 60, "\n"; +} + +print "kill all using shutdown\n"; +for (1...$loops){ + use File::Temp qw / tempdir /; + my $dir = tempdir( CLEANUP => 1 ); + + my @procs; + for (1..10){ + + my $args= [ "$FindBin::Bin/dummyd.pl", "--vardir=$dir" ]; + my $proc= My::SafeProcess->new + ( + path => $perl_path, + args => \$args, + verbose => $verbose, + shutdown => sub { }, # Does nothing + ); + push(@procs, $proc); + } + + My::SafeProcess::shutdown(2, @procs); + + print "=" x 60, "\n"; +} + +exit(0); diff --git a/mysql-test/lib/t/copytree.t b/mysql-test/lib/t/copytree.t new file mode 100644 index 00000000000..15e4d1a7b1b --- /dev/null +++ b/mysql-test/lib/t/copytree.t @@ -0,0 +1,34 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; + +use My::File::Path; + +use Test::Simple tests => 7; +use File::Temp qw / tempdir /; +my $dir = tempdir( CLEANUP => 1 ); +my $testdir="$dir/test"; +my $test_todir="$dir/to"; + +my $subdir= "$testdir/test1/test2/test3"; + +# +# 1. Create, copy and remove a directory structure +# +mkpath($subdir); +ok( -d $subdir, "Check '$subdir' is created"); + +copytree($testdir, $test_todir); +ok( -d $test_todir, "Check '$test_todir' is created"); +ok( -d "$test_todir/test1", "Check 'test1' is created"); +ok( -d "$test_todir/test1/test2", "Check 'test2' is created"); +ok( -d "$test_todir/test1/test2/test3", "Check 'test3' is created"); + + +rmtree($testdir); +ok( ! -d $testdir, "Check '$testdir' is gone"); + +rmtree($test_todir); +ok( ! -d $test_todir, "Check '$test_todir' is gone"); + diff --git a/mysql-test/lib/t/dummyd.pl b/mysql-test/lib/t/dummyd.pl new file mode 100644 index 00000000000..07336e3c2d2 --- /dev/null +++ b/mysql-test/lib/t/dummyd.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use Getopt::Long; +use IO::File; + +my $vardir; +my $randie= 0; +my $sleep= 0; +GetOptions + ( + # Directory where to write files + 'vardir=s' => \$vardir, + 'die-randomly' => \$randie, + 'sleep=i' => \$sleep, + ); + +die("invalid vardir ") unless defined $vardir and -d $vardir; + +my $pid= $$; +while(1){ + for my $i (1..64){ + # Write to file + my $name= "$vardir/$pid.$i.tmp"; + my $F= IO::File->new($name, "w") + or warn "$$, Could not open $name: $!" and next; + print $F rand($.) for (1..1000); + $F->close(); + sleep($sleep); + die "ooops!" if $randie and rand() < 0.0001 + } +} + + +exit (0); + + diff --git a/mysql-test/lib/t/rmtree.t b/mysql-test/lib/t/rmtree.t new file mode 100644 index 00000000000..08c9077d001 --- /dev/null +++ b/mysql-test/lib/t/rmtree.t @@ -0,0 +1,52 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; + +use My::File::Path; + +use Test::Simple tests => 8; +use File::Temp qw / tempdir /; +my $dir = tempdir( CLEANUP => 1 ); +my $testdir="$dir/test"; + +my $subdir= "$testdir/test1/test2/test3"; + +# +# 1. Create and remove a directory structure +# +mkpath($subdir); +ok( -d $subdir, "Check '$subdir' is created"); + +rmtree($testdir); +ok( ! -d $testdir, "Check '$testdir' is gone"); + +# +# 2. Create and remove a directory structure +# where one directory is chmod to 0000 +# +mkpath($subdir); +ok( -d $subdir, "Check '$subdir' is created"); + +ok( chmod(0000, $subdir) == 1 , "Check one dir was chmoded"); + +rmtree($testdir); +ok( ! -d $testdir, "Check '$testdir' is gone"); + +# +# 3. Create and remove a directory structure +# where one file is chmod to 0000 +# +mkpath($subdir); +ok( -d $subdir, "Check '$subdir' is created"); + +my $testfile= "$subdir/test.file"; +open(F, ">", $testfile) or die; +print F "hello\n"; +close(F); + +ok( chmod(0000, $testfile) == 1 , "Check one file was chmoded"); + +rmtree($testdir); +ok( ! -d $testdir, "Check '$testdir' is gone"); + diff --git a/mysql-test/lib/t/testMyConfig.t b/mysql-test/lib/t/testMyConfig.t new file mode 100755 index 00000000000..da08cb8b4d1 --- /dev/null +++ b/mysql-test/lib/t/testMyConfig.t @@ -0,0 +1,131 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use warnings; +use File::Temp qw / tempdir /; +my $dir = tempdir( CLEANUP => 1 ); + +use Test::More qw(no_plan); + +BEGIN { use_ok ( "My::Config" ) }; + +my $test_cnf= "$dir/test.cnf"; + +# Write test config file +open(OUT, ">", $test_cnf) or die; +print $test_cnf, "\n"; + +print OUT <new($test_cnf); +isa_ok( $config, "My::Config" ); + +print $config; + +ok ( $config->group("mysqld_2"), "group mysqld_2 exists"); +ok ( $config->group("mysqld_1"), "group mysqld_1 exists"); +ok ( $config->group("mysqld.9"), "group mysqld.9 exists"); +ok ( $config->group("mysqld.9")->suffix() eq ".9", "group mysqld.9 has suffix .9"); + +ok ( $config->group("mysqld"), "group mysqld exists"); +ok ( $config->group("client"), "group client exists"); +ok ( !$config->group("mysqld_3"), "group mysqld_3 does not exist"); + +ok ( $config->options_in_group("mysqld") == 4, "options in [mysqld] is 4"); +ok ( $config->options_in_group("nonexist") == 0, "options in [nonexist] is 0"); + +{ + my @groups= $config->groups(); + ok(@groups == 5, "5 groups"); + my $idx= 0; + foreach my $name ('mysqld', 'mysqld_1', 'mysqld_2', 'mysqld.9', 'client') { + is($groups[$idx++]->name(), $name, "checking groups $idx"); + } +} + +{ + my @groups= $config->like("mysqld"); + ok(@groups == 4, "4 groups like mysqld"); + my $idx= 0; + foreach my $name ('mysqld', 'mysqld_1', 'mysqld_2', 'mysqld.9') { + is($groups[$idx++]->name(), $name, "checking like(\"mysqld\") $idx"); + } +} + +{ + my @groups= $config->like("not"); + ok(@groups == 0, "checking like(\"not\")"); +} + +is($config->first_like("mysqld_")->name(), "mysqld_1", "first_like"); + +is( $config->value('mysqld', 'option4'), undef, + "mysqld_option4 exists, does not have a value"); + +ok( $config->exists('mysqld', 'option4'), + "mysqld_option4 exists"); +ok( $config->exists('mysqld', 'option2'), + "mysqld_option2 exists"); +ok( !$config->exists('mysqld', 'option5'), + "mysqld_option5 does not exists"); + +# Save the config to file +my $test2_cnf= "$dir/test2.cnf"; +$config->save($test2_cnf); + +# read it back and check it's the same +my $config2= My::Config->new($test2_cnf); +isa_ok( $config2, "My::Config" ); +is_deeply( \$config, \$config2, "test.cnf is equal to test2.cnf"); + + +my $test_include_cnf= "$dir/test_include.cnf"; +# Write test config file that includes test.cnf +open(OUT, ">", $test_include_cnf) or die; + +print OUT <new($test_include_cnf); +isa_ok( $config3, "My::Config" ); +print $config3; +is( $config3->value('mysqld', 'basedir'), 'anotherbasedir', + "mysqld_basedir has been overriden by value in test_include.cnf"); + +is( $config3->value('mysqld', 'option1'), 'values3', + "mysqld_option1 has been overriden by value in test_include.cnf"); + +is( $config3->value('mysqld', 'option2'), 'value4', + "mysqld_option2 is from included file"); + +is( $config3->value('client', 'socket'), 'asocketpath', + "client.socket is from included file"); + +is( $config3->value('mysqld', 'option4'), undef, + "mysqld_option4 exists, does not have a value"); + +print "$config3\n"; + diff --git a/mysql-test/lib/t/testMyConfigFactory.t b/mysql-test/lib/t/testMyConfigFactory.t new file mode 100755 index 00000000000..16fdd9db539 --- /dev/null +++ b/mysql-test/lib/t/testMyConfigFactory.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use warnings; + +use File::Temp qw / tempdir /; +my $dir = tempdir( CLEANUP => 1 ); + +use Test::More qw(no_plan); + +BEGIN { use_ok ( "My::ConfigFactory" ) }; + +my $gen1_cnf= "$dir/gen1.cnf"; +open(OUT, ">", $gen1_cnf) or die; + +print OUT <new_config +( + { + basedir => $basedir, + template_path => $gen1_cnf, + vardir => "/path/to/var", + baseport => 10987, + #hosts => [ 'host1', 'host2' ], + } +); + +print $config; + +ok ( $config->group("mysqld.master"), "group mysqld.master exists"); +ok ( $config->group("mysqld.1"), "group mysqld.1 exists"); +ok ( $config->group("client"), "group client exists"); +ok ( !$config->group("mysqld.3"), "group mysqld.3 does not exist"); + +ok ( $config->first_like("mysqld"), "group like 'mysqld' exists"); + +is( $config->value('mysqld.1', '#host'), 'localhost', + "mysqld.1.#host has been generated"); + +is( $config->value('client', 'host'), 'localhost', + "client.host has been generated"); + +is( $config->value('client', 'host'), + $config->value('mysqld.master', '#host'), + "client.host is same as mysqld.master.host"); + +ok ( $config->value("mysqld.1", 'character-sets-dir') =~ /$basedir.*charsets$/, + "'character-sets-dir' generated"); + +ok ( $config->value("mysqld.1", 'language') =~ /$basedir.*english$/, + "'language' generated"); + +ok ( $config->value("ENV", 'MASTER_MY_PORT') =~ /\d/, + "'language' generated"); + +my $gen2_cnf= "$dir/gen2.cnf"; +open(OUT, ">", $gen2_cnf) or die; + +print OUT <new_config +( + { + basedir => $basedir, + template_path => $gen2_cnf, + vardir => "/path/to/var", + baseport => 10987, + #hosts => [ 'host1', 'host2' ], + } +); + +print $config2; + +ok ( $config2->first_like("mysqld"), "group like 'mysqld' exists"); diff --git a/mysql-test/lib/t/test_child.pl b/mysql-test/lib/t/test_child.pl new file mode 100755 index 00000000000..99f4e68003d --- /dev/null +++ b/mysql-test/lib/t/test_child.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# -*- cperl -*- + +use strict; +use Getopt::Long; + +my $opt_exit_code= 0; + +GetOptions + ( + # Exit with the specified exit code + 'exit-code=i' => \$opt_exit_code + ); + + +print "Hello stdout\n"; +print STDERR "Hello stderr\n"; + +exit ($opt_exit_code); + + diff --git a/mysql-test/lib/v1/My/Config.pm b/mysql-test/lib/v1/My/Config.pm new file mode 100644 index 00000000000..5491e341ddc --- /dev/null +++ b/mysql-test/lib/v1/My/Config.pm @@ -0,0 +1,422 @@ +# -*- cperl -*- + +package My::Config::Option; + +use strict; +use warnings; + + +sub new { + my ($class, $option_name, $option_value)= @_; + my $self= bless { name => $option_name, + value => $option_value + }, $class; + return $self; +} + + +sub name { + my ($self)= @_; + return $self->{name}; +} + + +sub value { + my ($self)= @_; + return $self->{value}; +} + + +package My::Config::Group; + +use strict; +use warnings; + + +sub new { + my ($class, $group_name)= @_; + my $self= bless { name => $group_name, + options => [], + options_by_name => {}, + }, $class; + return $self; +} + + +sub insert { + my ($self, $option_name, $value, $if_not_exist)= @_; + my $option= $self->option($option_name); + if (defined($option) and !$if_not_exist) { + $option->{value}= $value; + } + else { + my $option= My::Config::Option->new($option_name, $value); + # Insert option in list + push(@{$self->{options}}, $option); + # Insert option in hash + $self->{options_by_name}->{$option_name}= $option; + } + return $option; +} + +sub remove { + my ($self, $option_name)= @_; + + # Check that option exists + my $option= $self->option($option_name); + + return undef unless defined $option; + + # Remove from the hash + delete($self->{options_by_name}->{$option_name}) or die; + + # Remove from the array + @{$self->{options}}= grep { $_->name ne $option_name } @{$self->{options}}; + + return $option; +} + + +sub options { + my ($self)= @_; + return @{$self->{options}}; +} + + +sub name { + my ($self)= @_; + return $self->{name}; +} + + +# +# Return a specific option in the group +# +sub option { + my ($self, $option_name)= @_; + + return $self->{options_by_name}->{$option_name}; +} + + +# +# Return a specific value for an option in the group +# +sub value { + my ($self, $option_name)= @_; + my $option= $self->option($option_name); + + die "No option named '$option_name' in this group" + if ! defined($option); + + return $option->value(); +} + + +package My::Config; + +use strict; +use warnings; +use IO::File; +use File::Basename; + +# +# Constructor for My::Config +# - represents a my.cnf config file +# +# Array of arrays +# +sub new { + my ($class, $path)= @_; + my $group_name= undef; + + my $self= bless { groups => [] }, $class; + my $F= IO::File->new($path, "<") + or die "Could not open '$path': $!"; + + while ( my $line= <$F> ) { + chomp($line); + + # [group] + if ( $line =~ /\[(.*)\]/ ) { + # New group found + $group_name= $1; + #print "group: $group_name\n"; + + $self->insert($group_name, undef, undef); + } + + # Magic #! comments + elsif ( $line =~ /^#\!/) { + my $magic= $line; + die "Found magic comment '$magic' outside of group" + unless $group_name; + + #print "$magic\n"; + $self->insert($group_name, $magic, undef); + } + + # Comments + elsif ( $line =~ /^#/ || $line =~ /^;/) { + # Skip comment + next; + } + + # Empty lines + elsif ( $line =~ /^$/ ) { + # Skip empty lines + next; + } + + # !include + elsif ( $line =~ /^\!include\s*(.*?)\s*$/ ) { + my $include_file_name= dirname($path)."/".$1; + # Check that the file exists + die "The include file '$include_file_name' does not exist" + unless -f $include_file_name; + + $self->append(My::Config->new($include_file_name)); + } + + #